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
59 html
.add_class
("nitcode")
64 # Used to remember the first node, thus knowing when the whole visit is over
65 private var first_node
: nullable ANode
67 private var seen_token
= new HashSet[Token]
69 private fun process_upto_token
(node
: Token)
71 # recursively process previous tokens
72 var prev
= node
.prev_token
73 if prev
!= null and not seen_token
.has
(prev
) then
74 process_upto_token
(prev
)
75 prev
.accept_highlight_visitor
(self)
78 # Add text between `last_token` and `node`
79 var pstart
= node
.location
.pstart
81 var text
= node
.location
.file
.string
.substring
(pos
, pstart-pos
)
82 token_head
.append
(text
)
83 #node.debug("WRT: {token_head.classes} << '{text.escape_to_c}' ")
85 pos
= node
.location
.pend
+ 1
87 node
.debug
("pos={pos}, pstart={pstart}, pend={node.location.pend}")
94 private fun where
(node
: ANode, tag
: String)
98 node
.debug
"{tag}-> {token_head.classes} : {prod_head.classes}"
100 node
.debug
"{tag}-> {token_head.classes} : {pr.classes}..{prod_head.classes}"
104 redef fun visit
(node
)
106 if first_node
== null then first_node
= node
108 if node
isa Token then
109 process_upto_token
(node
)
114 #node.debug("ADD: {token_head.classes} << {pr.classes} ")
116 token_head
= prod_head
121 var oldph
= prod_head
123 node
.accept_highlight_visitor
(self)
127 assert token_head
== prod_head
129 assert token_head
!= prod_head
137 if node
== first_node
then
138 html
.append
(node
.location
.file
.string
.substring_from
(pos
))
142 # Return a default CSS content related to CSS classes used in the `html` tree.
143 # Could be inlined in the `.html` file of saved as a specific `.css` file.
144 fun css_content
: String
147 .nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
148 .nitcode a:hover { text-decoration: underline; } /* underline links */
149 .nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
150 /* lexical raw tokens. independent of usage or semantic: */
151 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
152 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
153 .nitcode .nc_k { font-weight: bold; } /* keyword */
154 .nitcode .nc_o {} /* operator */
155 .nitcode .nc_i {} /* standard identifier */
156 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
157 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
158 .nitcode .nc_l { color: #009999; } /* char and number literal */
159 .nitcode .nc_s { color: #8F1546; } /* string literal */
160 /* syntactic token usage. added because of their position in the AST */
161 .nitcode .nc_ast { color: blue; } /* assert label */
162 .nitcode .nc_la { color: blue; } /* break/continue label */
163 .nitcode .nc_m { color: #445588; } /* module name */
164 /* syntactic groups */
165 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
166 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
167 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
168 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
169 .nitcode .nc_cdef {} /* A whole class definition */
170 .nitcode .nc_pdef {} /* A whole property definition */
171 /* semantic token usage */
172 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
173 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
175 .nitcode .nc_error { border: 1px red solid;} /* not used */
181 private fun accept_highlight_visitor
(v
: HighlightVisitor)
184 var res
= new HTMLTag("span")
185 res
.add_class
(class_name
)
190 private fun decorate_tag
(res
: HTMLTag, token
: Token)
192 #debug("no decoration for {token.inspect}")
193 #res.add_class("nc_error")
197 redef class AStdClassdef
198 redef fun accept_highlight_visitor
(v
)
200 var res
= new HTMLTag("span")
201 res
.add_class
("nc_cdef")
204 var a
= new HTMLTag("a")
205 a
.attr
("id", md
.to_s
)
211 redef fun decorate_tag
(res
, token
)
213 res
.add_class
("nc_def")
216 if md
== null then return
218 res
.attrs
["title"] = mc
.full_name
221 res
.attrs
["link"] = mi
.mmodule
.name
+ ".html#" + mi
.to_s
226 redef fun accept_highlight_visitor
(v
)
228 var res
= new HTMLTag("span")
229 res
.add_class
("nc_pdef")
232 if mpd
!= null then res
.add
(tag
(mpd
))
233 if self isa AAttrPropdef then
235 if mpd
!= null then res
.add
(tag
(mpd
))
237 if mpd
!= null then res
.add
(tag
(mpd
))
243 private fun tag
(mpd
: MPropDef): HTMLTag
245 var a
= new HTMLTag("a")
246 a
.attr
("id", mpd
.to_s
)
252 # Produce an HTMLTag with the correct contents and CSS classes
253 # Subclasses can redefine it to decorate the tag
254 protected fun make_tag
(v
: HighlightVisitor): HTMLTag
256 var res
= new HTMLTag("span")
261 # Use `empty_tag` to create the tag ; then fill it and add it to the html
262 redef fun accept_highlight_visitor
(v
)
265 if n
.attrs
.is_empty
and n
.classes
.is_empty
then
266 for c
in n
.children
do
269 else if n
.attrs
.has_key
("link") then
270 var a
= new HTMLTag("a")
271 a
.attrs
["href"] = n
.attrs
["link"]
272 n
.attrs
.keys
.remove
("link")
278 #debug("WRT: {v.token_head.classes} << '{text.escape_to_c}' ")
281 redef class TokenKeyword
282 redef fun make_tag
(v
)
285 res
.add_class
("nc_k")
289 redef class TokenOperator
290 redef fun make_tag
(v
)
294 if p
!= null then p
.decorate_tag
(res
, self)
295 res
.add_class
("nc_o")
301 private fun decorate_tag
(res
: HTMLTag, token
: Token)
303 if declared_type
== null then return
304 res
.attrs
["title"] = name
+ ": " + declared_type
.to_s
308 redef class AVarFormExpr
309 redef fun decorate_tag
(res
, token
)
311 res
.add_class
("nc_v")
312 var variable
= self.variable
313 if variable
== null then return
314 variable
.decorate_tag
(res
, token
)
318 redef class AVardeclExpr
319 redef fun decorate_tag
(res
, token
)
321 res
.add_class
("nc_v")
322 var variable
= self.variable
323 if variable
== null then return
324 variable
.decorate_tag
(res
, token
)
329 redef fun decorate_tag
(res
, token
)
331 res
.add_class
("nc_v")
333 if vs
== null then return
334 var idx
= n_ids
.index_of
(token
.as(TId))
335 var variable
= vs
[idx
]
336 variable
.decorate_tag
(res
, token
)
341 redef fun decorate_tag
(res
, token
)
343 res
.add_class
("nc_v")
345 if mp
== null then return
346 res
.attrs
["title"] = mp
.name
+ ": " + mp
.mtype
.to_s
350 redef class AAssertExpr
351 redef fun decorate_tag
(res
, token
)
353 res
.add_class
("nc_ast")
358 redef fun decorate_tag
(res
, token
)
360 res
.add_class
("nc_la")
364 redef class ASendExpr
365 redef fun decorate_tag
(res
, token
)
367 if callsite
== null then return
368 var mpropdef
= callsite
.mpropdef
369 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
370 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
375 redef fun decorate_tag
(res
, token
)
377 if callsite
== null then return
378 var mpropdef
= callsite
.mpropdef
379 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
380 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
384 redef class AAssignOp
385 redef fun decorate_tag
(res
, v
)
388 assert p
isa AReassignFormExpr
390 var callsite
= p
.reassign_callsite
391 if callsite
== null then return
392 var mpropdef
= callsite
.mpropdef
393 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
394 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
398 redef class AModuleName
399 redef fun decorate_tag
(res
, token
)
401 parent
.decorate_tag
(res
, token
)
405 redef class AModuledecl
406 redef fun decorate_tag
(res
, token
)
408 res
.add_class
("nc_def")
409 res
.add_class
("nc_m")
413 if mm
== null then return
414 res
.attrs
["title"] = mm
.full_name
418 redef class AStdImport
419 redef fun decorate_tag
(res
, token
)
421 res
.add_class
("nc_m")
423 if mm
== null then return
424 res
.attrs
["title"] = mm
.full_name
425 res
.attrs
["link"] = mm
.name
+ ".html"
429 redef class AAttrPropdef
430 redef fun decorate_tag
(res
, token
)
432 res
.add_class
("nc_def")
433 var mpd
: nullable MPropDef
435 if mpd
== null then mpd
= mpropdef
436 if mpd
== null then return
437 var mp
= mpd
.mproperty
438 res
.attrs
["title"] = mp
.full_name
439 if mp
.intro
!= mpd
then
441 res
.attrs
["link"] = mpd
.mclassdef
.mmodule
.name
+ ".html#" + mpd
.to_s
447 redef fun make_tag
(v
)
451 if p
!= null then p
.decorate_tag
(res
, self)
452 res
.add_class
("nc_i")
457 redef fun accept_highlight_visitor
(v
)
459 var res
= new HTMLTag("span")
460 res
.add_class
("nc_def")
462 if p
isa AMethPropdef then
465 var mp
= mpd
.mproperty
466 res
.attr
("title", mp
.full_name
)
467 if mp
.intro
!= mpd
then
469 var link
= mpd
.mclassdef
.mmodule
.name
+ ".html#" + mpd
.to_s
470 var l
= new HTMLTag("a")
479 redef fun decorate_tag
(res
, v
)
481 # nothing to decorate
485 redef fun make_tag
(v
)
489 if p
!= null then p
.decorate_tag
(res
, self)
490 res
.add_class
("nc_a")
494 redef class AAttrFormExpr
495 redef fun decorate_tag
(res
, v
)
498 if p
== null then return
499 res
.attrs
["title"] = p
.full_name
501 res
.attrs
["link"] = pi
.mclassdef
.mmodule
.name
+ ".html#" + pi
.to_s
505 redef fun make_tag
(v
)
509 if p
!= null then p
.decorate_tag
(res
, self)
510 res
.add_class
("nc_t")
515 redef fun decorate_tag
(res
, token
)
518 if mt
== null then return
520 if mt
isa MNullableType then mt
= mt
.mtype
521 if mt
isa MVirtualType or mt
isa MParameterType then
522 res
.add_class
("nc_vt")
523 else if mt
isa MClassType then
524 title
= mt
.mclass
.full_name
525 res
.attrs
["link"] = mt
.mclass
.intro
.mmodule
.name
+ ".html#" + mt
.mclass
.intro
.to_s
527 res
.attrs
["title"] = title
530 redef class AFormaldef
531 redef fun decorate_tag
(res
, token
)
533 res
.add_class
("nc_vt")
534 if mtype
== null then return
535 res
.attrs
["title"] = "{mtype.to_s}: {bound.to_s}"
538 redef class ATypePropdef
539 redef fun decorate_tag
(res
, token
)
541 res
.add_class
("nc_def")
543 if md
== null then return
544 var mp
= mpropdef
.mproperty
545 res
.attrs
["title"] = mp
.full_name
548 res
.attrs
["link"] = mi
.mclassdef
.mmodule
.name
+ ".html#" + mi
.to_s
553 redef fun make_tag
(v
)
556 if parent
== null then
557 res
.add_class
("nc_c")
559 assert parent
isa ADoc
565 redef fun accept_highlight_visitor
(v
)
567 var res
= new HTMLTag("span")
568 res
.add_class
("nc_d")
573 redef class TokenLiteral
574 redef fun make_tag
(v
)
577 res
.add_class
("nc_l")
579 if p
isa AStringFormExpr then p
.decorate_tag
(res
, self)
583 redef class ASuperstringExpr
584 redef fun accept_highlight_visitor
(v
)
586 var res
= new HTMLTag("span")
587 res
.add_class
("nc_ss")
592 redef class AStringFormExpr
593 redef fun decorate_tag
(res
, v
)
595 # Workarount to tag strings
596 res
.classes
.remove
("nc_l")
597 res
.add_class
("nc_s")