diff --git a/doc/utils/converters/lammpsdoc/txt2html.py b/doc/utils/converters/lammpsdoc/txt2html.py index 6e559723b4..79a75d72f6 100755 --- a/doc/utils/converters/lammpsdoc/txt2html.py +++ b/doc/utils/converters/lammpsdoc/txt2html.py @@ -25,6 +25,7 @@ import re import sys import argparse + class Markup(object): BOLD_START = "[" BOLD_END = "]" @@ -77,6 +78,7 @@ class Markup(object): text = text.replace('\"%s\"_%s' % (name, link), href, 1) return text + class HTMLMarkup(Markup): def __init__(self): super().__init__() @@ -101,6 +103,7 @@ class HTMLMarkup(Markup): return "" + content + "" + class Formatting(object): UNORDERED_LIST_MODE = "unordered-list" ORDERED_LIST_MODE = "ordered-list" @@ -435,6 +438,7 @@ class Formatting(object): return rows + class HTMLFormatting(Formatting): def __init__(self, markup): super().__init__(markup) @@ -448,6 +452,7 @@ class HTMLFormatting(Formatting): def raw_html(self, content): return content + class TxtParser(object): def __init__(self): self.markup = HTMLMarkup() @@ -630,6 +635,7 @@ class TxtParser(object): i += 1 + class Txt2Html(TxtParser): def __init__(self): super().__init__() @@ -641,6 +647,7 @@ class Txt2Html(TxtParser): line.startswith(".. END_HTML_ONLY") or \ super().is_paragraph_separator(line) + class TxtConverter: def get_argument_parser(self): return None @@ -665,7 +672,15 @@ class TxtConverter: print("Converting", filename, "...", file=err) content = f.read() converter = self.create_converter(parsed_args) - result = converter.convert(content) + + try: + result = converter.convert(content) + except Exception as e: + msg = "###########################################################################\n" \ + " ERROR: " + e.args[0] + "\n" \ + "###########################################################################\n" + print(msg, file=err) + result = msg if write_to_files: output_filename = self.get_output_filename(filename) @@ -674,6 +689,7 @@ class TxtConverter: else: print(result, end='', file=out) + class Txt2HtmlConverter(TxtConverter): def get_argument_parser(self): parser = argparse.ArgumentParser(description='converts a text file with simple formatting & markup into HTML.\n' diff --git a/doc/utils/converters/lammpsdoc/txt2rst.py b/doc/utils/converters/lammpsdoc/txt2rst.py index 1bc279c0f3..0997af80cd 100755 --- a/doc/utils/converters/lammpsdoc/txt2rst.py +++ b/doc/utils/converters/lammpsdoc/txt2rst.py @@ -24,6 +24,7 @@ import argparse from lammpsdoc import lammps_filters from lammpsdoc.txt2html import Markup, Formatting, TxtParser, TxtConverter + class RSTMarkup(Markup): def __init__(self): super().__init__() @@ -65,6 +66,7 @@ class RSTMarkup(Markup): def escape_rst_chars(self, text): text = text.replace('*', '\\*') text = text.replace('^', '\\^') + text = text.replace('|', '\\|') text = re.sub(r'([^"])_', r'\1\\_', text) return text @@ -72,6 +74,7 @@ class RSTMarkup(Markup): text = text.replace('\\*', '*') text = text.replace('\\^', '^') text = text.replace('\\_', '_') + text = text.replace('\\|', '|') return text def inline_math(self, text): @@ -112,13 +115,18 @@ class RSTMarkup(Markup): return "`%s <%s>`_" % (content, href) + class RSTFormatting(Formatting): RST_HEADER_TYPES = '#*=-^"' def __init__(self, markup): super().__init__(markup) + self.indent_level = 0 def paragraph(self, content): + if self.indent_level > 0: + return '\n' + self.list_indent(content.strip(), self.indent_level) + return content.strip() + "\n" def center(self, content): @@ -128,7 +136,9 @@ class RSTFormatting(Formatting): return content.strip() def preformat(self, content): - return ".. parsed-literal::\n\n" + self.indent(content.rstrip()) + if self.indent_level > 0: + return self.list_indent("\n.. parsed-literal::\n\n" + self.indent(content.rstrip()), self.indent_level) + return "\n.. parsed-literal::\n\n" + self.indent(content.rstrip()) def horizontal_rule(self, content): return "\n----------\n\n" + content.strip() @@ -159,7 +169,7 @@ class RSTFormatting(Formatting): def header(self, content, level): header_content = content.strip() - header_content = re.sub(r'[0-9]+\.[0-9]*\s+', '', header_content) + header_content = re.sub(r'[0-9]+\.([0-9]*\.?)*\s+', '', header_content) header_underline = RSTFormatting.RST_HEADER_TYPES[level-1] * len(header_content) return header_content + "\n" + header_underline + "\n" @@ -178,14 +188,17 @@ class RSTFormatting(Formatting): return self.indent(paragraph.strip()) def unordered_list_begin(self, paragraph): + self.indent_level += 1 return paragraph def unordered_list_end(self, paragraph): + self.indent_level -= 1 return paragraph.rstrip() + '\n' def ordered_list_begin(self, paragraph): if paragraph.startswith('* '): paragraph = '#. ' + paragraph[2:] + self.indent_level += 1 return paragraph def definition_list_begin(self, paragraph): @@ -195,6 +208,7 @@ class RSTFormatting(Formatting): return paragraph def ordered_list_end(self, paragraph): + self.indent_level -= 1 return paragraph.rstrip() + '\n' def ordered_list(self, paragraph): @@ -228,6 +242,12 @@ class RSTFormatting(Formatting): indented += " %s\n" % line return indented + def list_indent(self, content, level=1): + indented = "" + for line in content.splitlines(): + indented += " " * level + ("%s\n" % line) + return indented + def get_max_column_widths(self, rows): num_columns = max([len(row) for row in rows]) max_widths = [0] * num_columns @@ -321,6 +341,7 @@ class RSTFormatting(Formatting): return text + post + class Txt2Rst(TxtParser): def __init__(self): super().__init__() @@ -354,6 +375,11 @@ class Txt2Rst(TxtParser): return commands return super().order_commands(commands) + def transform_paragraphs(self, content): + if self.format.indent_level > 0: + raise Exception("unbalanced number of ulb,ule or olb,ole pairs!") + return super().transform_paragraphs(content) + class Txt2RstConverter(TxtConverter): def get_argument_parser(self): @@ -370,6 +396,7 @@ class Txt2RstConverter(TxtConverter): filename, ext = os.path.splitext(path) return filename + ".rst" + def main(): app = Txt2RstConverter() app.run() diff --git a/doc/utils/converters/tests/test_txt2rst.py b/doc/utils/converters/tests/test_txt2rst.py index 2fa2bd699e..904eeb4749 100644 --- a/doc/utils/converters/tests/test_txt2rst.py +++ b/doc/utils/converters/tests/test_txt2rst.py @@ -81,6 +81,10 @@ class TestMarkup(unittest.TestCase): s = self.markup.convert("[*bold] and {italic*}") self.assertEqual("**\*bold** and *italic\**", s) + def test_escape_rst_characters(self): + s = self.markup.convert("[|bold|] and {|italic|}") + self.assertEqual("**\|bold\|** and *\|italic\|*", s) + def test_escape_hat_character(self): s = self.markup.convert("x^2") self.assertEqual("x\^2", s) @@ -155,13 +159,13 @@ class TestFormatting(unittest.TestCase): def test_preformat_formatting(self): s = self.txt2rst.convert("Hello :pre\n") - self.assertEqual(".. parsed-literal::\n\n" + self.assertEqual("\n.. parsed-literal::\n\n" " Hello\n\n", s) def test_preformat_formatting_with_indentation(self): s = self.txt2rst.convert(" Hello\n" " World :pre\n") - self.assertEqual(".. parsed-literal::\n\n" + self.assertEqual("\n.. parsed-literal::\n\n" " Hello\n" " World\n\n", s) @@ -196,6 +200,16 @@ class TestFormatting(unittest.TestCase): self.assertEqual("Level\n" "#####\n\n", s) + def test_filter_header_numbers_deep(self): + s = self.txt2rst.convert("1.1.1.1.1 Level :h1\n") + self.assertEqual("Level\n" + "#####\n\n", s) + + def test_no_filter_date(self): + s = self.txt2rst.convert("9 Sept 2016 version :h1\n") + self.assertEqual("9 Sept 2016 version\n" + "###################\n\n", s) + def test_all_breaks(self): s = self.txt2rst.convert("one\n" "two\n" @@ -308,8 +322,6 @@ class TestListFormatting(unittest.TestCase): "* third\n" " paragraph\n\n", s) - - def test_definition_list(self): s = self.txt2rst.convert("A\n" "first\n" @@ -322,6 +334,39 @@ class TestListFormatting(unittest.TestCase): " second\n" "\n\n", s) + def test_multi_paragraph_lists(self): + s = self.txt2rst.convert("first\n" + "paragraph of first bullet :ulb,l\n\n" + "second paragraph of first bullet\n\n" + "first paragraph of second bullet :l\n\n" + ":ule\n") + self.assertEqual("* first\n" + " paragraph of first bullet\n" + "\n" + " second paragraph of first bullet\n" + "\n" + "* first paragraph of second bullet\n\n\n", s) + + def test_multi_paragraph_lists_with_listing(self): + s = self.txt2rst.convert("first\n" + "paragraph of first bullet :ulb,l\n\n" + "code1 :pre\n" + "or\n" + "\n" + "first paragraph of second bullet :l\n\n" + ":ule\n") + self.assertEqual("* first\n" + " paragraph of first bullet\n" + " \n" + " .. parsed-literal::\n" + " \n" + " code1\n" + "\n\n" + " or\n" + "\n" + "* first paragraph of second bullet\n\n\n", s) + + class TestSpecialCommands(unittest.TestCase): def setUp(self): self.txt2rst = txt2rst.Txt2Rst()