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