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