rta: store real types in live_cast_type
[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 import highlight
21
22 # The class that does the convertion from a `ADoc` to HTML
23 private class Doc2Mdwn
24 var toolcontext: ToolContext
25
26 # The lines of the current code block, empty is no current code block
27 var curblock = new Array[String]
28
29 fun work(ndoc: ADoc): HTMLTag
30 do
31 var root = new HTMLTag("div")
32 root.add_class("nitdoc")
33
34 # Indent level of the previous line
35 var lastindent = 0
36
37 # Indent level of the current line
38 var indent = 0
39
40 # The current element (p, li, etc.) if any
41 var n: nullable HTMLTag = null
42
43 # The current ul element (if any)
44 var ul: nullable HTMLTag = null
45
46 # Local variable to benefit adaptive typing
47 for c in ndoc.n_comment do
48 # Remove the starting `#`
49 var text = c.text.substring_from(1)
50
51 # Count the number of spaces
52 lastindent = indent
53 indent = 0
54 while text.length > indent and text.chars[indent] == ' ' do indent += 1
55
56 # Is codeblock? Then just collect them
57 if indent > 4 then
58 var part = text.substring_from(4)
59 curblock.add(part)
60 continue
61 end
62
63 # Was a codblock just before the current line ?
64 close_codeblock(n or else root)
65
66 # Cleanup the string
67 text = text.trim
68
69 # The HTML node of the last line, so we know if we continue the same block
70 var old = n
71
72 # No line or loss of indentation: reset
73 if text.is_empty or indent < lastindent then
74 n = null
75 ul = null
76 if text.is_empty then continue
77 end
78
79 # Special first word: new paragraph
80 if text.has_prefix("TODO") or text.has_prefix("FIXME") then
81 n = new HTMLTag("p")
82 root.add n
83 n.add_class("todo")
84 ul = null
85 else if text.has_prefix("REQUIRE") or text.has_prefix("Require") or text.has_prefix("ENSURE") or text.has_prefix("Ensure") then
86 n = new HTMLTag("p")
87 root.add n
88 n.add_class("contract")
89 ul = null
90 end
91
92 # List
93 if text.has_prefix("* ") or text.has_prefix("- ") then
94 text = text.substring_from(1).trim
95 if ul == null then
96 ul = new HTMLTag("ul")
97 root.add ul
98 end
99 n = new HTMLTag("li")
100 ul.add(n)
101 end
102
103 # Nothing? then paragraph
104 if n == null then
105 n = new HTMLTag("p")
106 root.add n
107 ul = null
108 end
109
110 if old == n then
111 # Because spaces and `\n` where trimmed
112 n.append("\n")
113 end
114
115 process_line(n, text)
116
117 # Special case, the fist line is the synopsys and is in its own paragraph
118 if c == ndoc.n_comment.first then
119 n.add_class("synopsys")
120 n = null
121 end
122 end
123
124 # If the codeblock was the last code sequence
125 close_codeblock(n or else root)
126
127 return root
128 end
129
130 fun short_work(ndoc: ADoc): HTMLTag
131 do
132 var text = ndoc.n_comment.first.text.substring_from(1)
133 var n = new HTMLTag("span")
134 n.add_class("synopsys")
135 n.add_class("nitdoc")
136 process_line(n, text)
137 return n
138 end
139
140 fun process_line(n: HTMLTag, text: String)
141 do
142 # Loosly detect code parts
143 var parts = text.split("`")
144
145 # Process each code parts, thus alternate between text and code
146 var is_text = true
147 for part in parts do
148 if is_text then
149 # Text part
150 n.append part
151 else
152 # Code part
153 var n2 = new HTMLTag("code")
154 n.add(n2)
155 process_code(n2, part)
156 end
157 is_text = not is_text
158 end
159 end
160
161 fun close_codeblock(root: HTMLTag)
162 do
163 # Is there a codeblock to manage?
164 if not curblock.is_empty then
165 var n = new HTMLTag("pre")
166 root.add(n)
167 var btext = curblock.to_s
168 process_code(n, btext)
169 curblock.clear
170 end
171 end
172
173 fun process_code(n: HTMLTag, text: String)
174 do
175 # Try to parse it
176 var ast = toolcontext.parse_something(text)
177
178 if ast isa AError then
179 n.append text
180 # n.attrs["title"] = ast.message
181 n.add_class("rawcode")
182 else
183 var v = new HighlightVisitor
184 v.enter_visit(ast)
185 n.add(v.html)
186 n.add_class("nitcode")
187 end
188 end
189 end
190
191 redef class ADoc
192 # Build a `<div>` element that contains the full documentation in HTML
193 fun full_markdown: HTMLTag
194 do
195 var tc = new ToolContext
196 var d2m = new Doc2Mdwn(tc)
197 return d2m.work(self)
198 end
199
200 # Build a `<span>` element that contains the synopsys in HTML
201 fun short_markdown: HTMLTag
202 do
203 var tc = new ToolContext
204 var d2m = new Doc2Mdwn(tc)
205 return d2m.short_work(self)
206 end
207 end