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