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