src/doc/commands: clean `commands_docdown`
[nit.git] / src / doc / templates / templates_html.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 # Translate mentities to html blocks.
16 module templates_html
17
18 import model::model_collect
19 import catalog
20
21 import markdown
22 import htmlight
23 import html::bootstrap
24 private import parser_util
25
26 redef class MEntity
27
28 # The MEntity unique ID in the HTML output
29 var html_id: String is lazy do return full_name.to_cmangle
30
31 # The MEntity URL in the HTML output
32 #
33 # You MUST redefine this method.
34 # Depending on your implementation, this URL can be a page URL or an anchor.
35 var html_url: String is lazy do return html_id
36
37 # The MEntity name escaped for HTML
38 var html_name: String is lazy do return name.html_escape
39
40 # The MEntity `full_name` escaped for HTML
41 var html_full_name: String is lazy do return full_name.html_escape
42
43 # Link to the MEntity in the HTML output
44 #
45 # You should redefine this method depending on the organization or your
46 # output.
47 fun html_link(text, title: nullable String): Link do
48 if text == null then
49 text = html_name
50 end
51 var mdoc = self.mdoc_or_fallback
52 if title == null and mdoc != null then
53 title = mdoc.synopsis
54 end
55 return new Link(html_url, text, title)
56 end
57
58 # Returns the complete MEntity declaration decorated with HTML
59 #
60 # Examples:
61 # * MPackage: `package foo`
62 # * MGroup: `group foo`
63 # * MModule: `module foo`
64 # * MClass: `private abstract class Foo[E: Object]`
65 # * MClassDef: `redef class Foo[E]`
66 # * MProperty: `private fun foo(e: Object): Int`
67 # * MPropdef: `redef fun foo(e)`
68 fun html_declaration: Template do
69 var tpl = new Template
70 tpl.add "<span class='signature'>"
71 for modifier in collect_modifiers do
72 tpl.add "<span class='modifier'>{modifier}</span>&nbsp;"
73 end
74 tpl.add "<span class='name'>{html_link.write_to_string}</span>"
75 tpl.add html_signature(false)
76 tpl.add "</span>"
77 return tpl
78 end
79
80 # Returns the MEntity signature decorated with HTML
81 #
82 # This function only returns the parenthesis and return types.
83 # See `html_declaration` for the full declaration including modifiers and name.
84 fun html_signature(short: nullable Bool): Template do return new Template
85
86 # Returns `full_name` decorated with HTML links
87 fun html_namespace: Template is abstract
88
89 # An icon representative of the mentity
90 fun html_icon: BSIcon do return new BSIcon("tag", ["text-muted"])
91
92 # CSS classes used to decorate `self`
93 #
94 # Mainly used for icons.
95 var css_classes: Array[String] = collect_modifiers is lazy
96 end
97
98 redef class MPackage
99 redef fun html_namespace do return html_link
100 redef fun html_icon do return new BSIcon("book", ["text-muted"])
101 redef var css_classes = ["public"]
102 end
103
104 redef class MGroup
105 redef fun html_icon do return new BSIcon("folder-close", ["text-muted"])
106
107 redef fun html_namespace do
108 var tpl = new Template
109 var parent = self.parent
110 if parent != null then
111 tpl.add parent.html_namespace
112 tpl.add " > "
113 end
114 tpl.add html_link
115 return tpl
116 end
117 end
118
119 redef class MModule
120 redef fun html_icon do return new BSIcon("file", ["text-muted"])
121
122 redef fun html_namespace do
123 var mpackage = self.mpackage
124 var tpl = new Template
125 if mpackage != null then
126 tpl.add mpackage.html_namespace
127 tpl.add " :: "
128 end
129 tpl.add html_link
130 return tpl
131 end
132 end
133
134 redef class MClass
135 redef fun html_icon do return new BSIcon("stop", css_classes)
136 redef fun html_signature(short) do return intro.html_signature(short)
137 redef fun css_classes do return super + [visibility.to_s]
138
139 redef fun html_namespace do
140 var mgroup = intro_mmodule.mgroup
141 var tpl = new Template
142 if mgroup != null then
143 tpl.add mgroup.mpackage.html_namespace
144 tpl.add " :: "
145 end
146 tpl.add "<span>"
147 tpl.add html_link
148 tpl.add "</span>"
149 return tpl
150 end
151 end
152
153 redef class MClassDef
154 redef fun css_classes do return super + mclass.css_classes
155
156 redef fun html_namespace do
157 var tpl = new Template
158 var mpackage = mmodule.mpackage
159 if mpackage != null and is_intro then
160 if is_intro then
161 tpl.add mpackage.html_namespace
162 tpl.add " $ "
163 else
164 tpl.add mmodule.html_namespace
165 tpl.add " $ "
166 var intro_mpackage = mclass.intro.mmodule.mpackage
167 if intro_mpackage != null and mpackage != intro_mpackage then
168 tpl.add intro_mpackage.html_namespace
169 tpl.add " :: "
170 end
171 end
172 else
173 tpl.add mmodule.html_namespace
174 tpl.add " $ "
175 end
176 tpl.add html_link
177 return tpl
178 end
179
180 redef fun html_icon do
181 if is_intro then
182 return new BSIcon("plus", css_classes)
183 end
184 return new BSIcon("asterisk", css_classes)
185 end
186
187 redef fun html_signature(short) do
188 var tpl = new Template
189 var mparameters = mclass.mparameters
190 if not mparameters.is_empty then
191 tpl.add "["
192 for i in [0..mparameters.length[ do
193 tpl.add mparameters[i].html_name
194 if short == null or not short then
195 tpl.add ": "
196 tpl.add bound_mtype.arguments[i].html_signature(short)
197 end
198 if i < mparameters.length - 1 then tpl.add ", "
199 end
200 tpl.add "]"
201 end
202 return tpl
203 end
204 end
205
206 redef class MProperty
207 redef fun html_declaration do return intro.html_declaration
208 redef fun html_signature(short) do return intro.html_signature(short)
209 redef fun html_icon do return new BSIcon("tag", css_classes)
210 redef fun css_classes do return super + [visibility.to_s]
211
212 redef fun html_namespace do
213 var tpl = new Template
214 tpl.add intro_mclassdef.mclass.html_namespace
215 tpl.add " :: "
216 tpl.add intro.html_link
217 return tpl
218 end
219 end
220
221 redef class MPropDef
222 redef fun css_classes do return super + mproperty.css_classes
223
224 redef fun html_namespace do
225 var tpl = new Template
226 tpl.add mclassdef.html_namespace
227 tpl.add " :: "
228 tpl.add html_link
229 return tpl
230 end
231
232 redef fun html_icon do
233 if is_intro then
234 return new BSIcon("plus", css_classes)
235 end
236 return new BSIcon("asterisk", css_classes)
237 end
238 end
239
240 redef class MAttributeDef
241 redef fun html_signature(short) do
242 var static_mtype = self.static_mtype
243 var tpl = new Template
244 if static_mtype != null then
245 tpl.add ": "
246 tpl.add static_mtype.html_signature(short)
247 end
248 return tpl
249 end
250 end
251
252 redef class MMethodDef
253 redef fun html_signature(short) do
254 var new_msignature = self.new_msignature
255 if mproperty.is_root_init and new_msignature != null then
256 return new_msignature.html_signature(short)
257 end
258 var msignature = self.msignature
259 if msignature == null then return new Template
260 return msignature.html_signature(short)
261 end
262 end
263
264 redef class MVirtualTypeProp
265 redef fun html_link(text, title) do return mvirtualtype.html_link(text, title)
266 end
267
268 redef class MVirtualTypeDef
269 redef fun html_signature(short) do
270 var bound = self.bound
271 var tpl = new Template
272 if bound == null then return tpl
273 tpl.add ": "
274 tpl.add bound.html_signature(short)
275 return tpl
276 end
277 end
278
279 redef class MType
280 redef fun html_signature(short) do return html_link
281 end
282
283 redef class MClassType
284 redef fun html_link(text, title) do return mclass.html_link(text, title)
285 end
286
287 redef class MNullableType
288 redef fun html_signature(short) do
289 var tpl = new Template
290 tpl.add "nullable "
291 tpl.add mtype.html_signature(short)
292 return tpl
293 end
294 end
295
296 redef class MGenericType
297 redef fun html_signature(short) do
298 var lnk = html_link
299 var tpl = new Template
300 tpl.add new Link(lnk.href, mclass.name.html_escape, lnk.title)
301 tpl.add "["
302 for i in [0..arguments.length[ do
303 tpl.add arguments[i].html_signature(short)
304 if i < arguments.length - 1 then tpl.add ", "
305 end
306 tpl.add "]"
307 return tpl
308 end
309 end
310
311 redef class MParameterType
312 redef fun html_link(text, title) do
313 if text == null then text = name
314 if title == null then title = "formal type"
315 return new Link("{mclass.html_url}#FT_{name.to_cmangle}", text, title)
316 end
317 end
318
319 redef class MVirtualType
320 redef fun html_link(text, title) do return mproperty.intro.html_link(text, title)
321 end
322
323 redef class MSignature
324 redef fun html_signature(short) do
325 var tpl = new Template
326 if not mparameters.is_empty then
327 tpl.add "("
328 for i in [0..mparameters.length[ do
329 tpl.add mparameters[i].html_signature(short)
330 if i < mparameters.length - 1 then tpl.add ", "
331 end
332 tpl.add ")"
333 end
334 if short == null or not short then
335 var return_mtype = self.return_mtype
336 if return_mtype != null then
337 tpl.add ": "
338 tpl.add return_mtype.html_signature(short)
339 end
340 end
341 return tpl
342 end
343 end
344
345 redef class MParameter
346 redef fun html_signature(short) do
347 var tpl = new Template
348 tpl.add name
349 if short == null or not short then
350 tpl.add ": "
351 tpl.add mtype.html_signature(short)
352 end
353 if is_vararg then tpl.add "..."
354 return tpl
355 end
356 end
357
358 # Catalog
359
360 redef class Person
361
362 # HTML uniq id
363 fun html_id: String do return name.to_cmangle
364
365 # HTML default URL
366 #
367 # Should be redefined in clients.
368 fun html_url: String do return "person_{html_id}.html"
369
370 # Link to this person `html_url`
371 fun html_link: Link do return new Link(html_url, name)
372
373 redef fun to_html do
374 var tpl = new Template
375 tpl.addn "<span>"
376 var gravatar = self.gravatar
377 if gravatar != null then
378 tpl.addn "<img class='avatar' src='https://secure.gravatar.com/avatar/{gravatar}?size=14&amp;default=retro' />"
379 end
380 tpl.addn html_link
381 tpl.addn "</span>"
382 return tpl.write_to_string
383 end
384 end
385
386 # MDoc
387
388 redef class MDoc
389
390 private var markdown_proc: MarkdownProcessor is lazy, writable do
391 return original_mentity.as(not null).model.nitdoc_md_processor
392 end
393
394 private var inline_proc: MarkdownProcessor is lazy, writable do
395 return original_mentity.as(not null).model.nitdoc_inline_processor
396 end
397
398 # Renders the synopsis as a HTML comment block.
399 var html_synopsis: Writable is lazy do
400 var res = new Template
401 var syn = inline_proc.process(content.first)
402 res.add "<span class=\"synopsys nitdoc\">{syn}</span>"
403 return res
404 end
405
406 # Renders the comment without the synopsis as a HTML comment block.
407 var html_comment: Writable is lazy do
408 var lines = content.to_a
409 if not lines.is_empty then lines.shift
410 return lines_to_html(lines)
411 end
412
413 # Renders the synopsis and the comment as a HTML comment block.
414 var html_documentation: Writable is lazy do return lines_to_html(content.to_a)
415
416 # Renders markdown line as a HTML comment block.
417 private fun lines_to_html(lines: Array[String]): Writable do
418 var res = new Template
419 var decorator = markdown_proc.decorator.as(NitdocDecorator)
420 decorator.current_mdoc = self
421 res.add "<div class=\"nitdoc\">"
422 # do not use DocUnit as synopsys
423 if not lines.is_empty then
424 if not lines.first.has_prefix(" ") and
425 not lines.first.has_prefix("\t") then
426 # parse synopsys
427 var syn = inline_proc.process(lines.shift)
428 res.add "<h1 class=\"synopsys\">{syn}</h1>"
429 end
430 end
431 # check for annotations
432 for i in [0 .. lines.length[ do
433 var line = lines[i]
434 if line.to_upper.has_prefix("ENSURE") or line.to_upper.has_prefix("REQUIRE") then
435 var html = inline_proc.process(line)
436 lines[i] = "<p class=\"contract\">{html}</p>"
437 else if line.to_upper.has_prefix("TODO") or line.to_upper.has_prefix("FIXME") then
438 var html = inline_proc.process(line)
439 lines[i] = "<p class=\"todo\">{html}</p>"
440 end
441 end
442 # add other lines
443 res.add markdown_proc.process(lines.join("\n"))
444 res.add "</div>"
445 decorator.current_mdoc = null
446 return res
447 end
448 end
449
450 # The specific markdown decorator used internally to process MDoc object.
451 #
452 # You should use the various methods of `MDoc` like `MDoc::html_documentation`
453 #
454 # The class is public so specific behavior can be plugged on it.
455 class NitdocDecorator
456 super HTMLDecorator
457
458 private var toolcontext = new ToolContext
459
460 # The currently processed mdoc.
461 #
462 # Unfortunately, this seems to be the simpler way to get the currently processed `MDoc` object.
463 var current_mdoc: nullable MDoc = null
464
465 redef fun add_code(v, block) do
466 var meta = block.meta or else "nit"
467
468 # Do not try to highlight non-nit code.
469 if meta != "nit" and meta != "nitish" then
470 v.add "<pre class=\"{meta}\"><code>"
471 v.emit_in block
472 v.add "</code></pre>\n"
473 return
474 end
475 # Try to parse code
476 var code = block.raw_content
477 var ast = toolcontext.parse_something(code)
478 if ast isa AError then
479 v.add "<pre class=\"{meta}\"><code>"
480 v.emit_in block
481 v.add "</code></pre>\n"
482 return
483 end
484 v.add "<pre class=\"nitcode\"><code>"
485 var hl = new HtmlightVisitor
486 hl.line_id_prefix = ""
487 hl.highlight_node(ast)
488 v.add(hl.html)
489 v.add "</code></pre>\n"
490 end
491
492 redef fun add_span_code(v, text, from, to) do
493 # Try to parse it
494 var code = code_from_text(text, from, to)
495 var ast = toolcontext.parse_something(code)
496
497 if ast isa AError then
498 v.add "<code class=\"rawcode\">"
499 append_code(v, text, from, to)
500 else
501 v.add "<code class=\"nitcode\">"
502 var hl = new HtmlightVisitor
503 hl.line_id_prefix = ""
504 hl.highlight_node(ast)
505 v.add(hl.html)
506 end
507 v.add "</code>"
508 end
509
510 private fun code_from_text(buffer: Text, from, to: Int): String do
511 var out = new FlatBuffer
512 for i in [from..to[ do out.add buffer[i]
513 return out.write_to_string
514 end
515 end
516
517 # Decorator for span elements.
518 #
519 # Because inline comments can appear as span elements,
520 # InlineDecorator do not decorate things like paragraphs or headers.
521 class InlineDecorator
522 super NitdocDecorator
523
524 redef fun add_paragraph(v, block) do
525 v.emit_in block
526 end
527
528 redef fun add_headline(v, block) do
529 # save headline
530 var line = block.block.first_line
531 if line == null then return
532 var txt = line.value
533 var id = strip_id(txt)
534 var lvl = block.depth
535 headlines[id] = new HeadLine(id, txt, lvl)
536
537 v.emit_in block
538 end
539
540 redef fun add_code(v, block) do
541 # Try to parse code
542 var ast = toolcontext.parse_something(block.block.text.to_s)
543 if ast isa AError then
544 v.add "<code>"
545 v.emit_in block
546 v.add "</code>"
547 return
548 end
549 v.add "<code class=\"nitcode\">"
550 var hl = new HtmlightVisitor
551 hl.highlight_node(ast)
552 v.add(hl.html)
553 v.add "</code>"
554 end
555 end
556
557 redef class Model
558 # Get a markdown processor for Nitdoc comments.
559 var nitdoc_md_processor: MarkdownProcessor is lazy, writable do
560 var proc = new MarkdownProcessor
561 proc.decorator = new NitdocDecorator
562 return proc
563 end
564
565 # Get a markdown inline processor for Nitdoc comments.
566 #
567 # This processor is specificaly designed to inlinable doc elements like synopsys.
568 var nitdoc_inline_processor: MarkdownProcessor is lazy, writable do
569 var proc = new MarkdownProcessor
570 proc.decorator = new InlineDecorator
571 return proc
572 end
573 end