# 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 docdown private import parser import html private import highlight private import parser_util # The class that does the convertion from a `ADoc` to HTML private class Doc2Mdwn var toolcontext: ToolContext # The lines of the current code block, empty is no current code block var curblock = new Array[String] # Count empty lines between code blocks var empty_lines = 0 # Optional tag for a fence var fence_tag = "" fun work(mdoc: MDoc): 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 # Expected fencing closing tag (if any) var in_fence: nullable String = null # The current element (p, li, etc.) if any var n: nullable HTMLTag = null # The current ul element (if any) var ul: nullable HTMLTag = null var is_first_line = true # Local variable to benefit adaptive typing for text in mdoc.content do # Count the number of spaces lastindent = indent indent = 0 while text.length > indent and text.chars[indent] == ' ' do indent += 1 # In a fence if in_fence != null then # fence closing if text.substring(0,in_fence.length) == in_fence then close_codeblock(n or else root) in_fence = null continue end # else fence content curblock.add(text) continue end # Is codeblock? Then just collect them if indent >= 3 then for i in [0..empty_lines[ do curblock.add("") empty_lines = 0 # to allows 4 spaces including the one that follows the # curblock.add(text) fence_tag = "" continue end # fence opening if text.substring(0,3) == "~~~" then # Was a codblock just before the current line ? close_codeblock(n or else root) var l = 3 while l < text.length and text.chars[l] == '~' do l += 1 in_fence = text.substring(0, l) while l < text.length and (text.chars[l] == '.' or text.chars[l] == ' ') do l += 1 fence_tag = text.substring_from(l) continue end # 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 if not curblock.is_empty then empty_lines += 1 continue end end # Was a codblock just before the current line ? close_codeblock(n or else root) # 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 is_first_line then n.add_class("synopsys") n = null is_first_line = false end end # If the codeblock was the last code sequence close_codeblock(n or else root) return root end fun short_work(mdoc: MDoc): HTMLTag do var text = mdoc.content.first 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) process_code(n2, part, null) 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 empty_lines = 0 # determine the smalest indent var minindent = -1 for text in curblock do var indent = 0 while indent < text.length and text.chars[indent] == ' ' do indent += 1 # skip white lines if indent >= text.length then continue if minindent == -1 or indent < minindent then minindent = indent end end if minindent < 0 then minindent = 0 # Generate the text var btext = new FlatBuffer for text in curblock do btext.append text.substring_from(minindent) btext.add '\n' end # add the node var n = new HTMLTag("pre") root.add(n) process_code(n, btext.to_s, fence_tag) curblock.clear end end fun process_code(n: HTMLTag, text: String, tag: nullable String) do # Do not try to highlight non-nit code. if tag != null and tag != "" and tag != "nit" and tag != "nitish" then n.append text n.add_class("rawcode") return end # Try to parse it var ast = toolcontext.parse_something(text) if ast isa AError then n.append text # n.attrs["title"] = ast.message n.add_class("rawcode") else var v = new HighlightVisitor v.enter_visit(ast) n.add(v.html) n.add_class("nitcode") end end end redef class MDoc # Build a `
` element that contains the full documentation in HTML fun full_markdown: HTMLTag do var res = full_markdown_cache if res != null then return res var tc = new ToolContext var d2m = new Doc2Mdwn(tc) res = d2m.work(self) full_markdown_cache = res return res end private var full_markdown_cache: nullable HTMLTag # Build a `` element that contains the synopsys in HTML fun short_markdown: HTMLTag do var res = short_markdown_cache if res != null then return res var tc = new ToolContext var d2m = new Doc2Mdwn(tc) res = d2m.short_work(self) short_markdown_cache = res return res end private var short_markdown_cache: nullable HTMLTag end