# 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
+
+# Fully process a content as a nit source file.
+fun hightlightcode(hl: HighlightVisitor, content: String): 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
+ tc.opt_warn.value = -1 # no output, obviously
+
+ # 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(hl, content, source)
+
+ # Check syntax error
+ var eof = tree.n_eof
+ if eof isa AError then
+ mb.error(eof, eof.message)
+ hl.hightlight_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
+ hl.enter_visit(amodule)
+ return hlcode
+end
+
+# 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 = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{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 `<span class"{type_of_node}">` element for each `ANode` of the AST?
+ # Should the HTML include a nested `<span class"{type_of_node}">` 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 `<span>` 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 `<a>` elements.
+ #
+ # It is used to refer to some specific entities when generating links.
+ # If `null` is returned, then no link are generated and `<a>` elements become `<span>`.
+ #
+ # 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
+ # 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
+
+ # The entry-point of the highlighting.
+ # Will fill `html` with the generated HTML content.
fun enter_visit(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
+
+ htmlize(f, l)
+ end
+
+ private fun full_tag(anode: ANode, hv: HighlightVisitor): nullable HTMLTag
+ do
+ var tag = anode.make_tag(hv)
+ if tag == null then return null
+ var infobox = anode.infobox(hv)
+ if infobox == null and anode isa Token then
+ var pa = anode.parent
+ if pa != null then
+ infobox = pa.decorate_tag(hv, 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(hv, "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 hightlight_source(source: SourceFile)
+ do
+ htmlize(source.first_token.as(not null), null)
end
# Produce HTML between two tokens
- protected fun htmlize(first_token, last_token: Token)
+ protected fun htmlize(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
+ var hv = self
while c != null do
var starting
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, hv)
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
# 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
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, hv)
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
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, hv)
+ if tag != null then html.add tag
end
# Handle ending span productions
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.
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 */
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 """<div class="dropdown"> <a data-toggle="dropdown" href="#"><b>"""
content.append(title)
content.add_raw_html "</b> "
- content.append(text)
+ if text_is_html == true then
+ content.add_raw_html(text)
+ else content.append(text)
content.add_raw_html """<span class="caret"></span></a>"""
var res = content.open("ul").add_class("dropdown-menu").attr("role", "menu").attr("aria-labelledby", "dLabel")
content.add_raw_html "</div>"
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
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 `<a>` and `<span>` elements can be generated.
+ # E.g. `Array[Int]` might become `<a>Array</a>[<a>Int</a>]` 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 `<a>` element.
+ #
+ # The `href` used is provided by `v.hrefto`.
+ # If `href` is null then a `<span>` element is used instead of `<a>`.
+ 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
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
- var name = mclass.intro.parameter_names[rank]
- return (new HTMLTag("span")).text(name)
+ return mtype.infobox(v)
+ end
+ redef fun linkto(v)
+ do
+ 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
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
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
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
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
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)
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
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")
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")
end
end
-redef class AForExpr
+redef class AForGroup
redef fun decorate_tag(v, res, token)
do
if not token isa TId then return null
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
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
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
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
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
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
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
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
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
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
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)
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
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
- mt = mt.as_notnullable
- 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)
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
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
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
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
# Workaround to tag strings
res.classes.remove("nc_l")
res.add_class("nc_s")
- return null
+ return super
end
end
redef class AExpr
return t.infobox(v)
end
end
-