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