rev |
line source |
bos@133
|
1 #!/usr/bin/env python
|
bos@133
|
2
|
bos@133
|
3 import getopt
|
bos@133
|
4 import itertools
|
bos@133
|
5 import os
|
bos@133
|
6 import re
|
bos@133
|
7 import sys
|
bos@133
|
8
|
bos@133
|
9 def usage(exitcode):
|
bos@133
|
10 print >> sys.stderr, ('usage: %s [-H|--hidden] hg_repo' %
|
bos@133
|
11 os.path.basename(sys.argv[0]))
|
bos@133
|
12 sys.exit(exitcode)
|
bos@133
|
13
|
bos@133
|
14 try:
|
bos@133
|
15 opts, args = getopt.getopt(sys.argv[1:], 'AHh?', ['all', 'help', 'hidden'])
|
bos@133
|
16 opt_all = False
|
bos@133
|
17 opt_hidden = False
|
bos@133
|
18 for o, a in opts:
|
bos@133
|
19 if o in ('-h', '-?', '--help'):
|
bos@133
|
20 usage(0)
|
bos@133
|
21 if o in ('-A', '--all'):
|
bos@133
|
22 opt_all = True
|
bos@133
|
23 if o in ('-H', '--hidden'):
|
bos@133
|
24 opt_hidden = True
|
bos@133
|
25 except getopt.GetoptError, err:
|
bos@133
|
26 print >> sys.stderr, 'error:', err
|
bos@133
|
27 usage(1)
|
bos@133
|
28
|
bos@133
|
29 try:
|
bos@133
|
30 hg_repo, ltx_file = args
|
bos@133
|
31 except ValueError:
|
bos@133
|
32 usage(1)
|
bos@133
|
33
|
bos@133
|
34 if not os.path.isfile(os.path.join(hg_repo, 'mercurial', 'commands.py')):
|
bos@133
|
35 print >> sys.stderr, ('error: %r does not contain mercurial code' %
|
bos@133
|
36 hg_repo)
|
bos@133
|
37 sys.exit(1)
|
bos@133
|
38
|
bos@133
|
39 sys.path.insert(0, hg_repo)
|
bos@133
|
40
|
bos@133
|
41 from mercurial import commands
|
bos@133
|
42
|
bos@133
|
43 def get_commands():
|
bos@133
|
44 seen = {}
|
bos@133
|
45 for name, info in sorted(commands.table.iteritems()):
|
bos@133
|
46 aliases = name.split('|', 1)
|
bos@133
|
47 name = aliases.pop(0).lstrip('^')
|
bos@133
|
48 function, options, synopsis = info
|
bos@133
|
49 seen[name] = {}
|
bos@133
|
50 for shortopt, longopt, arg, desc in options:
|
bos@133
|
51 seen[name][longopt] = shortopt
|
bos@133
|
52 return seen
|
bos@133
|
53
|
bos@133
|
54 def cmd_filter((name, aliases, options)):
|
bos@133
|
55 if opt_all:
|
bos@133
|
56 return True
|
bos@133
|
57 if opt_hidden:
|
bos@133
|
58 return name.startswith('debug')
|
bos@133
|
59 return not name.startswith('debug')
|
bos@133
|
60
|
bos@133
|
61 def scan(ltx_file):
|
bos@133
|
62 cmdref_re = re.compile(r'^\\cmdref{(?P<cmd>\w+)}')
|
bos@133
|
63 optref_re = re.compile(r'^\\l?optref{(?P<cmd>\w+)}'
|
bos@133
|
64 r'(?:{(?P<short>[^}])})?'
|
bos@133
|
65 r'{(?P<long>[^}]+)}')
|
bos@133
|
66
|
bos@133
|
67 seen = {}
|
bos@133
|
68 locs = {}
|
bos@133
|
69 for lnum, line in enumerate(open(ltx_file)):
|
bos@133
|
70 m = cmdref_re.match(line)
|
bos@133
|
71 if m:
|
bos@133
|
72 d = m.groupdict()
|
bos@133
|
73 cmd = d['cmd']
|
bos@133
|
74 seen[cmd] = {}
|
bos@133
|
75 locs[cmd] = lnum + 1
|
bos@133
|
76 continue
|
bos@133
|
77 m = optref_re.match(line)
|
bos@133
|
78 if m:
|
bos@133
|
79 d = m.groupdict()
|
bos@133
|
80 seen[d['cmd']][d['long']] = d['short']
|
bos@133
|
81 continue
|
bos@133
|
82 return seen, locs
|
bos@133
|
83
|
bos@133
|
84 documented, locs = scan(ltx_file)
|
bos@133
|
85 known = get_commands()
|
bos@133
|
86
|
bos@133
|
87 doc_set = set(documented)
|
bos@133
|
88 known_set = set(known)
|
bos@133
|
89
|
bos@133
|
90 errors = 0
|
bos@133
|
91
|
bos@133
|
92 for nonexistent in sorted(doc_set.difference(known_set)):
|
bos@133
|
93 print >> sys.stderr, ('%s:%d: %r command does not exist' %
|
bos@133
|
94 (ltx_file, locs[nonexistent], nonexistent))
|
bos@133
|
95 errors += 1
|
bos@133
|
96
|
bos@133
|
97 def optcmp(a, b):
|
bos@133
|
98 la, sa = a
|
bos@133
|
99 lb, sb = b
|
bos@133
|
100 sc = cmp(sa, sb)
|
bos@133
|
101 if sc:
|
bos@133
|
102 return sc
|
bos@133
|
103 return cmp(la, lb)
|
bos@133
|
104
|
bos@133
|
105 for cmd in doc_set.intersection(known_set):
|
bos@133
|
106 doc_opts = documented[cmd]
|
bos@133
|
107 known_opts = known[cmd]
|
bos@133
|
108
|
bos@133
|
109 do_set = set(doc_opts)
|
bos@133
|
110 ko_set = set(known_opts)
|
bos@133
|
111
|
bos@133
|
112 for nonexistent in sorted(do_set.difference(ko_set)):
|
bos@133
|
113 print >> sys.stderr, ('%s:%d: %r option to %r command does not exist' %
|
bos@133
|
114 (ltx_file, locs[cmd], nonexistent, cmd))
|
bos@133
|
115 errors += 1
|
bos@133
|
116
|
bos@133
|
117 def mycmp(la, lb):
|
bos@133
|
118 sa = known_opts[la]
|
bos@133
|
119 sb = known_opts[lb]
|
bos@133
|
120 return optcmp((la, sa), (lb, sb))
|
bos@133
|
121
|
bos@133
|
122 for undocumented in sorted(ko_set.difference(do_set), cmp=mycmp):
|
bos@133
|
123 print >> sys.stderr, ('%s:%d: %r option to %r command not documented' %
|
bos@133
|
124 (ltx_file, locs[cmd], undocumented, cmd))
|
bos@133
|
125 shortopt = known_opts[undocumented]
|
bos@133
|
126 if shortopt:
|
bos@133
|
127 print '\optref{%s}{%s}{%s}' % (cmd, shortopt, undocumented)
|
bos@133
|
128 else:
|
bos@133
|
129 print '\loptref{%s}{%s}' % (cmd, undocumented)
|
bos@133
|
130 errors += 1
|
bos@133
|
131 sys.stdout.flush()
|
bos@133
|
132
|
bos@133
|
133 if errors:
|
bos@133
|
134 sys.exit(1)
|
bos@133
|
135
|
bos@133
|
136 sorted_locs = sorted(locs.iteritems(), key=lambda x:x[1])
|
bos@133
|
137
|
bos@133
|
138 def next_loc(cmd):
|
bos@133
|
139 for i, (name, loc) in enumerate(sorted_locs):
|
bos@133
|
140 if name >= cmd:
|
bos@133
|
141 return sorted_locs[i-1][1] + 1
|
bos@133
|
142 return loc
|
bos@133
|
143
|
bos@133
|
144 for undocumented in sorted(known_set.difference(doc_set)):
|
bos@133
|
145 print >> sys.stderr, ('%s:%d: %r command not documented' %
|
bos@133
|
146 (ltx_file, next_loc(undocumented), undocumented))
|
bos@133
|
147 print '\cmdref{%s}' % undocumented
|
bos@133
|
148 for longopt, shortopt in sorted(known[undocumented].items(), cmp=optcmp):
|
bos@133
|
149 if shortopt:
|
bos@133
|
150 print '\optref{%s}{%s}{%s}' % (undocumented, shortopt, longopt)
|
bos@133
|
151 else:
|
bos@133
|
152 print '\loptref{%s}{%s}' % (undocumented, longopt)
|
bos@133
|
153 sys.stdout.flush()
|
bos@133
|
154 errors += 1
|
bos@133
|
155
|
bos@133
|
156 sys.exit(errors and 1 or 0)
|