summaryrefslogtreecommitdiff
path: root/support/hook-pre-commit-pkg
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/hook-pre-commit-pkg
Initial commit
Diffstat (limited to 'support/hook-pre-commit-pkg')
-rw-r--r--support/hook-pre-commit-pkg/README39
-rwxr-xr-xsupport/hook-pre-commit-pkg/pre-commit-latex255
2 files changed, 294 insertions, 0 deletions
diff --git a/support/hook-pre-commit-pkg/README b/support/hook-pre-commit-pkg/README
new file mode 100644
index 0000000000..8d8cf08e65
--- /dev/null
+++ b/support/hook-pre-commit-pkg/README
@@ -0,0 +1,39 @@
+Pre-commit hook for LaTeX package developers
+
+
+=== What it is ?
+A pre-commit hook to check basic LaTeX syntax for developer of package.
+
+==== How to install
+Copy pre-commit in the .git/hooks file
+Add execution right (chmod +x)
+Enjoy !
+
+====Checked files
+ - .sty
+ - .dtx
+ - .bbx
+ - .cbx
+ - .lbx
+
+====What are checked
+Only for new line, these properties are checked:
+ - All line must finish by a %, without space before.
+ Empty line are allowed, but not line with blank space.
+ - \begin{macro} and \end{macro} must be paired.
+ - \begin{macrocode} and \end{macrocode} must be paired.
+ - \begin{macro} must have a second argument.
+ - 1 space must be printed between % and \begin{macro} of \end{macro}. % Must be the first line character.
+ - 4 spaces must be printed between % and \begin{macrocode} or \end{macrocode}.
+ - \cs argument must NOT start by an \
+
+=== Licence and copyright
+Maïeul Rouquette 2014-....
+v 1.1.2
+Licence GPl3 https://www.gnu.org/licenses/gpl-3.0.txt
+
+=== Help and github repository
+https://github.com/maieul/git-hooks
+Open an issue for any needs.
+
+
diff --git a/support/hook-pre-commit-pkg/pre-commit-latex b/support/hook-pre-commit-pkg/pre-commit-latex
new file mode 100755
index 0000000000..2e77321c2a
--- /dev/null
+++ b/support/hook-pre-commit-pkg/pre-commit-latex
@@ -0,0 +1,255 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""Pre-commit hook for LaTeX package developers
+
+
+=== What it is ?
+A pre-commit hook to check basic LaTeX syntax for developer of package.
+
+==== How to install
+Copy pre-commit in the .git/hooks file
+Add execution right (chmod +x)
+Enjoy !
+
+====Checked files
+ - .sty
+ - .dtx
+ - .bbx
+ - .cbx
+ - .lbx
+
+====What are checked
+Only for new line, these properties are checked:
+ - All line must finish by a %, without space before.
+ Empty line are allowed, but not line with blank space.
+ - \begin{macro} and \end{macro} must be paired.
+ - \begin{macrocode} and \end{macrocode} must be paired.
+ - \begin{macro} must have a second argument.
+ - 1 space must be printed between % and \begin{macro} of \end{macro}. % Must be the first line character.
+ - 4 spaces must be printed between % and \begin{macrocode} or \end{macrocode}.
+ - \cs argument must NOT start by an \
+
+=== Licence and copyright
+Maïeul Rouquette 2014-....
+v 1.1.2
+Licence GPl3 https://www.gnu.org/licenses/gpl-3.0.txt
+
+=== Help and github repository
+https://github.com/maieul/git-hooks
+Open an issue for any needs.
+
+
+"""
+
+import os
+import os.path
+import re
+import sys
+
+# Setting
+to_be_checked = ["dtx","sty","bbx","cbx","lbx"]
+commands = {
+ "end_line_percent_signe":"Spurious space",
+ "cs_cmd":"Don't use \ inside \cs command",
+ "macro_env":{
+ "indent":"bad indent before \\begin{macro} or \end{macro}",
+ "pairing":"\\begin{macro} without \end{macro} (or vice-versa)",
+ "#2":"\\begin{macro} without macro name"
+ },
+ "macrocode_env":{
+ "indent":"bad indent before \\begin{macrocode} or \end{macrocode}",
+ "pairing":"\\begin{macrocode} without \end{macrocode} (or vice-versa)",
+ }
+ }
+# General code
+def change_line_number(line_number,line):
+ """Change line number, depending of the current line"""
+ if line[0:2] == "@@": # line number
+ line_number = re.findall("\+(\d+),?",line)
+ line_number = int(line_number[0]) -1
+ elif not line[0] == "-" and not line[0:1] == "\\":
+ line_number = line_number + 1
+ return line_number
+
+lines_results ={}#global, bad, I have to find an other way. Key = filename_linenumber. content : see check_lines
+def check_lines():
+ """ Check all modified lines"""
+ diff = os.popen("git diff --cached")
+ line_number = 0
+ file_name = ""
+ for line in diff:
+ line_number = change_line_number(line_number,line)
+ # what is the file_name?
+ if "+++ b/" in line:
+ file_name = line[6:-1]
+ extension = os.path.splitext(file_name)[1][1:]
+ lines_results[file_name] ={}
+ elif "++ /dev/null" in line:
+ extension = ""
+ file_name = ""
+ elif line[0] == "+" and extension in to_be_checked:
+ check = check_line(line,line_number,file_name)
+ lines_results[file_name][line_number]={
+ "content":line,
+ "results":check,
+ "file_name":file_name
+ }
+ return lines_results
+
+def check_line(line,line_number,file_name):
+ """Check individual added line"""
+ results = {}
+
+ for cmd in commands: #Use all commands, keep results
+ f = getattr(sys.modules[__name__],"check_"+cmd)
+ check = f(line,line_number,file_name)
+ if check==False:
+ results[cmd] = True
+ elif isinstance(check,list) and not check==[]:
+ results[cmd] = check
+ return results
+# Tests
+
+begin_macro ={} # keys file name, content = list of current \begin{macro} not closed.
+def check_macro_env(line,line_number,file_name):
+ """Check macro environnment:
+ a) only one space between % and \\begin{macro}
+ b) don't forget arg two
+ c) pairing setting : each \begin{macro} must have \end{macro}
+ """
+ line = line[1:]#delete the inital +
+ begin = "\\begin{macro}"
+ end = "\\end{macro}"
+ # only for line concerning macro env
+ errors = []
+ if begin in line:
+
+ # Check only one space after %
+ normal_indent = "% \\begin{macro}"
+ if line[:len(normal_indent)] != normal_indent:
+ errors.append("indent")
+
+ #Check there is second argument
+ if line.count("{") != 2 or line.count("}") != 2:
+ errors.append("#2")
+
+ # we think its not paired before anyone paired
+ errors.append("pairing")
+
+ if file_name in begin_macro:
+ begin_macro[file_name].append(line_number)
+ else:
+ begin_macro[file_name]=[line_number]
+
+ return errors
+ elif end in line:
+
+ #check only
+ normal_indent = "% \end{macro}"
+ if line[:len(normal_indent)] != normal_indent:
+ errors.append("indent")
+
+ #correct pairing
+ try:
+ begin_pairing = begin_macro[file_name].pop()#get the line of the \begin{macro}
+ lines_results[file_name][begin_pairing]["results"]["macro_env"].remove("pairing")
+ if lines_results[file_name][begin_pairing]["results"]["macro_env"] ==[]:
+ del(lines_results[file_name][begin_pairing]["results"]["macro_env"])
+ except:
+ errors.append("pairing")
+ return errors
+ else:
+ return True
+
+begin_macrocode ={} # keys file name, content = list of current \begin{macrocode} not closed.
+def check_macrocode_env(line,line_number,file_name):
+ """Check macrocode environnment:
+ a) only one space between % and \\begin{macrocode}
+ b) don't forget arg two
+ c) pairing setting : each \begin{macrocode} must have \end{macrocode}
+ """
+ line = line[1:]#delete the inital +
+ begin = "\\begin{macrocode}"
+ end = "\\end{macrocode}"
+ # only for line concerning macrocode env
+ errors = []
+ if begin in line:
+
+ # Check only one space after %
+ normal_indent = "% \\begin{macrocode}"
+ if line[:len(normal_indent)] != normal_indent:
+ errors.append("indent")
+
+ # we think its not paired before anyone paired
+ errors.append("pairing")
+ if file_name in begin_macrocode:
+ begin_macrocode[file_name].append(line_number)
+ else:
+ begin_macrocode[file_name]=[line_number]
+
+ return errors
+ elif end in line:
+
+ #check only
+ normal_indent = "% \end{macrocode}"
+ if line[:len(normal_indent)] != normal_indent:
+ errors.append("indent")
+
+ #correct pairing
+ try:
+ begin_pairing = begin_macrocode[file_name].pop()#get the line of the \begin{macrocode}
+ lines_results[file_name][begin_pairing]["results"]["macrocode_env"].remove("pairing")
+ if lines_results[file_name][begin_pairing]["results"]["macrocode_env"] ==[]:
+ del(lines_results[file_name][begin_pairing]["results"]["macrocode_env"])
+ except:
+ errors.append("pairing")
+ return errors
+ else:
+ return True
+
+
+def check_cs_cmd(line,line_number,file_name):
+ """Check we don't start \cs argument by a \\"""
+ return "\cs{\\" not in line
+
+def check_end_line_percent_signe(line,line_number,file_name):
+ """"Check line finish by %"""
+
+ line = line.replace("\%","") # Don't look for protected %
+
+ if line == "+\n": # Allow empty line
+ return True
+
+ elif "%" not in line: # If not % -> problem
+ return False
+
+ elif re.search ("\s+%",line): # Spaces before % -> problem
+ return False
+ else:
+ return True
+
+# Main function
+def __main__():
+ """Main function: calls the check to bad line, print them if need, and return exit if error"""
+ lines_results = check_lines()
+ exit = 0 #Set to 1 if we have ONE bad line.
+ for file_name in lines_results:
+ print (file_name)
+ for line_number in sorted(lines_results[file_name].keys()):
+ line = lines_results[file_name][line_number]
+ if line["results"]!={}: #there is some error
+ exit=1
+ print ("\x1b[31m\tl."+ str(line_number) + ": " + line["content"][:-1])
+
+ results = line["results"]
+ for error in line["results"]:
+ if results[error] == True:
+ print ("\t\t " + commands[error])
+ else:
+ for e in results[error]:
+ print ("\t\t " + commands[error][e])
+ print("\x1b[0m")
+ sys.exit(exit)
+
+
+__main__()