highlight: remove the modelbuilder attribute
[nit.git] / src / highlight.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Highliting of Nit AST
16 module highlight
17
18 import modelize_property
19 import frontend
20 import parser_util
21 import html
22
23 # Visitor used to produce a HTML tree based on a AST on a `Source`
24 class HighlightVisitor
25 super Visitor
26
27 # The root of the HTML hierarchy
28 var html = new HTMLTag("span")
29
30 private var token_head: HTMLTag
31
32 private var prod_head: HTMLTag
33
34 private var prod_root: nullable HTMLTag
35
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
39
40 # Enter in a new node
41 # Exit is automatic
42 fun enter(n: HTMLTag)
43 do
44 if prod_root != null then
45 prod_head.add(n)
46 prod_head = n
47 else
48 prod_root = n
49 prod_head = n
50 end
51 end
52
53 # The position in the source file.
54 # Used to print parts of the source betwen tokens of the AST
55 private var pos = 0
56
57 init
58 do
59 html.add_class("nitcode")
60 token_head = html
61 prod_head = html
62 end
63
64 # Used to remember the first node, thus knowing when the whole visit is over
65 private var first_node: nullable ANode
66
67 private var seen_token = new HashSet[Token]
68
69 private fun process_upto_token(node: Token)
70 do
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)
76 end
77
78 # Add text between `last_token` and `node`
79 var pstart = node.location.pstart
80 if pos < pstart then
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}' ")
84 end
85 pos = node.location.pend + 1
86 if pos < pstart then
87 node.debug("pos={pos}, pstart={pstart}, pend={node.location.pend}")
88 end
89
90 seen_token.add node
91 end
92
93 # Dubuging method
94 private fun where(node: ANode, tag: String)
95 do
96 var pr = prod_root
97 if pr == null then
98 node.debug "{tag}-> {token_head.classes} : {prod_head.classes}"
99 else
100 node.debug "{tag}-> {token_head.classes} : {pr.classes}..{prod_head.classes}"
101 end
102 end
103
104 redef fun visit(node)
105 do
106 if first_node == null then first_node = node
107
108 if node isa Token then
109 process_upto_token(node)
110
111 #where(node, "TOK")
112 var pr = prod_root
113 if pr != null then
114 #node.debug("ADD: {token_head.classes} << {pr.classes} ")
115 token_head.add(pr)
116 token_head = prod_head
117 prod_root = null
118 end
119 end
120
121 var oldph = prod_head
122 #where(node, " IN")
123 node.accept_highlight_visitor(self)
124 #where(node, "OUT")
125 var pr = prod_root
126 if pr == null then
127 assert token_head == prod_head
128 else
129 assert token_head != prod_head
130 token_head.add(pr)
131 prod_root = null
132 end
133 prod_head = oldph
134 token_head = oldph
135 #where(node, " IS")
136
137 if node == first_node then
138 html.append(node.location.file.string.substring_from(pos))
139 end
140 end
141
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
145 do
146 return """
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 */
174
175 .nitcode .nc_error { border: 1px red solid;} /* not used */
176 """
177 end
178 end
179
180 redef class ANode
181 private fun accept_highlight_visitor(v: HighlightVisitor)
182 do
183 if v.with_ast then
184 var res = new HTMLTag("span")
185 res.add_class(class_name)
186 v.enter res
187 end
188 visit_all(v)
189 end
190 private fun decorate_tag(res: HTMLTag, token: Token)
191 do
192 #debug("no decoration for {token.inspect}")
193 #res.add_class("nc_error")
194 end
195 end
196
197 redef class AStdClassdef
198 redef fun accept_highlight_visitor(v)
199 do
200 var res = new HTMLTag("span")
201 res.add_class("nc_cdef")
202 var md = mclassdef
203 if md != null then
204 var a = new HTMLTag("a")
205 a.attr("id", md.to_s)
206 res.add(a)
207 end
208 v.enter res
209 super
210 end
211 redef fun decorate_tag(res, token)
212 do
213 res.add_class("nc_def")
214
215 var md = mclassdef
216 if md == null then return
217 var mc = md.mclass
218 res.attrs["title"] = mc.full_name
219 var mi = mc.intro
220 if md != mi then
221 res.attrs["link"] = mi.mmodule.name + ".html#" + mi.to_s
222 end
223 end
224 end
225 redef class APropdef
226 redef fun accept_highlight_visitor(v)
227 do
228 var res = new HTMLTag("span")
229 res.add_class("nc_pdef")
230 var mpd
231 mpd = mpropdef
232 if mpd != null then res.add(tag(mpd))
233 if self isa AAttrPropdef then
234 mpd = mreadpropdef
235 if mpd != null then res.add(tag(mpd))
236 mpd = mwritepropdef
237 if mpd != null then res.add(tag(mpd))
238 end
239 v.enter res
240 super
241 end
242
243 private fun tag(mpd: MPropDef): HTMLTag
244 do
245 var a = new HTMLTag("a")
246 a.attr("id", mpd.to_s)
247 return a
248 end
249 end
250
251 redef class Token
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
255 do
256 var res = new HTMLTag("span")
257 res.text(text)
258 return res
259 end
260
261 # Use `empty_tag` to create the tag ; then fill it and add it to the html
262 redef fun accept_highlight_visitor(v)
263 do
264 var n = make_tag(v)
265 if n.attrs.is_empty and n.classes.is_empty then
266 for c in n.children do
267 v.token_head.add(c)
268 end
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")
273 a.add(n)
274 v.token_head.add(a)
275 else
276 v.token_head.add(n)
277 end
278 #debug("WRT: {v.token_head.classes} << '{text.escape_to_c}' ")
279 end
280 end
281 redef class TokenKeyword
282 redef fun make_tag(v)
283 do
284 var res = super
285 res.add_class("nc_k")
286 return res
287 end
288 end
289 redef class TokenOperator
290 redef fun make_tag(v)
291 do
292 var res = super
293 var p = parent
294 if p != null then p.decorate_tag(res, self)
295 res.add_class("nc_o")
296 return res
297 end
298 end
299
300 redef class Variable
301 private fun decorate_tag(res: HTMLTag, token: Token)
302 do
303 if declared_type == null then return
304 res.attrs["title"] = name + ": " + declared_type.to_s
305 end
306 end
307
308 redef class AVarFormExpr
309 redef fun decorate_tag(res, token)
310 do
311 res.add_class("nc_v")
312 var variable = self.variable
313 if variable == null then return
314 variable.decorate_tag(res, token)
315 end
316 end
317
318 redef class AVardeclExpr
319 redef fun decorate_tag(res, token)
320 do
321 res.add_class("nc_v")
322 var variable = self.variable
323 if variable == null then return
324 variable.decorate_tag(res, token)
325 end
326 end
327
328 redef class AForExpr
329 redef fun decorate_tag(res, token)
330 do
331 res.add_class("nc_v")
332 var vs = variables
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)
337 end
338 end
339
340 redef class AParam
341 redef fun decorate_tag(res, token)
342 do
343 res.add_class("nc_v")
344 var mp = mparameter
345 if mp == null then return
346 res.attrs["title"] = mp.name + ": " + mp.mtype.to_s
347 end
348 end
349
350 redef class AAssertExpr
351 redef fun decorate_tag(res, token)
352 do
353 res.add_class("nc_ast")
354 end
355 end
356
357 redef class ALabel
358 redef fun decorate_tag(res, token)
359 do
360 res.add_class("nc_la")
361 end
362 end
363
364 redef class ASendExpr
365 redef fun decorate_tag(res, token)
366 do
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
371 end
372 end
373
374 redef class ANewExpr
375 redef fun decorate_tag(res, token)
376 do
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
381 end
382 end
383
384 redef class AAssignOp
385 redef fun decorate_tag(res, v)
386 do
387 var p = parent
388 assert p isa AReassignFormExpr
389
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
395 end
396 end
397
398 redef class AModuleName
399 redef fun decorate_tag(res, token)
400 do
401 parent.decorate_tag(res, token)
402 end
403 end
404
405 redef class AModuledecl
406 redef fun decorate_tag(res, token)
407 do
408 res.add_class("nc_def")
409 res.add_class("nc_m")
410 var p = parent
411 assert p isa AModule
412 var mm = p.mmodule
413 if mm == null then return
414 res.attrs["title"] = mm.full_name
415 end
416 end
417
418 redef class AStdImport
419 redef fun decorate_tag(res, token)
420 do
421 res.add_class("nc_m")
422 var mm = mmodule
423 if mm == null then return
424 res.attrs["title"] = mm.full_name
425 res.attrs["link"] = mm.name + ".html"
426 end
427 end
428
429 redef class AAttrPropdef
430 redef fun decorate_tag(res, token)
431 do
432 res.add_class("nc_def")
433 var mpd: nullable MPropDef
434 mpd = mreadpropdef
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
440 mpd = mp.intro
441 res.attrs["link"] = mpd.mclassdef.mmodule.name + ".html#" + mpd.to_s
442 end
443 end
444 end
445
446 redef class TId
447 redef fun make_tag(v)
448 do
449 var res = super
450 var p = parent
451 if p != null then p.decorate_tag(res, self)
452 res.add_class("nc_i")
453 return res
454 end
455 end
456 redef class AMethid
457 redef fun accept_highlight_visitor(v)
458 do
459 var res = new HTMLTag("span")
460 res.add_class("nc_def")
461 var p = parent
462 if p isa AMethPropdef then
463 var mpd = p.mpropdef
464 if mpd != null then
465 var mp = mpd.mproperty
466 res.attr("title", mp.full_name)
467 if mp.intro != mpd then
468 mpd = mp.intro
469 var link = mpd.mclassdef.mmodule.name + ".html#" + mpd.to_s
470 var l = new HTMLTag("a")
471 l.attr("href", link)
472 v.enter l
473 end
474 end
475 end
476 v.enter res
477 super
478 end
479 redef fun decorate_tag(res, v)
480 do
481 # nothing to decorate
482 end
483 end
484 redef class TAttrid
485 redef fun make_tag(v)
486 do
487 var res = super
488 var p = parent
489 if p != null then p.decorate_tag(res, self)
490 res.add_class("nc_a")
491 return res
492 end
493 end
494 redef class AAttrFormExpr
495 redef fun decorate_tag(res, v)
496 do
497 var p = mproperty
498 if p == null then return
499 res.attrs["title"] = p.full_name
500 var pi = p.intro
501 res.attrs["link"] = pi.mclassdef.mmodule.name + ".html#" + pi.to_s
502 end
503 end
504 redef class TClassid
505 redef fun make_tag(v)
506 do
507 var res = super
508 var p = parent
509 if p != null then p.decorate_tag(res, self)
510 res.add_class("nc_t")
511 return res
512 end
513 end
514 redef class AType
515 redef fun decorate_tag(res, token)
516 do
517 var mt = mtype
518 if mt == null then return
519 var title = mt.to_s
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
526 end
527 res.attrs["title"] = title
528 end
529 end
530 redef class AFormaldef
531 redef fun decorate_tag(res, token)
532 do
533 res.add_class("nc_vt")
534 if mtype == null then return
535 res.attrs["title"] = "{mtype.to_s}: {bound.to_s}"
536 end
537 end
538 redef class ATypePropdef
539 redef fun decorate_tag(res, token)
540 do
541 res.add_class("nc_def")
542 var md = mpropdef
543 if md == null then return
544 var mp = mpropdef.mproperty
545 res.attrs["title"] = mp.full_name
546 var mi = mp.intro
547 if md != mi then
548 res.attrs["link"] = mi.mclassdef.mmodule.name + ".html#" + mi.to_s
549 end
550 end
551 end
552 redef class TComment
553 redef fun make_tag(v)
554 do
555 var res = super
556 if parent == null then
557 res.add_class("nc_c")
558 else
559 assert parent isa ADoc
560 end
561 return res
562 end
563 end
564 redef class ADoc
565 redef fun accept_highlight_visitor(v)
566 do
567 var res = new HTMLTag("span")
568 res.add_class("nc_d")
569 v.enter res
570 super
571 end
572 end
573 redef class TokenLiteral
574 redef fun make_tag(v)
575 do
576 var res = super
577 res.add_class("nc_l")
578 var p = parent
579 if p isa AStringFormExpr then p.decorate_tag(res, self)
580 return res
581 end
582 end
583 redef class ASuperstringExpr
584 redef fun accept_highlight_visitor(v)
585 do
586 var res = new HTMLTag("span")
587 res.add_class("nc_ss")
588 v.enter res
589 super
590 end
591 end
592 redef class AStringFormExpr
593 redef fun decorate_tag(res, v)
594 do
595 # Workarount to tag strings
596 res.classes.remove("nc_l")
597 res.add_class("nc_s")
598 end
599 end
600