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