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