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