html_model: Remove new_msignature
[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 msignature = self.msignature
255 if msignature == null then return new Template
256 return msignature.html_signature(short)
257 end
258 end
259
260 redef class MVirtualTypeProp
261 redef fun html_link(text, title) do return mvirtualtype.html_link(text, title)
262 end
263
264 redef class MVirtualTypeDef
265 redef fun html_signature(short) do
266 var bound = self.bound
267 var tpl = new Template
268 if bound == null then return tpl
269 tpl.add ": "
270 tpl.add bound.html_signature(short)
271 return tpl
272 end
273 end
274
275 redef class MType
276 redef fun html_signature(short) do return html_link
277 end
278
279 redef class MClassType
280 redef fun html_link(text, title) do return mclass.html_link(text, title)
281 end
282
283 redef class MNullableType
284 redef fun html_signature(short) do
285 var tpl = new Template
286 tpl.add "nullable "
287 tpl.add mtype.html_signature(short)
288 return tpl
289 end
290 end
291
292 redef class MGenericType
293 redef fun html_signature(short) do
294 var lnk = html_link
295 var tpl = new Template
296 tpl.add new Link(lnk.href, mclass.name.html_escape, lnk.title)
297 tpl.add "["
298 for i in [0..arguments.length[ do
299 tpl.add arguments[i].html_signature(short)
300 if i < arguments.length - 1 then tpl.add ", "
301 end
302 tpl.add "]"
303 return tpl
304 end
305 end
306
307 redef class MParameterType
308 redef fun html_link(text, title) do
309 if text == null then text = name
310 if title == null then title = "formal type"
311 return new Link("{mclass.html_url}#FT_{name.to_cmangle}", text, title)
312 end
313 end
314
315 redef class MVirtualType
316 redef fun html_link(text, title) do return mproperty.intro.html_link(text, title)
317 end
318
319 redef class MSignature
320 redef fun html_signature(short) do
321 var tpl = new Template
322 if not mparameters.is_empty then
323 tpl.add "("
324 for i in [0..mparameters.length[ do
325 tpl.add mparameters[i].html_signature(short)
326 if i < mparameters.length - 1 then tpl.add ", "
327 end
328 tpl.add ")"
329 end
330 if short == null or not short then
331 var return_mtype = self.return_mtype
332 if return_mtype != null then
333 tpl.add ": "
334 tpl.add return_mtype.html_signature(short)
335 end
336 end
337 return tpl
338 end
339 end
340
341 redef class MParameter
342 redef fun html_signature(short) do
343 var tpl = new Template
344 tpl.add name
345 if short == null or not short then
346 tpl.add ": "
347 tpl.add mtype.html_signature(short)
348 end
349 if is_vararg then tpl.add "..."
350 return tpl
351 end
352 end
353
354 # Catalog
355
356 redef class Person
357
358 # HTML uniq id
359 fun html_id: String do return name.to_cmangle
360
361 # HTML default URL
362 #
363 # Should be redefined in clients.
364 fun html_url: String do return "person_{html_id}.html"
365
366 # Link to this person `html_url`
367 fun html_link: Link do return new Link(html_url, name)
368
369 # Render `self` as HTML
370 fun to_html: String do
371 var tpl = new Template
372 tpl.addn "<span>"
373 var gravatar = self.gravatar
374 if gravatar != null then
375 tpl.addn "<img class='avatar' src='https://secure.gravatar.com/avatar/{gravatar}?size=14&amp;default=retro' />"
376 end
377 tpl.addn html_link
378 tpl.addn "</span>"
379 return tpl.write_to_string
380 end
381 end
382
383 # MDoc
384
385 redef class MDoc
386
387 private var markdown_proc: MarkdownProcessor is lazy, writable do
388 return original_mentity.as(not null).model.nitdoc_md_processor
389 end
390
391 private var inline_proc: MarkdownProcessor is lazy, writable do
392 return original_mentity.as(not null).model.nitdoc_inline_processor
393 end
394
395 # Renders the synopsis as a HTML comment block.
396 var html_synopsis: Writable is lazy do
397 var res = new Template
398 var syn = inline_proc.process(content.first)
399 res.add "<span class=\"synopsis nitdoc\">{syn}</span>"
400 return res
401 end
402
403 # Renders the comment without the synopsis as a HTML comment block.
404 var html_comment: Writable is lazy do
405 var lines = content.to_a
406 if not lines.is_empty then lines.shift
407 return lines_to_html(lines)
408 end
409
410 # Renders the synopsis and the comment as a HTML comment block.
411 var html_documentation: Writable is lazy do return lines_to_html(content.to_a)
412
413 # Renders markdown line as a HTML comment block.
414 private fun lines_to_html(lines: Array[String]): Writable do
415 var res = new Template
416 var decorator = markdown_proc.decorator.as(NitdocDecorator)
417 decorator.current_mdoc = self
418 res.add "<div class=\"nitdoc\">"
419 # do not use DocUnit as synopsis
420 if not lines.is_empty then
421 if not lines.first.has_prefix(" ") and
422 not lines.first.has_prefix("\t") then
423 # parse synopsis
424 var syn = inline_proc.process(lines.shift)
425 res.add "<h1 class=\"synopsis\">{syn}</h1>"
426 end
427 end
428 # check for annotations
429 for i in [0 .. lines.length[ do
430 var line = lines[i]
431 if line.to_upper.has_prefix("ENSURE") or line.to_upper.has_prefix("REQUIRE") then
432 var html = inline_proc.process(line)
433 lines[i] = "<p class=\"contract\">{html}</p>"
434 else if line.to_upper.has_prefix("TODO") or line.to_upper.has_prefix("FIXME") then
435 var html = inline_proc.process(line)
436 lines[i] = "<p class=\"todo\">{html}</p>"
437 end
438 end
439 # add other lines
440 res.add markdown_proc.process(lines.join("\n"))
441 res.add "</div>"
442 decorator.current_mdoc = null
443 return res
444 end
445 end
446
447 # The specific markdown decorator used internally to process MDoc object.
448 #
449 # You should use the various methods of `MDoc` like `MDoc::html_documentation`
450 #
451 # The class is public so specific behavior can be plugged on it.
452 class NitdocDecorator
453 super HTMLDecorator
454
455 private var toolcontext = new ToolContext
456
457 # The currently processed mdoc.
458 #
459 # Unfortunately, this seems to be the simpler way to get the currently processed `MDoc` object.
460 var current_mdoc: nullable MDoc = null
461
462 redef fun add_code(v, block) do
463 var meta = block.meta or else "nit"
464
465 # Do not try to highlight non-nit code.
466 if meta != "nit" and meta != "nitish" then
467 v.add "<pre class=\"{meta}\"><code>"
468 v.emit_in block
469 v.add "</code></pre>\n"
470 return
471 end
472 # Try to parse code
473 var code = block.raw_content
474 var ast = toolcontext.parse_something(code)
475 if ast isa AError then
476 v.add "<pre class=\"{meta}\"><code>"
477 v.emit_in block
478 v.add "</code></pre>\n"
479 return
480 end
481 v.add "<pre class=\"nitcode\"><code>"
482 var hl = new HtmlightVisitor
483 hl.line_id_prefix = ""
484 hl.highlight_node(ast)
485 v.add(hl.html)
486 v.add "</code></pre>\n"
487 end
488
489 redef fun add_span_code(v, text, from, to) do
490 # Try to parse it
491 var code = code_from_text(text, from, to)
492 var ast = toolcontext.parse_something(code)
493
494 if ast isa AError then
495 v.add "<code class=\"rawcode\">"
496 append_code(v, text, from, to)
497 else
498 v.add "<code class=\"nitcode\">"
499 var hl = new HtmlightVisitor
500 hl.line_id_prefix = ""
501 hl.highlight_node(ast)
502 v.add(hl.html)
503 end
504 v.add "</code>"
505 end
506
507 private fun code_from_text(buffer: Text, from, to: Int): String do
508 var out = new FlatBuffer
509 for i in [from..to[ do out.add buffer[i]
510 return out.write_to_string
511 end
512 end
513
514 # Decorator for span elements.
515 #
516 # Because inline comments can appear as span elements,
517 # InlineDecorator do not decorate things like paragraphs or headers.
518 class InlineDecorator
519 super NitdocDecorator
520
521 redef fun add_paragraph(v, block) do
522 v.emit_in block
523 end
524
525 redef fun add_headline(v, block) do
526 # save headline
527 var line = block.block.first_line
528 if line == null then return
529 var txt = line.value
530 var id = strip_id(txt)
531 var lvl = block.depth
532 headlines[id] = new HeadLine(id, txt, lvl)
533
534 v.emit_in block
535 end
536
537 redef fun add_code(v, block) do
538 # Try to parse code
539 var ast = toolcontext.parse_something(block.block.text.to_s)
540 if ast isa AError then
541 v.add "<code>"
542 v.emit_in block
543 v.add "</code>"
544 return
545 end
546 v.add "<code class=\"nitcode\">"
547 var hl = new HtmlightVisitor
548 hl.highlight_node(ast)
549 v.add(hl.html)
550 v.add "</code>"
551 end
552 end
553
554 redef class Model
555 # Get a markdown processor for Nitdoc comments.
556 var nitdoc_md_processor: MarkdownProcessor is lazy, writable do
557 var proc = new MarkdownProcessor
558 proc.decorator = new NitdocDecorator
559 return proc
560 end
561
562 # Get a markdown inline processor for Nitdoc comments.
563 #
564 # This processor is specificaly designed to inlinable doc elements like synopsis.
565 var nitdoc_inline_processor: MarkdownProcessor is lazy, writable do
566 var proc = new MarkdownProcessor
567 proc.decorator = new InlineDecorator
568 return proc
569 end
570 end