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