src: new module markdown.nit to convert ADoc to HTML
[nit.git] / src / markdown.nit
diff --git a/src/markdown.nit b/src/markdown.nit
new file mode 100644 (file)
index 0000000..9f21f32
--- /dev/null
@@ -0,0 +1,188 @@
+# 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.
+
+# Transform Nit verbatim documentation into HTML
+module markdown
+
+import parser
+import html
+
+# The class that does the convertion from a `ADoc` to HTML
+private class Doc2Mdwn
+       # The lines of the current code block, empty is no current code block
+       var curblock = new Array[String]
+
+       fun work(ndoc: ADoc): HTMLTag
+       do
+               var root = new HTMLTag("div")
+               root.add_class("nitdoc")
+
+               # Indent level of the previous line
+               var lastindent = 0
+
+               # Indent level of the current line
+               var indent = 0
+
+               # The current element (p, li, etc.) if any
+               var n: nullable HTMLTag = null
+
+               # The current ul element (if any)
+               var ul: nullable HTMLTag = null
+
+               # Local variable to benefit adaptive typing
+               for c in ndoc.n_comment do
+                       # Remove the starting `#`
+                       var text = c.text.substring_from(1)
+
+                       # Count the number of spaces
+                       lastindent = indent
+                       indent = 0
+                       while text.length > indent and text[indent] == ' ' do indent += 1
+
+                       # Is codeblock? Then just collect them
+                       if indent > 4 then
+                               var part = text.substring_from(4)
+                               curblock.add(part)
+                               continue
+                       end
+
+                       # Was a codblock just before the current line ?
+                       close_codeblock(n or else root)
+
+                       # Cleanup the string
+                       text = text.trim
+
+                       # The HTML node of the last line, so we know if we continue the same block
+                       var old = n
+
+                       # No line or loss of indentation: reset
+                       if text.is_empty or indent < lastindent then
+                               n = null
+                               ul = null
+                               if text.is_empty then continue
+                       end
+
+                       # Special first word: new paragraph
+                       if text.has_prefix("TODO") or text.has_prefix("FIXME") then
+                               n = new HTMLTag("p")
+                               root.add n
+                               n.add_class("todo")
+                               ul = null
+                       else if text.has_prefix("REQUIRE") or text.has_prefix("Require") or text.has_prefix("ENSURE") or text.has_prefix("Ensure") then
+                               n = new HTMLTag("p")
+                               root.add n
+                               n.add_class("contract")
+                               ul = null
+                       end
+
+                       # List
+                       if text.has_prefix("* ") or text.has_prefix("- ") then
+                               text = text.substring_from(1).trim
+                               if ul == null then
+                                       ul = new HTMLTag("ul")
+                                       root.add ul
+                               end
+                               n = new HTMLTag("li")
+                               ul.add(n)
+                       end
+
+                       # Nothing? then paragraph
+                       if n == null then
+                               n = new HTMLTag("p")
+                               root.add n
+                               ul = null
+                       end
+
+                       if old == n then
+                               # Because spaces and `\n` where trimmed
+                               n.append("\n")
+                       end
+
+                       process_line(n, text)
+
+                       # Special case, the fist line is the synopsys and is in its own paragraph
+                       if c == ndoc.n_comment.first then
+                               n.add_class("synopsys")
+                               n = null
+                       end
+               end
+
+               # If the codeblock was the last code sequence
+               close_codeblock(n or else root)
+
+               return root
+       end
+
+       fun short_work(ndoc: ADoc): HTMLTag
+       do
+                       var text = ndoc.n_comment.first.text.substring_from(1)
+                       var n = new HTMLTag("span")
+                       n.add_class("synopsys")
+                       n.add_class("nitdoc")
+                       process_line(n, text)
+                       return n
+       end
+
+       fun process_line(n: HTMLTag, text: String)
+       do
+               # Loosly detect code parts
+               var parts = text.split("`")
+
+               # Process each code parts, thus alternate between text and code
+               var is_text = true
+               for part in parts do
+                       if is_text then
+                               # Text part
+                               n.append part
+                       else
+                               # Code part
+                               var n2 = new HTMLTag("code")
+                               n.add(n2)
+
+                               n2.text part
+                       end
+                       is_text = not is_text
+               end
+       end
+
+       fun close_codeblock(root: HTMLTag)
+       do
+               # Is there a codeblock to manage?
+               if not curblock.is_empty then
+                       var n = new HTMLTag("pre")
+                       root.add(n)
+                       var btext = curblock.to_s
+
+                       n.append btext
+
+                       curblock.clear
+               end
+       end
+end
+
+redef class ADoc
+       # Build a `<div>` element that contains the full documentation in HTML
+       fun full_markdown: HTMLTag
+       do
+               var d2m = new Doc2Mdwn
+               return d2m.work(self)
+       end
+
+       # Build a `<span>` element that contains the synopsys in HTML
+       fun short_markdown: HTMLTag
+       do
+               var d2m = new Doc2Mdwn
+               return d2m.short_work(self)
+       end
+end