1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Transform Nit verbatim documentation into HTML
20 private import highlight
21 private import parser_util
23 # The class that does the convertion from a `ADoc` to HTML
24 private class Doc2Mdwn
25 var toolcontext
: ToolContext
27 # The lines of the current code block, empty is no current code block
28 var curblock
= new Array[String]
30 # Count empty lines between code blocks
33 # Optional tag for a fence
36 fun work
(mdoc
: MDoc): HTMLTag
38 var root
= new HTMLTag("div")
39 root
.add_class
("nitdoc")
41 # Indent level of the previous line
44 # Indent level of the current line
47 # Expected fencing closing tag (if any)
48 var in_fence
: nullable String = null
50 # The current element (p, li, etc.) if any
51 var n
: nullable HTMLTag = null
53 # The current ul element (if any)
54 var ul
: nullable HTMLTag = null
56 var is_first_line
= true
57 # Local variable to benefit adaptive typing
58 for text
in mdoc
.content
do
59 # Count the number of spaces
62 while text
.length
> indent
and text
.chars
[indent
] == ' ' do indent
+= 1
65 if in_fence
!= null then
67 if text
.substring
(0,in_fence
.length
) == in_fence
then
68 close_codeblock
(n
or else root
)
77 # Is codeblock? Then just collect them
79 for i
in [0..empty_lines
[ do curblock
.add
("")
81 # to allows 4 spaces including the one that follows the #
88 if text
.substring
(0,3) == "~~~" then
89 # Was a codblock just before the current line ?
90 close_codeblock
(n
or else root
)
93 while l
< text
.length
and text
.chars
[l
] == '~' do l
+= 1
94 in_fence
= text
.substring
(0, l
)
95 while l
< text
.length
and (text
.chars
[l
] == '.' or text
.chars
[l
] == ' ') do l
+= 1
96 fence_tag
= text
.substring_from
(l
)
103 # The HTML node of the last line, so we know if we continue the same block
106 # No line or loss of indentation: reset
107 if text
.is_empty
or indent
< lastindent
then
110 if text
.is_empty
then
111 if not curblock
.is_empty
then empty_lines
+= 1
116 # Was a codblock just before the current line ?
117 close_codeblock
(n
or else root
)
119 # Special first word: new paragraph
120 if text
.has_prefix
("TODO") or text
.has_prefix
("FIXME") then
125 else if text
.has_prefix
("REQUIRE") or text
.has_prefix
("Require") or text
.has_prefix
("ENSURE") or text
.has_prefix
("Ensure") then
128 n
.add_class
("contract")
133 if text
.has_prefix
("* ") or text
.has_prefix
("- ") then
134 text
= text
.substring_from
(1).trim
136 ul
= new HTMLTag("ul")
139 n
= new HTMLTag("li")
143 # Nothing? then paragraph
151 # Because spaces and `\n` where trimmed
155 process_line
(n
, text
)
157 # Special case, the fist line is the synopsys and is in its own paragraph
158 if is_first_line
then
159 n
.add_class
("synopsys")
161 is_first_line
= false
165 # If the codeblock was the last code sequence
166 close_codeblock
(n
or else root
)
171 fun short_work
(mdoc
: MDoc): HTMLTag
173 var text
= mdoc
.content
.first
174 var n
= new HTMLTag("span")
175 n
.add_class
("synopsys")
176 n
.add_class
("nitdoc")
177 process_line
(n
, text
)
181 fun process_line
(n
: HTMLTag, text
: String)
183 # Loosly detect code parts
184 var parts
= text
.split
("`")
186 # Process each code parts, thus alternate between text and code
194 var n2
= new HTMLTag("code")
196 process_code
(n2
, part
, null)
198 is_text
= not is_text
202 fun close_codeblock
(root
: HTMLTag)
204 # Is there a codeblock to manage?
205 if not curblock
.is_empty
then
208 # determine the smalest indent
210 for text
in curblock
do
212 while indent
< text
.length
and text
.chars
[indent
] == ' ' do indent
+= 1
214 if indent
>= text
.length
then continue
215 if minindent
== -1 or indent
< minindent
then
219 if minindent
< 0 then minindent
= 0
222 var btext
= new FlatBuffer
223 for text
in curblock
do
224 btext
.append text
.substring_from
(minindent
)
229 var n
= new HTMLTag("pre")
231 process_code
(n
, btext
.to_s
, fence_tag
)
236 fun process_code
(n
: HTMLTag, text
: String, tag
: nullable String)
238 # Do not try to highlight non-nit code.
239 if tag
!= null and tag
!= "" and tag
!= "nit" and tag
!= "nitish" then
241 n
.add_class
("rawcode")
246 var ast
= toolcontext
.parse_something
(text
)
248 if ast
isa AError then
250 # n.attrs["title"] = ast.message
251 n
.add_class
("rawcode")
253 var v
= new HighlightVisitor
256 n
.add_class
("nitcode")
262 # Build a `<div>` element that contains the full documentation in HTML
263 fun full_markdown
: HTMLTag
265 var res
= full_markdown_cache
266 if res
!= null then return res
267 var tc
= new ToolContext
268 var d2m
= new Doc2Mdwn(tc
)
270 full_markdown_cache
= res
274 private var full_markdown_cache
: nullable HTMLTag
276 # Build a `<span>` element that contains the synopsys in HTML
277 fun short_markdown
: HTMLTag
279 var res
= short_markdown_cache
280 if res
!= null then return res
281 var tc
= new ToolContext
282 var d2m
= new Doc2Mdwn(tc
)
283 res
= d2m
.short_work
(self)
284 short_markdown_cache
= res
288 private var short_markdown_cache
: nullable HTMLTag