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
18 import modelize_property
24 # Visitor used to produce a HTML tree based on a AST on a `Source`
25 class HighlightVisitor
26 # The root of the HTML hierarchy
27 var html
= new HTMLTag("span")
29 # Is the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
30 # Used to have a really huge and verbose HTML (mainly for debug)
31 var with_ast
writable = false
33 # The first line to generate, null if start at the first line
34 var first_line
: nullable Int writable = null
36 # The last line to generate, null if finish at the last line
37 var last_line
: nullable Int writable = null
41 html
.add_class
("nitcode")
44 fun enter_visit
(n
: ANode)
47 var s
= n
.location
.file
48 htmlize
(s
.first_token
.as(not null), s
.last_token
.as(not null))
51 # Produce HTML between two tokens
52 protected fun htmlize
(first_token
, last_token
: Token)
54 var stack2
= new Array[HTMLTag]
55 var stack
= new Array[Prod]
56 var closes
= new Array[Prod]
58 var c
: nullable Token = first_token
59 var hv
= new HighlightVisitor
63 # Handle start of line
64 var cline
= c
.location
.line_start
66 # Handle starting block productions,
67 # Because c could be a detached token, get prods in
69 var c0
= c
.first_token_in_line
71 if c0
!= null then starting
= c0
.starting_prods
72 if starting
!= null then for p
in starting
do
73 if not p
.is_block
then continue
74 var tag
= p
.make_tag
(hv
)
75 if tag
== null then continue
76 tag
.add_class
("foldable")
77 var infobox
= p
.infobox
(hv
)
78 if infobox
!= null then tag
.attach_infobox
(infobox
)
85 # Add a div for the whole line
86 var tag
= new HTMLTag("span")
87 tag
.attrs
["id"] = "L{cline}"
88 tag
.classes
.add
"line"
95 # Add the blank, verbatim
96 html
.add_raw_html c
.blank_before
98 # Handle starting span production
99 starting
= c
.starting_prods
100 if starting
!= null then for p
in starting
do
101 if not p
.is_span
then continue
102 var tag
= p
.make_tag
(hv
)
103 if tag
== null then continue
104 var infobox
= p
.infobox
(hv
)
105 if infobox
!= null then tag
.attach_infobox
(infobox
)
116 var tag
= c
.make_tag
(hv
)
119 if c
isa TId or c
isa TClassid or c
isa TAttrid or c
isa TokenLiteral or c
isa TokenOperator then
121 if pa
!= null then infobox
= pa
.decorate_tag
(hv
, tag
, c
)
122 else if c
isa TComment and pa
isa ADoc then
123 infobox
= pa
.decorate_tag
(hv
, tag
, c
)
125 if infobox
!= null then tag
.attach_infobox
(infobox
)
129 # Handle ending span productions
130 var ending
= c
.ending_prods
131 if ending
!= null then for p
in ending
do
132 if not p
.is_span
then continue
133 if stack
.is_empty
or p
!= stack
.last
then continue
138 # Handle end of line and end of file
140 if c
== last_token
then n
= null
141 if n
== null or n
.location
.line_start
!= line
then
142 # closes the line div
145 # close the block production divs
146 var c0
= c
.last_token_in_line
148 if c0
!= null then ending
= c0
.ending_prods
149 if ending
!= null then for p
in ending
do
150 if not p
.is_block
then continue
151 if stack
.is_empty
or p
!= stack
.last
then continue
159 assert stack
.is_empty
160 assert stack2
.is_empty
163 # Return a default CSS content related to CSS classes used in the `html` tree.
164 # Could be inlined in the `.html` file of saved as a specific `.css` file.
165 fun css_content
: String
168 .nitcode a { color: inherit; cursor:pointer; }
169 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
170 pre.nitcode .foldable { display: block } /* for block productions*/
171 pre.nitcode .line{ display: block } /* for lines */
172 pre.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
173 .nitcode :target { background-color: #FFF3C2 } /* target highlight*/
174 /* lexical raw tokens. independent of usage or semantic: */
175 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
176 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
177 .nitcode .nc_k { font-weight: bold; } /* keyword */
178 .nitcode .nc_o {} /* operator */
179 .nitcode .nc_i {} /* standard identifier */
180 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
181 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
182 .nitcode .nc_l { color: #009999; } /* char and number literal */
183 .nitcode .nc_s { color: #8F1546; } /* string literal */
184 /* syntactic token usage. added because of their position in the AST */
185 .nitcode .nc_ast { color: blue; } /* assert label */
186 .nitcode .nc_la { color: blue; } /* break/continue label */
187 .nitcode .nc_m { color: #445588; } /* module name */
188 /* syntactic groups */
189 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
190 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
191 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
192 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
193 .nitcode .nc_cdef {} /* A whole class definition */
194 .nitcode .nc_pdef {} /* A whole property definition */
195 /* semantic token usage */
196 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
197 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
199 .nitcode .nc_error { border: 1px red solid;} /* not used */
200 .popover { max-width: 800px !important; }
204 # Additional content to inject in the <head> tag
205 # Note: does not include `css_content`; handle it yourself.
206 fun head_content
: String
208 return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
211 # Additional content to inject just before the closing </body> tag
212 fun foot_content
: String
215 <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
216 <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
217 <script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
222 # Attach the infobox to the node by using BootStrap popover
223 fun attach_infobox
(infobox
: HInfoBox)
225 classes
.add
("popupable")
226 attrs
["title"] = infobox
.title
227 var href
= infobox
.href
229 attrs
["data-title"] = """<a href="{{{href}}}">{{{infobox.title}}}</a>"""
231 attrs
["data-content"] = infobox
.content
.write_to_string
232 attrs
["data-toggle"] = "popover"
237 # A generic information container that can be used to decorate AST entities
239 # The visitor used for contextualisation, if needed
240 var visitor
: HighlightVisitor
242 # A short title for the AST element
245 # The primary link where the entity points
247 var href
: nullable String = null
249 # The content of the popuped infobox
250 var content
= new HTMLTag("div")
252 # Append a new field in the popuped infobox
253 fun new_field
(title
: String): HTMLTag
255 content
.open
("b").text
(title
)
257 var res
= content
.open
("span")
262 # Append a new dropdown in the popuped content
263 fun new_dropdown
(title
, text
: String): HTMLTag
265 content
.add_raw_html
"""<div class="dropdown"> <a data-toggle="dropdown" href="#"><b>"""
266 content
.append
(title
)
267 content
.add_raw_html
"</b> "
269 content
.add_raw_html
"""<span class="caret"></span></a>"""
270 var res
= content
.open
("ul").add_class
("dropdown-menu").attr
("role", "menu").attr
("aria-labelledby", "dLabel")
271 content
.add_raw_html
"</div>"
278 # Model entity or whatever that can produce an infobox
279 interface HInfoBoxable
280 # An new infobox documenting the entity
281 fun infobox
(v
: HighlightVisitor): HInfoBox is abstract
283 # A human-readable hyper-text for the entity
284 fun linkto
: HTMLTag is abstract
288 # Append an entry for the doc in the given infobox
289 fun fill_infobox
(res
: HInfoBox)
291 if content
.length
< 2 then
292 res
.new_field
("doc").text
(content
.first
)
295 var c
= res
.new_dropdown
("doc", content
.first
)
296 for x
in content
.iterator
.skip_head
(1) do
298 c
.add_raw_html
"<br>"
310 var res
= new HInfoBox(v
, "module {name}")
312 res
.new_field
("module").add
(linkto
)
314 if mdoc
!= null then mdoc
.fill_infobox
(res
)
315 if in_importation
.greaters
.length
> 1 then
316 var c
= res
.new_dropdown
("imports", "{in_importation.greaters.length-1} modules")
317 for x
in in_importation
.greaters
do
318 if x
== self then continue
319 c
.open
("li").add x
.linkto
327 return name
+ ".html"
330 redef fun linkto
do return linkto_text
(name
)
332 # Link to the entitiy with a specific text
333 fun linkto_text
(text
: String): HTMLTag
335 return (new HTMLTag("a")).attr
("href", href
).text
(text
)
339 redef class MClassDef
342 var res
= new HInfoBox(v
, "class {mclass.name}")
345 res
.new_field
("class").text
(mclass
.name
)
347 res
.new_field
("redef class").text
(mclass
.name
)
348 res
.new_field
("intro").add mclass
.intro
.linkto_text
("in {mclass.intro.mmodule.to_s}")
351 if mdoc
== null then mdoc
= mclass
.intro
.mdoc
352 if mdoc
!= null then mdoc
.fill_infobox
(res
)
354 if in_hierarchy
.greaters
.length
> 1 then
355 var c
= res
.new_dropdown
("hier", "super-classes")
356 for x
in in_hierarchy
.greaters
do
357 if x
== self then continue
358 if not x
.is_intro
then continue
359 c
.open
("li").add x
.linkto
362 if in_hierarchy
.smallers
.length
> 1 then
363 var c
= res
.new_dropdown
("hier", "sub-classes")
364 for x
in in_hierarchy
.smallers
do
365 if x
== self then continue
366 if not x
.is_intro
then continue
367 c
.open
("li").add x
.linkto
370 if mclass
.mclassdefs
.length
> 1 then
371 var c
= res
.new_dropdown
("redefs", "refinements")
372 for x
in mclass
.mclassdefs
do
373 if x
== self then continue
374 c
.open
("li").add x
.linkto_text
("in {x.mmodule}")
382 return mmodule
.href
+ "#" + to_s
385 redef fun linkto
do return linkto_text
(mclass
.name
)
387 # Link to the entitiy with a specific text
388 fun linkto_text
(text
: String): HTMLTag
390 return (new HTMLTag("a")).attr
("href", href
).text
(text
)
397 var res
= new HInfoBox(v
, to_s
)
399 if self isa MMethodDef then
400 res
.new_field
("fun").append
(mproperty
.name
).add msignature
.linkto
401 else if self isa MAttributeDef then
402 res
.new_field
("fun").append
(mproperty
.name
).add static_mtype
.linkto
403 else if self isa MVirtualTypeDef then
404 res
.new_field
("add").append
(mproperty
.name
).add bound
.linkto
406 res
.new_field
("wat?").append
(mproperty
.name
)
411 res
.new_field
("intro").add mproperty
.intro
.linkto_text
("in {mproperty.intro.mclassdef}")
414 if mdoc
== null then mdoc
= mproperty
.intro
.mdoc
415 if mdoc
!= null then mdoc
.fill_infobox
(res
)
416 if mproperty
.mpropdefs
.length
> 1 then
417 var c
= res
.new_dropdown
("redef", "redefinitions")
418 for x
in mproperty
.mpropdefs
do
419 c
.open
("li").add x
.linkto_text
("in {x.mclassdef}")
428 return self.mclassdef
.mmodule
.href
+ "#" + self.to_s
431 redef fun linkto
do return linkto_text
(mproperty
.name
)
433 # Link to the entitiy with a specific text
434 fun linkto_text
(text
: String): HTMLTag
436 return (new HTMLTag("a")).attr
("href", href
).text
(text
)
440 redef class MClassType
443 var res
= new HInfoBox(v
, to_s
)
444 res
.href
= mclass
.intro
.href
445 res
.new_field
("class").add mclass
.intro
.linkto
446 var mdoc
= mclass
.mdoc
447 if mdoc
== null then mdoc
= mclass
.intro
.mdoc
448 if mdoc
!= null then mdoc
.fill_infobox
(res
)
453 return mclass
.intro
.linkto
456 redef class MVirtualType
459 var res
= new HInfoBox(v
, to_s
)
460 res
.href
= mproperty
.intro
.href
463 res
.new_field
("virtual type").add pd
.linkto
465 if mdoc
!= null then mdoc
.fill_infobox
(res
)
470 return mproperty
.intro
.linkto
473 redef class MParameterType
476 var res
= new HInfoBox(v
, to_s
)
477 var name
= mclass
.intro
.parameter_names
[rank
]
478 res
.new_field
("parameter type").append
("{name} from class ").add mclass
.intro
.linkto
483 var name
= mclass
.intro
.parameter_names
[rank
]
484 return (new HTMLTag("span")).text
(name
)
488 redef class MNullableType
491 return mtype
.infobox
(v
)
495 var res
= new HTMLTag("span")
496 res
.append
("nullable ").add
(mtype
.linkto
)
501 redef class MSignature
504 var res
= new HTMLTag("span")
506 if not mparameters
.is_empty
then
508 for p
in mparameters
do
516 res
.add p
.mtype
.linkto
520 var ret
= return_mtype
533 var res
= new HInfoBox(v
, "call {mpropdef}")
534 res
.href
= mpropdef
.href
535 res
.new_field
("call").add
(mpropdef
.linkto
).add
(msignature
.linkto
)
536 if mpropdef
.is_intro
then
538 res
.new_field
("intro").add mproperty
.intro
.linkto_text
("in {mproperty.intro.mclassdef}")
540 var mdoc
= mpropdef
.mdoc
541 if mdoc
== null then mdoc
= mproperty
.intro
.mdoc
542 if mdoc
!= null then mdoc
.fill_infobox
(res
)
548 return mpropdef
.linkto
556 var declared_type
= self.declared_type
557 if declared_type
== null then
558 var res
= new HInfoBox(v
, "{name}")
559 res
.new_field
("local var").append
("{name}")
562 var res
= new HInfoBox(v
, "{name}: {declared_type}")
563 res
.new_field
("local var").append
("{name}:").add
(declared_type
.linkto
)
568 return (new HTMLTag("span")).text
(name
)
576 # Optionally creates a tag that encapsulate the AST element on HTML rendering
577 protected fun make_tag
(v
: HighlightVisitor): nullable HTMLTag do return null
579 # Add aditionnal information on a child-token and return an additionnal HInfoBox on it
580 protected fun decorate_tag
(v
: HighlightVisitor, res
: HTMLTag, token
: Token): nullable HInfoBox
582 #debug("no decoration for {token.inspect}")
583 #res.add_class("nc_error")
587 # Return a optional infobox
588 fun infobox
(v
: HighlightVisitor): nullable HInfoBox do return null
591 redef class AStdClassdef
592 redef fun make_tag
(v
)
594 var res
= new HTMLTag("span")
595 res
.add_class
("nc_cdef")
597 if md
!= null then res
.attr
("id", md
.to_s
)
600 redef fun decorate_tag
(v
, res
, token
)
602 res
.add_class
("nc_def")
605 if md
== null then return null
610 redef fun make_tag
(v
)
612 var res
= new HTMLTag("span")
613 res
.add_class
("nc_pdef")
618 res
.attr
("id", mpd
.to_s
)
620 if self isa AAttrPropdef then
622 if mpd
!= null then res
.add
(tag
(mpd
))
624 if mpd
!= null then res
.add
(tag
(mpd
))
629 private fun tag
(mpd
: MPropDef): HTMLTag
631 var a
= new HTMLTag("a")
632 a
.attr
("id", mpd
.to_s
)
638 # Produce an HTMLTag with the correct contents and CSS classes
639 # Subclasses can redefine it to decorate the tag
640 redef fun make_tag
(v
: HighlightVisitor): HTMLTag
642 var res
= new HTMLTag("span")
648 redef class TokenKeyword
649 redef fun make_tag
(v
)
652 res
.add_class
("nc_k")
656 redef class TokenOperator
657 redef fun make_tag
(v
)
661 if p
!= null then p
.decorate_tag
(v
, res
, self)
662 res
.add_class
("nc_o")
667 redef class AVarFormExpr
668 redef fun decorate_tag
(v
, res
, token
)
670 var variable
= self.variable
671 if variable
== null then return null
672 res
.add_class
("nc_v")
673 return variable
.infobox
(v
)
677 redef class AVardeclExpr
678 redef fun decorate_tag
(v
, res
, token
)
680 var variable
= self.variable
681 if variable
== null then return null
682 res
.add_class
("nc_v")
683 return variable
.infobox
(v
)
688 redef fun decorate_tag
(v
, res
, token
)
690 if not token
isa TId then return null
692 if vs
== null then return null
693 res
.add_class
("nc_v")
694 var idx
= n_ids
.index_of
(token
)
695 var variable
= vs
[idx
]
696 return variable
.infobox
(v
)
701 redef fun decorate_tag
(v
, res
, token
)
704 if mp
== null then return null
705 var variable
= self.variable
706 if variable
== null then return null
707 res
.add_class
("nc_v")
708 return variable
.infobox
(v
)
712 redef class AAssertExpr
713 redef fun decorate_tag
(v
, res
, token
)
715 res
.add_class
("nc_ast")
721 redef fun decorate_tag
(v
, res
, token
)
723 res
.add_class
("nc_la")
728 redef class ASendExpr
729 redef fun decorate_tag
(v
, res
, token
)
731 if callsite
== null then return null
732 return callsite
.infobox
(v
)
737 redef fun decorate_tag
(v
, res
, token
)
739 if callsite
== null then return null
740 return callsite
.infobox
(v
)
744 redef class AAssignOp
745 redef fun decorate_tag
(v
, res
, token
)
748 assert p
isa AReassignFormExpr
750 var callsite
= p
.reassign_callsite
751 if callsite
== null then return null
752 return callsite
.infobox
(v
)
756 redef class AModuleName
757 redef fun decorate_tag
(v
, res
, token
)
759 return parent
.decorate_tag
(v
, res
, token
)
763 redef class AModuledecl
764 redef fun decorate_tag
(v
, res
, token
)
766 res
.add_class
("nc_def")
767 res
.add_class
("nc_m")
771 if mm
== null then return null
776 redef class AStdImport
777 redef fun decorate_tag
(v
, res
, token
)
779 res
.add_class
("nc_m")
781 if mm
== null then return null
785 redef class AAttrPropdef
786 redef fun decorate_tag
(v
, res
, token
)
788 res
.add_class
("nc_def")
789 var mpd
: nullable MPropDef
791 if mpd
== null then mpd
= mpropdef
792 if mpd
== null then return null
793 return mpd
.infobox
(v
)
798 redef fun make_tag
(v
)
802 if p
!= null then p
.decorate_tag
(v
, res
, self)
803 res
.add_class
("nc_i")
808 redef fun make_tag
(v
)
810 var res
= new HTMLTag("span")
811 res
.add_class
("nc_def")
814 redef fun decorate_tag
(v
, res
, token
)
817 # nothing to decorate
822 if not p
isa AMethPropdef then return null
824 if mpd
== null then return null
825 return mpd
.infobox
(v
)
829 redef fun make_tag
(v
)
833 if p
!= null then p
.decorate_tag
(v
, res
, self)
834 res
.add_class
("nc_a")
838 redef class AAttrFormExpr
839 redef fun decorate_tag
(v
, res
, token
)
842 if p
== null then return null
843 return p
.intro
.infobox
(v
)
847 redef fun make_tag
(v
)
851 if p
!= null then p
.decorate_tag
(v
, res
, self)
852 res
.add_class
("nc_t")
857 redef fun decorate_tag
(v
, res
, token
)
860 if mt
== null then return null
861 if mt
isa MNullableType then mt
= mt
.mtype
862 if mt
isa MVirtualType or mt
isa MParameterType then
863 res
.add_class
("nc_vt")
868 redef class AFormaldef
869 redef fun decorate_tag
(v
, res
, token
)
871 res
.add_class
("nc_vt")
872 if mtype
== null then return null
873 return mtype
.infobox
(v
)
876 redef class ATypePropdef
877 redef fun decorate_tag
(v
, res
, token
)
879 res
.add_class
("nc_def")
881 if md
== null then return null
886 redef fun make_tag
(v
)
889 if not parent
isa ADoc then
890 res
.add_class
("nc_c")
896 redef fun make_tag
(v
)
898 var res
= new HTMLTag("span")
899 res
.add_class
("nc_d")
903 redef class TokenLiteral
904 redef fun make_tag
(v
)
907 res
.add_class
("nc_l")
909 if p
!= null then p
.decorate_tag
(v
, res
, self)
913 redef class ASuperstringExpr
914 redef fun make_tag
(v
)
916 var res
= new HTMLTag("span")
917 res
.add_class
("nc_ss")
921 redef class AStringFormExpr
922 redef fun decorate_tag
(v
, res
, token
)
924 # Workaround to tag strings
925 res
.classes
.remove
("nc_l")
926 res
.add_class
("nc_s")
931 redef fun decorate_tag
(v
, res
, token
)
934 if t
== null then return null