|
1 #!/usr/bin/env python |
|
2 # |
|
3 # check-config - a config flag documentation checker for Mercurial |
|
4 # |
|
5 # Copyright 2015 Matt Mackall <mpm@selenic.com> |
|
6 # |
|
7 # This software may be used and distributed according to the terms of the |
|
8 # GNU General Public License version 2 or any later version. |
|
9 |
|
10 import re |
|
11 import sys |
|
12 |
|
13 foundopts = {} |
|
14 documented = {} |
|
15 |
|
16 configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"], ?""" |
|
17 r"""['"](\S+)['"](,\s(?:default=)?(\S+?))?\)""") |
|
18 |
|
19 def main(args): |
|
20 for f in args: |
|
21 sect = '' |
|
22 prevname = '' |
|
23 confsect = '' |
|
24 for l in open(f): |
|
25 |
|
26 # check topic-like bits |
|
27 m = re.match('\s*``(\S+)``', l) |
|
28 if m: |
|
29 prevname = m.group(1) |
|
30 continue |
|
31 if re.match('^\s*-+$', l): |
|
32 sect = prevname |
|
33 prevname = '' |
|
34 continue |
|
35 |
|
36 if sect and prevname: |
|
37 name = sect + '.' + prevname |
|
38 documented[name] = 1 |
|
39 |
|
40 # check docstring bits |
|
41 m = re.match(r'^\s+\[(\S+)\]', l) |
|
42 if m: |
|
43 confsect = m.group(1) |
|
44 continue |
|
45 m = re.match(r'^\s+(?:#\s*)?([a-z._]+) = ', l) |
|
46 if m: |
|
47 name = confsect + '.' + m.group(1) |
|
48 documented[name] = 1 |
|
49 |
|
50 # like the bugzilla extension |
|
51 m = re.match(r'^\s*([a-z]+\.[a-z]+)$', l) |
|
52 if m: |
|
53 documented[m.group(1)] = 1 |
|
54 |
|
55 # quoted in help or docstrings |
|
56 m = re.match(r'.*?``([-a-z_]+\.[-a-z_]+)``', l) |
|
57 if m: |
|
58 documented[m.group(1)] = 1 |
|
59 |
|
60 # look for ignore markers |
|
61 m = re.search(r'# (?:internal|experimental|deprecated|developer)' |
|
62 ' config: (\S+.\S+)$', l) |
|
63 if m: |
|
64 documented[m.group(1)] = 1 |
|
65 |
|
66 # look for code-like bits |
|
67 m = re.search(configre, l) |
|
68 if m: |
|
69 ctype = m.group(1) |
|
70 if not ctype: |
|
71 ctype = 'str' |
|
72 name = m.group(2) + "." + m.group(3) |
|
73 default = m.group(5) |
|
74 if default in (None, 'False', 'None', '0', '[]', '""', "''"): |
|
75 default = '' |
|
76 if re.match('[a-z.]+$', default): |
|
77 default = '<variable>' |
|
78 if name in foundopts and (ctype, default) != foundopts[name]: |
|
79 print l |
|
80 print "conflict on %s: %r != %r" % (name, (ctype, default), |
|
81 foundopts[name]) |
|
82 foundopts[name] = (ctype, default) |
|
83 |
|
84 for name in sorted(foundopts): |
|
85 if name not in documented: |
|
86 if not (name.startswith("devel.") or |
|
87 name.startswith("experimental.") or |
|
88 name.startswith("debug.")): |
|
89 ctype, default = foundopts[name] |
|
90 if default: |
|
91 default = ' [%s]' % default |
|
92 print "undocumented: %s (%s)%s" % (name, ctype, default) |
|
93 |
|
94 if __name__ == "__main__": |
|
95 sys.exit(main(sys.argv[1:])) |