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