source: doc/user-guide/genhelp.py @ 727a68b

Last change on this file since 727a68b was 028ca92, checked in by dequis <dx@…>, at 2016-09-21T02:48:14Z

doc: some 'chat list' related help improvements

  • Property mode set to 100644
File size: 6.8 KB
Line 
1#!/usr/bin/env python
2
3# Usage: python genhelp.py input.xml output.txt
4# (Both python2 (>=2.5) or python3 work)
5#
6# The shebang above isn't used, set the PYTHON environment variable
7# before running ./configure instead
8
9# This program is free software; you can redistribute it and/or
10# modify it under the terms of the GNU General Public License
11# as published by the Free Software Foundation; either version 2
12# of the License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor,
22# Boston, MA  02110-1301, USA.
23
24
25import os
26import re
27import sys
28import xml.etree.ElementTree as ET
29
30NORMALIZE_RE = re.compile(r"([^<>\s\t])[\s\t]+([^<>\s\t])")
31
32# Helpers
33
34def normalize(x):
35    """Normalize whitespace of a string.
36
37    The regexp turns any sequence of whitespace into a single space if it's in
38    the middle of the tag text, and then all newlines and tabs are removed,
39    keeping spaces.
40    """
41
42    x = NORMALIZE_RE.sub(r"\1 \2", x or '')
43    return x.replace("\n", "").replace("\t", "")
44
45def join(list):
46    """Turns any iterator into a string"""
47    return ''.join([str(x) for x in list])
48
49def fix_tree(tag, debug=False, lvl=''):
50    """Walks the XML tree and modifies it in-place fixing various details"""
51
52    # The include tags have an ugly namespace in the tag name. Simplify that.
53    if tag.tag.count("XInclude"):
54        tag.tag = 'include'
55
56    # Print a pretty tree-like representation of the processed tags
57    if debug:
58        print("%s<%s>%r" % (lvl, tag.tag, [tag.text, normalize(tag.text)]))
59
60    for subtag in tag:
61        fix_tree(subtag, debug, lvl + "  ")
62
63    if debug:
64        print("%s</%s>%r" % (lvl, tag.tag, [tag.tail, normalize(tag.tail)]))
65
66    # Actually normalize whitespace
67    if 'pre' not in tag.attrib:
68        tag.text = normalize(tag.text)
69    tag.tail = normalize(tag.tail)
70
71
72# Main logic
73
74def process_file(filename, parent=None):
75    tree = ET.parse(open(filename)).getroot()
76    fix_tree(tree)
77    return parse_tag(tree, parent)
78
79def parse_tag(tag, parent):
80    """Calls a tag_... function based on the tag name"""
81
82    fun = globals()["tag_%s" % tag.tag.replace("-", "_")]
83    return join(fun(tag, parent))
84
85def parse_subtags(tag, parent=None):
86    yield tag.text
87
88    for subtag in tag:
89        yield parse_tag(subtag, tag)
90
91    yield tag.tail
92
93
94# Main tag handlers
95
96def handle_subject(tag, parent):
97    """Tag handler for preface, chapter, sect1 and sect2 (aliased below)"""
98
99    yield '?%s\n' % tag.attrib['id']
100
101    first = True
102    for element in tag:
103        if element.tag in ["para", "variablelist", "simplelist",
104                           "command-list", "ircexample"]:
105            if not first:
106                # Spaces between paragraphs
107                yield "\n"
108            first = False
109
110            if element.attrib.get('title', ''):
111                yield element.attrib['title']
112                yield "\n"
113            yield join(parse_tag(element, tag)).rstrip("\n")
114            yield "\n"
115
116    yield "%\n"
117
118    for element in tag:
119        if element.tag in ["sect1", "sect2"]:
120            yield join(handle_subject(element, tag))
121
122    for element in tag.findall("bitlbee-command"):
123        yield join(handle_command(element))
124
125    for element in tag.findall("bitlbee-setting"):
126        yield join(handle_setting(element))
127
128def handle_command(tag, prefix=''):
129    """Tag handler for <bitlbee-command> (called from handle_subject)"""
130
131    this_cmd = prefix + tag.attrib['name']
132
133    yield "?%s\n" % this_cmd
134    for syntax in tag.findall("syntax"):
135        yield '\x02Syntax:\x02 %s\n' % syntax.text
136
137    yield "\n"
138    yield join(parse_subtags(tag.find("description"))).rstrip()
139    yield "\n"
140
141    for example in tag.findall("ircexample"):
142        yield "\n\x02Example:\x02\n"
143        yield join(parse_subtags(example)).rstrip()
144        yield "\n"
145
146    yield "%\n"
147
148    for element in tag.findall("bitlbee-command"):
149        yield join(handle_command(element, this_cmd + " "))
150
151def handle_setting(tag):
152    """Tag handler for <bitlbee-setting> (called from handle_subject)"""
153
154    yield "?set %s\n" % tag.attrib['name']
155    yield "\x02Type:\x02 %s\n" % tag.attrib["type"]
156    yield "\x02Scope:\x02 %s\n" % tag.attrib["scope"]
157
158    if tag.find("default") is not None:
159        yield "\x02Default:\x02 %s\n" % tag.findtext("default")
160
161    if tag.find("possible-values") is not None:
162        yield "\x02Possible Values:\x02 %s\n" % tag.findtext("possible-values")
163
164    yield "\n"
165    yield join(parse_subtags(tag.find("description"))).rstrip()
166    yield "\n%\n"
167
168
169# Aliases for tags that behave like subjects
170tag_preface = handle_subject
171tag_chapter = handle_subject
172tag_sect1 = handle_subject
173tag_sect2 = handle_subject
174
175# Aliases for tags that don't have any special behavior
176tag_ulink = parse_subtags
177tag_note = parse_subtags
178tag_book = parse_subtags
179tag_ircexample = parse_subtags
180
181
182# Handlers for specific tags
183
184def tag_include(tag, parent):
185    return process_file(tag.attrib['href'], tag)
186
187def tag_para(tag, parent):
188    return join(parse_subtags(tag)) + "\n\n"
189
190def tag_emphasis(tag, parent):
191    return "\x02%s\x02%s" % (tag.text, tag.tail)
192
193def tag_ircline(tag, parent):
194    return "\x02<%s>\x02 %s\n" % (tag.attrib['nick'], join(parse_subtags(tag)))
195
196def tag_ircaction(tag, parent):
197    return "\x02* %s\x02 %s\n" % (tag.attrib['nick'], join(parse_subtags(tag)))
198
199def tag_command_list(tag, parent):
200    yield "These are all root commands. See \x02help <command name>\x02 " \
201          "for more details on each command.\n\n"
202
203    for subtag in parent.findall("bitlbee-command"):
204        yield " * \x02%s\x02 - %s\n" % \
205            (subtag.attrib['name'],
206             subtag.findtext("short-description"))
207
208    yield "\nMost commands can be shortened. For example instead of " \
209          "\x02account list\x02, try \x02ac l\x02.\n\n"
210
211def tag_variablelist(tag, parent):
212    for subtag in tag:
213        yield " \x02%s\x02 - %s\n" % \
214            (subtag.findtext("term"),
215             join(parse_subtags(subtag.find("listitem/para"))))
216    yield '\n'
217
218def tag_simplelist(tag, parent):
219    for subtag in tag:
220        yield " - %s\n" % join(parse_subtags(subtag))
221    yield '\n'
222
223
224def main():
225    if len(sys.argv) != 3:
226        print("Usage: python genhelp.py input.xml output.txt")
227        return
228
229    # ensure that we really are in the same directory as the input file
230    os.chdir(os.path.dirname(os.path.abspath(sys.argv[1])))
231
232    txt = process_file(sys.argv[1])
233    open(sys.argv[2], "w").write(txt)
234
235if __name__ == '__main__':
236    main()
Note: See TracBrowser for help on using the repository browser.