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