lib/markdown2: introduce markdown rendering to Manpages
authorAlexandre Terrasa <alexandre@moz-code.org>
Tue, 29 May 2018 23:54:13 +0000 (19:54 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Wed, 20 Jun 2018 23:11:18 +0000 (19:11 -0400)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

lib/markdown2/markdown_man_rendering.nit [new file with mode: 0644]
lib/markdown2/tests/test_markdown_man.nit [new file with mode: 0644]

diff --git a/lib/markdown2/markdown_man_rendering.nit b/lib/markdown2/markdown_man_rendering.nit
new file mode 100644 (file)
index 0000000..b2ee9eb
--- /dev/null
@@ -0,0 +1,232 @@
+# 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.
+
+# Manpages rendering of Markdown documents
+module markdown_man_rendering
+
+import markdown_rendering
+
+# Markdown document renderer to Manpage
+class ManRenderer
+       super MdRenderer
+
+       # Output under construction
+       private var man: Buffer is noinit
+
+       # Render `node` as Markdown
+       redef fun render(node) do
+               man = new Buffer
+               enter_visit(node)
+               return man.write_to_string
+       end
+
+       redef fun visit(node) do node.render_man(self)
+
+       # Add `string` to `man`
+       fun add(string: String) do
+               man.append(string.replace("-", "\\-"))
+       end
+
+       # Add code that need to be escaped
+       fun add_code(code: String) do
+               add code.replace(" ", "\\ ")
+       end
+
+       # Add a blank line to the output
+       fun add_line do
+               add "\n"
+       end
+end
+
+redef class MdNode
+
+       # Render `self` as Manpage format
+       fun render_man(v: ManRenderer) do visit_all(v)
+end
+
+# Blocks
+
+redef class MdBlockQuote
+       redef fun render_man(v) do
+               v.add ".RS"
+               visit_all(v)
+               v.add ".RE"
+               v.add_line
+       end
+end
+
+redef class MdCodeBlock
+       redef fun render_man(v) do
+               v.add ".RS\n.nf\n\\f[C]"
+               v.add_line
+
+               var literal = self.literal
+               if literal != null then
+                       var lines = literal.split("\n")
+                       for i in [0 .. lines.length[ do
+                               if i == lines.length - 1 then break
+                               var line = lines[i]
+                               v.add_code line
+                               v.add_line
+                       end
+               end
+
+               v.add "\\f[]\n.fi\n.RE"
+               v.add_line
+       end
+end
+
+redef class MdHeading
+       redef fun render_man(v) do
+               var level = self.level
+
+               if level == 1 then
+                       v.add ".SH "
+               else if level == 2 then
+                       v.add ".SS "
+               else if level >= 3 then
+                       # We use dictionary (titled paragraph) to simulate a 3rd level (or more)
+                       v.add ".TP\n"
+               end
+               visit_all(v)
+               v.add_line
+       end
+end
+
+redef class MdUnorderedList
+       redef fun render_man(v) do
+               v.add ".RS"
+               v.add_line
+
+               var node = first_child
+               while node != null do
+                       v.add ".IP \\[bu] 3"
+                       v.add_line
+                       v.enter_visit node
+                       v.add_line
+                       node = node.next
+               end
+
+               v.add ".RE"
+               v.add_line
+       end
+end
+
+redef class MdOrderedList
+       redef fun render_man(v) do
+               v.add ".RS"
+               v.add_line
+
+               var index = start_number
+               var node = first_child
+               while node != null do
+                       v.add ".IP \"{index}.\" 3"
+                       v.add_line
+                       v.enter_visit node
+                       v.add_line
+                       node = node.next
+                       index += 1
+               end
+
+               v.add ".RE"
+               v.add_line
+       end
+end
+
+redef class MdParagraph
+       redef fun render_man(v) do
+               var in_list = is_in_list
+               if not in_list then
+                       v.add_line
+               end
+               visit_all(v)
+               if not in_list then
+                       v.add_line
+               end
+       end
+end
+
+redef class MdThematicBreak
+       redef fun render_man(v) do
+               v.add "***"
+               v.add_line
+       end
+end
+
+redef class MdHtmlBlock
+       redef fun render_man(v) do
+               v.add_line
+               v.add literal or else ""
+               v.add_line
+       end
+end
+
+# Inlines
+
+redef class MdLineBreak
+       redef fun render_man(v) do
+               v.add_line
+       end
+end
+
+redef class MdCode
+       redef fun render_man(v) do
+               v.add "\\f[C]"
+               v.add_code literal
+               v.add "\\f[]"
+       end
+end
+
+redef class MdEmphasis
+       redef fun render_man(v) do
+               v.add "\\f[I]"
+               visit_all(v)
+               v.add "\\f[]"
+       end
+end
+
+redef class MdStrongEmphasis
+       redef fun render_man(v) do
+               v.add "\\f[B]"
+               visit_all(v)
+               v.add "\\f[]"
+       end
+end
+
+redef class MdHtmlInline
+       redef fun render_man(v) do
+               v.add literal
+       end
+end
+
+redef class MdLinkOrImage
+       redef fun render_man(v) do
+               var title = self.title
+
+               visit_all(v)
+               v.add " ("
+               v.add destination
+               if title != null and not title.is_empty then
+                       v.add " "
+                       v.add title
+               end
+               v.add ")"
+       end
+end
+
+redef class MdText
+       redef fun render_man(v) do
+               v.add literal
+       end
+end
diff --git a/lib/markdown2/tests/test_markdown_man.nit b/lib/markdown2/tests/test_markdown_man.nit
new file mode 100644 (file)
index 0000000..e64dd2a
--- /dev/null
@@ -0,0 +1,386 @@
+# 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 rendering to manpage
+module test_markdown_man is test
+
+import test_markdown
+import markdown_man_rendering
+
+# Abstract test class that defines the test methods for Man rendering
+abstract class TestMarkdownMan
+       super TestMarkdown
+
+       # Man renderer used in tests
+       var man_renderer = new ManRenderer
+
+       # Render the `md` string as Manpage format
+       fun md_to_man(md: String): String do
+               var node = parse_md(md)
+               return man_renderer.render(node)
+       end
+end
+
+class TestManRendering
+       super TestMarkdownMan
+       test
+
+       fun test_headings is test do
+               var md = """# title1\n## title2\n### title3\n#### title4\n##### title5\n###### title6\n"""
+               var man = """.SH title1\n.SS title2\n.TP\ntitle3\n.TP\ntitle4\n.TP\ntitle5\n.TP\ntitle6\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_bquotes is test do
+               var md = """> line 1\n> line 2\n\n> line 3\n>line 4"""
+               var man = """.RS\nline 1\nline 2\n.RE\n.RS\nline 3\nline 4\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_breaks is test do
+               var md = """* * *"""
+               var man = """***\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_indented_code is test do
+               var md = """\tline 1\n\tline 2\n"""
+               var man = """.RS\n.nf\n\\f[C]\nline\\ 1\nline\\ 2\n\\f[]\n.fi\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_fenced_code is test do
+               var md = """~~~\nline 1\nline 2\n~~~\n"""
+               var man = """.RS\n.nf\n\\f[C]\nline\\ 1\nline\\ 2\n\\f[]\n.fi\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_escaped_code is test do
+               var md = """\tline - 1\n\tline - 2\n"""
+               var man = """.RS\n.nf\n\\f[C]\nline\\ \\-\\ 1\nline\\ \\-\\ 2\n\\f[]\n.fi\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_unordered_list is test do
+               var md = """* line 1\n* line 2\n"""
+               var man = """.RS\n.IP \\[bu] 3\nline 1\n.IP \\[bu] 3\nline 2\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_ordered_list is test do
+               var md = """2) line 1\n3) line 2\n"""
+               var man = """.RS\n.IP "2." 3\nline 1\n.IP "3." 3\nline 2\n.RE\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_paragraph is test do
+               var md = """line 1\nline 2\n\nline 3\nline 4\n"""
+               var man = """\nline 1\nline 2\n\nline 3\nline 4\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_escaped_text is test do
+               var md = """foo - bar\n"""
+               var man = """\nfoo \\- bar\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_inline_code is test do
+               var md = """`foo - bar`\n"""
+               var man = """\n\\f[C]foo\\ \\-\\ bar\\f[]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_emphasis is test do
+               var md = """*foo*\n"""
+               var man = """\n\\f[I]foo\\f[]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_strong_emphasis is test do
+               var md = """**foo**\n"""
+               var man = """\n\\f[B]foo\\f[]\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_link is test do
+               var md = """[foo](url "title")\n"""
+               var man = """\nfoo (url title)\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_image is test do
+               var md = """![foo](url "title")\n"""
+               var man = """\nfoo (url title)\n"""
+               assert md_to_man(md) == man
+       end
+
+       fun test_full_document is test do
+
+               var md = """
+# NAME
+
+nitdoc - generates HTML pages of API documentation from Nit source files.
+
+# SYNOPSIS
+
+nitdoc [*options*]... FILE...
+
+# DESCRIPTION
+
+`nitdoc` takes one or more modules and generate HTML pages of API documentation for these modules and their imported modules.
+
+The documentation is extracted from the comments found above the definition of modules, classes, and properties.
+
+Internally, `nitdoc` relies on the presence of the `dot` command from the [graphviz] project.
+If the dot program is not present or not found, no image of hierarchies are generated.
+See option `--no-dot`.
+
+The documentation of the Nit [standard library] is generated with this tool.
+
+  [graphviz]: http://www.graphviz.org
+  [standard library]: http://nitlanguage.org/doc/stdlib
+
+# DOCUMENTATION FORMAT
+
+The format of the documentation is a dialect of [markdown] that allows GitHub fences (`~~~`).
+
+Code blocks are interpreted as snippets of Nit programs and intended to be used as examples of code.
+When these code snippets are valid, executable and contain at least and `assert` clause, they could be automatically executed and verified.
+See `nitunit(1)` for details.
+
+  [markdown]: http://daringfireball.net/projects/markdown
+
+# OPTIONS
+
+### `-d`, `--dir`
+Output directory.
+
+Where the HTML files are generated.
+
+By default, the directory is named `doc`.
+
+### `--source`
+Format to link source code.
+
+The format string is used to generated links to some parts of the source-code.
+Use `%f` for filename, `%l` for first line, and `%L` for last line.
+
+For instance, the [standard library] use the following value to link to files in GitHub:
+
+    "https://github.com/nitlang/nit/blob/$(git rev-parse HEAD)/%f#L%l-%L"
+
+Here, the `git rev-parse HEAD` is used to link to the current snapshot revision of the file.
+
+### `--no-attributes`
+Ignore the attributes.
+
+Note: In Nit, attributes are private. Therefore, this option is only useful
+when combined with `--private`.
+
+### `--no-dot`
+Do not generate graphs with graphviz.
+
+### `--private`
+Also generate private API.
+
+## CUSTOMIZATION
+
+### `--share-dir`
+Directory containing tools assets.
+
+By default `$NIT_DIR/share/nitdoc/` is used.
+
+### `--shareurl`
+Use shareurl instead of copy shared files.
+
+By default, assets from the sharedir a copied into the output directory and referred with a relative path in the generated files.
+With this option, the assets are not copied and the given URL of path is used in the generated files to locate assets.
+
+### `--custom-title`
+Custom title for homepage.
+
+### `--custom-footer-text`
+Custom footer text.
+
+### `--custom-overview-text`
+Custom intro text for homepage.
+
+### `--custom-brand`
+Custom link to external site.
+
+## SERVICES
+
+### `--github-upstream`
+Git branch where edited commits will be pulled into (ex: user:repo:branch).
+
+### `--github-base-sha1`
+Git sha1 of base commit used to create pull request.
+
+### `--github-gitdir`
+Git working directory used to resolve path name (ex: /home/me/myproject/).
+
+### `--piwik-tracker`
+Piwik tracker URL (ex: `nitlanguage.org/piwik/`).
+
+### `--piwik-site-id`
+Piwik site ID.
+
+## TESTING
+
+### `--test`
+Print test data (metrics and structure).
+
+### `--no-render`
+Do not render HTML files.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
+"""
+
+               var man = """
+.SH NAME
+
+nitdoc \\- generates HTML pages of API documentation from Nit source files.
+.SH SYNOPSIS
+
+nitdoc [\\f[I]options\\f[]]... FILE...
+.SH DESCRIPTION
+
+\\f[C]nitdoc\\f[] takes one or more modules and generate HTML pages of API documentation for these modules and their imported modules.
+
+The documentation is extracted from the comments found above the definition of modules, classes, and properties.
+
+Internally, \\f[C]nitdoc\\f[] relies on the presence of the \\f[C]dot\\f[] command from the graphviz (http://www.graphviz.org) project.
+If the dot program is not present or not found, no image of hierarchies are generated.
+See option \\f[C]\\-\\-no\\-dot\\f[].
+
+The documentation of the Nit standard library (http://nitlanguage.org/doc/stdlib) is generated with this tool.
+.SH DOCUMENTATION FORMAT
+
+The format of the documentation is a dialect of markdown (http://daringfireball.net/projects/markdown) that allows GitHub fences (\\f[C]~~~\\f[]).
+
+Code blocks are interpreted as snippets of Nit programs and intended to be used as examples of code.
+When these code snippets are valid, executable and contain at least and \\f[C]assert\\f[] clause, they could be automatically executed and verified.
+See \\f[C]nitunit(1)\\f[] for details.
+.SH OPTIONS
+.TP
+\\f[C]\\-d\\f[], \\f[C]\\-\\-dir\\f[]
+
+Output directory.
+
+Where the HTML files are generated.
+
+By default, the directory is named \\f[C]doc\\f[].
+.TP
+\\f[C]\\-\\-source\\f[]
+
+Format to link source code.
+
+The format string is used to generated links to some parts of the source\\-code.
+Use \\f[C]%f\\f[] for filename, \\f[C]%l\\f[] for first line, and \\f[C]%L\\f[] for last line.
+
+For instance, the standard library (http://nitlanguage.org/doc/stdlib) use the following value to link to files in GitHub:
+.RS
+.nf
+\\f[C]
+"https://github.com/nitlang/nit/blob/$(git\\ rev\\-parse\\ HEAD)/%f#L%l\\-%L"
+\\f[]
+.fi
+.RE
+
+Here, the \\f[C]git\\ rev\\-parse\\ HEAD\\f[] is used to link to the current snapshot revision of the file.
+.TP
+\\f[C]\\-\\-no\\-attributes\\f[]
+
+Ignore the attributes.
+
+Note: In Nit, attributes are private. Therefore, this option is only useful
+when combined with \\f[C]\\-\\-private\\f[].
+.TP
+\\f[C]\\-\\-no\\-dot\\f[]
+
+Do not generate graphs with graphviz.
+.TP
+\\f[C]\\-\\-private\\f[]
+
+Also generate private API.
+.SS CUSTOMIZATION
+.TP
+\\f[C]\\-\\-share\\-dir\\f[]
+
+Directory containing tools assets.
+
+By default \\f[C]$NIT_DIR/share/nitdoc/\\f[] is used.
+.TP
+\\f[C]\\-\\-shareurl\\f[]
+
+Use shareurl instead of copy shared files.
+
+By default, assets from the sharedir a copied into the output directory and referred with a relative path in the generated files.
+With this option, the assets are not copied and the given URL of path is used in the generated files to locate assets.
+.TP
+\\f[C]\\-\\-custom\\-title\\f[]
+
+Custom title for homepage.
+.TP
+\\f[C]\\-\\-custom\\-footer\\-text\\f[]
+
+Custom footer text.
+.TP
+\\f[C]\\-\\-custom\\-overview\\-text\\f[]
+
+Custom intro text for homepage.
+.TP
+\\f[C]\\-\\-custom\\-brand\\f[]
+
+Custom link to external site.
+.SS SERVICES
+.TP
+\\f[C]\\-\\-github\\-upstream\\f[]
+
+Git branch where edited commits will be pulled into (ex: user:repo:branch).
+.TP
+\\f[C]\\-\\-github\\-base\\-sha1\\f[]
+
+Git sha1 of base commit used to create pull request.
+.TP
+\\f[C]\\-\\-github\\-gitdir\\f[]
+
+Git working directory used to resolve path name (ex: /home/me/myproject/).
+.TP
+\\f[C]\\-\\-piwik\\-tracker\\f[]
+
+Piwik tracker URL (ex: \\f[C]nitlanguage.org/piwik/\\f[]).
+.TP
+\\f[C]\\-\\-piwik\\-site\\-id\\f[]
+
+Piwik site ID.
+.SS TESTING
+.TP
+\\f[C]\\-\\-test\\f[]
+
+Print test data (metrics and structure).
+.TP
+\\f[C]\\-\\-no\\-render\\f[]
+
+Do not render HTML files.
+.SH SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from http://nitlanguage.org (http://nitlanguage.org)
+"""
+               assert md_to_man(md) == man
+       end
+end