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