doc: Rename `synopsys` into `synopsis`
[nit.git] / src / doc / templates / html_model.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 html_model
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 # Render `self` as HTML
374 fun to_html: String do
375 var tpl = new Template
376 tpl.addn "<span>"
377 var gravatar = self.gravatar
378 if gravatar != null then
379 tpl.addn "<img class='avatar' src='https://secure.gravatar.com/avatar/{gravatar}?size=14&amp;default=retro' />"
380 end
381 tpl.addn html_link
382 tpl.addn "</span>"
383 return tpl.write_to_string
384 end
385 end
386
387 # MDoc
388
389 redef class MDoc
390
391 private var markdown_proc: MarkdownProcessor is lazy, writable do
392 return original_mentity.as(not null).model.nitdoc_md_processor
393 end
394
395 private var inline_proc: MarkdownProcessor is lazy, writable do
396 return original_mentity.as(not null).model.nitdoc_inline_processor
397 end
398
399 # Renders the synopsis as a HTML comment block.
400 var html_synopsis: Writable is lazy do
401 var res = new Template
402 var syn = inline_proc.process(content.first)
403 res.add "<span class=\"synopsis nitdoc\">{syn}</span>"
404 return res
405 end
406
407 # Renders the comment without the synopsis as a HTML comment block.
408 var html_comment: Writable is lazy do
409 var lines = content.to_a
410 if not lines.is_empty then lines.shift
411 return lines_to_html(lines)
412 end
413
414 # Renders the synopsis and the comment as a HTML comment block.
415 var html_documentation: Writable is lazy do return lines_to_html(content.to_a)
416
417 # Renders markdown line as a HTML comment block.
418 private fun lines_to_html(lines: Array[String]): Writable do
419 var res = new Template
420 var decorator = markdown_proc.decorator.as(NitdocDecorator)
421 decorator.current_mdoc = self
422 res.add "<div class=\"nitdoc\">"
423 # do not use DocUnit as synopsis
424 if not lines.is_empty then
425 if not lines.first.has_prefix(" ") and
426 not lines.first.has_prefix("\t") then
427 # parse synopsis
428 var syn = inline_proc.process(lines.shift)
429 res.add "<h1 class=\"synopsis\">{syn}</h1>"
430 end
431 end
432 # check for annotations
433 for i in [0 .. lines.length[ do
434 var line = lines[i]
435 if line.to_upper.has_prefix("ENSURE") or line.to_upper.has_prefix("REQUIRE") then
436 var html = inline_proc.process(line)
437 lines[i] = "<p class=\"contract\">{html}</p>"
438 else if line.to_upper.has_prefix("TODO") or line.to_upper.has_prefix("FIXME") then
439 var html = inline_proc.process(line)
440 lines[i] = "<p class=\"todo\">{html}</p>"
441 end
442 end
443 # add other lines
444 res.add markdown_proc.process(lines.join("\n"))
445 res.add "</div>"
446 decorator.current_mdoc = null
447 return res
448 end
449 end
450
451 # The specific markdown decorator used internally to process MDoc object.
452 #
453 # You should use the various methods of `MDoc` like `MDoc::html_documentation`
454 #
455 # The class is public so specific behavior can be plugged on it.
456 class NitdocDecorator
457 super HTMLDecorator
458
459 private var toolcontext = new ToolContext
460
461 # The currently processed mdoc.
462 #
463 # Unfortunately, this seems to be the simpler way to get the currently processed `MDoc` object.
464 var current_mdoc: nullable MDoc = null
465
466 redef fun add_code(v, block) do
467 var meta = block.meta or else "nit"
468
469 # Do not try to highlight non-nit code.
470 if meta != "nit" and meta != "nitish" then
471 v.add "<pre class=\"{meta}\"><code>"
472 v.emit_in block
473 v.add "</code></pre>\n"
474 return
475 end
476 # Try to parse code
477 var code = block.raw_content
478 var ast = toolcontext.parse_something(code)
479 if ast isa AError then
480 v.add "<pre class=\"{meta}\"><code>"
481 v.emit_in block
482 v.add "</code></pre>\n"
483 return
484 end
485 v.add "<pre class=\"nitcode\"><code>"
486 var hl = new HtmlightVisitor
487 hl.line_id_prefix = ""
488 hl.highlight_node(ast)
489 v.add(hl.html)
490 v.add "</code></pre>\n"
491 end
492
493 redef fun add_span_code(v, text, from, to) do
494 # Try to parse it
495 var code = code_from_text(text, from, to)
496 var ast = toolcontext.parse_something(code)
497
498 if ast isa AError then
499 v.add "<code class=\"rawcode\">"
500 append_code(v, text, from, to)
501 else
502 v.add "<code class=\"nitcode\">"
503 var hl = new HtmlightVisitor
504 hl.line_id_prefix = ""
505 hl.highlight_node(ast)
506 v.add(hl.html)
507 end
508 v.add "</code>"
509 end
510
511 private fun code_from_text(buffer: Text, from, to: Int): String do
512 var out = new FlatBuffer
513 for i in [from..to[ do out.add buffer[i]
514 return out.write_to_string
515 end
516 end
517
518 # Decorator for span elements.
519 #
520 # Because inline comments can appear as span elements,
521 # InlineDecorator do not decorate things like paragraphs or headers.
522 class InlineDecorator
523 super NitdocDecorator
524
525 redef fun add_paragraph(v, block) do
526 v.emit_in block
527 end
528
529 redef fun add_headline(v, block) do
530 # save headline
531 var line = block.block.first_line
532 if line == null then return
533 var txt = line.value
534 var id = strip_id(txt)
535 var lvl = block.depth
536 headlines[id] = new HeadLine(id, txt, lvl)
537
538 v.emit_in block
539 end
540
541 redef fun add_code(v, block) do
542 # Try to parse code
543 var ast = toolcontext.parse_something(block.block.text.to_s)
544 if ast isa AError then
545 v.add "<code>"
546 v.emit_in block
547 v.add "</code>"
548 return
549 end
550 v.add "<code class=\"nitcode\">"
551 var hl = new HtmlightVisitor
552 hl.highlight_node(ast)
553 v.add(hl.html)
554 v.add "</code>"
555 end
556 end
557
558 redef class Model
559 # Get a markdown processor for Nitdoc comments.
560 var nitdoc_md_processor: MarkdownProcessor is lazy, writable do
561 var proc = new MarkdownProcessor
562 proc.decorator = new NitdocDecorator
563 return proc
564 end
565
566 # Get a markdown inline processor for Nitdoc comments.
567 #
568 # This processor is specificaly designed to inlinable doc elements like synopsis.
569 var nitdoc_inline_processor: MarkdownProcessor is lazy, writable do
570 var proc = new MarkdownProcessor
571 proc.decorator = new InlineDecorator
572 return proc
573 end
574 end