highlight: add two configuration, show_messages and show_infobox
[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 AStdClassdef
706 redef fun make_tag(v)
707 do
708 var res = new HTMLTag("span")
709 res.add_class("nc_cdef")
710 var md = mclassdef
711 if md != null then res.attr("id", md.to_s)
712 return res
713 end
714 redef fun decorate_tag(v, res, token)
715 do
716 if not token isa TClassid then return null
717 res.add_class("nc_def")
718
719 var md = mclassdef
720 if md == null then return null
721 return md.infobox(v)
722 end
723 end
724 redef class APropdef
725 redef fun make_tag(v)
726 do
727 var res = new HTMLTag("span")
728 res.add_class("nc_pdef")
729 var mpd
730 mpd = mpropdef
731 if mpd != null then
732 #res.add(tag(mpd))
733 res.attr("id", mpd.to_s)
734 end
735 if self isa AAttrPropdef then
736 mpd = mreadpropdef
737 if mpd != null then res.add(tag(mpd))
738 mpd = mwritepropdef
739 if mpd != null then res.add(tag(mpd))
740 end
741 return res
742 end
743
744 private fun tag(mpd: MPropDef): HTMLTag
745 do
746 var a = new HTMLTag("a")
747 a.attr("id", mpd.to_s)
748 return a
749 end
750 end
751
752 redef class Token
753 # Produce an HTMLTag with the correct contents and CSS classes
754 # Subclasses can redefine it to decorate the tag
755 redef fun make_tag(v: HighlightVisitor): HTMLTag
756 do
757 var res = new HTMLTag("span")
758 res.text(text)
759 return res
760 end
761 end
762
763 redef class TokenKeyword
764 redef fun make_tag(v)
765 do
766 var res = super
767 res.add_class("nc_k")
768 return res
769 end
770 end
771 redef class TokenOperator
772 redef fun make_tag(v)
773 do
774 var res = super
775 res.add_class("nc_o")
776 return res
777 end
778 end
779
780 redef class AVarFormExpr
781 redef fun decorate_tag(v, res, token)
782 do
783 if token != n_id then return null
784 var variable = self.variable
785 if variable == null then return null
786 res.add_class("nc_v")
787 return variable.infobox(v)
788 end
789 end
790
791 redef class AVardeclExpr
792 redef fun decorate_tag(v, res, token)
793 do
794 if token != n_id then return null
795 var variable = self.variable
796 if variable == null then return null
797 res.add_class("nc_v")
798 return variable.infobox(v)
799 end
800 end
801
802 redef class AForGroup
803 redef fun decorate_tag(v, res, token)
804 do
805 if not token isa TId then return null
806 var vs = variables
807 if vs == null then return null
808 res.add_class("nc_v")
809 var idx = n_ids.index_of(token)
810 var variable = vs[idx]
811 return variable.infobox(v)
812 end
813 end
814
815 redef class AParam
816 redef fun decorate_tag(v, res, token)
817 do
818 if token != n_id then return null
819 var mp = mparameter
820 if mp == null then return null
821 var variable = self.variable
822 if variable == null then return null
823 res.add_class("nc_v")
824 return variable.infobox(v)
825 end
826 end
827
828 redef class AAssertExpr
829 redef fun decorate_tag(v, res, token)
830 do
831 if not token isa TId then return null
832 res.add_class("nc_ast")
833 return null
834 end
835 end
836
837 redef class ALabel
838 redef fun decorate_tag(v, res, token)
839 do
840 if not token isa TId then return null
841 res.add_class("nc_la")
842 return null
843 end
844 end
845
846 redef class ASendExpr
847 redef fun decorate_tag(v, res, token)
848 do
849 if callsite == null then return null
850 return callsite.infobox(v)
851 end
852 end
853
854 redef class ANewExpr
855 redef fun decorate_tag(v, res, token)
856 do
857 if callsite == null then return null
858 return callsite.infobox(v)
859 end
860 end
861
862 redef class AAssignOp
863 redef fun decorate_tag(v, res, token)
864 do
865 var p = parent
866 assert p isa AReassignFormExpr
867
868 var callsite = p.reassign_callsite
869 if callsite == null then return null
870 return callsite.infobox(v)
871 end
872 end
873
874 redef class AModuleName
875 redef fun decorate_tag(v, res, token)
876 do
877 return parent.decorate_tag(v, res, token)
878 end
879 end
880
881 redef class AModuledecl
882 redef fun decorate_tag(v, res, token)
883 do
884 if not token isa TId then return null
885 res.add_class("nc_def")
886 res.add_class("nc_m")
887 var p = parent
888 assert p isa AModule
889 var mm = p.mmodule
890 if mm == null then return null
891 return mm.infobox(v)
892 end
893 end
894
895 redef class AStdImport
896 redef fun decorate_tag(v, res, token)
897 do
898 if not token isa TId then return null
899 res.add_class("nc_m")
900 var mm = mmodule
901 if mm == null then return null
902 return mm.infobox(v)
903 end
904 end
905 redef class AAttrPropdef
906 redef fun decorate_tag(v, res, token)
907 do
908 if not token isa TId then return null
909 res.add_class("nc_def")
910 var mpd: nullable MPropDef
911 mpd = mreadpropdef
912 if mpd == null then mpd = mpropdef
913 if mpd == null then return null
914 return mpd.infobox(v)
915 end
916 end
917
918 redef class TId
919 redef fun make_tag(v)
920 do
921 var res = super
922 res.add_class("nc_i")
923 return res
924 end
925 end
926 redef class AMethid
927 redef fun make_tag(v)
928 do
929 var res = new HTMLTag("span")
930 res.add_class("nc_def")
931 return res
932 end
933 redef fun decorate_tag(v, res, token)
934 do
935 return null
936 # nothing to decorate
937 end
938 redef fun infobox(v)
939 do
940 var p = parent
941 if not p isa AMethPropdef then return null
942 var mpd = p.mpropdef
943 if mpd == null then return null
944 return mpd.infobox(v)
945 end
946 end
947 redef class TAttrid
948 redef fun make_tag(v)
949 do
950 var res = super
951 res.add_class("nc_a")
952 return res
953 end
954 end
955 redef class AAttrFormExpr
956 redef fun decorate_tag(v, res, token)
957 do
958 if not token isa TAttrid then return null
959 var p = mproperty
960 if p == null then return null
961 return p.intro.infobox(v)
962 end
963 end
964 redef class TClassid
965 redef fun make_tag(v)
966 do
967 var res = super
968 res.add_class("nc_t")
969 return res
970 end
971 end
972 redef class AType
973 redef fun decorate_tag(v, res, token)
974 do
975 if not token isa TClassid then return null
976 var mt = mtype
977 if mt == null then return null
978 mt = mt.undecorate
979 if mt isa MFormalType then
980 res.add_class("nc_vt")
981 end
982 return mt.infobox(v)
983 end
984 end
985 redef class AFormaldef
986 redef fun decorate_tag(v, res, token)
987 do
988 if not token isa TClassid then return null
989 res.add_class("nc_vt")
990 if mtype == null then return null
991 return mtype.infobox(v)
992 end
993 end
994 redef class ATypePropdef
995 redef fun decorate_tag(v, res, token)
996 do
997 if not token isa TClassid then return null
998 res.add_class("nc_def")
999 var md = mpropdef
1000 if md == null then return null
1001 return md.infobox(v)
1002 end
1003 end
1004 redef class TComment
1005 redef fun make_tag(v)
1006 do
1007 var res = super
1008 if is_loose then
1009 res.add_class("nc_c")
1010 end
1011 return res
1012 end
1013 end
1014 redef class ADoc
1015 redef fun make_tag(v)
1016 do
1017 var res = new HTMLTag("span")
1018 res.add_class("nc_d")
1019 return res
1020 end
1021 end
1022 redef class TokenLiteral
1023 redef fun make_tag(v)
1024 do
1025 var res = super
1026 res.add_class("nc_l")
1027 return res
1028 end
1029 end
1030 redef class ASuperstringExpr
1031 redef fun make_tag(v)
1032 do
1033 var res = new HTMLTag("span")
1034 res.add_class("nc_ss")
1035 return res
1036 end
1037 end
1038 redef class AStringFormExpr
1039 redef fun decorate_tag(v, res, token)
1040 do
1041 # Workaround to tag strings
1042 res.classes.remove("nc_l")
1043 res.add_class("nc_s")
1044 return super
1045 end
1046 end
1047 redef class AExpr
1048 redef fun decorate_tag(v, res, token)
1049 do
1050 var t = mtype
1051 if t == null then return null
1052 return t.infobox(v)
1053 end
1054 end