From e6d35b7b37ac9650b8a60bd7463d2a323d120cd6 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 29 May 2018 20:04:50 -0400 Subject: [PATCH] lib/markdown2: introduce maths mode Signed-off-by: Alexandre Terrasa --- lib/markdown2/markdown_html_rendering.nit | 36 ++++ lib/markdown2/markdown_latex_rendering.nit | 11 ++ lib/markdown2/markdown_man_rendering.nit | 11 ++ lib/markdown2/markdown_maths.nit | 146 ++++++++++++++++ lib/markdown2/markdown_md_rendering.nit | 11 ++ lib/markdown2/tests/test_markdown_maths.nit | 176 ++++++++++++++++++++ 6 files changed, 391 insertions(+) create mode 100644 lib/markdown2/markdown_maths.nit create mode 100644 lib/markdown2/tests/test_markdown_maths.nit diff --git a/lib/markdown2/markdown_html_rendering.nit b/lib/markdown2/markdown_html_rendering.nit index 11addf61d1..749157f7e0 100644 --- a/lib/markdown2/markdown_html_rendering.nit +++ b/lib/markdown2/markdown_html_rendering.nit @@ -18,11 +18,20 @@ module markdown_html_rendering import markdown_rendering import markdown_github import markdown_wikilinks +import markdown_maths + +import md5 # Markdown document renderer to HTML class HtmlRenderer super MdRenderer + # Output directory for Maths mode images + # + # If `null`, do not generate images. + # Default: `null`. + var maths_img_outdir: nullable String = null is writable + # HTML output under construction private var html: Buffer is noinit @@ -456,3 +465,30 @@ redef class MdWikilink v.add_raw "" end end + +# Math mode + +redef class MdMaths + redef fun render_html(v) do + var out_dir = v.maths_img_outdir + + if out_dir == null then + v.add_raw opening_delimiter + v.add_raw literal or else "" + v.add_raw closing_delimiter + return + end + + # generate image + out_dir.mkdir + var maths = literal or else "" + var out = "{out_dir}/{maths.md5}.png" + if not out.file_exists then + sys.system "tex2im -o {out} -z -n -r 200x200 \"{maths.escape_to_sh}\"" + end + + v.add_raw "\""" + end +end diff --git a/lib/markdown2/markdown_latex_rendering.nit b/lib/markdown2/markdown_latex_rendering.nit index b7c4abd836..931a08b89d 100644 --- a/lib/markdown2/markdown_latex_rendering.nit +++ b/lib/markdown2/markdown_latex_rendering.nit @@ -18,6 +18,7 @@ module markdown_latex_rendering import markdown_rendering import markdown_github import markdown_wikilinks +import markdown_maths # Markdown document renderer to LaTeX class LatexRenderer @@ -436,3 +437,13 @@ redef class MdWikilink v.add_raw "\}" end end + +# Math mode + +redef class MdMaths + redef fun render_latex(v) do + v.add_raw "$" + v.add_raw literal or else "" + v.add_raw "$" + end +end diff --git a/lib/markdown2/markdown_man_rendering.nit b/lib/markdown2/markdown_man_rendering.nit index 1e047d6c09..c46a51e1ea 100644 --- a/lib/markdown2/markdown_man_rendering.nit +++ b/lib/markdown2/markdown_man_rendering.nit @@ -18,6 +18,7 @@ module markdown_man_rendering import markdown_rendering import markdown_github import markdown_wikilinks +import markdown_maths # Markdown document renderer to Manpage class ManRenderer @@ -256,3 +257,13 @@ redef class MdWikilink v.add ")" end end + +# Math mode + +redef class MdMaths + redef fun render_man(v) do + v.add "$" + v.add literal or else "" + v.add "$" + end +end diff --git a/lib/markdown2/markdown_maths.nit b/lib/markdown2/markdown_maths.nit new file mode 100644 index 0000000000..98ba2f6a37 --- /dev/null +++ b/lib/markdown2/markdown_maths.nit @@ -0,0 +1,146 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Markdown Maths mode +# +# Allows to write Maths equation with the LaTeX Maths mode. +# +# This mode requires `tex2im` to be installed and added to the path. +# See: +module markdown_maths + +intrude import markdown_inline_parsing +intrude import markdown_block_parsing + +redef class MdParser + + # Enable maths mode + var maths_mode = false is writable + + redef var inline_parser is lazy do + var parser = super + parser.maths_mode = maths_mode + return parser + end +end + +redef class MdInlineParser + + # Enable maths mode + private var maths_mode = false + + redef var delimiter_processors is lazy do + var processors = super + if maths_mode then + processors.add new MdMathsProcessor + end + return processors + end +end + +# Maths equation processor +class MdMathsProcessor + super MdDelimiterProcessor + + # Maths equation delimiter + # + # Default is `$`. + var delimiter_char = '$' + + redef var min_length = 1 + redef fun opening_delimiter do return delimiter_char + redef fun closing_delimiter do return delimiter_char + redef fun delimiter_use(opener, closer) do return 1 + + redef fun process(opener, closer, delimiter_use) do + var node = new MdMaths( + new MdLocation( + opener.location.line_start, opener.location.column_start, + closer.location.line_end, closer.location.column_end), + opening_delimiter.to_s) + + var buffer = new Buffer + var tmp = opener.next + while tmp != null and tmp != closer do + var next = tmp.next + tmp.maths_literal(buffer) + tmp.unlink + tmp = next + end + node.literal = buffer.to_s + opener.insert_after(node) + end +end + +# Math equation node +class MdMaths + super MdDelimited + + # Literal Maths string + var literal: nullable String = null +end + +# Inline nodes + +redef class MdNode + + # Return the content of the node as a literal string + private fun maths_literal(buffer: Buffer) do + var node = first_child + while node != null do + node.maths_literal(buffer) + node = node.next + end + end +end + +redef class MdCode + redef fun maths_literal(buffer) do buffer.append "{delimiter}{literal}{delimiter}" +end + +redef class MdDelimited + redef fun maths_literal(buffer) do + buffer.append opening_delimiter + super + buffer.append closing_delimiter + end +end + +redef class MdHtmlInline + redef fun maths_literal(buffer) do buffer.append literal +end + +redef class MdLinkOrImage + redef fun maths_literal(buffer) do + if self isa MdLink and is_autolink then + buffer.append "<{destination}>" + return + end + if self isa MdImage then + buffer.append "!" + end + buffer.append "[" + super + buffer.append "]" + buffer.append "({destination})" + end +end + +redef class MdLink + redef fun maths_literal(buffer) do buffer.append "[{title or else ""}]({destination})" +end + +redef class MdText + redef fun maths_literal(buffer) do buffer.append literal +end diff --git a/lib/markdown2/markdown_md_rendering.nit b/lib/markdown2/markdown_md_rendering.nit index eadda1b684..1123ba874f 100644 --- a/lib/markdown2/markdown_md_rendering.nit +++ b/lib/markdown2/markdown_md_rendering.nit @@ -18,6 +18,7 @@ module markdown_md_rendering import markdown_rendering import markdown_github import markdown_wikilinks +import markdown_maths # Markdown document renderer to Markdown class MarkdownRenderer @@ -390,3 +391,13 @@ redef class MdWikilink v.add_raw "]]" end end + +# Math mode + +redef class MdMaths + redef fun render_md(v) do + v.add_raw "$" + v.add_raw literal or else "" + v.add_raw "$" + end +end diff --git a/lib/markdown2/tests/test_markdown_maths.nit b/lib/markdown2/tests/test_markdown_maths.nit new file mode 100644 index 0000000000..cc36137340 --- /dev/null +++ b/lib/markdown2/tests/test_markdown_maths.nit @@ -0,0 +1,176 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tests for markdown Maths mode +module test_markdown_maths is test + +import test_markdown +import test_markdown_location +import test_markdown_md +import test_markdown_man +import test_markdown_latex + +redef class TestMarkdown + redef var md_parser is lazy do + var parser = super + parser.maths_mode = true + return parser + end +end + +class TestMathsLocation + super TestMarkdownLocation + test + + fun test_maths_strike is test do + var md = """ +A $formula$ text. +""" + var loc = """ +MdDocument: 1,1--1,17 + MdParagraph: 1,1--1,17 + MdText: 1,1--1,2 + MdMaths: 1,3--1,11 + MdText: 1,12--1,17 +""" + assert md_to_loc(md) == loc + end + + fun test_maths_strike2 is test do + var md = """ +A $$formula$$ text. +""" + var loc = """ +MdDocument: 1,1--1,19 + MdParagraph: 1,1--1,19 + MdText: 1,1--1,2 + MdMaths: 1,3--1,13 + MdText: 1,14--1,19 +""" + assert md_to_loc(md) == loc + end +end + +class TestMathsHtml + super TestMarkdownHtml + test + + var img_out_dir = "maths.out" + + fun before is before do + html_renderer.maths_img_outdir = null + end + + fun after is after do + img_out_dir.rmdir + end + + fun test_maths1 is test do + var md = """foo $bar$ baz\n""" + var html = """

foo $bar$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths2 is test do + var md = """foo $1 * 2 * 3$ baz\n""" + var html = """

foo $1 * 2 * 3$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths3 is test do + var md = """foo $1 ^ 2 ^ 3$ baz\n""" + var html = """

foo $1 ^ 2 ^ 3$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths4 is test do + var md = """foo $1 _2_3$ baz\n""" + var html = """

foo $1 _2_3$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths5 is test do + var md = """foo $\\psi_{tot}(x,-t_0,r) = \\frac{1}{(2\\pi)^2} \\int\\!\\!\\!\\int\\tilde\\Psi_{tot}\\left(k_x,\\frac{c}{2}\\sqrt{k_x^2 + k_r^2},r=0\\right)$ baz\n""" + var html = """

foo $\\psi_{tot}(x,-t_0,r) = \\frac{1}{(2\\pi)^2} \\int!!!\\int\\tilde\\Psi_{tot}\\left(k_x,\\frac{c}{2}\\sqrt{k_x^2 + k_r^2},r=0\\right)$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths6 is test do + html_renderer.maths_img_outdir = img_out_dir + var md = """foo $\\psi_{tot}(x,-t_0,r) = \\frac{1}{(2\\pi)^2}$ baz\n""" + var html = """

foo \\psi_{tot}(x,-t_0,r) = \\frac{1}{(2\\pi)^2} baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths_bad is test do + var md = """foo $bar baz\n""" + var html = """

foo $bar baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths_bad2 is test do + var md = """foo $$bar$ baz\n""" + var html = """

foo $$bar$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths_bad3 is test do + var md = """foo $$$bar$ baz\n""" + var html = """

foo $$$bar$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths_bad4 is test do + var md = """foo bar$ baz\n""" + var html = """

foo bar$ baz

\n""" + assert md_to_html(md) == html + end + + fun test_maths_bad5 is test do + var md = """foo $bar$$$$ baz\n""" + var html = """

foo $bar$$$$ baz

\n""" + assert md_to_html(md) == html + end +end + +class TestMathsMd + super TestMarkdownMd + test + + fun test_maths_md is test do + var md = """$foo$\n""" + assert md_to_md(md) == md + end +end + +class TessMathsMan + super TestMarkdownMan + test + + fun test_maths_man is test do + var md = """$formula$\n""" + var man = """\n$formula$\n""" + assert md_to_man(md) == man + end +end + +class TestMathsLatex + super TestMarkdownLatex + test + + fun test_maths_latex is test do + var md = """A $formula$ text.\n""" + assert md_to_tex(md) == md + end +end