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