diff options
Diffstat (limited to 'Master/texmf-dist/doc/support/hook-pre-commit-pkg')
-rw-r--r-- | Master/texmf-dist/doc/support/hook-pre-commit-pkg/README | 39 | ||||
-rwxr-xr-x | Master/texmf-dist/doc/support/hook-pre-commit-pkg/pre-commit-latex | 257 |
2 files changed, 296 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/support/hook-pre-commit-pkg/README b/Master/texmf-dist/doc/support/hook-pre-commit-pkg/README new file mode 100644 index 00000000000..ab146317b61 --- /dev/null +++ b/Master/texmf-dist/doc/support/hook-pre-commit-pkg/README @@ -0,0 +1,39 @@ +Pre-commit hook for LaTeX package developpers + + +=== What it is ? +A pre-commit hook to check basic LaTeX syntaxe for develloper 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.0 +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/Master/texmf-dist/doc/support/hook-pre-commit-pkg/pre-commit-latex b/Master/texmf-dist/doc/support/hook-pre-commit-pkg/pre-commit-latex new file mode 100755 index 00000000000..70e8b14e80d --- /dev/null +++ b/Master/texmf-dist/doc/support/hook-pre-commit-pkg/pre-commit-latex @@ -0,0 +1,257 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Pre-commit hook for LaTeX package developpers + + +=== What it is ? +A pre-commit hook to check basic LaTeX syntaxe for develloper 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.0 +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:] + 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+"_"+str(line_number)]={ + "line_number":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(str(line_number)) + else: + begin_macro[file_name]=[str(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(str(line_number)) + else: + begin_macrocode[file_name]=[str(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. + seen_file_names=[] + for line_result in lines_results: + line= lines_results[line_result] + if line["results"]!={}: #there is some error + exit=1 + if line["file_name"] not in seen_file_names: + seen_file_names.append(line["file_name"]) + print (line["file_name"]) + print ("\x1b[31m\tl."+ str(line["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__() |