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