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