src: cleanup importations
[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 private import parser
19 import html
20 private import highlight
21 private import parser_util
22
23 # The class that does the convertion from a `ADoc` to HTML
24 private class Doc2Mdwn
25 var toolcontext: ToolContext
26
27 # The lines of the current code block, empty is no current code block
28 var curblock = new Array[String]
29
30 fun work(mdoc: MDoc): HTMLTag
31 do
32 var root = new HTMLTag("div")
33 root.add_class("nitdoc")
34
35 # Indent level of the previous line
36 var lastindent = 0
37
38 # Indent level of the current line
39 var indent = 0
40
41 # Expected fencing closing tag (if any)
42 var in_fence: nullable String = null
43
44 # The current element (p, li, etc.) if any
45 var n: nullable HTMLTag = null
46
47 # The current ul element (if any)
48 var ul: nullable HTMLTag = null
49
50 var is_first_line = true
51 # Local variable to benefit adaptive typing
52 for text in mdoc.content do
53 # Count the number of spaces
54 lastindent = indent
55 indent = 0
56 while text.length > indent and text.chars[indent] == ' ' do indent += 1
57
58 # In a fence
59 if in_fence != null then
60 # fence closing
61 if text.substring(0,in_fence.length) == in_fence then
62 close_codeblock(n or else root)
63 in_fence = null
64 continue
65 end
66 # else fence content
67 curblock.add(text)
68 continue
69 end
70
71 # Is codeblock? Then just collect them
72 if indent >= 3 then
73 # to allows 4 spaces including the one that follows the #
74 curblock.add(text)
75 continue
76 end
77
78 # Was a codblock just before the current line ?
79 close_codeblock(n or else root)
80
81 # fence opening
82 if text.substring(0,3) == "~~~" then
83 var l = 3
84 while l < text.length and text.chars[l] == '~' do l += 1
85 in_fence = text.substring(0, l)
86 continue
87 end
88
89 # Cleanup the string
90 text = text.trim
91
92 # The HTML node of the last line, so we know if we continue the same block
93 var old = n
94
95 # No line or loss of indentation: reset
96 if text.is_empty or indent < lastindent then
97 n = null
98 ul = null
99 if text.is_empty then continue
100 end
101
102 # Special first word: new paragraph
103 if text.has_prefix("TODO") or text.has_prefix("FIXME") then
104 n = new HTMLTag("p")
105 root.add n
106 n.add_class("todo")
107 ul = null
108 else if text.has_prefix("REQUIRE") or text.has_prefix("Require") or text.has_prefix("ENSURE") or text.has_prefix("Ensure") then
109 n = new HTMLTag("p")
110 root.add n
111 n.add_class("contract")
112 ul = null
113 end
114
115 # List
116 if text.has_prefix("* ") or text.has_prefix("- ") then
117 text = text.substring_from(1).trim
118 if ul == null then
119 ul = new HTMLTag("ul")
120 root.add ul
121 end
122 n = new HTMLTag("li")
123 ul.add(n)
124 end
125
126 # Nothing? then paragraph
127 if n == null then
128 n = new HTMLTag("p")
129 root.add n
130 ul = null
131 end
132
133 if old == n then
134 # Because spaces and `\n` where trimmed
135 n.append("\n")
136 end
137
138 process_line(n, text)
139
140 # Special case, the fist line is the synopsys and is in its own paragraph
141 if is_first_line then
142 n.add_class("synopsys")
143 n = null
144 is_first_line = false
145 end
146 end
147
148 # If the codeblock was the last code sequence
149 close_codeblock(n or else root)
150
151 return root
152 end
153
154 fun short_work(mdoc: MDoc): HTMLTag
155 do
156 var text = mdoc.content.first
157 var n = new HTMLTag("span")
158 n.add_class("synopsys")
159 n.add_class("nitdoc")
160 process_line(n, text)
161 return n
162 end
163
164 fun process_line(n: HTMLTag, text: String)
165 do
166 # Loosly detect code parts
167 var parts = text.split("`")
168
169 # Process each code parts, thus alternate between text and code
170 var is_text = true
171 for part in parts do
172 if is_text then
173 # Text part
174 n.append part
175 else
176 # Code part
177 var n2 = new HTMLTag("code")
178 n.add(n2)
179 process_code(n2, part)
180 end
181 is_text = not is_text
182 end
183 end
184
185 fun close_codeblock(root: HTMLTag)
186 do
187 # Is there a codeblock to manage?
188 if not curblock.is_empty then
189 # determine the smalest indent
190 var minindent = -1
191 for text in curblock do
192 var indent = 0
193 while indent < text.length and text.chars[indent] == ' ' do indent += 1
194 if minindent == -1 or indent < minindent then
195 minindent = indent
196 end
197 end
198
199 # Generate the text
200 var btext = new FlatBuffer
201 for text in curblock do
202 btext.append text.substring_from(minindent)
203 btext.add '\n'
204 end
205
206 # add the node
207 var n = new HTMLTag("pre")
208 root.add(n)
209 process_code(n, btext.to_s)
210 curblock.clear
211 end
212 end
213
214 fun process_code(n: HTMLTag, text: String)
215 do
216 # Try to parse it
217 var ast = toolcontext.parse_something(text)
218
219 if ast isa AError then
220 n.append text
221 # n.attrs["title"] = ast.message
222 n.add_class("rawcode")
223 else
224 var v = new HighlightVisitor
225 v.enter_visit(ast)
226 n.add(v.html)
227 n.add_class("nitcode")
228 end
229 end
230 end
231
232 redef class MDoc
233 # Build a `<div>` element that contains the full documentation in HTML
234 fun full_markdown: HTMLTag
235 do
236 var res = full_markdown_cache
237 if res != null then return res
238 var tc = new ToolContext
239 var d2m = new Doc2Mdwn(tc)
240 res = d2m.work(self)
241 full_markdown_cache = res
242 return res
243 end
244
245 private var full_markdown_cache: nullable HTMLTag
246
247 # Build a `<span>` element that contains the synopsys in HTML
248 fun short_markdown: HTMLTag
249 do
250 var res = short_markdown_cache
251 if res != null then return res
252 var tc = new ToolContext
253 var d2m = new Doc2Mdwn(tc)
254 res = d2m.short_work(self)
255 short_markdown_cache = res
256 return res
257 end
258
259 private var short_markdown_cache: nullable HTMLTag
260 end