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