Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6e0f5e0

Browse files
committed
* Added file globbing to make it easier to check many LaTeX files.
* Delimiter mismatch now prints a warning instead of raising an exception. * Offer style warnings for use of e.g. and i.e. * Bypass false positive warnings for forward slashes in urls and in /rfc822. * Put non-LaTex delimiter matching first to make -d option more reliable.
1 parent fa19f7c commit 6e0f5e0

1 file changed

Lines changed: 60 additions & 39 deletions

File tree

Tools/scripts/texcheck.py

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,29 @@
55
66
Designed to catch common markup errors including:
77
* Unbalanced or mismatched parenthesis, brackets, and braces.
8-
* Unbalanced of mismatched \begin and \end blocks.
8+
* Unbalanced or mismatched \\begin and \\end blocks.
99
* Misspelled or invalid LaTeX commands.
1010
* Use of forward slashes instead of backslashes for commands.
11-
* Table line size mismatches (only \lineii used in a tableii).
11+
* Table line size mismatches.
1212
13-
Command line usage:
14-
python texcheck.py [-h] [-k keyword] foobar.tex
13+
Sample command line usage:
14+
python texcheck.py -k chapterheading -m lib/librandomtex *.tex
1515
1616
Options:
1717
-m Munge parenthesis and brackets. [0,n) would normally mismatch.
1818
-k keyword: Keyword is a valid LaTeX command. Do not include the backslash.
1919
-d: Delimiter check only (useful for non-LaTeX files).
2020
-h: Help
2121
-s lineno: Start at lineno (useful for skipping complex sections).
22-
-v: Verbose. Shows current delimiter and unclosed delimiters.
22+
-v: Verbose. Trace the matching of //begin and //end blocks.
2323
"""
2424

2525
import re
2626
import sets
2727
import sys
2828
import getopt
2929
from itertools import izip, count, islice
30+
import glob
3031

3132
cmdstr = r"""
3233
\section \module \declaremodule \modulesynopsis \moduleauthor
@@ -63,19 +64,19 @@ def matchclose(c_lineno, c_symbol, openers, pairmap):
6364
try:
6465
o_lineno, o_symbol = openers.pop()
6566
except IndexError:
66-
msg = "Delimiter mismatch. On line %d, encountered closing '%s' without corresponding open" % (c_lineno, c_symbol)
67-
raise Exception, msg
67+
print "\nDelimiter mismatch. On line %d, encountered closing '%s' without corresponding open" % (c_lineno, c_symbol)
68+
return
6869
if o_symbol in pairmap.get(c_symbol, [c_symbol]): return
69-
msg = "Opener '%s' on line %d was not closed before encountering '%s' on line %d" % (o_symbol, o_lineno, c_symbol, c_lineno)
70-
raise Exception, msg
70+
print "\nOpener '%s' on line %d was not closed before encountering '%s' on line %d" % (o_symbol, o_lineno, c_symbol, c_lineno)
71+
return
7172

7273
def checkit(source, opts, morecmds=[]):
7374
"""Check the LaTeX formatting in a sequence of lines.
7475
7576
Opts is a mapping of options to option values if any:
7677
-m munge parenthesis and brackets
7778
-d delimiters only checking
78-
-v verbose listing of delimiters
79+
-v verbose trace of delimiter matching
7980
-s lineno: linenumber to start scan (default is 1).
8081
8182
Morecmds is a sequence of LaTeX commands (without backslashes) that
@@ -93,7 +94,7 @@ def checkit(source, opts, morecmds=[]):
9394
pairmap = {']':'[(', ')':'(['} # Munged openers
9495
else:
9596
pairmap = {']':'[', ')':'('} # Normal opener for a given closer
96-
openpunct = sets.Set('([') # Set of valid openers
97+
openpunct = sets.Set('([') # Set of valid openers
9798

9899
delimiters = re.compile(r'\\(begin|end){([_a-zA-Z]+)}|([()\[\]])')
99100
braces = re.compile(r'({)|(})')
@@ -113,24 +114,7 @@ def checkit(source, opts, morecmds=[]):
113114
for lineno, line in izip(count(startline), islice(source, startline-1, None)):
114115
line = line.rstrip()
115116

116-
if '/' in line and '-d' not in opts:
117-
# Warn whenever forward slashes encountered with a LaTeX command
118-
for cmd in falsetexcmd.findall(line):
119-
if '\\' + cmd in validcmds:
120-
print 'Warning, forward slash used on line %d with cmd: /%s' % (lineno, cmd)
121-
122-
if '-d' not in opts:
123-
# Validate commands
124-
nc = line.find(r'\newcommand')
125-
if nc != -1:
126-
start = line.find('{', nc)
127-
end = line.find('}', start)
128-
validcmds.add(line[start+1:end])
129-
for cmd in texcmd.findall(line):
130-
if cmd not in validcmds:
131-
print r'Warning, unknown tex cmd on line %d: \%s' % (lineno, cmd)
132-
133-
# Check balancing of open/close parenthesis and brackets
117+
# Check balancing of open/close parenthesis, brackets, and begin/end blocks
134118
for begend, name, punct in delimiters.findall(line):
135119
if '-v' in opts:
136120
print lineno, '|', begend, name, punct,
@@ -154,8 +138,27 @@ def checkit(source, opts, morecmds=[]):
154138
bracestack.pop()
155139
except IndexError:
156140
print r'Warning, unmatched } on line %s.' % (lineno,)
157-
if '-v' in opts:
158-
print ' --> ', bracestack
141+
142+
# Optionally, skip LaTeX specific checks
143+
if '-d' in opts:
144+
continue
145+
146+
# Warn whenever forward slashes encountered with a LaTeX command
147+
for cmd in falsetexcmd.findall(line):
148+
if '822' in line or '.html' in line:
149+
continue # Ignore false positives for urls and for /rfc822
150+
if '\\' + cmd in validcmds:
151+
print 'Warning, forward slash used on line %d with cmd: /%s' % (lineno, cmd)
152+
153+
# Validate commands
154+
nc = line.find(r'\newcommand')
155+
if nc != -1:
156+
start = line.find('{', nc)
157+
end = line.find('}', start)
158+
validcmds.add(line[start+1:end])
159+
for cmd in texcmd.findall(line):
160+
if cmd not in validcmds:
161+
print r'Warning, unknown tex cmd on line %d: \%s' % (lineno, cmd)
159162

160163
# Check table levels (make sure lineii only inside tableii)
161164
m = tablestart.search(line)
@@ -168,6 +171,11 @@ def checkit(source, opts, morecmds=[]):
168171
if tableend.search(line):
169172
tablelevel = ''
170173

174+
# Style guide warnings
175+
if 'e.g.' in line or 'i.e.' in line:
176+
print r'Style warning, avoid use of i.e or e.g. on line %d' % (lineno,)
177+
178+
171179
lastline = lineno
172180
for lineno, symbol in openers:
173181
print "Unmatched open delimiter '%s' on line %d" % (symbol, lineno)
@@ -189,15 +197,28 @@ def main(args=None):
189197
print 'Please specify a file to be checked'
190198
return 1
191199

192-
morecmds = [v for k,v in optitems if k=='-k']
193-
194-
try:
195-
f = open(arglist[0])
196-
except IOError:
197-
print 'Cannot open file %s.' % arglist[0]
198-
return 2
200+
for i, filespec in enumerate(arglist):
201+
if '*' in filespec or '?' in filespec:
202+
arglist[i:i+1] = glob.glob(filespec)
199203

200-
return(checkit(f, opts, morecmds))
204+
morecmds = [v for k,v in optitems if k=='-k']
205+
err = []
206+
207+
for filename in arglist:
208+
print '=' * 30
209+
print "Checking", filename
210+
try:
211+
f = open(filename)
212+
except IOError:
213+
print 'Cannot open file %s.' % arglist[0]
214+
return 2
215+
216+
try:
217+
err.append(checkit(f, opts, morecmds))
218+
finally:
219+
f.close()
220+
221+
return max(err)
201222

202223
if __name__ == '__main__':
203224
sys.exit(main())

0 commit comments

Comments
 (0)