typing: add `ARangeExpr::init_callsite` and use it everywhere
[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(mdoc: MDoc): 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 var is_first_line = true
47 # Local variable to benefit adaptive typing
48 for text in mdoc.content do
49 # Count the number of spaces
50 lastindent = indent
51 indent = 0
52 while text.length > indent and text.chars[indent] == ' ' do indent += 1
53
54 # Is codeblock? Then just collect them
55 if indent >= 4 then
56 var part = text.substring_from(4)
57 curblock.add(part)
58 curblock.add("\n")
59 continue
60 end
61
62 # Was a codblock just before the current line ?
63 close_codeblock(n or else root)
64
65 # Cleanup the string
66 text = text.trim
67
68 # The HTML node of the last line, so we know if we continue the same block
69 var old = n
70
71 # No line or loss of indentation: reset
72 if text.is_empty or indent < lastindent then
73 n = null
74 ul = null
75 if text.is_empty then continue
76 end
77
78 # Special first word: new paragraph
79 if text.has_prefix("TODO") or text.has_prefix("FIXME") then
80 n = new HTMLTag("p")
81 root.add n
82 n.add_class("todo")
83 ul = null
84 else if text.has_prefix("REQUIRE") or text.has_prefix("Require") or text.has_prefix("ENSURE") or text.has_prefix("Ensure") then
85 n = new HTMLTag("p")
86 root.add n
87 n.add_class("contract")
88 ul = null
89 end
90
91 # List
92 if text.has_prefix("* ") or text.has_prefix("- ") then
93 text = text.substring_from(1).trim
94 if ul == null then
95 ul = new HTMLTag("ul")
96 root.add ul
97 end
98 n = new HTMLTag("li")
99 ul.add(n)
100 end
101
102 # Nothing? then paragraph
103 if n == null then
104 n = new HTMLTag("p")
105 root.add n
106 ul = null
107 end
108
109 if old == n then
110 # Because spaces and `\n` where trimmed
111 n.append("\n")
112 end
113
114 process_line(n, text)
115
116 # Special case, the fist line is the synopsys and is in its own paragraph
117 if is_first_line then
118 n.add_class("synopsys")
119 n = null
120 is_first_line = false
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(mdoc: MDoc): HTMLTag
131 do
132 var text = mdoc.content.first
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 MDoc
192 # Build a `<div>` element that contains the full documentation in HTML
193 fun full_markdown: HTMLTag
194 do
195 var res = full_markdown_cache
196 if res != null then return res
197 var tc = new ToolContext
198 var d2m = new Doc2Mdwn(tc)
199 res = d2m.work(self)
200 full_markdown_cache = res
201 return res
202 end
203
204 private var full_markdown_cache: nullable HTMLTag
205
206 # Build a `<span>` element that contains the synopsys in HTML
207 fun short_markdown: HTMLTag
208 do
209 var res = short_markdown_cache
210 if res != null then return res
211 var tc = new ToolContext
212 var d2m = new Doc2Mdwn(tc)
213 res = d2m.short_work(self)
214 short_markdown_cache = res
215 return res
216 end
217
218 private var short_markdown_cache: nullable HTMLTag
219 end