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