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