a_star: don't crash on deserialization errors and limit static types
[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 a content as a nit source file.
25 fun hightlightcode(hl: HighlightVisitor, content: String): HLCode
26 do
27 # Prepare a stand-alone tool context
28 var tc = new ToolContext
29 tc.nit_dir = tc.locate_nit_dir # still use the common lib to have core
30 tc.keep_going = true # no exit, obviously
31 tc.opt_warn.value = -1 # no output, obviously
32
33 # Prepare an stand-alone model and model builder.
34 # Unfortunately, models are enclosing and append-only.
35 # There is no way (yet?) to have a shared module `core` with
36 # isolated and throwable user modules.
37 var model = new Model
38 var mb = new ModelBuilder(model, tc)
39
40 # Parse the code
41 var source = new SourceFile.from_string("", content + "\n")
42 var lexer = new Lexer(source)
43 var parser = new Parser(lexer)
44 var tree = parser.parse
45
46 var hlcode = new HLCode(hl, content, source)
47
48 # Check syntax error
49 var eof = tree.n_eof
50 if eof isa AError then
51 mb.error(eof, eof.message)
52 hl.hightlight_source(source)
53 return hlcode
54 end
55 var amodule = tree.n_base.as(not null)
56
57 # Load the AST as a module in the model
58 # Then process it
59 mb.load_rt_module(null, amodule, "")
60 mb.run_phases
61
62 # Highlight the processed module
63 hl.enter_visit(amodule)
64 return hlcode
65 end
66
67 # A standalone highlighted piece of code
68 class HLCode
69 super Serializable
70
71 # The highlighter used
72 var hl: HighlightVisitor
73
74 # The raw code source
75 var content: String
76
77 # The pseudo source-file
78 var source: SourceFile
79
80 # JavaScript code to update an existing codemirror editor.
81 fun code_mirror_update: Template
82 do
83
84 var res = new Template
85 res.add """
86 function nitmessage() {
87 editor.operation(function(){
88 for (var i = 0; i < widgets.length; ++i)
89 editor.removeLineWidget(widgets[i]);
90 widgets.length = 0;
91 """
92
93 for m in source.messages do
94 res.add """
95 var l = document.createElement("div");
96 l.className = "lint-error"
97 l.innerHTML = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{m.text.html_escape}}}";
98 var w = editor.addLineWidget({{{m.location.line_start-1}}}, l);
99 widgets.push(w);
100 """
101 end
102 res.add """});}"""
103 return res
104 end
105
106 redef fun core_serialize_to(v)
107 do
108 v.serialize_attribute("code", hl.html.write_to_string)
109 var msgs = new Array[Map[String, Serializable]]
110 for m in source.messages do
111 var o = new Map[String, Serializable]
112 msgs.add o
113 o["line"] = m.location.line_start-1
114 o["message"] = m.text
115 end
116 v.serialize_attribute("messages", msgs)
117 end
118 end
119
120 # Visitor used to produce a HTML tree based on a AST on a `Source`
121 class HighlightVisitor
122 # The root of the HTML hierarchy
123 var html = new HTMLTag("span")
124
125 # Should the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
126 #
127 # Used to have a really huge and verbose HTML (mainly for debug)
128 var with_ast = false is writable
129
130 # Prefixes used in generated IDs for line `<span>` elements.
131 # Useful if more than one highlighted code is present in the same HTML document.
132 #
133 # If set to the empty string, id for lines are disabled.
134 #
135 # Is `"L"` by default.
136 var line_id_prefix = "L" is writable
137
138 # The first line to generate, null if start at the first line
139 var first_line: nullable Int = null is writable
140
141 # The last line to generate, null if finish at the last line
142 var last_line: nullable Int = null is writable
143
144 # When highlighting a node, show its messages (errors, warnings), if any.
145 #
146 # default: true
147 var show_messages = true is writable
148
149 # When highlighting a node, attach a full popupable infobox, if any.
150 #
151 # If `false`, only a simple `title` tooltip is used.
152 #
153 # default: true
154 var show_infobox = true is writable
155
156 # A reference to an entity used in generated `<a>` elements.
157 #
158 # It is used to refer to some specific entities when generating links.
159 # If `null` is returned, then no link are generated and `<a>` elements become `<span>`.
160 #
161 # By default, `null` is returned.
162 # Clients are therefore encouraged to redefine the method in a subclass to control where entities should link to.
163 fun hrefto(entity: MEntity): nullable String do return null
164
165 init
166 do
167 html.add_class("nitcode")
168 end
169
170 # When highlighting a node, also consider the loose tokens around it.
171 #
172 # Loose tokens are tokens discarded from the AST but attached before
173 # or after some non-loose tokens. See `Token::is_loose`.
174 #
175 # When this flag is set to `true`, the loose tokens that are before the
176 # first token and after the last token are also highlighted.
177 #
178 # Default: false.
179 var include_loose_tokens = false is writable
180
181 # When highlighting a node, the first and the last lines are fully included.
182 #
183 # If the highlighted node starts (or ends) in the middle of a line,
184 # this flags forces the whole line to be highlighted.
185 #
186 # Default: false
187 var include_whole_lines = false is writable
188
189 # The entry-point of the highlighting.
190 # Will fill `html` with the generated HTML content.
191 fun enter_visit(n: ANode)
192 do
193 n.parentize_tokens
194
195 var f
196 var l
197
198 if n isa Token then
199 f = n
200 l = n
201 else
202 assert n isa Prod
203 f = n.first_token
204 if f == null then return
205 l = n.last_token
206 if l == null then return
207 end
208
209 if include_loose_tokens then
210 if f.prev_looses.not_empty then f = f.prev_looses.first
211 if l.next_looses.not_empty then l = l.next_looses.last
212 end
213
214 var line = first_line
215 if line != null then
216 while f.location.line_start < line do
217 f = f.next_token
218 if f == null then return
219 end
220 end
221
222 line = last_line
223 if line != null then
224 while l.location.line_end > line do
225 l = l.prev_token
226 if l == null then return
227 end
228 end
229
230 if include_whole_lines then
231 f = f.first_real_token_in_line
232 l = l.last_real_token_in_line
233 end
234
235 htmlize(f, l)
236 end
237
238 private fun full_tag(anode: ANode, hv: HighlightVisitor): nullable HTMLTag
239 do
240 var tag = anode.make_tag(hv)
241 if tag == null then return null
242 var infobox = anode.infobox(hv)
243 if infobox == null and anode isa Token then
244 var pa = anode.parent
245 if pa != null then
246 infobox = pa.decorate_tag(hv, tag, anode)
247 end
248 end
249 if infobox != null and not show_infobox then
250 tag.attr("title", infobox.title)
251 tag.classes.add "titled"
252 infobox = null
253 end
254 var messages = anode.location.messages
255 if messages != null and show_messages then
256 tag.css("border-bottom", "solid 2px red")
257 if infobox == null then
258 infobox = new HInfoBox(hv, "Messages")
259 end
260 var c = infobox.new_dropdown("{messages.length} message(s)", "")
261 for m in messages do
262 c.open("li").append(m.text)
263 end
264 end
265 if infobox != null then
266 tag.attach_infobox(infobox)
267 end
268 return tag
269 end
270
271 # Highlight a full lexed source file.
272 #
273 # REQUIRE `source.first_token != null`
274 fun hightlight_source(source: SourceFile)
275 do
276 htmlize(source.first_token.as(not null), null)
277 end
278
279 # Produce HTML between two tokens
280 protected fun htmlize(first_token: Token, last_token: nullable Token)
281 do
282 var stack2 = new Array[HTMLTag]
283 var stack = new Array[Prod]
284 var line = 0
285 var c: nullable Token = first_token
286 var hv = self
287 while c != null do
288 var starting
289
290 # Handle start of line
291 var cline = c.location.line_start
292 if cline != line then
293 # Handle starting block productions,
294 # Because c could be a detached token, get prods in
295 # the first AST token
296 var c0 = c.first_token_in_line
297 starting = null
298 if c0 != null then starting = c0.starting_prods
299 if starting != null then for p in starting do
300 if not p.is_block then continue
301 var tag = full_tag(p, hv)
302 if tag == null then continue
303 tag.add_class("foldable")
304 stack2.add(html)
305 html.add tag
306 html = tag
307 stack.add(p)
308 end
309
310 # Add a div for the whole line
311 var tag = new HTMLTag("span")
312 var p = line_id_prefix
313 if p != "" then tag.attrs["id"] = "{p}{cline}"
314 tag.classes.add "line"
315 stack2.add(html)
316 html.add tag
317 html = tag
318 line = cline
319 end
320
321 # Add the blank, verbatim
322 html.add_raw_html c.blank_before
323
324 # Handle starting span production
325 starting = c.starting_prods
326 if starting != null then for p in starting do
327 if not p.is_span then continue
328 var tag = full_tag(p, hv)
329 if tag == null then continue
330 stack2.add(html)
331 html.add tag
332 html = tag
333 stack.add(p)
334 end
335
336 # Add the token
337 if c isa TEol then
338 html.append "\n"
339 else
340 var tag = full_tag(c, hv)
341 if tag != null then html.add tag
342 end
343
344 # Handle ending span productions
345 var ending = c.ending_prods
346 if ending != null then for p in ending do
347 if not p.is_span then continue
348 if stack.is_empty or p != stack.last then continue
349 stack.pop
350 html = stack2.pop
351 end
352
353 # Handle end of line and end of file
354 var n = c.next_token
355 if c == last_token then n = null
356 if n == null or n.location.line_start != line then
357 # closes the line div
358 html = stack2.pop
359
360 # close the block production divs
361 var c0 = c.last_token_in_line
362 ending = null
363 if c0 != null then ending = c0.ending_prods
364 if ending != null then for p in ending do
365 if not p.is_block then continue
366 if stack.is_empty or p != stack.last then continue
367 stack.pop
368 html = stack2.pop
369 end
370 end
371
372 c = n
373 end
374 if not stack2.is_empty then html = stack2.first
375 end
376
377 # Return a default CSS content related to CSS classes used in the `html` tree.
378 # Could be inlined in the `.html` file of saved as a specific `.css` file.
379 fun css_content: String
380 do
381 return """
382 .nitcode a { color: inherit; cursor:pointer; }
383 .nitcode .titled:hover { text-decoration: underline; } /* underline titles */
384 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
385 .nitcode .foldable { display: block } /* for block productions*/
386 .nitcode .line{ display: block } /* for lines */
387 .nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
388 .nitcode :target { background-color: #FFF3C2 } /* target highlight*/
389 /* lexical raw tokens. independent of usage or semantic: */
390 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
391 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
392 .nitcode .nc_k { font-weight: bold; } /* keyword */
393 .nitcode .nc_o {} /* operator */
394 .nitcode .nc_i {} /* standard identifier */
395 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
396 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
397 .nitcode .nc_l { color: #009999; } /* char and number literal */
398 .nitcode .nc_s { color: #8F1546; } /* string literal */
399 /* syntactic token usage. added because of their position in the AST */
400 .nitcode .nc_ast { color: blue; } /* assert label */
401 .nitcode .nc_la { color: blue; } /* break/continue label */
402 .nitcode .nc_m { color: #445588; } /* module name */
403 /* syntactic groups */
404 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
405 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
406 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
407 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
408 .nitcode .nc_cdef {} /* A whole class definition */
409 .nitcode .nc_pdef {} /* A whole property definition */
410 /* semantic token usage */
411 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
412 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
413
414 .nitcode .nc_error { border: 1px red solid;} /* not used */
415 .popover { max-width: 800px !important; }
416 """
417 end
418
419 # Additional content to inject in the <head> tag
420 # Note: does not include `css_content`; handle it yourself.
421 fun head_content: String
422 do
423 return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
424 end
425
426 # Additional content to inject just before the closing </body> tag
427 fun foot_content: String
428 do
429 return """
430 <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
431 <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
432 <script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
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