Show count of variables/config settings.
[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 print 'counted %i text objects' % len(objects)
81
82 doc_objects = []
83 exp = re.compile("\s*<command><option>(\w*)</option></command>.*")
84 print "checking docs -> objs consistency (in %s)" % (file_names["text_objects"])
85 for var in vars:
86         term = getkey(var)
87         doc_objects.append(term)
88         if ['templaten', 'colorn'].count(doc_objects[len(doc_objects) - 1].lower()):
89                 # ignore these
90                 continue
91         if doc_objects[len(doc_objects) - 1] not in objects:
92                 print "   '%s' is documented, but doesn't seem to be an object" % (doc_objects[len(doc_objects) - 1])
93 print "done\n"
94
95 print "checking objs -> docs consistency (in %s)" % (file_names["variables"])
96 for obj in objects:
97         if obj not in doc_objects:
98                 print "   '%s' seems to be undocumented" % (obj)
99 print "done\n"
100
101 #
102 # Now we'll do config settings
103 #
104
105 config_entries = []
106
107 file = open(file_names["conky"], "r")
108 exp1 = re.compile('\s*CONF\("(\w*)".*')
109 exp2 = re.compile('\s*CONF2\("(\w*)".*')
110 exp3 = re.compile('\s*CONF3\("(\w*)".*')
111 while file:
112         line = file.readline()
113         if len(line) == 0:
114                 break
115         res = exp1.match(line)
116         if not res:
117                 res = exp2.match(line)
118         if not res:
119                 res = exp3.match(line)
120         if res:
121                 conf = res.group(1)
122                 if re.match("color\d", conf):
123                         conf = "colorN"
124                 if config_entries.count(conf) == 0:
125                         config_entries.append(conf)
126 file.close()
127 print 'counted %i config settings' % len(config_entries)
128
129 doc_configs = []
130 print "checking docs -> configs consistency (in %s)" % (file_names["conky"])
131 for config in configs:
132         term = getkey(config)
133         doc_configs.append(term)
134         if ['text', 'templaten'].count(doc_configs[len(doc_configs) - 1].lower()):
135                 # ignore these
136                 continue
137         if doc_configs[len(doc_configs) - 1] not in config_entries:
138                 print "   '%s' is documented, but doesn't seem to be a config setting" % (doc_configs[len(doc_configs) - 1])
139 print "done\n"
140
141 print "checking configs -> docs consistency (in %s)" % (file_names["config_settings"])
142 for obj in config_entries:
143         if obj != "text" and obj != "template" and obj not in doc_configs:
144                 print "   '%s' seems to be undocumented" % (obj)
145 print "done\n"
146
147
148
149 # Cheat and add the colour/template stuff.
150
151 for i in range(0, 10):
152         objects.append("color" + str(i))
153         config_entries.append("color" + str(i))
154         objects.append("template" + str(i))
155         config_entries.append("template" + str(i))
156
157 # Finally, sort everything.
158 objects.sort()
159 config_entries.sort()
160
161 #
162 # Update nano syntax stuff
163 #
164
165 print "updating nano syntax...",
166 sys.stdout.flush()
167 file = open(file_names["nano_syntax"], "rw+")
168 lines = []
169 while file:
170         line = file.readline()
171         if len(line) == 0:
172                 break
173         lines.append(line)
174
175 # find the line we want to update
176 for line in lines:
177         if re.match("color green ", line):
178                 idx = lines.index(line)
179                 lines.pop(idx) # remove old line
180                 line = 'color green "\<('
181                 for obj in config_entries:
182                         line += "%s|" % (obj)
183                 line = line[:len(line) - 1]
184                 line += ')\>"\n'
185                 lines.insert(idx, line)
186         if re.match("color brightblue ", line):
187                 idx = lines.index(line)
188                 lines.pop(idx) # remove old line
189                 line = 'color brightblue "\<('
190                 for obj in objects:
191                         line += "%s|" % (obj)
192                 line = line[:len(line) - 1]
193                 line += ')\>"\n'
194                 lines.insert(idx, line)
195                 break # want to ignore everything after this line
196 file.truncate(0)
197 file.seek(0)
198 file.writelines(lines)
199 file.close()
200 print "done."
201
202 #
203 # Update vim syntax stuff
204 #
205
206 print "updating vim syntax...",
207 sys.stdout.flush()
208 file = open(file_names["vim_syntax"], "rw+")
209 lines = []
210 while file:
211         line = file.readline()
212         if len(line) == 0:
213                 break
214         lines.append(line)
215
216 # find the line we want to update
217 for line in lines:
218         if re.match("syn keyword ConkyrcSetting ", line):
219                 idx = lines.index(line)
220                 lines.pop(idx) # remove old line
221                 line = 'syn keyword ConkyrcSetting '
222                 for obj in config_entries:
223                         line += "%s " % (obj)
224                 line = line[:len(line) - 1]
225                 line += '\n'
226                 lines.insert(idx, line)
227         if re.match("syn keyword ConkyrcVarName contained nextgroup=ConkyrcNumber,ConkyrcColour skipwhite ", line):
228                 idx = lines.index(line)
229                 lines.pop(idx) # remove old line
230                 line = 'syn keyword ConkyrcVarName contained nextgroup=ConkyrcNumber,ConkyrcColour skipwhite '
231                 for obj in objects:
232                         line += "%s " % (obj)
233                 line = line[:len(line) - 1]
234                 line += '\n'
235                 lines.insert(idx, line)
236                 break # want to ignore everything after this line
237 file.truncate(0)
238 file.seek(0)
239 file.writelines(lines)
240 file.close()
241
242 print "done."