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