X-Git-Url: http://nitlanguage.org diff --git a/src/highlight.nit b/src/highlight.nit index cd400bd..1dfbb8f 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -12,51 +12,233 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Highliting of Nit AST +# Highlighting of Nit AST module highlight -import modelize_property import frontend import html import pipeline import astutil +import serialization + +# A standalone highlighted piece of code +class HLCode + super Serializable + + # The highlighter used + var hl: HighlightVisitor + + # The raw code source + var content: String + + # The pseudo source-file + var source: SourceFile + + # JavaScript code to update an existing codemirror editor. + fun code_mirror_update: Template + do + + var res = new Template + res.add """ + function nitmessage() { + editor.operation(function(){ + for (var i = 0; i < widgets.length; ++i) + editor.removeLineWidget(widgets[i]); + widgets.length = 0; +""" + + for m in source.messages do + res.add """ + var l = document.createElement("div"); + l.className = "lint-error" + l.innerHTML = " {{{m.text.html_escape}}}"; + var w = editor.addLineWidget({{{m.location.line_start-1}}}, l); + widgets.push(w); +""" + end + res.add """});}""" + return res + end + + redef fun core_serialize_to(v) + do + v.serialize_attribute("code", hl.html.write_to_string) + var msgs = new Array[Map[String, Serializable]] + for m in source.messages do + var o = new Map[String, Serializable] + msgs.add o + o["line"] = m.location.line_start-1 + o["message"] = m.text + end + v.serialize_attribute("messages", msgs) + end +end # Visitor used to produce a HTML tree based on a AST on a `Source` class HighlightVisitor # The root of the HTML hierarchy var html = new HTMLTag("span") - # Is the HTML include a nested `` element for each `ANode` of the AST? + # Should the HTML include a nested `` element for each `ANode` of the AST? + # # Used to have a really huge and verbose HTML (mainly for debug) - var with_ast writable = false + var with_ast = false is writable + + # Prefixes used in generated IDs for line `` elements. + # Useful if more than one highlighted code is present in the same HTML document. + # + # If set to the empty string, id for lines are disabled. + # + # Is `"L"` by default. + var line_id_prefix = "L" is writable # The first line to generate, null if start at the first line - var first_line: nullable Int writable = null + var first_line: nullable Int = null is writable # The last line to generate, null if finish at the last line - var last_line: nullable Int writable = null + var last_line: nullable Int = null is writable + + # When highlighting a node, show its messages (errors, warnings), if any. + # + # default: true + var show_messages = true is writable + + # When highlighting a node, attach a full popupable infobox, if any. + # + # If `false`, only a simple `title` tooltip is used. + # + # default: true + var show_infobox = true is writable + + # A reference to an entity used in generated `` elements. + # + # It is used to refer to some specific entities when generating links. + # If `null` is returned, then no link are generated and `` elements become ``. + # + # By default, `null` is returned. + # Clients are therefore encouraged to redefine the method in a subclass to control where entities should link to. + fun hrefto(entity: MEntity): nullable String do return null init do html.add_class("nitcode") end - fun enter_visit(n: ANode) + # When highlighting a node, also consider the loose tokens around it. + # + # Loose tokens are tokens discarded from the AST but attached before + # or after some non-loose tokens. See `Token::is_loose`. + # + # When this flag is set to `true`, the loose tokens that are before the + # first token and after the last token are also highlighted. + # + # Default: false. + var include_loose_tokens = false is writable + + # When highlighting a node, the first and the last lines are fully included. + # + # If the highlighted node starts (or ends) in the middle of a line, + # this flags forces the whole line to be highlighted. + # + # Default: false + var include_whole_lines = false is writable + + # Highlight a AST element. + fun highlight_node(n: ANode) do n.parentize_tokens - var s = n.location.file - htmlize(s.first_token.as(not null), s.last_token.as(not null)) + + var f + var l + + if n isa Token then + f = n + l = n + else + assert n isa Prod + f = n.first_token + if f == null then return + l = n.last_token + if l == null then return + end + + if include_loose_tokens then + if f.prev_looses.not_empty then f = f.prev_looses.first + if l.next_looses.not_empty then l = l.next_looses.last + end + + var line = first_line + if line != null then + while f.location.line_start < line do + f = f.next_token + if f == null then return + end + end + + line = last_line + if line != null then + while l.location.line_end > line do + l = l.prev_token + if l == null then return + end + end + + if include_whole_lines then + f = f.first_real_token_in_line + l = l.last_real_token_in_line + end + + do_highlight(f, l) end - # Produce HTML between two tokens - protected fun htmlize(first_token, last_token: Token) + private fun full_tag(anode: ANode): nullable HTMLTag + do + var tag = anode.make_tag(self) + if tag == null then return null + var infobox = anode.infobox(self) + if infobox == null and anode isa Token then + var pa = anode.parent + if pa != null then + infobox = pa.decorate_tag(self, tag, anode) + end + end + if infobox != null and not show_infobox then + tag.attr("title", infobox.title) + tag.classes.add "titled" + infobox = null + end + var messages = anode.location.messages + if messages != null and show_messages then + tag.css("border-bottom", "solid 2px red") + if infobox == null then + infobox = new HInfoBox(self, "Messages") + end + var c = infobox.new_dropdown("{messages.length} message(s)", "") + for m in messages do + c.open("li").append(m.text) + end + end + if infobox != null then + tag.attach_infobox(infobox) + end + return tag + end + + # Highlight a full lexed source file. + # + # REQUIRE `source.first_token != null` + fun highlight_source(source: SourceFile) + do + do_highlight(source.first_token.as(not null), null) + end + + # Low-level highlighting between 2 tokens + protected fun do_highlight(first_token: Token, last_token: nullable Token) do var stack2 = new Array[HTMLTag] var stack = new Array[Prod] - var closes = new Array[Prod] var line = 0 var c: nullable Token = first_token - var hv = new HighlightVisitor while c != null do var starting @@ -71,11 +253,9 @@ class HighlightVisitor if c0 != null then starting = c0.starting_prods if starting != null then for p in starting do if not p.is_block then continue - var tag = p.make_tag(hv) + var tag = full_tag(p) if tag == null then continue tag.add_class("foldable") - var infobox = p.infobox(hv) - if infobox != null then tag.attach_infobox(infobox) stack2.add(html) html.add tag html = tag @@ -84,7 +264,8 @@ class HighlightVisitor # Add a div for the whole line var tag = new HTMLTag("span") - tag.attrs["id"] = "L{cline}" + var p = line_id_prefix + if p != "" then tag.attrs["id"] = "{p}{cline}" tag.classes.add "line" stack2.add(html) html.add tag @@ -99,10 +280,8 @@ class HighlightVisitor starting = c.starting_prods if starting != null then for p in starting do if not p.is_span then continue - var tag = p.make_tag(hv) + var tag = full_tag(p) if tag == null then continue - var infobox = p.infobox(hv) - if infobox != null then tag.attach_infobox(infobox) stack2.add(html) html.add tag html = tag @@ -110,20 +289,11 @@ class HighlightVisitor end # Add the token - if c isa TEol then + if c isa TEol then html.append "\n" else - var tag = c.make_tag(hv) - var pa = c.parent - var infobox = null - if c isa TId or c isa TClassid or c isa TAttrid or c isa TokenLiteral or c isa TokenOperator then - assert c != null - if pa != null then infobox = pa.decorate_tag(hv, tag, c) - else if c isa TComment and pa isa ADoc then - infobox = pa.decorate_tag(hv, tag, c) - end - if infobox != null then tag.attach_infobox(infobox) - html.add tag + var tag = full_tag(c) + if tag != null then html.add tag end # Handle ending span productions @@ -156,8 +326,7 @@ class HighlightVisitor c = n end - assert stack.is_empty - assert stack2.is_empty + if not stack2.is_empty then html = stack2.first end # Return a default CSS content related to CSS classes used in the `html` tree. @@ -166,10 +335,11 @@ class HighlightVisitor do return """ .nitcode a { color: inherit; cursor:pointer; } +.nitcode .titled:hover { text-decoration: underline; } /* underline titles */ .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */ -pre.nitcode .foldable { display: block } /* for block productions*/ -pre.nitcode .line{ display: block } /* for lines */ -pre.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */ +.nitcode .foldable { display: block } /* for block productions*/ +.nitcode .line{ display: block } /* for lines */ +.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */ .nitcode :target { background-color: #FFF3C2 } /* target highlight*/ /* lexical raw tokens. independent of usage or semantic: */ .nitcode .nc_c { color: gray; font-style: italic; } /* comment */ @@ -216,6 +386,51 @@ pre.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */ """ end + + # Fully process `content` as a Nit source file. + # + # Set `print_errors = true` to print errors in the code to the console. + fun highlightcode(content: String, print_errors: nullable Bool): HLCode + do + # Prepare a stand-alone tool context + var tc = new ToolContext + tc.nit_dir = tc.locate_nit_dir # still use the common lib to have core + tc.keep_going = true # no exit, obviously + if print_errors != true then tc.opt_warn.value = -1 # no output + + # Prepare an stand-alone model and model builder. + # Unfortunately, models are enclosing and append-only. + # There is no way (yet?) to have a shared module `core` with + # isolated and throwable user modules. + var model = new Model + var mb = new ModelBuilder(model, tc) + + # Parse the code + var source = new SourceFile.from_string("", content + "\n") + var lexer = new Lexer(source) + var parser = new Parser(lexer) + var tree = parser.parse + + var hlcode = new HLCode(self, content, source) + + # Check syntax error + var eof = tree.n_eof + if eof isa AError then + mb.error(eof, eof.message) + highlight_source(source) + return hlcode + end + var amodule = tree.n_base.as(not null) + + # Load the AST as a module in the model + # Then process it + mb.load_rt_module(null, amodule, "") + mb.run_phases + + # Highlight the processed module + highlight_node(amodule) + return hlcode + end end redef class HTMLTag @@ -260,12 +475,14 @@ class HInfoBox end # Append a new dropdown in the popuped content - fun new_dropdown(title, text: String): HTMLTag + fun new_dropdown(title, text: String, text_is_html: nullable Bool): HTMLTag do content.add_raw_html """" @@ -279,9 +496,6 @@ end interface HInfoBoxable # An new infobox documenting the entity fun infobox(v: HighlightVisitor): HInfoBox is abstract - - # A human-readable hyper-text for the entity - fun linkto: HTMLTag is abstract end redef class MDoc @@ -302,61 +516,81 @@ end redef class MEntity super HInfoBoxable + + # A HTML version of `to_s` with hyper-links. + # + # By default, `linkto_text(v, to_s)` is used, c.f. see `linkto_text`. + # + # For some complex entities, like generic types, multiple `` and `` elements can be generated. + # E.g. `Array[Int]` might become `Array[Int]` with the correct `href` attributes + # provided by `v.hrefto`. + fun linkto(v: HighlightVisitor): HTMLTag do return linkto_text(v, to_s) + + # Link to the `self` with a specific text. + # + # The whole text is linked with a single `` element. + # + # The `href` used is provided by `v.hrefto`. + # If `href` is null then a `` element is used instead of ``. + fun linkto_text(v: HighlightVisitor, text: String): HTMLTag + do + var href = v.hrefto(self) + if href == null then + return (new HTMLTag("span")).text(text) + end + return (new HTMLTag("a")).attr("href", href).text(text) + end + + # Append an entry for the doc in the given infobox + private fun add_doc_to_infobox(res: HInfoBox) + do + var mdoc = mdoc_or_fallback + if mdoc != null then mdoc.fill_infobox(res) + end end redef class MModule redef fun infobox(v) do var res = new HInfoBox(v, "module {name}") - res.href = href - res.new_field("module").add(linkto) - var mdoc = self.mdoc - if mdoc != null then mdoc.fill_infobox(res) + res.href = v.hrefto(self) + res.new_field("module").add(linkto(v)) + add_doc_to_infobox(res) if in_importation.greaters.length > 1 then var c = res.new_dropdown("imports", "{in_importation.greaters.length-1} modules") for x in in_importation.greaters do if x == self then continue - c.open("li").add x.linkto + c.open("li").add x.linkto(v) end end return res end - fun href: String - do - return name + ".html" - end - - redef fun linkto do return linkto_text(name) - - # Link to the entitiy with a specific text - fun linkto_text(text: String): HTMLTag - do - return (new HTMLTag("a")).attr("href", href).text(text) - end + redef fun linkto(v) do return linkto_text(v, name) end redef class MClassDef redef fun infobox(v) do var res = new HInfoBox(v, "class {mclass.name}") - res.href = href + res.href = v.hrefto(self) if is_intro then res.new_field("class").text(mclass.name) else res.new_field("redef class").text(mclass.name) - res.new_field("intro").add mclass.intro.linkto_text("in {mclass.intro.mmodule.to_s}") + res.new_field("intro").add mclass.intro.linkto_text(v, "in {mclass.intro_mmodule.to_s}") end - var mdoc = self.mdoc - if mdoc == null then mdoc = mclass.intro.mdoc - if mdoc != null then mdoc.fill_infobox(res) + add_doc_to_infobox(res) + + var in_hierarchy = self.in_hierarchy + if in_hierarchy == null then return res if in_hierarchy.greaters.length > 1 then var c = res.new_dropdown("hier", "super-classes") for x in in_hierarchy.greaters do if x == self then continue if not x.is_intro then continue - c.open("li").add x.linkto + c.open("li").add x.linkto(v) end end if in_hierarchy.smallers.length > 1 then @@ -364,142 +598,134 @@ redef class MClassDef for x in in_hierarchy.smallers do if x == self then continue if not x.is_intro then continue - c.open("li").add x.linkto + c.open("li").add x.linkto(v) end end if mclass.mclassdefs.length > 1 then var c = res.new_dropdown("redefs", "refinements") for x in mclass.mclassdefs do if x == self then continue - c.open("li").add x.linkto_text("in {x.mmodule}") + c.open("li").add x.linkto_text(v, "in {x.mmodule}") end end return res end - - fun href: String - do - return mmodule.href + "#" + to_s - end - - redef fun linkto do return linkto_text(mclass.name) - - # Link to the entitiy with a specific text - fun linkto_text(text: String): HTMLTag - do - return (new HTMLTag("a")).attr("href", href).text(text) - end end redef class MPropDef redef fun infobox(v) do var res = new HInfoBox(v, to_s) - res.href = href + res.href = v.hrefto(self) if self isa MMethodDef then - res.new_field("fun").append(mproperty.name).add msignature.linkto + var msignature = self.msignature + if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto(v) else if self isa MAttributeDef then - res.new_field("fun").append(mproperty.name).add static_mtype.linkto + var static_mtype = self.static_mtype + if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto(v) else if self isa MVirtualTypeDef then - res.new_field("add").append(mproperty.name).add bound.linkto + var bound = self.bound + if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto(v) else res.new_field("wat?").append(mproperty.name) end if is_intro then else - res.new_field("intro").add mproperty.intro.linkto_text("in {mproperty.intro.mclassdef}") + res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}") end - var mdoc = self.mdoc - if mdoc == null then mdoc = mproperty.intro.mdoc - if mdoc != null then mdoc.fill_infobox(res) + add_doc_to_infobox(res) if mproperty.mpropdefs.length > 1 then var c = res.new_dropdown("redef", "redefinitions") for x in mproperty.mpropdefs do - c.open("li").add x.linkto_text("in {x.mclassdef}") + c.open("li").add x.linkto_text(v, "in {x.mclassdef}") end end return res end - - fun href: String - do - return self.mclassdef.mmodule.href + "#" + self.to_s - end - - redef fun linkto do return linkto_text(mproperty.name) - - # Link to the entitiy with a specific text - fun linkto_text(text: String): HTMLTag - do - return (new HTMLTag("a")).attr("href", href).text(text) - end end redef class MClassType redef fun infobox(v) do var res = new HInfoBox(v, to_s) - res.href = mclass.intro.href - res.new_field("class").add mclass.intro.linkto - var mdoc = mclass.mdoc - if mdoc == null then mdoc = mclass.intro.mdoc - if mdoc != null then mdoc.fill_infobox(res) + res.href = v.hrefto(self) + res.new_field("class").add mclass.intro.linkto(v) + add_doc_to_infobox(res) return res end - redef fun linkto + redef fun linkto(v) do - return mclass.intro.linkto + return mclass.intro.linkto(v) end end redef class MVirtualType redef fun infobox(v) do var res = new HInfoBox(v, to_s) - res.href = mproperty.intro.href + res.href = v.hrefto(mproperty) var p = mproperty - var pd = p.intro - res.new_field("virtual type").add pd.linkto - var mdoc = pd.mdoc - if mdoc != null then mdoc.fill_infobox(res) + res.new_field("virtual type").add p.intro.linkto(v) + add_doc_to_infobox(res) return res end - redef fun linkto + redef fun linkto(v) do - return mproperty.intro.linkto + return mproperty.intro.linkto(v) end end redef class MParameterType redef fun infobox(v) do var res = new HInfoBox(v, to_s) - var name = mclass.intro.parameter_names[rank] - res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto + res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v) return res end - redef fun linkto +end + +redef class MNullableType + redef fun infobox(v) + do + return mtype.infobox(v) + end + redef fun linkto(v) do - var name = mclass.intro.parameter_names[rank] - return (new HTMLTag("span")).text(name) + var res = new HTMLTag("span") + res.append("nullable ").add(mtype.linkto(v)) + return res end end -redef class MNullableType +redef class MNotNullType redef fun infobox(v) do return mtype.infobox(v) end - redef fun linkto + redef fun linkto(v) + do + var res = new HTMLTag("span") + res.append("not null ").add(mtype.linkto(v)) + return res + end +end + +redef class MNullType + redef fun infobox(v) + do + var res = new HInfoBox(v, to_s) + return res + end + redef fun linkto(v) do var res = new HTMLTag("span") - res.append("nullable ").add(mtype.linkto) + res.append("null") return res end end redef class MSignature - redef fun linkto + redef fun linkto(v) do var res = new HTMLTag("span") var first = true @@ -513,39 +739,36 @@ redef class MSignature end res.append p.name res.append ": " - res.add p.mtype.linkto + res.add p.mtype.linkto(v) end res.append ")" end var ret = return_mtype if ret != null then res.append ": " - res.add ret.linkto + res.add ret.linkto(v) end return res end end redef class CallSite - super HInfoBoxable redef fun infobox(v) do var res = new HInfoBox(v, "call {mpropdef}") - res.href = mpropdef.href - res.new_field("call").add(mpropdef.linkto).add(msignature.linkto) + res.href = v.hrefto(mpropdef) + res.new_field("call").add(mpropdef.linkto(v)).add(msignature.linkto(v)) if mpropdef.is_intro then else - res.new_field("intro").add mproperty.intro.linkto_text("in {mproperty.intro.mclassdef}") + res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}") end - var mdoc = mpropdef.mdoc - if mdoc == null then mdoc = mproperty.intro.mdoc - if mdoc != null then mdoc.fill_infobox(res) + add_doc_to_infobox(res) return res end - redef fun linkto + redef fun linkto(v) do - return mpropdef.linkto + return mpropdef.linkto(v) end end @@ -553,19 +776,16 @@ redef class Variable super HInfoBoxable redef fun infobox(v) do + var declared_type = self.declared_type if declared_type == null then var res = new HInfoBox(v, "{name}") res.new_field("local var").append("{name}") return res end var res = new HInfoBox(v, "{name}: {declared_type}") - res.new_field("local var").append("{name}:").add(declared_type.linkto) + res.new_field("local var").append("{name}:").add(declared_type.linkto(v)) return res end - redef fun linkto - do - return (new HTMLTag("span")).text(name) - end end @@ -587,6 +807,26 @@ redef class ANode fun infobox(v: HighlightVisitor): nullable HInfoBox do return null end +redef class AQclassid + redef fun decorate_tag(v, res, token) + do + if token != n_id then return null + var parent = self.parent + if parent == null then return null + return parent.decorate_tag(v, res, token) + end +end + +redef class AQid + redef fun decorate_tag(v, res, token) + do + if token != n_id then return null + var parent = self.parent + if parent == null then return null + return parent.decorate_tag(v, res, token) + end +end + redef class AStdClassdef redef fun make_tag(v) do @@ -598,6 +838,7 @@ redef class AStdClassdef end redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null res.add_class("nc_def") var md = mclassdef @@ -636,7 +877,7 @@ end redef class Token # Produce an HTMLTag with the correct contents and CSS classes # Subclasses can redefine it to decorate the tag - redef fun make_tag(v: HighlightVisitor): HTMLTag + redef fun make_tag(v): HTMLTag do var res = new HTMLTag("span") res.text(text) @@ -656,8 +897,6 @@ redef class TokenOperator redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(v, res, self) res.add_class("nc_o") return res end @@ -666,6 +905,7 @@ end redef class AVarFormExpr redef fun decorate_tag(v, res, token) do + if token != n_id then return null var variable = self.variable if variable == null then return null res.add_class("nc_v") @@ -676,6 +916,7 @@ end redef class AVardeclExpr redef fun decorate_tag(v, res, token) do + if token != n_id then return null var variable = self.variable if variable == null then return null res.add_class("nc_v") @@ -683,7 +924,7 @@ redef class AVardeclExpr end end -redef class AForExpr +redef class AForGroup redef fun decorate_tag(v, res, token) do if not token isa TId then return null @@ -699,6 +940,7 @@ end redef class AParam redef fun decorate_tag(v, res, token) do + if token != n_id then return null var mp = mparameter if mp == null then return null var variable = self.variable @@ -711,6 +953,7 @@ end redef class AAssertExpr redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_ast") return null end @@ -719,6 +962,7 @@ end redef class ALabel redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_la") return null end @@ -727,6 +971,7 @@ end redef class ASendExpr redef fun decorate_tag(v, res, token) do + var callsite = self.callsite if callsite == null then return null return callsite.infobox(v) end @@ -735,6 +980,7 @@ end redef class ANewExpr redef fun decorate_tag(v, res, token) do + var callsite = self.callsite if callsite == null then return null return callsite.infobox(v) end @@ -755,13 +1001,16 @@ end redef class AModuleName redef fun decorate_tag(v, res, token) do - return parent.decorate_tag(v, res, token) + var p = parent + if p == null then return null + return p.decorate_tag(v, res, token) end end redef class AModuledecl redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_def") res.add_class("nc_m") var p = parent @@ -775,6 +1024,7 @@ end redef class AStdImport redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_m") var mm = mmodule if mm == null then return null @@ -784,6 +1034,7 @@ end redef class AAttrPropdef redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_def") var mpd: nullable MPropDef mpd = mreadpropdef @@ -797,8 +1048,6 @@ redef class TId redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(v, res, self) res.add_class("nc_i") return res end @@ -828,8 +1077,6 @@ redef class TAttrid redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(v, res, self) res.add_class("nc_a") return res end @@ -837,6 +1084,7 @@ end redef class AAttrFormExpr redef fun decorate_tag(v, res, token) do + if not token isa TAttrid then return null var p = mproperty if p == null then return null return p.intro.infobox(v) @@ -846,8 +1094,6 @@ redef class TClassid redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(v, res, self) res.add_class("nc_t") return res end @@ -855,10 +1101,11 @@ end redef class AType redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null var mt = mtype if mt == null then return null - if mt isa MNullableType then mt = mt.mtype - if mt isa MVirtualType or mt isa MParameterType then + mt = mt.undecorate + if mt isa MFormalType then res.add_class("nc_vt") end return mt.infobox(v) @@ -867,7 +1114,9 @@ end redef class AFormaldef redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null res.add_class("nc_vt") + var mtype = self.mtype if mtype == null then return null return mtype.infobox(v) end @@ -875,6 +1124,7 @@ end redef class ATypePropdef redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null res.add_class("nc_def") var md = mpropdef if md == null then return null @@ -885,7 +1135,7 @@ redef class TComment redef fun make_tag(v) do var res = super - if not parent isa ADoc then + if is_loose then res.add_class("nc_c") end return res @@ -904,8 +1154,6 @@ redef class TokenLiteral do var res = super res.add_class("nc_l") - var p = parent - if p != null then p.decorate_tag(v, res, self) return res end end @@ -923,7 +1171,7 @@ redef class AStringFormExpr # Workaround to tag strings res.classes.remove("nc_l") res.add_class("nc_s") - return null + return super end end redef class AExpr @@ -934,4 +1182,3 @@ redef class AExpr return t.infobox(v) end end -