diff --git a/doc/scripts/wikiparser.py b/doc/scripts/wikiparser.py index bed2bf698..b133040f5 100644 --- a/doc/scripts/wikiparser.py +++ b/doc/scripts/wikiparser.py @@ -274,6 +274,7 @@ class ListType(Enum): PLAIN = 1 BULLETED = 2 NUMBERED = 3 + SPACED = 4 class List(Element): @@ -284,8 +285,10 @@ class List(Element): self.list_type = ListType.PLAIN elif list_type == 'bulleted': self.list_type = ListType.BULLETED - else: + elif list_type == 'numbered': self.list_type = ListType.NUMBERED + else: + self.list_type = ListType.SPACED else: self.list_type = list_type @@ -296,8 +299,10 @@ class List(Element): list_type = 'plain' elif self.list_type == ListType.BULLETED: list_type = 'bulleted' - else: + elif self.list_type == ListType.NUMBERED: list_type = 'numbered' + else: + list_type = 'spaced' return super().__repr__(list_type, self.items) @@ -309,8 +314,10 @@ class List(Element): xml = '' elif self.list_type == ListType.BULLETED: xml = '' - else: + elif self.list_type == ListType.NUMBERED: xml = '' + else: + xml = '' for item in self.items: xml += item.to_docbook(context) @@ -319,8 +326,10 @@ class List(Element): xml += '' elif self.list_type == ListType.BULLETED: xml += '' - else: + elif self.list_type == ListType.NUMBERED: xml += '' + else: + xml += '' return xml @@ -811,7 +820,7 @@ def parse_list(list_data, context=None): list_type = list_data[0][0] current_level = list_data[0][1] parsed_list = List(list_type) - override_marker = True if list_type == ListType.PLAIN else False + override_marker = (list_type in (ListType.PLAIN, ListType.SPACED)) while list_data: level = list_data[0][1] if level > current_level: @@ -920,7 +929,8 @@ def parse_wiki(text, context=None, begin_marker=None, end_marker=None): >>> parse_wiki('plain text') [Paragraph([PlainText('plain text ')])] >>> parse_wiki(' plain multispaced text ') - [Paragraph([PlainText(' plain multispaced text ')])] + [List('spaced', [ListItem([Paragraph([PlainText(\ +'plain multispaced text ')])])])] >>> parse_wiki('https://freedombox.org') [Paragraph([Url('https://freedombox.org'), PlainText(' ')])] >>> parse_wiki("''italic''") @@ -1087,7 +1097,7 @@ preformatted text (source code)\\n}}}''') \\n different indents.\\n}}}') [Paragraph([PlainText('text to introduce ')]), \ CodeText(' a multiliner\\nstarting at\\n different indents.')] - >>> parse_wiki('Blah, blah:\\n {{{\\nmulti-line\\nformatted text\\n\ + >>> parse_wiki('Blah, blah:\\n{{{\\nmulti-line\\nformatted text\\n\ starting at col #1\\n}}}') [Paragraph([PlainText('Blah, blah: ')]), \ CodeText('multi-line\\nformatted text\\nstarting at col #1')] @@ -1100,10 +1110,11 @@ CodeText('multi-line\\nformatted text\\nstarting at col #1')])])] (replace the ip/netmask with the one the router uses)\\n }}}\\n In \ most cases you can look at your current IP address, and change the last \ digits with zero to find your home network, like so: XXX.XXX.XXX.0/24') - [CodeText(' nmap -p 80 --open -sV 192.168.0.0/24 (replace the \ -ip/netmask with the one the router uses)'), Paragraph([PlainText(' In \ -most cases you can look at your current IP address, and change the last \ -digits with zero to find your home network, like so: XXX.XXX.XXX.0/24 ')])] + [List('spaced', [ListItem([CodeText(' nmap -p 80 --open -sV 192.168.\ +0.0/24 (replace the ip/netmask with the one the router uses)'), Paragraph(\ +[PlainText('In most cases you can look at your current IP address, and \ +change the last digits with zero to find your home network, like so: XXX.XXX\ +.XXX.0/24 ')])])])] >>> parse_wiki('text to introduce\\n----\\n<>') [Paragraph([PlainText('text to introduce ')]), \ HorizontalRule(4), TableOfContents()] @@ -1114,12 +1125,13 @@ the keys:\\n {{{\\n$ gpg --keyserver keys.gnupg.net --recv-keys \ BCBEBD57A11F70B23782BC5736C361440C9BC971\\n$ gpg --keyserver keys.gnupg.net \ --recv-keys 7D6ADB750F91085589484BE677C0C75E7B650808\\n$ gpg --keyserver \ keys.gnupg.net --recv-keys 013D86D8BA32EAB4A6691BF85D4153D6FE188FC8\\n }}}') - [Paragraph([PlainText(' If this command shows an error such as new key \ -but contains no user ID - skipped, then use a different keyserver to download \ -the keys: ')]), CodeText('$ gpg --keyserver keys.gnupg.net --recv-keys \ -BCBEBD57A11F70B23782BC5736C361440C9BC971\\n$ gpg --keyserver keys.gnupg.net \ ---recv-keys 7D6ADB750F91085589484BE677C0C75E7B650808\\n$ gpg --keyserver \ -keys.gnupg.net --recv-keys 013D86D8BA32EAB4A6691BF85D4153D6FE188FC8')] + [List('spaced', [ListItem([Paragraph([PlainText('If this command shows an \ +error such as new key but contains no user ID - skipped, then use a different \ +keyserver to download the keys: ')]), CodeText('$ gpg --keyserver keys.gnupg.\ +net --recv-keys BCBEBD57A11F70B23782BC5736C361440C9BC971\\n$ gpg --keyserver \ +keys.gnupg.net --recv-keys 7D6ADB750F91085589484BE677C0C75E7B650808\\n$ gpg \ +--keyserver keys.gnupg.net --recv-keys \ +013D86D8BA32EAB4A6691BF85D4153D6FE188FC8')])])] >>> parse_wiki('User documentation:\\n * List of \ [[FreedomBox/Features|applications]] offered by !FreedomBox.') @@ -1296,9 +1308,9 @@ width=394}}\\n\ List('numbered', \ [ListItem([Paragraph([PlainText('Launch Quassel Client. You will be greeted \ with a wizard to '), MonospaceText('Connect to Core'), PlainText('. ')]), \ -Paragraph([PlainText(' '), \ +List('spaced', [ListItem([Paragraph([\ EmbeddedAttachment('quassel-client-1-connect-to-core.png', \ -[PlainText('Connect to Core')], 'width=394'), PlainText(' ')])]), \ +[PlainText('Connect to Core')], 'width=394'), PlainText(' ')])])])]), \ ListItem([Paragraph([PlainText('Click the '), MonospaceText('Add'), \ PlainText(' button to launch '), MonospaceText('Add Core Account'), \ PlainText(' dialog. ')])])])] @@ -1321,6 +1333,11 @@ PlainText(' dialog. ')])])])] while lines: line = lines.pop(0) + # Empty line + match = re.match(r'^\s+$', line) + if match: + continue + # End of included file if end_marker and line.strip().startswith(end_marker): break # end parsing @@ -1387,17 +1404,31 @@ PlainText(' dialog. ')])])])] # List list_item_re = re.compile(r'(\s+)(\*|\.|\d\.|I\.|A\.)\s+(.*)') - match = list_item_re.match(line) - if match: + space_list_re = re.compile(r'(\s+)') + match = list_item_re.match(line) or space_list_re.match(line) + if match or re.match(r'\s+', line): # Collect lines until end of List is reached. list_lines = [] next_list_item = line top_indent = len(match.group(1)) + + if line.strip().startswith('{{{') and '}}}' not in line: + # Multi-line code text or admonition may not have expected + # indentation + while lines: + line = lines.pop(0) + if line.strip() == '}}}': + next_list_item += '\n}}}' + break + else: + next_list_item += '\n' + line + while lines: candidate = lines[0] - if re.match(r'^ *$', candidate): + if re.match(r'^\s*$', candidate): # Eat up empty lines - next_list_item += '\n' + lines.pop(0) + lines.pop(0) + next_list_item += '\n' continue if not candidate.startswith(' ' * top_indent): @@ -1417,7 +1448,7 @@ PlainText(' dialog. ')])])])] # have expected indentation while lines: line = lines.pop(0) - if '}}}' == line.strip(): + if line.strip() == '}}}': next_list_item += '\n}}}' break else: @@ -1436,15 +1467,23 @@ PlainText(' dialog. ')])])])] list_data = [] for line in list_lines: match = list_item_re.match(line) - indent = len(match.group(1)) - marker = match.group(2) - if marker == '.': - list_type = ListType.PLAIN - elif '*' in marker: - list_type = ListType.BULLETED + if match: + indent = len(match.group(1)) + marker = match.group(2) + if marker == '.': + list_type = ListType.PLAIN + elif '*' in marker: + list_type = ListType.BULLETED + else: + list_type = ListType.NUMBERED + + content = ' ' * indent + line.lstrip(match.group(2) + ' ') else: - list_type = ListType.NUMBERED - content = ' ' * indent + line.lstrip(match.group(2) + ' ') + match = space_list_re.match(line) + indent = len(match.group(1)) + list_type = ListType.SPACED + content = line + list_data.append((list_type, indent, content)) new_list, _ = parse_list(list_data, context)