diff --git a/doc/utils/converters/lammpsdoc/txt2rst.py b/doc/utils/converters/lammpsdoc/txt2rst.py index 3914e5782..0997af80c 100755 --- a/doc/utils/converters/lammpsdoc/txt2rst.py +++ b/doc/utils/converters/lammpsdoc/txt2rst.py @@ -1,405 +1,405 @@ #! /usr/bin/env python3 # LAMMPS Documentation Utilities # # Converter of LAMMPS documentation format to Sphinx ReStructured Text # # Copyright (C) 2015 Richard Berger # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import re import argparse from lammpsdoc import lammps_filters from lammpsdoc.txt2html import Markup, Formatting, TxtParser, TxtConverter class RSTMarkup(Markup): def __init__(self): super().__init__() def bold_start(self): return "**" def bold_end(self): return "**" def italic_start(self): return "*" def italic_end(self): return "*" def bold(self, text): """ RST requires a space after inline formats. For words which only partially apply a format add a backslash and whitespace to create valid RST""" text = re.sub(r'([^\s\\])\[([^\]\\]+)\]', r'\1\\ [\2]', text) text = re.sub(r'([^\\]?)\[([^\]\\]+)\]([^\s])', r'\1[\2]\\ \3', text) text = super().bold(text) return text def italic(self, text): """ RST requires a space after inline formats. For words which only partially apply a format add a backslash and whitespace to create valid RST""" text = re.sub(r'([^\s\\])\{([^\}\\]+)\}', r'\1\\ {\2}', text) text = re.sub(r'([^\\]?)\{([^\}\\]+)\}([^\s])', r'\1{\2}\\ \3', text) text = super().italic(text) return text def convert(self, text): text = self.escape_rst_chars(text) text = super().convert(text) text = self.inline_math(text) return text def escape_rst_chars(self, text): text = text.replace('*', '\\*') text = text.replace('^', '\\^') text = text.replace('|', '\\|') text = re.sub(r'([^"])_', r'\1\\_', text) return text def unescape_rst_chars(self, text): text = text.replace('\\*', '*') text = text.replace('\\^', '^') text = text.replace('\\_', '_') text = text.replace('\\|', '|') return text def inline_math(self, text): start_pos = text.find("\\(") end_pos = text.find("\\)") while start_pos >= 0 and end_pos >= 0: original = text[start_pos:end_pos+2] formula = original[2:-2] formula = self.unescape_rst_chars(formula) replacement = ":math:`" + formula.replace('\n', ' ').strip() + "`" text = text.replace(original, replacement) start_pos = text.find("\\(") end_pos = text.find("\\)") return text def create_link(self, content, href): content = content.strip() content = content.replace('\n', ' ') href = self.unescape_rst_chars(href) anchor_pos = href.find('#') if anchor_pos >= 0: href = href[anchor_pos+1:] return ":ref:`%s <%s>`" % (content, href) if href in self.references: return ":ref:`%s <%s>`" % (content, href) elif href in self.aliases: href = "%s_" % href elif href.endswith('.html') and not href.startswith('http') and 'USER/atc' not in href: href = href[0:-5] return ":doc:`%s <%s>`" % (content, href) 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): return content def linebreak(self, content): return content.strip() def preformat(self, content): 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() def image(self, content, file, link=None): if link and (link.lower().endswith('.jpg') or link.lower().endswith('.jpeg') or link.lower().endswith('.png') or link.lower().endswith('.gif')): converted = ".. thumbnail:: " + self.markup.unescape_rst_chars(link) + "\n" else: converted = ".. image:: " + self.markup.unescape_rst_chars(file) + "\n" if link: converted += " :target: " + self.markup.unescape_rst_chars(link) + "\n" if "c" in self.current_command_list: converted += " :align: center\n" return converted + content.strip() def named_link(self, paragraph, name): self.markup.add_internal_reference(name) return (".. _%s:\n\n" % name) + paragraph def define_link_alias(self, paragraph, alias, value): self.markup.add_link_alias(alias, value) return (".. _%s: %s\n\n" % (alias, value)) + paragraph 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" def unordered_list_item(self, paragraph): return "* " + paragraph.strip().replace('\n', '\n ') def ordered_list_item(self, paragraph, index): if index is None: index = "#" return str(index) + ". " + paragraph.strip().replace('\n', '\n ') def definition_term(self, paragraph): return paragraph.strip() def definition_description(self, paragraph): 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): return paragraph def definition_list_end(self, paragraph): return paragraph def ordered_list_end(self, paragraph): self.indent_level -= 1 return paragraph.rstrip() + '\n' def ordered_list(self, paragraph): paragraph = super().ordered_list(paragraph) return paragraph.rstrip() + '\n' def unordered_list(self, paragraph): paragraph = super().unordered_list(paragraph) return paragraph.rstrip() + '\n' def all_breaks(self, paragraph): indented = "" for line in paragraph.splitlines(): indented += "| %s\n" % line indented += "| \n" return indented def begin_document(self): return "" def end_document(self): return "" def raw_html(self, content): raw_directive = ".. raw:: html\n\n" return raw_directive + self.indent(content) def indent(self, content): indented = "" for line in content.splitlines(): 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 for columns in rows: for col_idx, column in enumerate(columns): max_widths[col_idx] = max(max_widths[col_idx], len(column.strip())+2) return max_widths def create_table_horizontal_line(self, max_widths): cell_borders = ['-' * width for width in max_widths] return '+' + '+'.join(cell_borders) + "+" def table(self, paragraph, configuration): paragraph = self.protect_rst_directives(paragraph) if configuration['num_columns'] == 0: rows = self.create_table_with_columns_based_on_newlines(paragraph, configuration['separator']) else: rows = self.create_table_with_fixed_number_of_columns(paragraph, configuration['separator'], configuration['num_columns']) column_widths = self.get_max_column_widths(rows) max_columns = len(column_widths) horizontal_line = self.create_table_horizontal_line(column_widths) + "\n" tbl = horizontal_line for row_idx in range(len(rows)): columns = rows[row_idx] tbl += "| " for col_idx in range(max_columns): if col_idx < len(columns): col = columns[col_idx].strip() else: col = "" tbl += col.ljust(column_widths[col_idx]-2, ' ') tbl += " |" if col_idx < max_columns - 1: tbl += " " tbl += "\n" tbl += horizontal_line tbl = self.restore_rst_directives(tbl) return tbl def protect_rst_directives(self, content): content = content.replace(":doc:", "0DOC0") content = content.replace(":ref:", "0REF0") return content def restore_rst_directives(self, content): content = content.replace("0DOC0", ":doc:") content = content.replace("0REF0", ":ref:") return content def math(self, content): eqs = content.split(r'\end{equation}') text = "" if len(eqs) > 1: post = eqs[-1].strip() eqs = eqs[0:-1] else: post = "" for eq in eqs: if len(eq.strip()) == 0: continue parts = eq.split(r'\begin{equation}') assert(len(parts) == 1 or len(parts) == 2) if len(parts) == 2: start = parts[0].strip() body = parts[1] else: start = "" body = parts[0] body = self.markup.unescape_rst_chars(body) if len(start) > 0: text += start + "\n" text += "\n.. math::\n\n" text += self.indent(r'\begin{equation}' + body.strip('\n') + r'\end{equation}') text += "\n" return text + post class Txt2Rst(TxtParser): def __init__(self): super().__init__() self.markup = RSTMarkup() self.format = RSTFormatting(self.markup) self.register_filters() def register_filters(self): self.paragraph_filters.append(lammps_filters.detect_and_format_notes) self.document_filters.append(lammps_filters.filter_file_header_until_first_horizontal_line) self.document_filters.append(lammps_filters.detect_and_add_command_to_index) self.document_filters.append(lammps_filters.filter_multiple_horizontal_rules) self.document_filters.append(lammps_filters.promote_doc_keywords) def is_ignored_textblock_begin(self, line): return line.startswith('') def is_ignored_textblock_end(self, line): return line.startswith('') def is_raw_textblock_begin(self, line): return line.startswith('') def order_commands(self, commands): if 'ule' in commands and 'l' in commands and commands.index('ule') > commands.index('l'): return commands elif 'ole' in commands and 'l' in commands and commands.index('ole') > commands.index('l'): 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): parser = argparse.ArgumentParser(description='converts a text file with simple formatting & markup into ' 'Restructured Text for Sphinx.') parser.add_argument('-x', metavar='file-to-skip', dest='skip_files', action='append') parser.add_argument('files', metavar='file', nargs='+', help='one or more files to convert') return parser def create_converter(self, args): return Txt2Rst() def get_output_filename(self, path): filename, ext = os.path.splitext(path) return filename + ".rst" def main(): app = Txt2RstConverter() app.run() if __name__ == "__main__": main() diff --git a/doc/utils/converters/tests/test_txt2rst.py b/doc/utils/converters/tests/test_txt2rst.py index d6a5cb5a6..904eeb474 100644 --- a/doc/utils/converters/tests/test_txt2rst.py +++ b/doc/utils/converters/tests/test_txt2rst.py @@ -1,510 +1,515 @@ # LAMMPS Documentation Utilities # # Copyright (C) 2015 Richard Berger # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import io import tempfile import unittest from lammpsdoc import txt2rst class TestBasicFormatting(unittest.TestCase): def setUp(self): self.txt2rst = txt2rst.Txt2Rst() def test_empty_string(self): self.assertEqual(self.txt2rst.convert(""), "") def test_single_paragraph(self): self.assertEqual("Hello World!\n\n", self.txt2rst.convert("Hello World!\n")) def test_two_paragraphs(self): s = self.txt2rst.convert("Hello World!\n\nBye World!\n") self.assertEqual("Hello World!\n\n" "Bye World!\n\n", s) def test_line_concat(self): s = self.txt2rst.convert("Hello World!\\\nBye World!\n") self.assertEqual(s, "Hello World!Bye World!\n\n") def test_html_pass_through(self): s = self.txt2rst.convert("
Raw HTML
\n") self.assertEqual(s, ".. raw:: html\n\n" "
Raw HTML
\n\n") def test_ignore_html_only_block(self): s = self.txt2rst.convert("\n" "content :p\n" "\n") self.assertEqual("", s) def test_pass_through_raw_rst(self): raw_rst = ".. toctree::\n" \ " :maxdepth: 2\n" \ " :numbered:\n" \ "\n" \ " Introduction\n" s = self.txt2rst.convert("\n") self.assertEqual(raw_rst, s) class TestMarkup(unittest.TestCase): def setUp(self): self.markup = txt2rst.RSTMarkup() self.txt2rst = txt2rst.Txt2Rst() def test_bold(self): self.assertEqual("**bold**", self.markup.convert("[bold]")) def test_italic(self): self.assertEqual("*italic*", self.markup.convert("{italic}")) def test_escape_markup(self): s = self.markup.convert("[bold] = \\[bold\\]\n" "{italic} = \\{italic\\}\n") self.assertEqual("**bold** = [bold]\n" "*italic* = {italic}\n", s) def test_escape_rst_characters(self): 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) def test_escape_underscore(self): s = self.markup.convert("x_") self.assertEqual("x\_", s) def test_paragraph_with_italic(self): self.assertEqual("A sentence with a *italic* word", self.markup.convert("A sentence with a {italic} word")) def test_paragraph_with_partial_italic(self): self.assertEqual("A sentence with a partial *italic*\ normal word", self.markup.convert("A sentence with a partial {italic}normal word")) def test_paragraph_with_partial_bold(self): self.assertEqual("A sentence with a partial **bold**\ normal word", self.markup.convert("A sentence with a partial [bold]normal word")) def test_paragraph_with_mixed_formats(self): self.assertEqual("A sentence with a partial normal\ **bold**\ *italic*\ normal word", self.markup.convert("A sentence with a partial normal[bold]{italic}normal word")) def test_link_markup(self): self.assertEqual("`Text `_", self.markup.convert('"Text"_link')) def test_document_cross_reference_link(self): self.assertEqual(":doc:`Text `", self.markup.convert('"Text"_link.html')) def test_user_atc_link(self): self.assertEqual("`Text `_", self.markup.convert('"Text"_USER/atc/link.html')) def test_external_link(self): self.assertEqual("`Text `_", self.markup.convert('"Text"_http://site/index.html')) def test_multiline_link_markup(self): s = self.txt2rst.convert('"Te\n' 'xt"_link\n') self.assertEqual("`Te xt `_\n\n", s) def test_ignore_punctuation_in_link(self): self.assertEqual("`Text `_.", self.markup.convert('"Text"_link.')) self.assertEqual("`Text `_,", self.markup.convert('"Text"_link,')) self.assertEqual("`Text `_;", self.markup.convert('"Text"_link;')) self.assertEqual("`Text `_:", self.markup.convert('"Text"_link:')) self.assertEqual("`Text `_?", self.markup.convert('"Text"_link?')) self.assertEqual("`Text `_!", self.markup.convert('"Text"_link!')) self.assertEqual("`Text `_(", self.markup.convert('"Text"_link(')) self.assertEqual("`Text `_)", self.markup.convert('"Text"_link)')) class TestFormatting(unittest.TestCase): def setUp(self): self.txt2rst = txt2rst.Txt2Rst() def test_paragraph_formatting(self): s = self.txt2rst.convert("Hello :p\n") self.assertEqual("Hello\n\n", s) def test_two_paragraphs_through_formatting(self): text = "Hello :p\nBye :p\n" p = list(self.txt2rst.paragraphs(text)) s = self.txt2rst.convert(text) self.assertEqual(len(p), 2) self.assertEqual(s, "Hello\n" "\n" "Bye\n" "\n") def test_break_formatting(self): s = self.txt2rst.convert("Hello :b\n") self.assertEqual("Hello\n", s) def test_preformat_formatting(self): s = self.txt2rst.convert("Hello :pre\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("\n.. parsed-literal::\n\n" " Hello\n" " World\n\n", s) def test_header_formatting(self): s = self.txt2rst.convert("Level 1 :h1\n" "Level 2 :h2\n" "Level 3 :h3\n" "Level 4 :h4\n" "Level 5 :h5\n" "Level 6 :h6\n") self.assertEqual("Level 1\n" "#######\n" "\n" "Level 2\n" "*******\n" "\n" "Level 3\n" "=======\n" "\n" "Level 4\n" "-------\n" "\n" "Level 5\n" "^^^^^^^\n" "\n" "Level 6\n" '"""""""\n' '\n', s) def test_filter_header_numbers(self): s = self.txt2rst.convert("1.1 Level :h1\n") 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" "three :all(b)\n") self.assertEqual("| one\n" "| two\n" "| three \n" "| \n" "\n", s) def test_links_with_all_breaks(self): s = self.txt2rst.convert("\"one\"_link\n" "\"two\"_link\n" "\"three\"_link :all(b)\n") self.assertEqual("| `one `_\n" "| `two `_\n" "| `three `_ \n" "| \n" "\n", s) class TestListFormatting(unittest.TestCase): def setUp(self): self.txt2rst = txt2rst.Txt2Rst() def test_unordered_list(self): s = self.txt2rst.convert("one\n" "two\n" "three :ul\n") self.assertEqual("* one\n" "* two\n" "* three\n\n", s) def test_elementwise_unordered_list(self): s = self.txt2rst.convert("one :ulb,l\n" "two :l\n" "three :ule,l\n") self.assertEqual("* one\n" "* two\n" "* three\n\n", s) def test_elementwise_unordered_list_reverse(self): s = self.txt2rst.convert("one :ulb,l\n" "two :l\n" "three :l,ule\n") self.assertEqual("* one\n" "* two\n" "* three\n\n", s) def test_multi_line_unordered_list_elements(self): s = self.txt2rst.convert("one :ulb,l\n" "two\n" "words :l\n" "three :ule,l\n") self.assertEqual("* one\n" "* two\n" " words\n" "* three\n\n", s) def test_ordered_list(self): s = self.txt2rst.convert("one\n" "two\n" "three :ol\n") self.assertEqual("1. one\n" "2. two\n" "3. three\n\n", s) def test_elementwise_ordered_list(self): s = self.txt2rst.convert("one :olb,l\n" "two :l\n" "three :ole,l\n") self.assertEqual("#. one\n" "#. two\n" "#. three\n\n", s) def test_multi_line_ordered_list_elements(self): s = self.txt2rst.convert("one :olb,l\n" "two\n" "words :l\n" "three :ole,l\n") self.assertEqual("#. one\n" "#. two\n" " words\n" "#. three\n\n", s) def test_paragraphs_ordered_list(self): s = self.txt2rst.convert("first\n" "paragraph :olb,l\n" "second\n" "paragraph :l\n" "third\n" "paragraph :ole,l\n") self.assertEqual("#. first\n" " paragraph\n" "#. second\n" " paragraph\n" "#. third\n" " paragraph\n\n", s) def test_paragraphs_unordered_list(self): s = self.txt2rst.convert("first\n" "paragraph :ulb,l\n" "second\n" "paragraph :l\n" "third\n" "paragraph :ule,l\n") self.assertEqual("* first\n" " paragraph\n" "* second\n" " paragraph\n" "* third\n" " paragraph\n\n", s) def test_definition_list(self): s = self.txt2rst.convert("A\n" "first\n" "B\n" "second :dl\n") self.assertEqual("A\n" " first\n" "\n" "B\n" " 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() def test_line(self): self.txt2rst.document_filters = [] s = self.txt2rst.convert("one :line\n") self.assertEqual("\n" "----------\n" "\n" "one\n", s) def test_image(self): s = self.txt2rst.convert("one :image(file)\n") self.assertEqual(".. image:: file\n" "one\n", s) def test_centered_image(self): s = self.txt2rst.convert(":image(file),c\n") self.assertEqual(".. image:: file\n" " :align: center\n\n", s) def test_image_with_link(self): s = self.txt2rst.convert("one :image(file,link)\n") self.assertEqual(s, ".. image:: file\n" " :target: link\n" "one\n") def test_thumbnail_image(self): # requires sphinxcontrib-images extension s = self.txt2rst.convert("one :image(file,large_file.jpg)\n") self.assertEqual(s, ".. thumbnail:: large_file.jpg\n" "one\n") def test_internal_reference_link(self): s = self.txt2rst.convert("one :link(name)\n" "a \"link\"_name to above\n") self.assertEqual(".. _name:\n" "\n" "one \n\n" "a :ref:`link ` to above\n\n", s) def test_local_anchor_link(self): s = self.txt2rst.convert("one :link(name)\n" "a \"link\"_#name to above\n") self.assertEqual(".. _name:\n" "\n" "one \n\n" "a :ref:`link ` to above\n\n", s) def test_define_link_alias(self): s = self.txt2rst.convert("one :link(alias,value)\n" "\"test\"_alias\n") self.assertEqual(".. _alias: value\n\n" "one \n" "\n" "`test `_\n\n", s) class TestTableCommand(unittest.TestCase): def setUp(self): self.txt2rst = txt2rst.Txt2Rst() def test_convert_table_to_grid_table(self): s = self.txt2rst.convert("a,b,c :tb") table = "+---+---+---+\n" \ "| a | b | c |\n" \ "+---+---+---+\n\n" self.assertEqual(table, s) def test_avoid_rst_syntax_conflicts_with_table_separator(self): s = self.txt2rst.convert("\"a\"_test.html: b: c :tb(s=:)") table = "+-----------------+---+---+\n" \ "| :doc:`a ` | b | c |\n" \ "+-----------------+---+---+\n\n" self.assertEqual(table, s) class TestTxt2RstCLI(unittest.TestCase): def setUp(self): self.out = io.StringIO() self.err = io.StringIO() self.app = txt2rst.Txt2RstConverter() def test_convert_single_file(self): with tempfile.NamedTemporaryFile(mode='w+t') as f: f.write('Hello World!\n') f.flush() args = [f.name] self.app.run(args=args, out=self.out, err=self.err) self.assertEqual("Hello World!\n\n", self.out.getvalue()) self.assertEqual("Converting " + f.name + " ...\n", self.err.getvalue()) class TestMathMarkup(unittest.TestCase): def setUp(self): self.markup = txt2rst.RSTMarkup() self.txt2rst = txt2rst.Txt2Rst() def test_detect_latex_equation(self): s = self.txt2rst.convert("\\begin\\{equation\\} T_\\{ij\\}(r_\\{ij\\}) = 1 - \\left( 1 +\n" "\\frac\{s_\\{ij\} r_\\{ij\} \\}\\{2\\} \\right)\n" "\\exp \\left( - s_\\{ij\\} r_\\{ij\\} \\right) \\end\\{equation\\}\n") self.assertEqual("\n.. math::\n\n" " \\begin{equation} T_{ij}(r_{ij}) = 1 - \\left( 1 +\n" " \\frac{s_{ij} r_{ij} }{2} \\right)\n" " \\exp \\left( - s_{ij} r_{ij} \\right) \\end{equation}\n\n", s) def test_detect_latex_equation_with_mult(self): s = self.txt2rst.convert("\\begin\\{equation\\} a = b * c \\end\\{equation\\}\n") self.assertEqual("\n.. math::\n\n" " \\begin{equation} a = b * c \\end{equation}\n\n", s) def test_detect_latex_equation_with_pow(self): s = self.txt2rst.convert("\\begin\\{equation\\} a = b^c \\end\\{equation\\}\n") self.assertEqual("\n.. math::\n\n" " \\begin{equation} a = b^c \\end{equation}\n\n", s) def test_detect_inline_latex_equation(self): s = self.txt2rst.convert("Masses: \\begin\\{equation\\} M' = M + m \\end\\{equation\\}\n" "\\begin\\{equation\\} m' = \\frac \\{M\\, m \\} \\{M'\\} \\end\\{equation\\}\n") self.assertEqual("Masses:\n" "\n.. math::\n\n" " \\begin{equation} M' = M + m \end{equation}\n" "\n" "\n.. math::\n" "\n" " \\begin{equation} m' = \\frac {M\\, m } {M'} \\end{equation}\n" "\n", s) def test_detect_inline_math(self): self.assertEqual(":math:`x^2`", self.markup.convert("\\( x^2 \\)")) def test_detect_inline_math_mult(self): self.assertEqual(":math:`x * 2`", self.markup.convert("\\( x * 2 \\)")) def test_detect_multiline_inline_math(self): line = "\\(\\sqrt \\{ \\frac \\{2\, k_B \\mathtt\\{Tcom\\}\, m'\\}\n" \ "\\{\\mathrm dt\\, \\mathtt\\{damp\\_com\\} \\}\n" \ "\\} \\). :b\n" \ "\(f_r'\) is a random force proportional to\n" expected = ":math:`\\sqrt { \\frac {2\\, k_B \\mathtt{Tcom}\, m'} " \ "{\\mathrm dt\\, \\mathtt{damp\\_com} } " \ "}`.\n" \ ":math:`f_r'` is a random force proportional to\n\n" self.assertEqual(expected, self.txt2rst.convert(line)) if __name__ == '__main__': unittest.main()