Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / htmlight.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 # Highlighting of Nit AST with HTML
16 module htmlight
17
18 import highlight
19 import html
20 import pipeline
21 import serialization
22
23 # A standalone highlighted piece of code
24 class HLCode
25 super Serializable
26
27 # The highlighter used
28 var hl: HtmlightVisitor
29
30 # The raw code source
31 var content: String
32
33 # The pseudo source-file
34 var source: SourceFile
35
36 # JavaScript code to update an existing codemirror editor.
37 fun code_mirror_update: Template
38 do
39
40 var res = new Template
41 res.add """
42 function nitmessage() {
43 editor.operation(function(){
44 for (var i = 0; i < widgets.length; ++i)
45 editor.removeLineWidget(widgets[i]);
46 widgets.length = 0;
47 """
48
49 for m in source.messages do
50 res.add """
51 var l = document.createElement("div");
52 l.className = "lint-error"
53 l.innerHTML = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{m.text.html_escape}}}";
54 var w = editor.addLineWidget({{{m.location.line_start-1}}}, l);
55 widgets.push(w);
56 """
57 end
58 res.add """});}"""
59 return res
60 end
61
62 redef fun core_serialize_to(v)
63 do
64 v.serialize_attribute("code", hl.html.write_to_string)
65 var msgs = new Array[Map[String, Serializable]]
66 for m in source.messages do
67 var o = new Map[String, Serializable]
68 msgs.add o
69 o["line"] = m.location.line_start-1
70 o["message"] = m.text
71 end
72 v.serialize_attribute("messages", msgs)
73 end
74 end
75
76 # Visitor used to produce a HTML tree based on a AST on a `Source`
77 class HtmlightVisitor
78 super AbstractHighlightVisitor
79
80 # The root of the HTML hierarchy
81 var html = new HTMLTag("span")
82
83 # Should the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
84 #
85 # Used to have a really huge and verbose HTML (mainly for debug)
86 var with_ast = false is writable
87
88 # Prefixes used in generated IDs for line `<span>` elements.
89 # Useful if more than one highlighted code is present in the same HTML document.
90 #
91 # If set to the empty string, id for lines are disabled.
92 #
93 # Is `"L"` by default.
94 var line_id_prefix = "L" is writable
95
96 # When highlighting a node, attach a full popupable infobox, if any.
97 #
98 # If `false`, only a simple `title` tooltip is used.
99 #
100 # default: true
101 var show_infobox = true is writable
102
103 # A reference to an entity used in generated `<a>` elements.
104 #
105 # It is used to refer to some specific entities when generating links.
106 # If `null` is returned, then no link are generated and `<a>` elements become `<span>`.
107 #
108 # By default, `null` is returned.
109 # Clients are therefore encouraged to redefine the method in a subclass to control where entities should link to.
110 fun hrefto(entity: MEntity): nullable String do return null
111
112 init
113 do
114 html.add_class("nitcode")
115 end
116
117 private fun full_tag(anode: ANode): nullable HTMLTag
118 do
119 var tag = anode.make_tag(self)
120 if tag == null then return null
121 var infobox = anode.infobox(self)
122 if infobox == null and anode isa Token then
123 var pa = anode.parent
124 if pa != null then
125 infobox = pa.decorate_tag(self, tag, anode)
126 end
127 end
128 if infobox != null and not show_infobox then
129 var href = infobox.href
130 if href != null then
131 # If there is an href, we inject a link around
132 var tag2 = new HTMLTag("a")
133 tag2.add tag
134 tag = tag2
135 tag.attr("href", href)
136 end
137 tag.attr("title", infobox.title)
138 tag.classes.add "titled"
139 infobox = null
140 end
141 var messages = anode.location.messages
142 if messages != null and show_messages then
143 tag.css("border-bottom", "solid 2px red")
144 if infobox == null then
145 infobox = new HInfoBox(self, "Messages")
146 end
147 var c = infobox.new_dropdown("{messages.length} message(s)", "")
148 for m in messages do
149 c.open("li").append(m.text)
150 end
151 end
152 if infobox != null then
153 tag.attach_infobox(infobox)
154 end
155 return tag
156 end
157
158 # Low-level highlighting between 2 tokens
159 redef fun do_highlight(first_token, last_token)
160 do
161 var stack2 = new Array[HTMLTag]
162 var stack = new Array[Prod]
163 var line = 0
164 var c: nullable Token = first_token
165 while c != null do
166 var starting
167
168 # Handle start of line
169 var cline = c.location.line_start
170 if cline != line then
171 # Handle starting block productions,
172 # Because c could be a detached token, get prods in
173 # the first AST token
174 var c0 = c.first_token_in_line
175 starting = null
176 if c0 != null then starting = c0.starting_prods
177 if starting != null then for p in starting do
178 if not p.is_block then continue
179 var tag = full_tag(p)
180 if tag == null then continue
181 tag.add_class("foldable")
182 stack2.add(html)
183 html.add tag
184 html = tag
185 stack.add(p)
186 end
187
188 # Add a div for the whole line
189 var tag = new HTMLTag("span")
190 var p = line_id_prefix
191 if p != "" then tag.attrs["id"] = "{p}{cline}"
192 tag.classes.add "line"
193 stack2.add(html)
194 html.add tag
195 html = tag
196 line = cline
197 end
198
199 # Add the blank, verbatim
200 html.add_raw_html c.blank_before
201
202 # Handle starting span production
203 starting = c.starting_prods
204 if starting != null then for p in starting do
205 if not p.is_span then continue
206 var tag = full_tag(p)
207 if tag == null then continue
208 stack2.add(html)
209 html.add tag
210 html = tag
211 stack.add(p)
212 end
213
214 # Add the token
215 if c isa TEol then
216 html.append "\n"
217 else
218 var tag = full_tag(c)
219 if tag != null then html.add tag
220 end
221
222 # Handle ending span productions
223 var ending = c.ending_prods
224 if ending != null then for p in ending do
225 if not p.is_span then continue
226 if stack.is_empty or p != stack.last then continue
227 stack.pop
228 html = stack2.pop
229 end
230
231 # Handle end of line and end of file
232 var n = c.next_token
233 if c == last_token then n = null
234 if n == null or n.location.line_start != line then
235 # closes the line div
236 html = stack2.pop
237
238 # close the block production divs
239 var c0 = c.last_token_in_line
240 ending = null
241 if c0 != null then ending = c0.ending_prods
242 if ending != null then for p in ending do
243 if not p.is_block then continue
244 if stack.is_empty or p != stack.last then continue
245 stack.pop
246 html = stack2.pop
247 end
248 end
249
250 c = n
251 end
252 if not stack2.is_empty then html = stack2.first
253 end
254
255 # Return a default CSS content related to CSS classes used in the `html` tree.
256 # Could be inlined in the `.html` file of saved as a specific `.css` file.
257 fun css_content: String
258 do
259 return """
260 .nitcode a { color: inherit; cursor:pointer; }
261 .nitcode .titled:hover { text-decoration: underline; } /* underline titles */
262 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
263 .nitcode .foldable { display: block } /* for block productions*/
264 .nitcode .line{ display: block } /* for lines */
265 .nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
266 .nitcode :target { background-color: #FFF3C2 } /* target highlight*/
267 /* lexical raw tokens. independent of usage or semantic: */
268 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
269 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
270 .nitcode .nc_k { font-weight: bold; } /* keyword */
271 .nitcode .nc_o {} /* operator */
272 .nitcode .nc_i {} /* standard identifier */
273 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
274 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
275 .nitcode .nc_l { color: #009999; } /* char and number literal */
276 .nitcode .nc_s { color: #8F1546; } /* string literal */
277 /* syntactic token usage. added because of their position in the AST */
278 .nitcode .nc_ast { color: blue; } /* assert label */
279 .nitcode .nc_la { color: blue; } /* break/continue label */
280 .nitcode .nc_m { color: #445588; } /* module name */
281 /* syntactic groups */
282 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
283 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
284 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
285 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
286 .nitcode .nc_cdef {} /* A whole class definition */
287 .nitcode .nc_pdef {} /* A whole property definition */
288 /* semantic token usage */
289 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
290 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
291
292 .nitcode .nc_error { border: 1px red solid;} /* not used */
293 .popover { max-width: 800px !important; }
294 """
295 end
296
297 # Additional content to inject in the <head> tag
298 # Note: does not include `css_content`; handle it yourself.
299 fun head_content: String
300 do
301 return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
302 end
303
304 # Additional content to inject just before the closing </body> tag
305 fun foot_content: String
306 do
307 return """
308 <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
309 <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
310 <script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
311 end
312
313 # Fully process `content` as a Nit source file.
314 #
315 # Set `print_errors = true` to print errors in the code to the console.
316 fun highlightcode(content: String, print_errors: nullable Bool): HLCode
317 do
318 # Prepare a stand-alone tool context
319 var tc = new ToolContext
320 tc.nit_dir = tc.locate_nit_dir # still use the common lib to have core
321 tc.keep_going = true # no exit, obviously
322 if print_errors != true then tc.opt_warn.value = -1 # no output
323
324 # Prepare an stand-alone model and model builder.
325 # Unfortunately, models are enclosing and append-only.
326 # There is no way (yet?) to have a shared module `core` with
327 # isolated and throwable user modules.
328 var model = new Model
329 var mb = new ModelBuilder(model, tc)
330
331 # Parse the code
332 var source = new SourceFile.from_string("", content + "\n")
333 var lexer = new Lexer(source)
334 var parser = new Parser(lexer)
335 var tree = parser.parse
336
337 var hlcode = new HLCode(self, content, source)
338
339 # Check syntax error
340 var eof = tree.n_eof
341 if eof isa AError then
342 mb.error(eof, eof.message)
343 highlight_source(source)
344 return hlcode
345 end
346 var amodule = tree.n_base.as(not null)
347
348 # Load the AST as a module in the model
349 # Then process it
350 mb.load_rt_module(null, amodule, "")
351 mb.run_phases
352
353 # Highlight the processed module
354 highlight_node(amodule)
355 return hlcode
356 end
357 end
358
359 redef class HTMLTag
360 # Attach the infobox to the node by using BootStrap popover
361 fun attach_infobox(infobox: HInfoBox)
362 do
363 classes.add("popupable")
364 attrs["title"] = infobox.title
365 var href = infobox.href
366 if href != null then
367 attrs["data-title"] = """<a href="{{{href}}}">{{{infobox.title}}}</a>"""
368 end
369 attrs["data-content"] = infobox.content.write_to_string
370 attrs["data-toggle"] = "popover"
371 end
372 end
373
374
375 # A generic information container that can be used to decorate AST entities
376 class HInfoBox
377 # The visitor used for contextualisation, if needed
378 var visitor: HtmlightVisitor
379
380 # A short title for the AST element
381 var title: String
382
383 # The primary link where the entity points
384 # null if no link
385 var href: nullable String = null
386
387 # The content of the popuped infobox
388 var content = new HTMLTag("div")
389
390 # Append a new field in the popuped infobox
391 fun new_field(title: String): HTMLTag
392 do
393 content.open("b").text(title)
394 content.append(" ")
395 var res = content.open("span")
396 content.open("br")
397 return res
398 end
399
400 # Append a new dropdown in the popuped content
401 fun new_dropdown(title, text: String, text_is_html: nullable Bool): HTMLTag
402 do
403 content.add_raw_html """<div class="dropdown"> <a data-toggle="dropdown" href="#"><b>"""
404 content.append(title)
405 content.add_raw_html "</b> "
406 if text_is_html == true then
407 content.add_raw_html(text)
408 else content.append(text)
409 content.add_raw_html """<span class="caret"></span></a>"""
410 var res = content.open("ul").add_class("dropdown-menu").attr("role", "menu").attr("aria-labelledby", "dLabel")
411 content.add_raw_html "</div>"
412 return res
413 end
414 end
415
416 ##
417
418 # Model entity or whatever that can produce an infobox
419 interface HInfoBoxable
420 # An new infobox documenting the entity
421 fun infobox(v: HtmlightVisitor): HInfoBox is abstract
422 end
423
424 redef class MDoc
425 # Append an entry for the doc in the given infobox
426 fun fill_infobox(res: HInfoBox)
427 do
428 if content.length < 2 then
429 res.new_field("doc").text(content.first)
430 return
431 end
432 var c = res.new_dropdown("doc", content.first)
433 for x in content.iterator.skip_head(1) do
434 c.append x
435 c.add_raw_html "<br>"
436 end
437 end
438 end
439
440 redef class MEntity
441 super HInfoBoxable
442
443 # A HTML version of `to_s` with hyper-links.
444 #
445 # By default, `linkto_text(v, to_s)` is used, c.f. see `linkto_text`.
446 #
447 # For some complex entities, like generic types, multiple `<a>` and `<span>` elements can be generated.
448 # E.g. `Array[Int]` might become `<a>Array</a>[<a>Int</a>]` with the correct `href` attributes
449 # provided by `v.hrefto`.
450 fun linkto(v: HtmlightVisitor): HTMLTag do return linkto_text(v, to_s)
451
452 # Link to the `self` with a specific text.
453 #
454 # The whole text is linked with a single `<a>` element.
455 #
456 # The `href` used is provided by `v.hrefto`.
457 # If `href` is null then a `<span>` element is used instead of `<a>`.
458 fun linkto_text(v: HtmlightVisitor, text: String): HTMLTag
459 do
460 var href = v.hrefto(self)
461 if href == null then
462 return (new HTMLTag("span")).text(text)
463 end
464 return (new HTMLTag("a")).attr("href", href).text(text)
465 end
466
467 # Append an entry for the doc in the given infobox
468 private fun add_doc_to_infobox(res: HInfoBox)
469 do
470 var mdoc = mdoc_or_fallback
471 if mdoc != null then mdoc.fill_infobox(res)
472 end
473 end
474
475 redef class MModule
476 redef fun infobox(v)
477 do
478 var res = new HInfoBox(v, "module {name}")
479 res.href = v.hrefto(self)
480 if not v.show_infobox then return res
481 res.new_field("module").add(linkto(v))
482 add_doc_to_infobox(res)
483 if in_importation.greaters.length > 1 then
484 var c = res.new_dropdown("imports", "{in_importation.greaters.length-1} modules")
485 for x in in_importation.greaters do
486 if x == self then continue
487 c.open("li").add x.linkto(v)
488 end
489 end
490 return res
491 end
492
493 redef fun linkto(v) do return linkto_text(v, name)
494 end
495
496 redef class MClassDef
497 redef fun infobox(v)
498 do
499 var res = new HInfoBox(v, "class {mclass.name}")
500 res.href = v.hrefto(self)
501 if not v.show_infobox then return res
502 if is_intro then
503 res.new_field("class").text(mclass.name)
504 else
505 res.new_field("redef class").text(mclass.name)
506 res.new_field("intro").add mclass.intro.linkto_text(v, "in {mclass.intro_mmodule.to_s}")
507 end
508 add_doc_to_infobox(res)
509
510 var in_hierarchy = self.in_hierarchy
511 if in_hierarchy == null then return res
512
513 if in_hierarchy.greaters.length > 1 then
514 var c = res.new_dropdown("hier", "super-classes")
515 for x in in_hierarchy.greaters do
516 if x == self then continue
517 if not x.is_intro then continue
518 c.open("li").add x.linkto(v)
519 end
520 end
521 if in_hierarchy.smallers.length > 1 then
522 var c = res.new_dropdown("hier", "sub-classes")
523 for x in in_hierarchy.smallers do
524 if x == self then continue
525 if not x.is_intro then continue
526 c.open("li").add x.linkto(v)
527 end
528 end
529 if mclass.mclassdefs.length > 1 then
530 var c = res.new_dropdown("redefs", "refinements")
531 for x in mclass.mclassdefs do
532 if x == self then continue
533 c.open("li").add x.linkto_text(v, "in {x.mmodule}")
534 end
535 end
536 return res
537 end
538 end
539
540 redef class MPropDef
541 redef fun infobox(v)
542 do
543 var res = new HInfoBox(v, to_s)
544 res.href = v.hrefto(self)
545 if not v.show_infobox then return res
546 if self isa MMethodDef then
547 var msignature = self.msignature
548 if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto(v)
549 else if self isa MAttributeDef then
550 var static_mtype = self.static_mtype
551 if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto(v)
552 else if self isa MVirtualTypeDef then
553 var bound = self.bound
554 if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto(v)
555 else
556 res.new_field("wat?").append(mproperty.name)
557 end
558
559 if is_intro then
560 else
561 res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
562 end
563 add_doc_to_infobox(res)
564 if mproperty.mpropdefs.length > 1 then
565 var c = res.new_dropdown("redef", "redefinitions")
566 for x in mproperty.mpropdefs do
567 c.open("li").add x.linkto_text(v, "in {x.mclassdef}")
568 end
569 end
570
571 return res
572 end
573 end
574
575 redef class MClassType
576 redef fun infobox(v)
577 do
578 var res = new HInfoBox(v, to_s)
579 res.href = v.hrefto(mclass.intro)
580 if not v.show_infobox then return res
581 res.new_field("class").add mclass.intro.linkto(v)
582 add_doc_to_infobox(res)
583 return res
584 end
585 redef fun linkto(v)
586 do
587 return mclass.intro.linkto(v)
588 end
589 end
590 redef class MVirtualType
591 redef fun infobox(v)
592 do
593 var res = new HInfoBox(v, to_s)
594 res.href = v.hrefto(mproperty)
595 if not v.show_infobox then return res
596 var p = mproperty
597 res.new_field("virtual type").add p.intro.linkto(v)
598 add_doc_to_infobox(res)
599 return res
600 end
601 redef fun linkto(v)
602 do
603 return mproperty.intro.linkto(v)
604 end
605 end
606 redef class MParameterType
607 redef fun infobox(v)
608 do
609 var res = new HInfoBox(v, to_s)
610 if not v.show_infobox then return res
611 res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v)
612 return res
613 end
614 end
615
616 redef class MNullableType
617 redef fun infobox(v)
618 do
619 return mtype.infobox(v)
620 end
621 redef fun linkto(v)
622 do
623 var res = new HTMLTag("span")
624 res.append("nullable ").add(mtype.linkto(v))
625 return res
626 end
627 end
628
629 redef class MNotNullType
630 redef fun infobox(v)
631 do
632 return mtype.infobox(v)
633 end
634 redef fun linkto(v)
635 do
636 var res = new HTMLTag("span")
637 res.append("not null ").add(mtype.linkto(v))
638 return res
639 end
640 end
641
642 redef class MNullType
643 redef fun infobox(v)
644 do
645 var res = new HInfoBox(v, to_s)
646 return res
647 end
648 redef fun linkto(v)
649 do
650 var res = new HTMLTag("span")
651 res.append("null")
652 return res
653 end
654 end
655
656 redef class MSignature
657 redef fun linkto(v)
658 do
659 var res = new HTMLTag("span")
660 var first = true
661 if not mparameters.is_empty then
662 res.append "("
663 for p in mparameters do
664 if first then
665 first = false
666 else
667 res.append ", "
668 end
669 res.append p.name
670 res.append ": "
671 res.add p.mtype.linkto(v)
672 end
673 res.append ")"
674 end
675 var ret = return_mtype
676 if ret != null then
677 res.append ": "
678 res.add ret.linkto(v)
679 end
680 return res
681 end
682 end
683
684 redef class CallSite
685 redef fun infobox(v)
686 do
687 var res = new HInfoBox(v, "call {mpropdef}")
688 res.href = v.hrefto(mpropdef)
689 if not v.show_infobox then return res
690 res.new_field("call").add(mpropdef.linkto(v)).add(msignature.linkto(v))
691 if mpropdef.is_intro then
692 else
693 res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
694 end
695 add_doc_to_infobox(res)
696
697 return res
698 end
699 redef fun linkto(v)
700 do
701 return mpropdef.linkto(v)
702 end
703 end
704
705 redef class Variable
706 super HInfoBoxable
707 redef fun infobox(v)
708 do
709 var declared_type = self.declared_type
710 if declared_type == null then
711 var res = new HInfoBox(v, "{name}")
712 res.new_field("local var").append("{name}")
713 return res
714 end
715 var res = new HInfoBox(v, "{name}: {declared_type}")
716 res.new_field("local var").append("{name}:").add(declared_type.linkto(v))
717 return res
718 end
719 end
720
721
722 ##
723
724 redef class ANode
725 # Optionally creates a tag that encapsulate the AST element on HTML rendering
726 protected fun make_tag(v: HtmlightVisitor): nullable HTMLTag do return null
727
728 # Add aditionnal information on a child-token and return an additionnal HInfoBox on it
729 protected fun decorate_tag(v: HtmlightVisitor, res: HTMLTag, token: Token): nullable HInfoBox
730 do
731 #debug("no decoration for {token.inspect}")
732 #res.add_class("nc_error")
733 return null
734 end
735
736 # Return a optional infobox
737 fun infobox(v: HtmlightVisitor): nullable HInfoBox do return null
738 end
739
740 redef class AQclassid
741 redef fun decorate_tag(v, res, token)
742 do
743 if token != n_id then return null
744 var parent = self.parent
745 if parent == null then return null
746 return parent.decorate_tag(v, res, token)
747 end
748 end
749
750 redef class AQid
751 redef fun decorate_tag(v, res, token)
752 do
753 if token != n_id then return null
754 var parent = self.parent
755 if parent == null then return null
756 return parent.decorate_tag(v, res, token)
757 end
758 end
759
760 redef class AStdClassdef
761 redef fun make_tag(v)
762 do
763 var res = new HTMLTag("span")
764 res.add_class("nc_cdef")
765 var md = mclassdef
766 if md != null then res.attr("id", md.to_s)
767 return res
768 end
769 redef fun decorate_tag(v, res, token)
770 do
771 if not token isa TClassid then return null
772 res.add_class("nc_def")
773
774 var md = mclassdef
775 if md == null then return null
776 return md.infobox(v)
777 end
778 end
779 redef class APropdef
780 redef fun make_tag(v)
781 do
782 var res = new HTMLTag("span")
783 res.add_class("nc_pdef")
784 var mpd
785 mpd = mpropdef
786 if mpd != null then
787 #res.add(tag(mpd))
788 res.attr("id", mpd.to_s)
789 end
790 if self isa AAttrPropdef then
791 mpd = mreadpropdef
792 if mpd != null then res.add(tag(mpd))
793 mpd = mwritepropdef
794 if mpd != null then res.add(tag(mpd))
795 end
796 return res
797 end
798
799 private fun tag(mpd: MPropDef): HTMLTag
800 do
801 var a = new HTMLTag("a")
802 a.attr("id", mpd.to_s)
803 return a
804 end
805 end
806
807 redef class Token
808 # Produce an HTMLTag with the correct contents and CSS classes
809 # Subclasses can redefine it to decorate the tag
810 redef fun make_tag(v): HTMLTag
811 do
812 var res = new HTMLTag("span")
813 res.text(text)
814 return res
815 end
816 end
817
818 redef class TokenKeyword
819 redef fun make_tag(v)
820 do
821 var res = super
822 res.add_class("nc_k")
823 return res
824 end
825 end
826 redef class TokenOperator
827 redef fun make_tag(v)
828 do
829 var res = super
830 res.add_class("nc_o")
831 return res
832 end
833 end
834
835 redef class AVarFormExpr
836 redef fun decorate_tag(v, res, token)
837 do
838 if token != n_id then return null
839 var variable = self.variable
840 if variable == null then return null
841 res.add_class("nc_v")
842 return variable.infobox(v)
843 end
844 end
845
846 redef class AVardeclExpr
847 redef fun decorate_tag(v, res, token)
848 do
849 if token != n_id then return null
850 var variable = self.variable
851 if variable == null then return null
852 res.add_class("nc_v")
853 return variable.infobox(v)
854 end
855 end
856
857 redef class AForGroup
858 redef fun decorate_tag(v, res, token)
859 do
860 if not token isa TId then return null
861 var vs = variables
862 if vs == null then return null
863 res.add_class("nc_v")
864 var idx = n_ids.index_of(token)
865 var variable = vs[idx]
866 return variable.infobox(v)
867 end
868 end
869
870 redef class AParam
871 redef fun decorate_tag(v, res, token)
872 do
873 if token != n_id then return null
874 var mp = mparameter
875 if mp == null then return null
876 var variable = self.variable
877 if variable == null then return null
878 res.add_class("nc_v")
879 return variable.infobox(v)
880 end
881 end
882
883 redef class AAssertExpr
884 redef fun decorate_tag(v, res, token)
885 do
886 if not token isa TId then return null
887 res.add_class("nc_ast")
888 return null
889 end
890 end
891
892 redef class ALabel
893 redef fun decorate_tag(v, res, token)
894 do
895 if not token isa TId then return null
896 res.add_class("nc_la")
897 return null
898 end
899 end
900
901 redef class ASendExpr
902 redef fun decorate_tag(v, res, token)
903 do
904 var callsite = self.callsite
905 if callsite == null then return null
906 return callsite.infobox(v)
907 end
908 end
909
910 redef class ANewExpr
911 redef fun decorate_tag(v, res, token)
912 do
913 var callsite = self.callsite
914 if callsite == null then return null
915 return callsite.infobox(v)
916 end
917 end
918
919 redef class AAssignOp
920 redef fun decorate_tag(v, res, token)
921 do
922 var p = parent
923 assert p isa AReassignFormExpr
924
925 var callsite = p.reassign_callsite
926 if callsite == null then return null
927 return callsite.infobox(v)
928 end
929 end
930
931 redef class AModuleName
932 redef fun decorate_tag(v, res, token)
933 do
934 var p = parent
935 if p == null then return null
936 return p.decorate_tag(v, res, token)
937 end
938 end
939
940 redef class AModuledecl
941 redef fun decorate_tag(v, res, token)
942 do
943 if not token isa TId then return null
944 res.add_class("nc_def")
945 res.add_class("nc_m")
946 var p = parent
947 assert p isa AModule
948 var mm = p.mmodule
949 if mm == null then return null
950 return mm.infobox(v)
951 end
952 end
953
954 redef class AStdImport
955 redef fun decorate_tag(v, res, token)
956 do
957 if not token isa TId then return null
958 res.add_class("nc_m")
959 var mm = mmodule
960 if mm == null then return null
961 return mm.infobox(v)
962 end
963 end
964 redef class AAttrPropdef
965 redef fun decorate_tag(v, res, token)
966 do
967 if not token isa TId then return null
968 res.add_class("nc_def")
969 var mpd: nullable MPropDef
970 mpd = mreadpropdef
971 if mpd == null then mpd = mpropdef
972 if mpd == null then return null
973 return mpd.infobox(v)
974 end
975 end
976
977 redef class TId
978 redef fun make_tag(v)
979 do
980 var res = super
981 res.add_class("nc_i")
982 return res
983 end
984 end
985 redef class AMethid
986 redef fun make_tag(v)
987 do
988 var res = new HTMLTag("span")
989 res.add_class("nc_def")
990 return res
991 end
992 redef fun decorate_tag(v, res, token)
993 do
994 return null
995 # nothing to decorate
996 end
997 redef fun infobox(v)
998 do
999 var p = parent
1000 if not p isa AMethPropdef then return null
1001 var mpd = p.mpropdef
1002 if mpd == null then return null
1003 return mpd.infobox(v)
1004 end
1005 end
1006 redef class TAttrid
1007 redef fun make_tag(v)
1008 do
1009 var res = super
1010 res.add_class("nc_a")
1011 return res
1012 end
1013 end
1014 redef class AAttrFormExpr
1015 redef fun decorate_tag(v, res, token)
1016 do
1017 if not token isa TAttrid then return null
1018 var p = mproperty
1019 if p == null then return null
1020 return p.intro.infobox(v)
1021 end
1022 end
1023 redef class TClassid
1024 redef fun make_tag(v)
1025 do
1026 var res = super
1027 res.add_class("nc_t")
1028 return res
1029 end
1030 end
1031 redef class AType
1032 redef fun decorate_tag(v, res, token)
1033 do
1034 if not token isa TClassid then return null
1035 var mt = mtype
1036 if mt == null then return null
1037 mt = mt.undecorate
1038 if mt isa MFormalType then
1039 res.add_class("nc_vt")
1040 end
1041 return mt.infobox(v)
1042 end
1043 end
1044 redef class AFormaldef
1045 redef fun decorate_tag(v, res, token)
1046 do
1047 if not token isa TClassid then return null
1048 res.add_class("nc_vt")
1049 var mtype = self.mtype
1050 if mtype == null then return null
1051 return mtype.infobox(v)
1052 end
1053 end
1054 redef class ATypePropdef
1055 redef fun decorate_tag(v, res, token)
1056 do
1057 if not token isa TClassid then return null
1058 res.add_class("nc_def")
1059 var md = mpropdef
1060 if md == null then return null
1061 return md.infobox(v)
1062 end
1063 end
1064 redef class TComment
1065 redef fun make_tag(v)
1066 do
1067 var res = super
1068 if is_loose then
1069 res.add_class("nc_c")
1070 end
1071 return res
1072 end
1073 end
1074 redef class ADoc
1075 redef fun make_tag(v)
1076 do
1077 var res = new HTMLTag("span")
1078 res.add_class("nc_d")
1079 return res
1080 end
1081 end
1082 redef class TokenLiteral
1083 redef fun make_tag(v)
1084 do
1085 var res = super
1086 res.add_class("nc_l")
1087 return res
1088 end
1089 end
1090 redef class ASuperstringExpr
1091 redef fun make_tag(v)
1092 do
1093 var res = new HTMLTag("span")
1094 res.add_class("nc_ss")
1095 return res
1096 end
1097 end
1098 redef class AStringFormExpr
1099 redef fun decorate_tag(v, res, token)
1100 do
1101 # Workaround to tag strings
1102 res.classes.remove("nc_l")
1103 res.add_class("nc_s")
1104 return super
1105 end
1106 end
1107 redef class AExpr
1108 redef fun decorate_tag(v, res, token)
1109 do
1110 var t = mtype
1111 if t == null then return null
1112 return t.infobox(v)
1113 end
1114 end