Fix check_docs.py to work with cleaned up XML.
[monky] / check_docs.py
1 #!/usr/bin/python
2 #
3 # This script will check the documentation consistency against the code.  It
4 # doesn't check the actual accuracy of the documentation, it just ensures that
5 # everything is documented and that nothing which doesn't exist in Conky
6 # appears in the documentation.
7 #
8 # This script also updates the vim and nano syntax files so it doesn't have to
9 # be done manually.
10 #
11 # Requires the ElementTree Python module for the sorting stuff, see:
12 # http://effbot.org/zone/element-index.htm
13 #
14 # You should also install htmltidy, but it's not necessary.
15 #
16
17 import os.path
18 import re
19 import sys
20
21 file_names = dict()
22 file_names["text_objects"]    = "src/text_object.h"
23 file_names["conky"]           = "src/conky.c"
24 file_names["vim_syntax"]      = "extras/vim/syntax/conkyrc.vim"
25 file_names["nano_syntax"]     = "extras/nano/conky.nanorc"
26 file_names["variables"]       = "doc/variables.xml"
27 file_names["config_settings"] = "doc/config_settings.xml"
28
29 for fn in file_names.values():
30         if not os.path.exists(fn) or not os.path.isfile(fn):
31                 print "'%s' doesn't exist, or isn't a file" % (fn)
32                 exit(1)
33
34 print 'sorting/tidying docs...'
35
36 # sort the docs by variable/config setting
37 import string
38 import xml.etree.ElementTree as ET
39
40 vars_xml = ET.parse(file_names['variables'])
41 config_xml = ET.parse(file_names['config_settings'])
42
43 getkey = lambda x: x.findtext('term/command/option')
44
45 vars = vars_xml.getroot()
46 vars[:] = sorted(vars, key=getkey)
47
48 configs = config_xml.getroot()
49 configs[:] = sorted(configs, key=getkey)
50
51 vars_xml.write(file_names['variables'])
52 config_xml.write(file_names['config_settings'])
53
54 def tidy(file):
55         command = ['tidy', '-qim', '-xml', '-utf8', '--indent-spaces', '4']
56         os.system('%s %s 2>/dev/null' % (string.join(command), file))
57
58 tidy(file_names['variables'])
59 tidy(file_names['config_settings'])
60
61 #
62 # Do all the objects first
63 #
64
65 objects = []
66
67 file = open(file_names["text_objects"], "r")
68 exp = re.compile("\s*OBJ_(\w*).*")
69 while file:
70         line = file.readline()
71         if len(line) == 0:
72                 break
73         res = exp.match(line)
74         if res:
75                 obj = res.group(1)
76                 if not re.match("color\d", obj) and obj != "text":
77                         # ignore colourN stuff
78                         objects.append(res.group(1))
79 file.close()
80
81 doc_objects = []
82 exp = re.compile("\s*<command><option>(\w*)</option></command>.*")
83 print "checking docs -> objs consistency (in %s)" % (file_names["text_objects"])
84 for var in vars:
85         term = getkey(var)
86         doc_objects.append(term)
87         if ['templaten', 'colorn'].count(doc_objects[len(doc_objects) - 1].lower()):
88                 # ignore these
89                 continue
90         if doc_objects[len(doc_objects) - 1] not in objects:
91                 print "   '%s' is documented, but doesn't seem to be an object" % (doc_objects[len(doc_objects) - 1])
92 print "done\n"
93
94 print "checking objs -> docs consistency (in %s)" % (file_names["variables"])
95 for obj in objects:
96         if obj not in doc_objects:
97                 print "   '%s' seems to be undocumented" % (obj)
98 print "done\n"
99
100 #
101 # Now we'll do config settings
102 #
103
104 config_entries = []
105
106 file = open(file_names["conky"], "r")
107 exp1 = re.compile('\s*CONF\("(\w*)".*')
108 exp2 = re.compile('\s*CONF2\("(\w*)".*')
109 exp3 = re.compile('\s*CONF3\("(\w*)".*')
110 while file:
111         line = file.readline()
112         if len(line) == 0:
113                 break
114         res = exp1.match(line)
115         if not res:
116                 res = exp2.match(line)
117         if not res:
118                 res = exp3.match(line)
119         if res:
120                 conf = res.group(1)
121                 if re.match("color\d", conf):
122                         conf = "colorN"
123                 if config_entries.count(conf) == 0:
124                         config_entries.append(conf)
125 file.close()
126
127 doc_configs = []
128 print "checking docs -> configs consistency (in %s)" % (file_names["conky"])
129 for config in configs:
130         term = getkey(config)
131         doc_configs.append(term)
132         if ['text', 'templaten'].count(doc_configs[len(doc_configs) - 1].lower()):
133                 # ignore these
134                 continue
135         if doc_configs[len(doc_configs) - 1] not in config_entries:
136                 print "   '%s' is documented, but doesn't seem to be a config setting" % (doc_configs[len(doc_configs) - 1])
137 print "done\n"
138
139 print "checking configs -> docs consistency (in %s)" % (file_names["config_settings"])
140 for obj in config_entries:
141         if obj != "text" and obj != "template" and obj not in doc_configs:
142                 print "   '%s' seems to be undocumented" % (obj)
143 print "done\n"
144
145
146
147 # Cheat and add the colour/template stuff.
148
149 for i in range(0, 10):
150         objects.append("color" + str(i))
151         config_entries.append("color" + str(i))
152         objects.append("template" + str(i))
153         config_entries.append("template" + str(i))
154
155 # Finally, sort everything.
156 objects.sort()
157 config_entries.sort()
158
159 #
160 # Update nano syntax stuff
161 #
162
163 print "updating nano syntax...",
164 sys.stdout.flush()
165 file = open(file_names["nano_syntax"], "rw+")
166 lines = []
167 while file:
168         line = file.readline()
169         if len(line) == 0:
170                 break
171         lines.append(line)
172
173 # find the line we want to update
174 for line in lines:
175         if re.match("color green ", line):
176                 idx = lines.index(line)
177                 lines.pop(idx) # remove old line
178                 line = 'color green "\<('
179                 for obj in config_entries:
180                         line += "%s|" % (obj)
181                 line = line[:len(line) - 1]
182                 line += ')\>"\n'
183                 lines.insert(idx, line)
184         if re.match("color brightblue ", line):
185                 idx = lines.index(line)
186                 lines.pop(idx) # remove old line
187                 line = 'color brightblue "\<('
188                 for obj in objects:
189                         line += "%s|" % (obj)
190                 line = line[:len(line) - 1]
191                 line += ')\>"\n'
192                 lines.insert(idx, line)
193                 break # want to ignore everything after this line
194 file.truncate(0)
195 file.seek(0)
196 file.writelines(lines)
197 file.close()
198 print "done."
199
200 #
201 # Update vim syntax stuff
202 #
203
204 print "updating vim syntax...",
205 sys.stdout.flush()
206 file = open(file_names["vim_syntax"], "rw+")
207 lines = []
208 while file:
209         line = file.readline()
210         if len(line) == 0:
211                 break
212         lines.append(line)
213
214 # find the line we want to update
215 for line in lines:
216         if re.match("syn keyword ConkyrcSetting ", line):
217                 idx = lines.index(line)
218                 lines.pop(idx) # remove old line
219                 line = 'syn keyword ConkyrcSetting '
220                 for obj in config_entries:
221                         line += "%s " % (obj)
222                 line = line[:len(line) - 1]
223                 line += '\n'
224                 lines.insert(idx, line)
225         if re.match("syn keyword ConkyrcVarName contained nextgroup=ConkyrcNumber,ConkyrcColour skipwhite ", line):
226                 idx = lines.index(line)
227                 lines.pop(idx) # remove old line
228                 line = 'syn keyword ConkyrcVarName contained nextgroup=ConkyrcNumber,ConkyrcColour skipwhite '
229                 for obj in objects:
230                         line += "%s " % (obj)
231                 line = line[:len(line) - 1]
232                 line += '\n'
233                 lines.insert(idx, line)
234                 break # want to ignore everything after this line
235 file.truncate(0)
236 file.seek(0)
237 file.writelines(lines)
238 file.close()
239
240 print "done."