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
23 # Visitor used to produce a HTML tree based on a AST on a `Source`
24 class HighlightVisitor
27 # The root of the HTML hierarchy
28 var html
= new HTMLTag("span")
30 private var token_head
: HTMLTag
32 private var prod_head
: HTMLTag
34 private var prod_root
: nullable HTMLTag
36 # Is the HTML include a nested `<span class"{type_of_node}">` element for each `ANode` of the AST?
37 # Used to have a really huge and verbose HTML (mainly for debug)
38 var with_ast
writable = false
44 if prod_root
!= null then
53 # The position in the source file.
54 # Used to print parts of the source betwen tokens of the AST
57 var modelbuilder
: ModelBuilder
59 init(modelbuilder
: ModelBuilder)
61 self.modelbuilder
= modelbuilder
62 html
.add_class
("nitcode")
67 # Used to remember the first node, thus knowing when the whole visit is over
68 private var first_node
: nullable ANode
70 private var seen_token
= new HashSet[Token]
72 private fun process_upto_token
(node
: Token)
74 # recursively process previous tokens
75 var prev
= node
.prev_token
76 if prev
!= null and not seen_token
.has
(prev
) then
77 process_upto_token
(prev
)
78 prev
.accept_highlight_visitor
(self)
81 # Add text between `last_token` and `node`
82 var pstart
= node
.location
.pstart
84 var text
= node
.location
.file
.string
.substring
(pos
, pstart-pos
)
85 token_head
.append
(text
)
86 #node.debug("WRT: {token_head.classes} << '{text.escape_to_c}' ")
88 pos
= node
.location
.pend
+ 1
90 node
.debug
("pos={pos}, pstart={pstart}, pend={node.location.pend}")
97 private fun where
(node
: ANode, tag
: String)
101 node
.debug
"{tag}-> {token_head.classes} : {prod_head.classes}"
103 node
.debug
"{tag}-> {token_head.classes} : {pr.classes}..{prod_head.classes}"
107 redef fun visit
(node
)
109 if first_node
== null then first_node
= node
111 if node
isa Token then
112 process_upto_token
(node
)
117 #node.debug("ADD: {token_head.classes} << {pr.classes} ")
119 token_head
= prod_head
124 var oldph
= prod_head
126 node
.accept_highlight_visitor
(self)
130 assert token_head
== prod_head
132 assert token_head
!= prod_head
140 if node
== first_node
then
141 html
.append
(node
.location
.file
.string
.substring_from
(pos
))
145 # Return a default CSS content related to CSS classes used in the `html` tree.
146 # Could be inlined in the `.html` file of saved as a specific `.css` file.
147 fun css_content
: String
150 .nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
151 .nitcode a:hover { text-decoration: underline; } /* underline links */
152 .nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
153 /* lexical raw tokens. independent of usage or semantic: */
154 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
155 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
156 .nitcode .nc_k { font-weight: bold; } /* keyword */
157 .nitcode .nc_o {} /* operator */
158 .nitcode .nc_i {} /* standard identifier */
159 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
160 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
161 .nitcode .nc_l { color: #009999; } /* char and number literal */
162 .nitcode .nc_s { color: #8F1546; } /* string literal */
163 /* syntactic token usage. added because of their position in the AST */
164 .nitcode .nc_ast { color: blue; } /* assert label */
165 .nitcode .nc_la { color: blue; } /* break/continue label */
166 .nitcode .nc_m { color: #445588; } /* module name */
167 /* syntactic groups */
168 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
169 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
170 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
171 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
172 .nitcode .nc_cdef {} /* A whole class definition */
173 .nitcode .nc_pdef {} /* A whole property definition */
174 /* semantic token usage */
175 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
176 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
178 .nitcode .nc_error { border: 1px red solid;} /* not used */
184 private fun accept_highlight_visitor
(v
: HighlightVisitor)
187 var res
= new HTMLTag("span")
188 res
.add_class
(class_name
)
193 private fun decorate_tag
(res
: HTMLTag, token
: Token)
195 #debug("no decoration for {token.inspect}")
196 #res.add_class("nc_error")
200 redef class AStdClassdef
201 redef fun accept_highlight_visitor
(v
)
203 var res
= new HTMLTag("span")
204 res
.add_class
("nc_cdef")
207 var a
= new HTMLTag("a")
208 a
.attr
("id", md
.to_s
)
214 redef fun decorate_tag
(res
, token
)
216 res
.add_class
("nc_def")
219 if md
== null then return
221 res
.attrs
["title"] = mc
.full_name
224 res
.attrs
["link"] = mi
.mmodule
.name
+ ".html#" + mi
.to_s
229 redef fun accept_highlight_visitor
(v
)
231 var res
= new HTMLTag("span")
232 res
.add_class
("nc_pdef")
235 if mpd
!= null then res
.add
(tag
(mpd
))
236 if self isa AAttrPropdef then
238 if mpd
!= null then res
.add
(tag
(mpd
))
240 if mpd
!= null then res
.add
(tag
(mpd
))
246 private fun tag
(mpd
: MPropDef): HTMLTag
248 var a
= new HTMLTag("a")
249 a
.attr
("id", mpd
.to_s
)
255 # Produce an HTMLTag with the correct contents and CSS classes
256 # Subclasses can redefine it to decorate the tag
257 protected fun make_tag
(v
: HighlightVisitor): HTMLTag
259 var res
= new HTMLTag("span")
264 # Use `empty_tag` to create the tag ; then fill it and add it to the html
265 redef fun accept_highlight_visitor
(v
)
268 if n
.attrs
.is_empty
and n
.classes
.is_empty
then
269 for c
in n
.children
do
272 else if n
.attrs
.has_key
("link") then
273 var a
= new HTMLTag("a")
274 a
.attrs
["href"] = n
.attrs
["link"]
275 n
.attrs
.keys
.remove
("link")
281 #debug("WRT: {v.token_head.classes} << '{text.escape_to_c}' ")
284 redef class TokenKeyword
285 redef fun make_tag
(v
)
288 res
.add_class
("nc_k")
292 redef class TokenOperator
293 redef fun make_tag
(v
)
297 if p
!= null then p
.decorate_tag
(res
, self)
298 res
.add_class
("nc_o")
304 private fun decorate_tag
(res
: HTMLTag, token
: Token)
306 if declared_type
== null then return
307 res
.attrs
["title"] = name
+ ": " + declared_type
.to_s
311 redef class AVarFormExpr
312 redef fun decorate_tag
(res
, token
)
314 res
.add_class
("nc_v")
315 var variable
= self.variable
316 if variable
== null then return
317 variable
.decorate_tag
(res
, token
)
321 redef class AVardeclExpr
322 redef fun decorate_tag
(res
, token
)
324 res
.add_class
("nc_v")
325 var variable
= self.variable
326 if variable
== null then return
327 variable
.decorate_tag
(res
, token
)
332 redef fun decorate_tag
(res
, token
)
334 res
.add_class
("nc_v")
336 if vs
== null then return
337 var idx
= n_ids
.index_of
(token
.as(TId))
338 var variable
= vs
[idx
]
339 variable
.decorate_tag
(res
, token
)
344 redef fun decorate_tag
(res
, token
)
346 res
.add_class
("nc_v")
348 if mp
== null then return
349 res
.attrs
["title"] = mp
.name
+ ": " + mp
.mtype
.to_s
353 redef class AAssertExpr
354 redef fun decorate_tag
(res
, token
)
356 res
.add_class
("nc_ast")
361 redef fun decorate_tag
(res
, token
)
363 res
.add_class
("nc_la")
367 redef class ASendExpr
368 redef fun decorate_tag
(res
, token
)
370 if callsite
== null then return
371 var mpropdef
= callsite
.mpropdef
372 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
373 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
378 redef fun decorate_tag
(res
, token
)
380 if callsite
== null then return
381 var mpropdef
= callsite
.mpropdef
382 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
383 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
387 redef class AAssignOp
388 redef fun decorate_tag
(res
, v
)
391 assert p
isa AReassignFormExpr
393 var callsite
= p
.reassign_callsite
394 if callsite
== null then return
395 var mpropdef
= callsite
.mpropdef
396 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
397 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
401 redef class AModuleName
402 redef fun decorate_tag
(res
, token
)
404 parent
.decorate_tag
(res
, token
)
408 redef class AModuledecl
409 redef fun decorate_tag
(res
, token
)
411 res
.add_class
("nc_def")
412 res
.add_class
("nc_m")
416 if mm
== null then return
417 res
.attrs
["title"] = mm
.full_name
421 redef class AStdImport
422 redef fun decorate_tag
(res
, token
)
424 res
.add_class
("nc_m")
426 if mm
== null then return
427 res
.attrs
["title"] = mm
.full_name
428 res
.attrs
["link"] = mm
.name
+ ".html"
432 redef class AAttrPropdef
433 redef fun decorate_tag
(res
, token
)
435 res
.add_class
("nc_def")
436 var mpd
: nullable MPropDef
438 if mpd
== null then mpd
= mpropdef
439 if mpd
== null then return
440 var mp
= mpd
.mproperty
441 res
.attrs
["title"] = mp
.full_name
442 if mp
.intro
!= mpd
then
444 res
.attrs
["link"] = mpd
.mclassdef
.mmodule
.name
+ ".html#" + mpd
.to_s
450 redef fun make_tag
(v
)
454 if p
!= null then p
.decorate_tag
(res
, self)
455 res
.add_class
("nc_i")
460 redef fun accept_highlight_visitor
(v
)
462 var res
= new HTMLTag("span")
463 res
.add_class
("nc_def")
465 if p
isa AMethPropdef then
468 var mp
= mpd
.mproperty
469 res
.attr
("title", mp
.full_name
)
470 if mp
.intro
!= mpd
then
472 var link
= mpd
.mclassdef
.mmodule
.name
+ ".html#" + mpd
.to_s
473 var l
= new HTMLTag("a")
482 redef fun decorate_tag
(res
, v
)
484 # nothing to decorate
488 redef fun make_tag
(v
)
492 if p
!= null then p
.decorate_tag
(res
, self)
493 res
.add_class
("nc_a")
497 redef class AAttrFormExpr
498 redef fun decorate_tag
(res
, v
)
501 if p
== null then return
502 res
.attrs
["title"] = p
.full_name
504 res
.attrs
["link"] = pi
.mclassdef
.mmodule
.name
+ ".html#" + pi
.to_s
508 redef fun make_tag
(v
)
512 if p
!= null then p
.decorate_tag
(res
, self)
513 res
.add_class
("nc_t")
518 redef fun decorate_tag
(res
, token
)
521 if mt
== null then return
523 if mt
isa MNullableType then mt
= mt
.mtype
524 if mt
isa MVirtualType or mt
isa MParameterType then
525 res
.add_class
("nc_vt")
526 else if mt
isa MClassType then
527 title
= mt
.mclass
.full_name
528 res
.attrs
["link"] = mt
.mclass
.intro
.mmodule
.name
+ ".html#" + mt
.mclass
.intro
.to_s
530 res
.attrs
["title"] = title
533 redef class AFormaldef
534 redef fun decorate_tag
(res
, token
)
536 res
.add_class
("nc_vt")
537 if mtype
== null then return
538 res
.attrs
["title"] = "{mtype.to_s}: {bound.to_s}"
541 redef class ATypePropdef
542 redef fun decorate_tag
(res
, token
)
544 res
.add_class
("nc_def")
546 if md
== null then return
547 var mp
= mpropdef
.mproperty
548 res
.attrs
["title"] = mp
.full_name
551 res
.attrs
["link"] = mi
.mclassdef
.mmodule
.name
+ ".html#" + mi
.to_s
556 redef fun make_tag
(v
)
559 if parent
== null then
560 res
.add_class
("nc_c")
562 assert parent
isa ADoc
568 redef fun accept_highlight_visitor
(v
)
570 var res
= new HTMLTag("span")
571 res
.add_class
("nc_d")
576 redef class TokenLiteral
577 redef fun make_tag
(v
)
580 res
.add_class
("nc_l")
582 if p
isa AStringFormExpr then p
.decorate_tag
(res
, self)
586 redef class ASuperstringExpr
587 redef fun accept_highlight_visitor
(v
)
589 var res
= new HTMLTag("span")
590 res
.add_class
("nc_ss")
595 redef class AStringFormExpr
596 redef fun decorate_tag
(res
, v
)
598 # Workarount to tag strings
599 res
.classes
.remove
("nc_l")
600 res
.add_class
("nc_s")