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