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