1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Highliting of Nit AST
23 # Visitor used to produce a HTML tree based on a AST on a `Source`
24 class HighlightVisitor
25 # The root of the HTML hierarchy
26 var html
= new HTMLTag("span")
28 # Is the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
29 # Used to have a really huge and verbose HTML (mainly for debug)
30 var with_ast
= false is writable
32 # The first line to generate, null if start at the first line
33 var first_line
: nullable Int = null is writable
35 # The last line to generate, null if finish at the last line
36 var last_line
: nullable Int = null is writable
40 html
.add_class
("nitcode")
43 fun enter_visit
(n
: ANode)
46 var s
= n
.location
.file
47 htmlize
(s
.first_token
.as(not null), s
.last_token
.as(not null))
50 # Produce HTML between two tokens
51 protected fun htmlize
(first_token
, last_token
: Token)
53 var stack2
= new Array[HTMLTag]
54 var stack
= new Array[Prod]
56 var c
: nullable Token = first_token
57 var hv
= new HighlightVisitor
61 # Handle start of line
62 var cline
= c
.location
.line_start
64 # Handle starting block productions,
65 # Because c could be a detached token, get prods in
67 var c0
= c
.first_token_in_line
69 if c0
!= null then starting
= c0
.starting_prods
70 if starting
!= null then for p
in starting
do
71 if not p
.is_block
then continue
72 var tag
= p
.make_tag
(hv
)
73 if tag
== null then continue
74 tag
.add_class
("foldable")
75 var infobox
= p
.infobox
(hv
)
76 if infobox
!= null then tag
.attach_infobox
(infobox
)
83 # Add a div for the whole line
84 var tag
= new HTMLTag("span")
85 tag
.attrs
["id"] = "L{cline}"
86 tag
.classes
.add
"line"
93 # Add the blank, verbatim
94 html
.add_raw_html c
.blank_before
96 # Handle starting span production
97 starting
= c
.starting_prods
98 if starting
!= null then for p
in starting
do
99 if not p
.is_span
then continue
100 var tag
= p
.make_tag
(hv
)
101 if tag
== null then continue
102 var infobox
= p
.infobox
(hv
)
103 if infobox
!= null then tag
.attach_infobox
(infobox
)
114 var tag
= c
.make_tag
(hv
)
117 if c
isa TId or c
isa TClassid or c
isa TAttrid or c
isa TokenLiteral or c
isa TokenOperator then
119 if pa
!= null then infobox
= pa
.decorate_tag
(hv
, tag
, c
)
120 else if c
isa TComment and pa
isa ADoc then
121 infobox
= pa
.decorate_tag
(hv
, tag
, c
)
123 if infobox
!= null then tag
.attach_infobox
(infobox
)
127 # Handle ending span productions
128 var ending
= c
.ending_prods
129 if ending
!= null then for p
in ending
do
130 if not p
.is_span
then continue
131 if stack
.is_empty
or p
!= stack
.last
then continue
136 # Handle end of line and end of file
138 if c
== last_token
then n
= null
139 if n
== null or n
.location
.line_start
!= line
then
140 # closes the line div
143 # close the block production divs
144 var c0
= c
.last_token_in_line
146 if c0
!= null then ending
= c0
.ending_prods
147 if ending
!= null then for p
in ending
do
148 if not p
.is_block
then continue
149 if stack
.is_empty
or p
!= stack
.last
then continue
157 assert stack
.is_empty
158 assert stack2
.is_empty
161 # Return a default CSS content related to CSS classes used in the `html` tree.
162 # Could be inlined in the `.html` file of saved as a specific `.css` file.
163 fun css_content
: String
166 .nitcode a { color: inherit; cursor:pointer; }
167 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
168 .nitcode .foldable { display: block } /* for block productions*/
169 .nitcode .line{ display: block } /* for lines */
170 .nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
171 .nitcode :target { background-color: #FFF3C2 } /* target highlight*/
172 /* lexical raw tokens. independent of usage or semantic: */
173 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
174 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
175 .nitcode .nc_k { font-weight: bold; } /* keyword */
176 .nitcode .nc_o {} /* operator */
177 .nitcode .nc_i {} /* standard identifier */
178 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
179 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
180 .nitcode .nc_l { color: #009999; } /* char and number literal */
181 .nitcode .nc_s { color: #8F1546; } /* string literal */
182 /* syntactic token usage. added because of their position in the AST */
183 .nitcode .nc_ast { color: blue; } /* assert label */
184 .nitcode .nc_la { color: blue; } /* break/continue label */
185 .nitcode .nc_m { color: #445588; } /* module name */
186 /* syntactic groups */
187 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
188 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
189 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
190 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
191 .nitcode .nc_cdef {} /* A whole class definition */
192 .nitcode .nc_pdef {} /* A whole property definition */
193 /* semantic token usage */
194 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
195 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
197 .nitcode .nc_error { border: 1px red solid;} /* not used */
198 .popover { max-width: 800px !important; }
202 # Additional content to inject in the <head> tag
203 # Note: does not include `css_content`; handle it yourself.
204 fun head_content
: String
206 return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
209 # Additional content to inject just before the closing </body> tag
210 fun foot_content
: String
213 <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
214 <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
215 <script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
220 # Attach the infobox to the node by using BootStrap popover
221 fun attach_infobox
(infobox
: HInfoBox)
223 classes
.add
("popupable")
224 attrs
["title"] = infobox
.title
225 var href
= infobox
.href
227 attrs
["data-title"] = """<a href="{{{href}}}">{{{infobox.title}}}</a>"""
229 attrs
["data-content"] = infobox
.content
.write_to_string
230 attrs
["data-toggle"] = "popover"
235 # A generic information container that can be used to decorate AST entities
237 # The visitor used for contextualisation, if needed
238 var visitor
: HighlightVisitor
240 # A short title for the AST element
243 # The primary link where the entity points
245 var href
: nullable String = null
247 # The content of the popuped infobox
248 var content
= new HTMLTag("div")
250 # Append a new field in the popuped infobox
251 fun new_field
(title
: String): HTMLTag
253 content
.open
("b").text
(title
)
255 var res
= content
.open
("span")
260 # Append a new dropdown in the popuped content
261 fun new_dropdown
(title
, text
: String): HTMLTag
263 content
.add_raw_html
"""<div class="dropdown"> <a data-toggle="dropdown" href="#"><b>"""
264 content
.append
(title
)
265 content
.add_raw_html
"</b> "
267 content
.add_raw_html
"""<span class="caret"></span></a>"""
268 var res
= content
.open
("ul").add_class
("dropdown-menu").attr
("role", "menu").attr
("aria-labelledby", "dLabel")
269 content
.add_raw_html
"</div>"
276 # Model entity or whatever that can produce an infobox
277 interface HInfoBoxable
278 # An new infobox documenting the entity
279 fun infobox
(v
: HighlightVisitor): HInfoBox is abstract
281 # A human-readable hyper-text for the entity
282 fun linkto
: HTMLTag is abstract
286 # Append an entry for the doc in the given infobox
287 fun fill_infobox
(res
: HInfoBox)
289 if content
.length
< 2 then
290 res
.new_field
("doc").text
(content
.first
)
293 var c
= res
.new_dropdown
("doc", content
.first
)
294 for x
in content
.iterator
.skip_head
(1) do
296 c
.add_raw_html
"<br>"
308 var res
= new HInfoBox(v
, "module {name}")
310 res
.new_field
("module").add
(linkto
)
312 if mdoc
!= null then mdoc
.fill_infobox
(res
)
313 if in_importation
.greaters
.length
> 1 then
314 var c
= res
.new_dropdown
("imports", "{in_importation.greaters.length-1} modules")
315 for x
in in_importation
.greaters
do
316 if x
== self then continue
317 c
.open
("li").add x
.linkto
325 return name
+ ".html"
328 redef fun linkto
do return linkto_text
(name
)
330 # Link to the entitiy with a specific text
331 fun linkto_text
(text
: String): HTMLTag
333 return (new HTMLTag("a")).attr
("href", href
).text
(text
)
337 redef class MClassDef
340 var res
= new HInfoBox(v
, "class {mclass.name}")
343 res
.new_field
("class").text
(mclass
.name
)
345 res
.new_field
("redef class").text
(mclass
.name
)
346 res
.new_field
("intro").add mclass
.intro
.linkto_text
("in {mclass.intro.mmodule.to_s}")
349 if mdoc
== null then mdoc
= mclass
.intro
.mdoc
350 if mdoc
!= null then mdoc
.fill_infobox
(res
)
352 if in_hierarchy
.greaters
.length
> 1 then
353 var c
= res
.new_dropdown
("hier", "super-classes")
354 for x
in in_hierarchy
.greaters
do
355 if x
== self then continue
356 if not x
.is_intro
then continue
357 c
.open
("li").add x
.linkto
360 if in_hierarchy
.smallers
.length
> 1 then
361 var c
= res
.new_dropdown
("hier", "sub-classes")
362 for x
in in_hierarchy
.smallers
do
363 if x
== self then continue
364 if not x
.is_intro
then continue
365 c
.open
("li").add x
.linkto
368 if mclass
.mclassdefs
.length
> 1 then
369 var c
= res
.new_dropdown
("redefs", "refinements")
370 for x
in mclass
.mclassdefs
do
371 if x
== self then continue
372 c
.open
("li").add x
.linkto_text
("in {x.mmodule}")
380 return mmodule
.href
+ "#" + to_s
383 redef fun linkto
do return linkto_text
(mclass
.name
)
385 # Link to the entitiy with a specific text
386 fun linkto_text
(text
: String): HTMLTag
388 return (new HTMLTag("a")).attr
("href", href
).text
(text
)
395 var res
= new HInfoBox(v
, to_s
)
397 if self isa MMethodDef then
398 res
.new_field
("fun").append
(mproperty
.name
).add msignature
.linkto
399 else if self isa MAttributeDef then
400 res
.new_field
("fun").append
(mproperty
.name
).add static_mtype
.linkto
401 else if self isa MVirtualTypeDef then
402 res
.new_field
("add").append
(mproperty
.name
).add bound
.linkto
404 res
.new_field
("wat?").append
(mproperty
.name
)
409 res
.new_field
("intro").add mproperty
.intro
.linkto_text
("in {mproperty.intro.mclassdef}")
412 if mdoc
== null then mdoc
= mproperty
.intro
.mdoc
413 if mdoc
!= null then mdoc
.fill_infobox
(res
)
414 if mproperty
.mpropdefs
.length
> 1 then
415 var c
= res
.new_dropdown
("redef", "redefinitions")
416 for x
in mproperty
.mpropdefs
do
417 c
.open
("li").add x
.linkto_text
("in {x.mclassdef}")
426 return self.mclassdef
.mmodule
.href
+ "#" + self.to_s
429 redef fun linkto
do return linkto_text
(mproperty
.name
)
431 # Link to the entitiy with a specific text
432 fun linkto_text
(text
: String): HTMLTag
434 return (new HTMLTag("a")).attr
("href", href
).text
(text
)
438 redef class MClassType
441 var res
= new HInfoBox(v
, to_s
)
442 res
.href
= mclass
.intro
.href
443 res
.new_field
("class").add mclass
.intro
.linkto
444 var mdoc
= mclass
.mdoc
445 if mdoc
== null then mdoc
= mclass
.intro
.mdoc
446 if mdoc
!= null then mdoc
.fill_infobox
(res
)
451 return mclass
.intro
.linkto
454 redef class MVirtualType
457 var res
= new HInfoBox(v
, to_s
)
458 res
.href
= mproperty
.intro
.href
461 res
.new_field
("virtual type").add pd
.linkto
463 if mdoc
!= null then mdoc
.fill_infobox
(res
)
468 return mproperty
.intro
.linkto
471 redef class MParameterType
474 var res
= new HInfoBox(v
, to_s
)
475 res
.new_field
("parameter type").append
("{name} from class ").add mclass
.intro
.linkto
480 return (new HTMLTag("span")).text
(name
)
484 redef class MNullableType
487 return mtype
.infobox
(v
)
491 var res
= new HTMLTag("span")
492 res
.append
("nullable ").add
(mtype
.linkto
)
497 redef class MSignature
500 var res
= new HTMLTag("span")
502 if not mparameters
.is_empty
then
504 for p
in mparameters
do
512 res
.add p
.mtype
.linkto
516 var ret
= return_mtype
529 var res
= new HInfoBox(v
, "call {mpropdef}")
530 res
.href
= mpropdef
.href
531 res
.new_field
("call").add
(mpropdef
.linkto
).add
(msignature
.linkto
)
532 if mpropdef
.is_intro
then
534 res
.new_field
("intro").add mproperty
.intro
.linkto_text
("in {mproperty.intro.mclassdef}")
536 var mdoc
= mpropdef
.mdoc
537 if mdoc
== null then mdoc
= mproperty
.intro
.mdoc
538 if mdoc
!= null then mdoc
.fill_infobox
(res
)
544 return mpropdef
.linkto
552 var declared_type
= self.declared_type
553 if declared_type
== null then
554 var res
= new HInfoBox(v
, "{name}")
555 res
.new_field
("local var").append
("{name}")
558 var res
= new HInfoBox(v
, "{name}: {declared_type}")
559 res
.new_field
("local var").append
("{name}:").add
(declared_type
.linkto
)
564 return (new HTMLTag("span")).text
(name
)
572 # Optionally creates a tag that encapsulate the AST element on HTML rendering
573 protected fun make_tag
(v
: HighlightVisitor): nullable HTMLTag do return null
575 # Add aditionnal information on a child-token and return an additionnal HInfoBox on it
576 protected fun decorate_tag
(v
: HighlightVisitor, res
: HTMLTag, token
: Token): nullable HInfoBox
578 #debug("no decoration for {token.inspect}")
579 #res.add_class("nc_error")
583 # Return a optional infobox
584 fun infobox
(v
: HighlightVisitor): nullable HInfoBox do return null
587 redef class AStdClassdef
588 redef fun make_tag
(v
)
590 var res
= new HTMLTag("span")
591 res
.add_class
("nc_cdef")
593 if md
!= null then res
.attr
("id", md
.to_s
)
596 redef fun decorate_tag
(v
, res
, token
)
598 res
.add_class
("nc_def")
601 if md
== null then return null
606 redef fun make_tag
(v
)
608 var res
= new HTMLTag("span")
609 res
.add_class
("nc_pdef")
614 res
.attr
("id", mpd
.to_s
)
616 if self isa AAttrPropdef then
618 if mpd
!= null then res
.add
(tag
(mpd
))
620 if mpd
!= null then res
.add
(tag
(mpd
))
625 private fun tag
(mpd
: MPropDef): HTMLTag
627 var a
= new HTMLTag("a")
628 a
.attr
("id", mpd
.to_s
)
634 # Produce an HTMLTag with the correct contents and CSS classes
635 # Subclasses can redefine it to decorate the tag
636 redef fun make_tag
(v
: HighlightVisitor): HTMLTag
638 var res
= new HTMLTag("span")
644 redef class TokenKeyword
645 redef fun make_tag
(v
)
648 res
.add_class
("nc_k")
652 redef class TokenOperator
653 redef fun make_tag
(v
)
657 if p
!= null then p
.decorate_tag
(v
, res
, self)
658 res
.add_class
("nc_o")
663 redef class AVarFormExpr
664 redef fun decorate_tag
(v
, res
, token
)
666 var variable
= self.variable
667 if variable
== null then return null
668 res
.add_class
("nc_v")
669 return variable
.infobox
(v
)
673 redef class AVardeclExpr
674 redef fun decorate_tag
(v
, res
, token
)
676 var variable
= self.variable
677 if variable
== null then return null
678 res
.add_class
("nc_v")
679 return variable
.infobox
(v
)
684 redef fun decorate_tag
(v
, res
, token
)
686 if not token
isa TId then return null
688 if vs
== null then return null
689 res
.add_class
("nc_v")
690 var idx
= n_ids
.index_of
(token
)
691 var variable
= vs
[idx
]
692 return variable
.infobox
(v
)
697 redef fun decorate_tag
(v
, res
, token
)
700 if mp
== null then return null
701 var variable
= self.variable
702 if variable
== null then return null
703 res
.add_class
("nc_v")
704 return variable
.infobox
(v
)
708 redef class AAssertExpr
709 redef fun decorate_tag
(v
, res
, token
)
711 res
.add_class
("nc_ast")
717 redef fun decorate_tag
(v
, res
, token
)
719 res
.add_class
("nc_la")
724 redef class ASendExpr
725 redef fun decorate_tag
(v
, res
, token
)
727 if callsite
== null then return null
728 return callsite
.infobox
(v
)
733 redef fun decorate_tag
(v
, res
, token
)
735 if callsite
== null then return null
736 return callsite
.infobox
(v
)
740 redef class AAssignOp
741 redef fun decorate_tag
(v
, res
, token
)
744 assert p
isa AReassignFormExpr
746 var callsite
= p
.reassign_callsite
747 if callsite
== null then return null
748 return callsite
.infobox
(v
)
752 redef class AModuleName
753 redef fun decorate_tag
(v
, res
, token
)
755 return parent
.decorate_tag
(v
, res
, token
)
759 redef class AModuledecl
760 redef fun decorate_tag
(v
, res
, token
)
762 res
.add_class
("nc_def")
763 res
.add_class
("nc_m")
767 if mm
== null then return null
772 redef class AStdImport
773 redef fun decorate_tag
(v
, res
, token
)
775 res
.add_class
("nc_m")
777 if mm
== null then return null
781 redef class AAttrPropdef
782 redef fun decorate_tag
(v
, res
, token
)
784 res
.add_class
("nc_def")
785 var mpd
: nullable MPropDef
787 if mpd
== null then mpd
= mpropdef
788 if mpd
== null then return null
789 return mpd
.infobox
(v
)
794 redef fun make_tag
(v
)
798 if p
!= null then p
.decorate_tag
(v
, res
, self)
799 res
.add_class
("nc_i")
804 redef fun make_tag
(v
)
806 var res
= new HTMLTag("span")
807 res
.add_class
("nc_def")
810 redef fun decorate_tag
(v
, res
, token
)
813 # nothing to decorate
818 if not p
isa AMethPropdef then return null
820 if mpd
== null then return null
821 return mpd
.infobox
(v
)
825 redef fun make_tag
(v
)
829 if p
!= null then p
.decorate_tag
(v
, res
, self)
830 res
.add_class
("nc_a")
834 redef class AAttrFormExpr
835 redef fun decorate_tag
(v
, res
, token
)
838 if p
== null then return null
839 return p
.intro
.infobox
(v
)
843 redef fun make_tag
(v
)
847 if p
!= null then p
.decorate_tag
(v
, res
, self)
848 res
.add_class
("nc_t")
853 redef fun decorate_tag
(v
, res
, token
)
856 if mt
== null then return null
857 mt
= mt
.as_notnullable
858 if mt
isa MVirtualType or mt
isa MParameterType then
859 res
.add_class
("nc_vt")
864 redef class AFormaldef
865 redef fun decorate_tag
(v
, res
, token
)
867 res
.add_class
("nc_vt")
868 if mtype
== null then return null
869 return mtype
.infobox
(v
)
872 redef class ATypePropdef
873 redef fun decorate_tag
(v
, res
, token
)
875 res
.add_class
("nc_def")
877 if md
== null then return null
882 redef fun make_tag
(v
)
885 if not parent
isa ADoc then
886 res
.add_class
("nc_c")
892 redef fun make_tag
(v
)
894 var res
= new HTMLTag("span")
895 res
.add_class
("nc_d")
899 redef class TokenLiteral
900 redef fun make_tag
(v
)
903 res
.add_class
("nc_l")
905 if p
!= null then p
.decorate_tag
(v
, res
, self)
909 redef class ASuperstringExpr
910 redef fun make_tag
(v
)
912 var res
= new HTMLTag("span")
913 res
.add_class
("nc_ss")
917 redef class AStringFormExpr
918 redef fun decorate_tag
(v
, res
, token
)
920 # Workaround to tag strings
921 res
.classes
.remove
("nc_l")
922 res
.add_class
("nc_s")
927 redef fun decorate_tag
(v
, res
, token
)
930 if t
== null then return null