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 # The line position in the source file.
58 private var line_pos
= 0
60 # The first line to generate, null if start at the first line
61 var first_line
: nullable Int writable = null
63 # The last line to generate, null if finish at the last line
64 var last_line
: nullable Int writable = null
68 html
.add_class
("nitcode")
73 # Used to remember the first node, thus knowing when the whole visit is over
74 private var first_node
: nullable ANode
76 private var seen_token
= new HashSet[Token]
78 private fun process_upto_token
(node
: Token)
80 # recursively process previous tokens
81 var prev
= node
.prev_token
82 if prev
!= null and not seen_token
.has
(prev
) then
83 process_upto_token
(prev
)
84 prev
.accept_highlight_visitor
(self)
87 # Add text between `last_token` and `node`
88 var pstart
= node
.location
.pstart
89 var line_start
= node
.location
.line_start
90 var line_end
= node
.location
.line_end
91 if pos
< pstart
and (first_line
== null or first_line
<= line_start
) and (last_line
== null or last_line
>= line_end
) then
92 var text
= node
.location
.file
.string
.substring
(pos
, pstart-pos
)
93 token_head
.append
(text
)
94 #node.debug("WRT: {token_head.classes} << '{text.escape_to_c}' ")
96 pos
= node
.location
.pend
+ 1
98 node
.debug
("pos={pos}, pstart={pstart}, pend={node.location.pend}")
105 private fun where
(node
: ANode, tag
: String)
109 node
.debug
"{tag}-> {token_head.classes} : {prod_head.classes}"
111 node
.debug
"{tag}-> {token_head.classes} : {pr.classes}..{prod_head.classes}"
115 redef fun visit
(node
)
117 if first_node
== null then first_node
= node
119 if node
isa Token then
120 process_upto_token
(node
)
125 #node.debug("ADD: {token_head.classes} << {pr.classes} ")
127 token_head
= prod_head
132 var oldph
= prod_head
134 node
.accept_highlight_visitor
(self)
138 assert token_head
== prod_head
140 assert token_head
!= prod_head
148 if node
== first_node
then
149 html
.append
(node
.location
.file
.string
.substring_from
(pos
))
153 # Return a default CSS content related to CSS classes used in the `html` tree.
154 # Could be inlined in the `.html` file of saved as a specific `.css` file.
155 fun css_content
: String
158 .nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
159 .nitcode a:hover { text-decoration: underline; } /* underline links */
160 .nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
161 /* lexical raw tokens. independent of usage or semantic: */
162 .nitcode .nc_c { color: gray; font-style: italic; } /* comment */
163 .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
164 .nitcode .nc_k { font-weight: bold; } /* keyword */
165 .nitcode .nc_o {} /* operator */
166 .nitcode .nc_i {} /* standard identifier */
167 .nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
168 .nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
169 .nitcode .nc_l { color: #009999; } /* char and number literal */
170 .nitcode .nc_s { color: #8F1546; } /* string literal */
171 /* syntactic token usage. added because of their position in the AST */
172 .nitcode .nc_ast { color: blue; } /* assert label */
173 .nitcode .nc_la { color: blue; } /* break/continue label */
174 .nitcode .nc_m { color: #445588; } /* module name */
175 /* syntactic groups */
176 .nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
177 .nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
178 .nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
179 .nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
180 .nitcode .nc_cdef {} /* A whole class definition */
181 .nitcode .nc_pdef {} /* A whole property definition */
182 /* semantic token usage */
183 .nitcode .nc_v { font-style: italic; } /* local variable or parameter */
184 .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
186 .nitcode .nc_error { border: 1px red solid;} /* not used */
192 private fun accept_highlight_visitor
(v
: HighlightVisitor)
195 var res
= new HTMLTag("span")
196 res
.add_class
(class_name
)
201 private fun decorate_tag
(res
: HTMLTag, token
: Token)
203 #debug("no decoration for {token.inspect}")
204 #res.add_class("nc_error")
208 redef class AStdClassdef
209 redef fun accept_highlight_visitor
(v
)
211 var res
= new HTMLTag("span")
212 res
.add_class
("nc_cdef")
215 var a
= new HTMLTag("a")
216 a
.attr
("id", md
.to_s
)
222 redef fun decorate_tag
(res
, token
)
224 res
.add_class
("nc_def")
227 if md
== null then return
229 res
.attrs
["title"] = mc
.full_name
232 res
.attrs
["link"] = mi
.mmodule
.name
+ ".html#" + mi
.to_s
237 redef fun accept_highlight_visitor
(v
)
239 var res
= new HTMLTag("span")
240 res
.add_class
("nc_pdef")
243 if mpd
!= null then res
.add
(tag
(mpd
))
244 if self isa AAttrPropdef then
246 if mpd
!= null then res
.add
(tag
(mpd
))
248 if mpd
!= null then res
.add
(tag
(mpd
))
254 private fun tag
(mpd
: MPropDef): HTMLTag
256 var a
= new HTMLTag("a")
257 a
.attr
("id", mpd
.to_s
)
263 # Produce an HTMLTag with the correct contents and CSS classes
264 # Subclasses can redefine it to decorate the tag
265 protected fun make_tag
(v
: HighlightVisitor): HTMLTag
267 var res
= new HTMLTag("span")
272 # Use `empty_tag` to create the tag ; then fill it and add it to the html
273 redef fun accept_highlight_visitor
(v
)
275 var fl
= v
.first_line
276 if fl
!= null and fl
> location
.line_start
then return
279 if ll
!= null and ll
< location
.line_end
then return
282 if n
.attrs
.is_empty
and n
.classes
.is_empty
then
283 for c
in n
.children
do
286 else if n
.attrs
.has_key
("link") then
287 var a
= new HTMLTag("a")
288 a
.attrs
["href"] = n
.attrs
["link"]
289 n
.attrs
.keys
.remove
("link")
295 #debug("WRT: {v.token_head.classes} << '{text.escape_to_c}' ")
298 redef class TokenKeyword
299 redef fun make_tag
(v
)
302 res
.add_class
("nc_k")
306 redef class TokenOperator
307 redef fun make_tag
(v
)
311 if p
!= null then p
.decorate_tag
(res
, self)
312 res
.add_class
("nc_o")
318 private fun decorate_tag
(res
: HTMLTag, token
: Token)
320 if declared_type
== null then return
321 res
.attrs
["title"] = name
+ ": " + declared_type
.to_s
325 redef class AVarFormExpr
326 redef fun decorate_tag
(res
, token
)
328 res
.add_class
("nc_v")
329 var variable
= self.variable
330 if variable
== null then return
331 variable
.decorate_tag
(res
, token
)
335 redef class AVardeclExpr
336 redef fun decorate_tag
(res
, token
)
338 res
.add_class
("nc_v")
339 var variable
= self.variable
340 if variable
== null then return
341 variable
.decorate_tag
(res
, token
)
346 redef fun decorate_tag
(res
, token
)
348 res
.add_class
("nc_v")
350 if vs
== null then return
351 var idx
= n_ids
.index_of
(token
.as(TId))
352 var variable
= vs
[idx
]
353 variable
.decorate_tag
(res
, token
)
358 redef fun decorate_tag
(res
, token
)
360 res
.add_class
("nc_v")
362 if mp
== null then return
363 res
.attrs
["title"] = mp
.name
+ ": " + mp
.mtype
.to_s
367 redef class AAssertExpr
368 redef fun decorate_tag
(res
, token
)
370 res
.add_class
("nc_ast")
375 redef fun decorate_tag
(res
, token
)
377 res
.add_class
("nc_la")
381 redef class ASendExpr
382 redef fun decorate_tag
(res
, token
)
384 if callsite
== null then return
385 var mpropdef
= callsite
.mpropdef
386 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
387 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
392 redef fun decorate_tag
(res
, token
)
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 AAssignOp
402 redef fun decorate_tag
(res
, v
)
405 assert p
isa AReassignFormExpr
407 var callsite
= p
.reassign_callsite
408 if callsite
== null then return
409 var mpropdef
= callsite
.mpropdef
410 res
.attrs
["title"] = mpropdef
.to_s
+ callsite
.msignature
.to_s
411 res
.attrs
["link"] = mpropdef
.mclassdef
.mmodule
.name
+ ".html#" + mpropdef
.to_s
415 redef class AModuleName
416 redef fun decorate_tag
(res
, token
)
418 parent
.decorate_tag
(res
, token
)
422 redef class AModuledecl
423 redef fun decorate_tag
(res
, token
)
425 res
.add_class
("nc_def")
426 res
.add_class
("nc_m")
430 if mm
== null then return
431 res
.attrs
["title"] = mm
.full_name
435 redef class AStdImport
436 redef fun decorate_tag
(res
, token
)
438 res
.add_class
("nc_m")
440 if mm
== null then return
441 res
.attrs
["title"] = mm
.full_name
442 res
.attrs
["link"] = mm
.name
+ ".html"
446 redef class AAttrPropdef
447 redef fun decorate_tag
(res
, token
)
449 res
.add_class
("nc_def")
450 var mpd
: nullable MPropDef
452 if mpd
== null then mpd
= mpropdef
453 if mpd
== null then return
454 var mp
= mpd
.mproperty
455 res
.attrs
["title"] = mp
.full_name
456 if mp
.intro
!= mpd
then
458 res
.attrs
["link"] = mpd
.mclassdef
.mmodule
.name
+ ".html#" + mpd
.to_s
464 redef fun make_tag
(v
)
468 if p
!= null then p
.decorate_tag
(res
, self)
469 res
.add_class
("nc_i")
474 redef fun accept_highlight_visitor
(v
)
476 var res
= new HTMLTag("span")
477 res
.add_class
("nc_def")
479 if p
isa AMethPropdef then
482 var mp
= mpd
.mproperty
483 res
.attr
("title", mp
.full_name
)
484 if mp
.intro
!= mpd
then
486 var link
= mpd
.mclassdef
.mmodule
.name
+ ".html#" + mpd
.to_s
487 var l
= new HTMLTag("a")
496 redef fun decorate_tag
(res
, v
)
498 # nothing to decorate
502 redef fun make_tag
(v
)
506 if p
!= null then p
.decorate_tag
(res
, self)
507 res
.add_class
("nc_a")
511 redef class AAttrFormExpr
512 redef fun decorate_tag
(res
, v
)
515 if p
== null then return
516 res
.attrs
["title"] = p
.full_name
518 res
.attrs
["link"] = pi
.mclassdef
.mmodule
.name
+ ".html#" + pi
.to_s
522 redef fun make_tag
(v
)
526 if p
!= null then p
.decorate_tag
(res
, self)
527 res
.add_class
("nc_t")
532 redef fun decorate_tag
(res
, token
)
535 if mt
== null then return
537 if mt
isa MNullableType then mt
= mt
.mtype
538 if mt
isa MVirtualType or mt
isa MParameterType then
539 res
.add_class
("nc_vt")
540 else if mt
isa MClassType then
541 title
= mt
.mclass
.full_name
542 res
.attrs
["link"] = mt
.mclass
.intro
.mmodule
.name
+ ".html#" + mt
.mclass
.intro
.to_s
544 res
.attrs
["title"] = title
547 redef class AFormaldef
548 redef fun decorate_tag
(res
, token
)
550 res
.add_class
("nc_vt")
551 if mtype
== null then return
552 res
.attrs
["title"] = "{mtype.to_s}: {bound.to_s}"
555 redef class ATypePropdef
556 redef fun decorate_tag
(res
, token
)
558 res
.add_class
("nc_def")
560 if md
== null then return
561 var mp
= mpropdef
.mproperty
562 res
.attrs
["title"] = mp
.full_name
565 res
.attrs
["link"] = mi
.mclassdef
.mmodule
.name
+ ".html#" + mi
.to_s
570 redef fun make_tag
(v
)
573 if parent
== null then
574 res
.add_class
("nc_c")
576 assert parent
isa ADoc
582 redef fun accept_highlight_visitor
(v
)
584 var res
= new HTMLTag("span")
585 res
.add_class
("nc_d")
590 redef class TokenLiteral
591 redef fun make_tag
(v
)
594 res
.add_class
("nc_l")
596 if p
isa AStringFormExpr then p
.decorate_tag
(res
, self)
600 redef class ASuperstringExpr
601 redef fun accept_highlight_visitor
(v
)
603 var res
= new HTMLTag("span")
604 res
.add_class
("nc_ss")
609 redef class AStringFormExpr
610 redef fun decorate_tag
(res
, v
)
612 # Workarount to tag strings
613 res
.classes
.remove
("nc_l")
614 res
.add_class
("nc_s")