highlight: extract HTML stuff from highlight into htmlight
[nit.git] / src / doc / doc_down.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 # Handle markdown formatting in Nit comments.
16 module doc_down
17
18 import markdown
19 import htmlight
20 private import parser_util
21
22 redef class MDoc
23
24 # Synopsis HTML escaped.
25 var synopsis: String is lazy do return content.first.html_escape
26
27 # Comment without synopsis HTML escaped
28 var comment: String is lazy do
29 var lines = content.to_a
30 if not lines.is_empty then lines.shift
31 return lines.join("\n")
32 end
33
34 # Full comment HTML escaped.
35 var documentation: String is lazy do return content.join("\n")
36
37 private var markdown_proc: MarkdownProcessor is lazy, writable do
38 return original_mentity.as(not null).model.nitdoc_md_processor
39 end
40
41 private var inline_proc: MarkdownProcessor is lazy, writable do
42 return original_mentity.as(not null).model.nitdoc_inline_processor
43 end
44
45 # Renders the synopsis as a HTML comment block.
46 var html_synopsis: Writable is lazy do
47 var res = new Template
48 var syn = inline_proc.process(content.first)
49 res.add "<span class=\"synopsys nitdoc\">{syn}</span>"
50 return res
51 end
52
53 # Renders the synopsis as a HTML comment block.
54 var md_synopsis: Writable is lazy do
55 if content.is_empty then return ""
56 return content.first
57 end
58
59 # Renders the comment without the synopsis as a HTML comment block.
60 var html_comment: Writable is lazy do
61 var lines = content.to_a
62 if not lines.is_empty then lines.shift
63 return lines_to_html(lines)
64 end
65
66 #
67 var md_comment: Writable is lazy do
68 if content.is_empty then return ""
69 var lines = content.to_a
70 lines.shift
71 return lines.join("\n")
72 end
73
74 # Renders the synopsis and the comment as a HTML comment block.
75 var html_documentation: Writable is lazy do return lines_to_html(content.to_a)
76
77 # Renders the synopsis and the comment as a HTML comment block.
78 var md_documentation: Writable is lazy do return lines_to_md(content.to_a)
79
80 # Renders markdown line as a HTML comment block.
81 private fun lines_to_html(lines: Array[String]): Writable do
82 var res = new Template
83 var decorator = markdown_proc.decorator.as(NitdocDecorator)
84 decorator.current_mdoc = self
85 res.add "<div class=\"nitdoc\">"
86 # do not use DocUnit as synopsys
87 if not lines.is_empty then
88 if not lines.first.has_prefix(" ") and
89 not lines.first.has_prefix("\t") then
90 # parse synopsys
91 var syn = inline_proc.process(lines.shift)
92 res.add "<h1 class=\"synopsys\">{syn}</h1>"
93 end
94 end
95 # check for annotations
96 for i in [0 .. lines.length[ do
97 var line = lines[i]
98 if line.to_upper.has_prefix("ENSURE") or line.to_upper.has_prefix("REQUIRE") then
99 var html = inline_proc.process(line)
100 lines[i] = "<p class=\"contract\">{html}</p>"
101 else if line.to_upper.has_prefix("TODO") or line.to_upper.has_prefix("FIXME") then
102 var html = inline_proc.process(line)
103 lines[i] = "<p class=\"todo\">{html}</p>"
104 end
105 end
106 # add other lines
107 res.add markdown_proc.process(lines.join("\n"))
108 res.add "</div>"
109 decorator.current_mdoc = null
110 return res
111 end
112
113 private fun lines_to_md(lines: Array[String]): Writable do
114 var res = new Template
115 if not lines.is_empty then
116 var syn = lines.first
117 if not syn.has_prefix(" ") and not syn.has_prefix("\t") and
118 not syn.trim.has_prefix("#") then
119 lines.shift
120 res.add "# {syn}\n"
121 end
122 end
123 res.add lines.join("\n")
124 return res
125 end
126 end
127
128 # The specific markdown decorator used internally to process MDoc object.
129 #
130 # You should use the various methods of `MDoc` like `MDoc::html_documentation`
131 #
132 # The class is public so specific behavior can be plugged on it.
133 class NitdocDecorator
134 super HTMLDecorator
135
136 private var toolcontext = new ToolContext
137
138 # The currently processed mdoc.
139 #
140 # Unfortunately, this seems to be the simpler way to get the currently processed `MDoc` object.
141 var current_mdoc: nullable MDoc = null
142
143 redef fun add_code(v, block) do
144 var meta = block.meta or else "nit"
145
146 # Do not try to highlight non-nit code.
147 if meta != "nit" and meta != "nitish" then
148 v.add "<pre class=\"{meta}\"><code>"
149 v.emit_in block
150 v.add "</code></pre>\n"
151 return
152 end
153 # Try to parse code
154 var code = block.raw_content
155 var ast = toolcontext.parse_something(code)
156 if ast isa AError then
157 v.add "<pre class=\"{meta}\"><code>"
158 v.emit_in block
159 v.add "</code></pre>\n"
160 return
161 end
162 v.add "<pre class=\"nitcode\"><code>"
163 var hl = new HtmlightVisitor
164 hl.line_id_prefix = ""
165 hl.highlight_node(ast)
166 v.add(hl.html)
167 v.add "</code></pre>\n"
168 end
169
170 redef fun add_span_code(v, text, from, to) do
171 # Try to parse it
172 var code = code_from_text(text, from, to)
173 var ast = toolcontext.parse_something(code)
174
175 if ast isa AError then
176 v.add "<code class=\"rawcode\">"
177 append_code(v, text, from, to)
178 else
179 v.add "<code class=\"nitcode\">"
180 var hl = new HtmlightVisitor
181 hl.line_id_prefix = ""
182 hl.highlight_node(ast)
183 v.add(hl.html)
184 end
185 v.add "</code>"
186 end
187
188 private fun code_from_text(buffer: Text, from, to: Int): String do
189 var out = new FlatBuffer
190 for i in [from..to[ do out.add buffer[i]
191 return out.write_to_string
192 end
193 end
194
195 # Decorator for span elements.
196 #
197 # Because inline comments can appear as span elements,
198 # InlineDecorator do not decorate things like paragraphs or headers.
199 private class InlineDecorator
200 super NitdocDecorator
201
202 redef fun add_paragraph(v, block) do
203 v.emit_in block
204 end
205
206 redef fun add_headline(v, block) do
207 v.emit_in block
208 end
209
210 redef fun add_code(v, block) do
211 # Try to parse code
212 var ast = toolcontext.parse_something(block.block.text.to_s)
213 if ast isa AError then
214 v.add "<code>"
215 v.emit_in block
216 v.add "</code>"
217 return
218 end
219 v.add "<code class=\"nitcode\">"
220 var hl = new HtmlightVisitor
221 hl.highlight_node(ast)
222 v.add(hl.html)
223 v.add "</code>"
224 end
225 end
226
227 redef class Model
228 # Get a markdown processor for Nitdoc comments.
229 var nitdoc_md_processor: MarkdownProcessor is lazy, writable do
230 var proc = new MarkdownProcessor
231 proc.decorator = new NitdocDecorator
232 return proc
233 end
234
235 # Get a markdown inline processor for Nitdoc comments.
236 #
237 # This processor is specificaly designed to inlinable doc elements like synopsys.
238 var nitdoc_inline_processor: MarkdownProcessor is lazy, writable do
239 var proc = new MarkdownProcessor
240 proc.decorator = new InlineDecorator
241 return proc
242 end
243 end