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