highlight: avoid some NPE and fix warnings
[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 end
429
430 redef class MModule
431 redef fun infobox(v)
432 do
433 var res = new HInfoBox(v, "module {name}")
434 res.href = v.hrefto(self)
435 res.new_field("module").add(linkto(v))
436 var mdoc = self.mdoc
437 if mdoc != null then mdoc.fill_infobox(res)
438 if in_importation.greaters.length > 1 then
439 var c = res.new_dropdown("imports", "{in_importation.greaters.length-1} modules")
440 for x in in_importation.greaters do
441 if x == self then continue
442 c.open("li").add x.linkto(v)
443 end
444 end
445 return res
446 end
447
448 redef fun linkto(v) do return linkto_text(v, name)
449 end
450
451 redef class MClassDef
452 redef fun infobox(v)
453 do
454 var res = new HInfoBox(v, "class {mclass.name}")
455 res.href = v.hrefto(self)
456 if is_intro then
457 res.new_field("class").text(mclass.name)
458 else
459 res.new_field("redef class").text(mclass.name)
460 res.new_field("intro").add mclass.intro.linkto_text(v, "in {mclass.intro_mmodule.to_s}")
461 end
462 var mdoc = self.mdoc
463 if mdoc == null then mdoc = mclass.intro.mdoc
464 if mdoc != null then mdoc.fill_infobox(res)
465
466 var in_hierarchy = self.in_hierarchy
467 if in_hierarchy == null then return res
468
469 if in_hierarchy.greaters.length > 1 then
470 var c = res.new_dropdown("hier", "super-classes")
471 for x in in_hierarchy.greaters do
472 if x == self then continue
473 if not x.is_intro then continue
474 c.open("li").add x.linkto(v)
475 end
476 end
477 if in_hierarchy.smallers.length > 1 then
478 var c = res.new_dropdown("hier", "sub-classes")
479 for x in in_hierarchy.smallers do
480 if x == self then continue
481 if not x.is_intro then continue
482 c.open("li").add x.linkto(v)
483 end
484 end
485 if mclass.mclassdefs.length > 1 then
486 var c = res.new_dropdown("redefs", "refinements")
487 for x in mclass.mclassdefs do
488 if x == self then continue
489 c.open("li").add x.linkto_text(v, "in {x.mmodule}")
490 end
491 end
492 return res
493 end
494 end
495
496 redef class MPropDef
497 redef fun infobox(v)
498 do
499 var res = new HInfoBox(v, to_s)
500 res.href = v.hrefto(self)
501 if self isa MMethodDef then
502 var msignature = self.msignature
503 if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto(v)
504 else if self isa MAttributeDef then
505 var static_mtype = self.static_mtype
506 if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto(v)
507 else if self isa MVirtualTypeDef then
508 var bound = self.bound
509 if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto(v)
510 else
511 res.new_field("wat?").append(mproperty.name)
512 end
513
514 if is_intro then
515 else
516 res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
517 end
518 var mdoc = self.mdoc
519 if mdoc == null then mdoc = mproperty.intro.mdoc
520 if mdoc != null then mdoc.fill_infobox(res)
521 if mproperty.mpropdefs.length > 1 then
522 var c = res.new_dropdown("redef", "redefinitions")
523 for x in mproperty.mpropdefs do
524 c.open("li").add x.linkto_text(v, "in {x.mclassdef}")
525 end
526 end
527
528 return res
529 end
530 end
531
532 redef class MClassType
533 redef fun infobox(v)
534 do
535 var res = new HInfoBox(v, to_s)
536 res.href = v.hrefto(self)
537 res.new_field("class").add mclass.intro.linkto(v)
538 var mdoc = mclass.mdoc
539 if mdoc == null then mdoc = mclass.intro.mdoc
540 if mdoc != null then mdoc.fill_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 var pd = p.intro
555 res.new_field("virtual type").add pd.linkto(v)
556 var mdoc = pd.mdoc
557 if mdoc != null then mdoc.fill_infobox(res)
558 return res
559 end
560 redef fun linkto(v)
561 do
562 return mproperty.intro.linkto(v)
563 end
564 end
565 redef class MParameterType
566 redef fun infobox(v)
567 do
568 var res = new HInfoBox(v, to_s)
569 res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v)
570 return res
571 end
572 end
573
574 redef class MNullableType
575 redef fun infobox(v)
576 do
577 return mtype.infobox(v)
578 end
579 redef fun linkto(v)
580 do
581 var res = new HTMLTag("span")
582 res.append("nullable ").add(mtype.linkto(v))
583 return res
584 end
585 end
586
587 redef class MNotNullType
588 redef fun infobox(v)
589 do
590 return mtype.infobox(v)
591 end
592 redef fun linkto(v)
593 do
594 var res = new HTMLTag("span")
595 res.append("not null ").add(mtype.linkto(v))
596 return res
597 end
598 end
599
600 redef class MNullType
601 redef fun infobox(v)
602 do
603 var res = new HInfoBox(v, to_s)
604 return res
605 end
606 redef fun linkto(v)
607 do
608 var res = new HTMLTag("span")
609 res.append("null")
610 return res
611 end
612 end
613
614 redef class MSignature
615 redef fun linkto(v)
616 do
617 var res = new HTMLTag("span")
618 var first = true
619 if not mparameters.is_empty then
620 res.append "("
621 for p in mparameters do
622 if first then
623 first = false
624 else
625 res.append ", "
626 end
627 res.append p.name
628 res.append ": "
629 res.add p.mtype.linkto(v)
630 end
631 res.append ")"
632 end
633 var ret = return_mtype
634 if ret != null then
635 res.append ": "
636 res.add ret.linkto(v)
637 end
638 return res
639 end
640 end
641
642 redef class CallSite
643 redef fun infobox(v)
644 do
645 var res = new HInfoBox(v, "call {mpropdef}")
646 res.href = v.hrefto(mpropdef)
647 res.new_field("call").add(mpropdef.linkto(v)).add(msignature.linkto(v))
648 if mpropdef.is_intro then
649 else
650 res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
651 end
652 var mdoc = mpropdef.mdoc
653 if mdoc == null then mdoc = mproperty.intro.mdoc
654 if mdoc != null then mdoc.fill_infobox(res)
655
656 return res
657 end
658 redef fun linkto(v)
659 do
660 return mpropdef.linkto(v)
661 end
662 end
663
664 redef class Variable
665 super HInfoBoxable
666 redef fun infobox(v)
667 do
668 var declared_type = self.declared_type
669 if declared_type == null then
670 var res = new HInfoBox(v, "{name}")
671 res.new_field("local var").append("{name}")
672 return res
673 end
674 var res = new HInfoBox(v, "{name}: {declared_type}")
675 res.new_field("local var").append("{name}:").add(declared_type.linkto(v))
676 return res
677 end
678 end
679
680
681 ##
682
683 redef class ANode
684 # Optionally creates a tag that encapsulate the AST element on HTML rendering
685 protected fun make_tag(v: HighlightVisitor): nullable HTMLTag do return null
686
687 # Add aditionnal information on a child-token and return an additionnal HInfoBox on it
688 protected fun decorate_tag(v: HighlightVisitor, res: HTMLTag, token: Token): nullable HInfoBox
689 do
690 #debug("no decoration for {token.inspect}")
691 #res.add_class("nc_error")
692 return null
693 end
694
695 # Return a optional infobox
696 fun infobox(v: HighlightVisitor): nullable HInfoBox do return null
697 end
698
699 redef class AQclassid
700 redef fun decorate_tag(v, res, token)
701 do
702 if token != n_id then return null
703 var parent = self.parent
704 if parent == null then return null
705 return parent.decorate_tag(v, res, token)
706 end
707 end
708
709 redef class AQid
710 redef fun decorate_tag(v, res, token)
711 do
712 if token != n_id then return null
713 var parent = self.parent
714 if parent == null then return null
715 return parent.decorate_tag(v, res, token)
716 end
717 end
718
719 redef class AStdClassdef
720 redef fun make_tag(v)
721 do
722 var res = new HTMLTag("span")
723 res.add_class("nc_cdef")
724 var md = mclassdef
725 if md != null then res.attr("id", md.to_s)
726 return res
727 end
728 redef fun decorate_tag(v, res, token)
729 do
730 if not token isa TClassid then return null
731 res.add_class("nc_def")
732
733 var md = mclassdef
734 if md == null then return null
735 return md.infobox(v)
736 end
737 end
738 redef class APropdef
739 redef fun make_tag(v)
740 do
741 var res = new HTMLTag("span")
742 res.add_class("nc_pdef")
743 var mpd
744 mpd = mpropdef
745 if mpd != null then
746 #res.add(tag(mpd))
747 res.attr("id", mpd.to_s)
748 end
749 if self isa AAttrPropdef then
750 mpd = mreadpropdef
751 if mpd != null then res.add(tag(mpd))
752 mpd = mwritepropdef
753 if mpd != null then res.add(tag(mpd))
754 end
755 return res
756 end
757
758 private fun tag(mpd: MPropDef): HTMLTag
759 do
760 var a = new HTMLTag("a")
761 a.attr("id", mpd.to_s)
762 return a
763 end
764 end
765
766 redef class Token
767 # Produce an HTMLTag with the correct contents and CSS classes
768 # Subclasses can redefine it to decorate the tag
769 redef fun make_tag(v): HTMLTag
770 do
771 var res = new HTMLTag("span")
772 res.text(text)
773 return res
774 end
775 end
776
777 redef class TokenKeyword
778 redef fun make_tag(v)
779 do
780 var res = super
781 res.add_class("nc_k")
782 return res
783 end
784 end
785 redef class TokenOperator
786 redef fun make_tag(v)
787 do
788 var res = super
789 res.add_class("nc_o")
790 return res
791 end
792 end
793
794 redef class AVarFormExpr
795 redef fun decorate_tag(v, res, token)
796 do
797 if token != n_id then return null
798 var variable = self.variable
799 if variable == null then return null
800 res.add_class("nc_v")
801 return variable.infobox(v)
802 end
803 end
804
805 redef class AVardeclExpr
806 redef fun decorate_tag(v, res, token)
807 do
808 if token != n_id then return null
809 var variable = self.variable
810 if variable == null then return null
811 res.add_class("nc_v")
812 return variable.infobox(v)
813 end
814 end
815
816 redef class AForGroup
817 redef fun decorate_tag(v, res, token)
818 do
819 if not token isa TId then return null
820 var vs = variables
821 if vs == null then return null
822 res.add_class("nc_v")
823 var idx = n_ids.index_of(token)
824 var variable = vs[idx]
825 return variable.infobox(v)
826 end
827 end
828
829 redef class AParam
830 redef fun decorate_tag(v, res, token)
831 do
832 if token != n_id then return null
833 var mp = mparameter
834 if mp == null then return null
835 var variable = self.variable
836 if variable == null then return null
837 res.add_class("nc_v")
838 return variable.infobox(v)
839 end
840 end
841
842 redef class AAssertExpr
843 redef fun decorate_tag(v, res, token)
844 do
845 if not token isa TId then return null
846 res.add_class("nc_ast")
847 return null
848 end
849 end
850
851 redef class ALabel
852 redef fun decorate_tag(v, res, token)
853 do
854 if not token isa TId then return null
855 res.add_class("nc_la")
856 return null
857 end
858 end
859
860 redef class ASendExpr
861 redef fun decorate_tag(v, res, token)
862 do
863 var callsite = self.callsite
864 if callsite == null then return null
865 return callsite.infobox(v)
866 end
867 end
868
869 redef class ANewExpr
870 redef fun decorate_tag(v, res, token)
871 do
872 var callsite = self.callsite
873 if callsite == null then return null
874 return callsite.infobox(v)
875 end
876 end
877
878 redef class AAssignOp
879 redef fun decorate_tag(v, res, token)
880 do
881 var p = parent
882 assert p isa AReassignFormExpr
883
884 var callsite = p.reassign_callsite
885 if callsite == null then return null
886 return callsite.infobox(v)
887 end
888 end
889
890 redef class AModuleName
891 redef fun decorate_tag(v, res, token)
892 do
893 var p = parent
894 if p == null then return null
895 return p.decorate_tag(v, res, token)
896 end
897 end
898
899 redef class AModuledecl
900 redef fun decorate_tag(v, res, token)
901 do
902 if not token isa TId then return null
903 res.add_class("nc_def")
904 res.add_class("nc_m")
905 var p = parent
906 assert p isa AModule
907 var mm = p.mmodule
908 if mm == null then return null
909 return mm.infobox(v)
910 end
911 end
912
913 redef class AStdImport
914 redef fun decorate_tag(v, res, token)
915 do
916 if not token isa TId then return null
917 res.add_class("nc_m")
918 var mm = mmodule
919 if mm == null then return null
920 return mm.infobox(v)
921 end
922 end
923 redef class AAttrPropdef
924 redef fun decorate_tag(v, res, token)
925 do
926 if not token isa TId then return null
927 res.add_class("nc_def")
928 var mpd: nullable MPropDef
929 mpd = mreadpropdef
930 if mpd == null then mpd = mpropdef
931 if mpd == null then return null
932 return mpd.infobox(v)
933 end
934 end
935
936 redef class TId
937 redef fun make_tag(v)
938 do
939 var res = super
940 res.add_class("nc_i")
941 return res
942 end
943 end
944 redef class AMethid
945 redef fun make_tag(v)
946 do
947 var res = new HTMLTag("span")
948 res.add_class("nc_def")
949 return res
950 end
951 redef fun decorate_tag(v, res, token)
952 do
953 return null
954 # nothing to decorate
955 end
956 redef fun infobox(v)
957 do
958 var p = parent
959 if not p isa AMethPropdef then return null
960 var mpd = p.mpropdef
961 if mpd == null then return null
962 return mpd.infobox(v)
963 end
964 end
965 redef class TAttrid
966 redef fun make_tag(v)
967 do
968 var res = super
969 res.add_class("nc_a")
970 return res
971 end
972 end
973 redef class AAttrFormExpr
974 redef fun decorate_tag(v, res, token)
975 do
976 if not token isa TAttrid then return null
977 var p = mproperty
978 if p == null then return null
979 return p.intro.infobox(v)
980 end
981 end
982 redef class TClassid
983 redef fun make_tag(v)
984 do
985 var res = super
986 res.add_class("nc_t")
987 return res
988 end
989 end
990 redef class AType
991 redef fun decorate_tag(v, res, token)
992 do
993 if not token isa TClassid then return null
994 var mt = mtype
995 if mt == null then return null
996 mt = mt.undecorate
997 if mt isa MFormalType then
998 res.add_class("nc_vt")
999 end
1000 return mt.infobox(v)
1001 end
1002 end
1003 redef class AFormaldef
1004 redef fun decorate_tag(v, res, token)
1005 do
1006 if not token isa TClassid then return null
1007 res.add_class("nc_vt")
1008 var mtype = self.mtype
1009 if mtype == null then return null
1010 return mtype.infobox(v)
1011 end
1012 end
1013 redef class ATypePropdef
1014 redef fun decorate_tag(v, res, token)
1015 do
1016 if not token isa TClassid then return null
1017 res.add_class("nc_def")
1018 var md = mpropdef
1019 if md == null then return null
1020 return md.infobox(v)
1021 end
1022 end
1023 redef class TComment
1024 redef fun make_tag(v)
1025 do
1026 var res = super
1027 if is_loose then
1028 res.add_class("nc_c")
1029 end
1030 return res
1031 end
1032 end
1033 redef class ADoc
1034 redef fun make_tag(v)
1035 do
1036 var res = new HTMLTag("span")
1037 res.add_class("nc_d")
1038 return res
1039 end
1040 end
1041 redef class TokenLiteral
1042 redef fun make_tag(v)
1043 do
1044 var res = super
1045 res.add_class("nc_l")
1046 return res
1047 end
1048 end
1049 redef class ASuperstringExpr
1050 redef fun make_tag(v)
1051 do
1052 var res = new HTMLTag("span")
1053 res.add_class("nc_ss")
1054 return res
1055 end
1056 end
1057 redef class AStringFormExpr
1058 redef fun decorate_tag(v, res, token)
1059 do
1060 # Workaround to tag strings
1061 res.classes.remove("nc_l")
1062 res.add_class("nc_s")
1063 return super
1064 end
1065 end
1066 redef class AExpr
1067 redef fun decorate_tag(v, res, token)
1068 do
1069 var t = mtype
1070 if t == null then return null
1071 return t.infobox(v)
1072 end
1073 end