src: new module markdown.nit to convert ADoc to HTML
[nit.git] / src / markdown.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Transform Nit verbatim documentation into HTML
16 module markdown
17
18 import parser
19 import html
20
21 # The class that does the convertion from a `ADoc` to HTML
22 private class Doc2Mdwn
23 # The lines of the current code block, empty is no current code block
24 var curblock = new Array[String]
25
26 fun work(ndoc: ADoc): HTMLTag
27 do
28 var root = new HTMLTag("div")
29 root.add_class("nitdoc")
30
31 # Indent level of the previous line
32 var lastindent = 0
33
34 # Indent level of the current line
35 var indent = 0
36
37 # The current element (p, li, etc.) if any
38 var n: nullable HTMLTag = null
39
40 # The current ul element (if any)
41 var ul: nullable HTMLTag = null
42
43 # Local variable to benefit adaptive typing
44 for c in ndoc.n_comment do
45 # Remove the starting `#`
46 var text = c.text.substring_from(1)
47
48 # Count the number of spaces
49 lastindent = indent
50 indent = 0
51 while text.length > indent and text[indent] == ' ' do indent += 1
52
53 # Is codeblock? Then just collect them
54 if indent > 4 then
55 var part = text.substring_from(4)
56 curblock.add(part)
57 continue
58 end
59
60 # Was a codblock just before the current line ?
61 close_codeblock(n or else root)
62
63 # Cleanup the string
64 text = text.trim
65
66 # The HTML node of the last line, so we know if we continue the same block
67 var old = n
68
69 # No line or loss of indentation: reset
70 if text.is_empty or indent < lastindent then
71 n = null
72 ul = null
73 if text.is_empty then continue
74 end
75
76 # Special first word: new paragraph
77 if text.has_prefix("TODO") or text.has_prefix("FIXME") then
78 n = new HTMLTag("p")
79 root.add n
80 n.add_class("todo")
81 ul = null
82 else if text.has_prefix("REQUIRE") or text.has_prefix("Require") or text.has_prefix("ENSURE") or text.has_prefix("Ensure") then
83 n = new HTMLTag("p")
84 root.add n
85 n.add_class("contract")
86 ul = null
87 end
88
89 # List
90 if text.has_prefix("* ") or text.has_prefix("- ") then
91 text = text.substring_from(1).trim
92 if ul == null then
93 ul = new HTMLTag("ul")
94 root.add ul
95 end
96 n = new HTMLTag("li")
97 ul.add(n)
98 end
99
100 # Nothing? then paragraph
101 if n == null then
102 n = new HTMLTag("p")
103 root.add n
104 ul = null
105 end
106
107 if old == n then
108 # Because spaces and `\n` where trimmed
109 n.append("\n")
110 end
111
112 process_line(n, text)
113
114 # Special case, the fist line is the synopsys and is in its own paragraph
115 if c == ndoc.n_comment.first then
116 n.add_class("synopsys")
117 n = null
118 end
119 end
120
121 # If the codeblock was the last code sequence
122 close_codeblock(n or else root)
123
124 return root
125 end
126
127 fun short_work(ndoc: ADoc): HTMLTag
128 do
129 var text = ndoc.n_comment.first.text.substring_from(1)
130 var n = new HTMLTag("span")
131 n.add_class("synopsys")
132 n.add_class("nitdoc")
133 process_line(n, text)
134 return n
135 end
136
137 fun process_line(n: HTMLTag, text: String)
138 do
139 # Loosly detect code parts
140 var parts = text.split("`")
141
142 # Process each code parts, thus alternate between text and code
143 var is_text = true
144 for part in parts do
145 if is_text then
146 # Text part
147 n.append part
148 else
149 # Code part
150 var n2 = new HTMLTag("code")
151 n.add(n2)
152
153 n2.text part
154 end
155 is_text = not is_text
156 end
157 end
158
159 fun close_codeblock(root: HTMLTag)
160 do
161 # Is there a codeblock to manage?
162 if not curblock.is_empty then
163 var n = new HTMLTag("pre")
164 root.add(n)
165 var btext = curblock.to_s
166
167 n.append btext
168
169 curblock.clear
170 end
171 end
172 end
173
174 redef class ADoc
175 # Build a `<div>` element that contains the full documentation in HTML
176 fun full_markdown: HTMLTag
177 do
178 var d2m = new Doc2Mdwn
179 return d2m.work(self)
180 end
181
182 # Build a `<span>` element that contains the synopsys in HTML
183 fun short_markdown: HTMLTag
184 do
185 var d2m = new Doc2Mdwn
186 return d2m.short_work(self)
187 end
188 end