X-Git-Url: http://nitlanguage.org diff --git a/src/highlight.nit b/src/highlight.nit index 78d7a23..5141fbe 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -12,131 +12,253 @@ # 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 parser_util import html +import pipeline +import astutil # Visitor used to produce a HTML tree based on a AST on a `Source` class HighlightVisitor - super Visitor - # The root of the HTML hierarchy var html = new HTMLTag("span") - private var token_head: HTMLTag + # Is 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 = false is writable - private var prod_head: HTMLTag + # 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 - private var prod_root: nullable HTMLTag + # The first line to generate, null if start at the first line + var first_line: nullable Int = null is writable - # Is 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 + # The last line to generate, null if finish at the last line + var last_line: nullable Int = null is writable - # Enter in a new node - # Exit is automatic - fun enter(n: HTMLTag) - do - if prod_root != null then - prod_head.add(n) - prod_head = n - else - prod_root = n - prod_head = n - end - end + # 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 - # The position in the source file. - # Used to print parts of the source betwen tokens of the AST - private var pos = 0 + # 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(entitiy: MEntity): nullable String do return null init do html.add_class("nitcode") - token_head = html - prod_head = html end - # Used to remember the first node, thus knowing when the whole visit is over - private var first_node: nullable 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 - private var seen_token = new HashSet[Token] + # 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 - private fun process_upto_token(node: Token) + # The entry-point of the highlighting. + # Will fill `html` with the generated HTML content. + fun enter_visit(n: ANode) do - # recursively process previous tokens - var prev = node.prev_token - if prev != null and not seen_token.has(prev) then - process_upto_token(prev) - prev.accept_highlight_visitor(self) + n.parentize_tokens + + 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 - # Add text between `last_token` and `node` - var pstart = node.location.pstart - if pos < pstart then - var text = node.location.file.string.substring(pos, pstart-pos) - token_head.append(text) - #node.debug("WRT: {token_head.classes} << '{text.escape_to_c}' ") + 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 - pos = node.location.pend + 1 - if pos < pstart then - node.debug("pos={pos}, pstart={pstart}, pend={node.location.pend}") + + if include_whole_lines then + f = f.first_real_token_in_line + l = l.last_real_token_in_line end - seen_token.add node + htmlize(f, l) end - # Dubuging method - private fun where(node: ANode, tag: String) + private fun full_tag(anode: ANode, hv: HighlightVisitor): nullable HTMLTag do - var pr = prod_root - if pr == null then - node.debug "{tag}-> {token_head.classes} : {prod_head.classes}" - else - node.debug "{tag}-> {token_head.classes} : {pr.classes}..{prod_head.classes}" + 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 - redef fun visit(node) + # Produce HTML between two tokens + protected fun htmlize(first_token: Token, last_token: nullable Token) do - if first_node == null then first_node = node + var stack2 = new Array[HTMLTag] + var stack = new Array[Prod] + var line = 0 + var c: nullable Token = first_token + var hv = self + while c != null do + var starting - if node isa Token then - process_upto_token(node) + # Handle start of line + var cline = c.location.line_start + if cline != line then + # Handle starting block productions, + # Because c could be a detached token, get prods in + # the first AST token + var c0 = c.first_token_in_line + starting = null + 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 = full_tag(p, hv) + if tag == null then continue + tag.add_class("foldable") + stack2.add(html) + html.add tag + html = tag + stack.add(p) + end - #where(node, "TOK") - var pr = prod_root - if pr != null then - #node.debug("ADD: {token_head.classes} << {pr.classes} ") - token_head.add(pr) - token_head = prod_head - prod_root = null + # Add a div for the whole line + var tag = new HTMLTag("span") + var p = line_id_prefix + if p != "" then tag.attrs["id"] = "{p}{cline}" + tag.classes.add "line" + stack2.add(html) + html.add tag + html = tag + line = cline end - end - var oldph = prod_head - #where(node, " IN") - node.accept_highlight_visitor(self) - #where(node, "OUT") - var pr = prod_root - if pr == null then - assert token_head == prod_head - else - assert token_head != prod_head - token_head.add(pr) - prod_root = null - end - prod_head = oldph - token_head = oldph - #where(node, " IS") + # Add the blank, verbatim + html.add_raw_html c.blank_before + + # Handle starting span production + starting = c.starting_prods + if starting != null then for p in starting do + if not p.is_span then continue + var tag = full_tag(p, hv) + if tag == null then continue + stack2.add(html) + html.add tag + html = tag + stack.add(p) + end + + # Add the token + if c isa TEol then + html.append "\n" + else + var tag = full_tag(c, hv) + if tag != null then html.add tag + end - if node == first_node then - html.append(node.location.file.string.substring_from(pos)) + # Handle ending span productions + var ending = c.ending_prods + if ending != null then for p in ending do + if not p.is_span then continue + if stack.is_empty or p != stack.last then continue + stack.pop + html = stack2.pop + end + + # Handle end of line and end of file + var n = c.next_token + if c == last_token then n = null + if n == null or n.location.line_start != line then + # closes the line div + html = stack2.pop + + # close the block production divs + var c0 = c.last_token_in_line + ending = null + if c0 != null then ending = c0.ending_prods + if ending != null then for p in ending do + if not p.is_block then continue + if stack.is_empty or p != stack.last then continue + stack.pop + html = stack2.pop + end + end + + c = n end + #assert stack.is_empty + #assert stack2.is_empty end # Return a default CSS content related to CSS classes used in the `html` tree. @@ -144,9 +266,13 @@ class HighlightVisitor fun css_content: String do return """ -.nitcode a { color: inherit; text-decoration: inherit; } /* hide links */ -.nitcode a:hover { text-decoration: underline; } /* underline links */ -.nitcode span[title]:hover { text-decoration: underline; } /* underline titles */ +.nitcode a { color: inherit; cursor:pointer; } +.nitcode .titled:hover { text-decoration: underline; } /* underline titles */ +.nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */ +.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 */ .nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */ @@ -173,71 +299,456 @@ class HighlightVisitor .nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */ .nitcode .nc_error { border: 1px red solid;} /* not used */ +.popover { max-width: 800px !important; } """ end + + # Additional content to inject in the tag + # Note: does not include `css_content`; handle it yourself. + fun head_content: String + do + return """\n""" + end + + # Additional content to inject just before the closing tag + fun foot_content: String + do + return """ + + +""" + end end -redef class ANode - private fun accept_highlight_visitor(v: HighlightVisitor) +redef class HTMLTag + # Attach the infobox to the node by using BootStrap popover + fun attach_infobox(infobox: HInfoBox) + do + classes.add("popupable") + attrs["title"] = infobox.title + var href = infobox.href + if href != null then + attrs["data-title"] = """{{{infobox.title}}}""" + end + attrs["data-content"] = infobox.content.write_to_string + attrs["data-toggle"] = "popover" + end +end + + +# A generic information container that can be used to decorate AST entities +class HInfoBox + # The visitor used for contextualisation, if needed + var visitor: HighlightVisitor + + # A short title for the AST element + var title: String + + # The primary link where the entity points + # null if no link + var href: nullable String = null + + # The content of the popuped infobox + var content = new HTMLTag("div") + + # Append a new field in the popuped infobox + fun new_field(title: String): HTMLTag + do + content.open("b").text(title) + content.append(" ") + var res = content.open("span") + content.open("br") + return res + end + + # Append a new dropdown in the popuped content + fun new_dropdown(title, text: String): HTMLTag + do + content.add_raw_html """" + return res + end +end + +## + +# Model entity or whatever that can produce an infobox +interface HInfoBoxable + # An new infobox documenting the entity + fun infobox(v: HighlightVisitor): HInfoBox is abstract +end + +redef class MDoc + # Append an entry for the doc in the given infobox + fun fill_infobox(res: HInfoBox) + do + if content.length < 2 then + res.new_field("doc").text(content.first) + return + end + var c = res.new_dropdown("doc", content.first) + for x in content.iterator.skip_head(1) do + c.append x + c.add_raw_html "
" + end + end +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 = 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(v) + end + end + return res + 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 = 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(v, "in {mclass.intro_mmodule.to_s}") + end + 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(v) + end + end + if in_hierarchy.smallers.length > 1 then + var c = res.new_dropdown("hier", "sub-classes") + 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(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(v, "in {x.mmodule}") + end + end + return res + end +end + +redef class MPropDef + redef fun infobox(v) do - if v.with_ast then - var res = new HTMLTag("span") - res.add_class(class_name) - v.enter res + var res = new HInfoBox(v, to_s) + res.href = v.hrefto(self) + if self isa MMethodDef then + 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 + 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 + 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(v, "in {mproperty.intro.mclassdef}") + end + 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(v, "in {x.mclassdef}") + end end - visit_all(v) + + return res + end +end + +redef class MClassType + redef fun infobox(v) + do + var res = new HInfoBox(v, to_s) + res.href = v.hrefto(self) + res.new_field("class").add mclass.intro.linkto(v) + add_doc_to_infobox(res) + return res end - private fun decorate_tag(res: HTMLTag, token: Token) + redef fun linkto(v) + do + return mclass.intro.linkto(v) + end +end +redef class MVirtualType + redef fun infobox(v) + do + var res = new HInfoBox(v, to_s) + res.href = v.hrefto(mproperty) + var p = mproperty + res.new_field("virtual type").add p.intro.linkto(v) + add_doc_to_infobox(res) + return res + end + redef fun linkto(v) + do + return mproperty.intro.linkto(v) + end +end +redef class MParameterType + redef fun infobox(v) + do + var res = new HInfoBox(v, to_s) + res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v) + return res + end +end + +redef class MNullableType + redef fun infobox(v) + do + 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 MNotNullType + redef fun infobox(v) + do + return mtype.infobox(v) + end + 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("null") + return res + end +end + +redef class MSignature + redef fun linkto(v) + do + var res = new HTMLTag("span") + var first = true + if not mparameters.is_empty then + res.append "(" + for p in mparameters do + if first then + first = false + else + res.append ", " + end + res.append p.name + res.append ": " + res.add p.mtype.linkto(v) + end + res.append ")" + end + var ret = return_mtype + if ret != null then + res.append ": " + res.add ret.linkto(v) + end + return res + end +end + +redef class CallSite + redef fun infobox(v) + do + var res = new HInfoBox(v, "call {mpropdef}") + 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(v, "in {mproperty.intro.mclassdef}") + end + add_doc_to_infobox(res) + + return res + end + redef fun linkto(v) + do + return mpropdef.linkto(v) + end +end + +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(v)) + return res + end +end + + +## + +redef class ANode + # Optionally creates a tag that encapsulate the AST element on HTML rendering + protected fun make_tag(v: HighlightVisitor): nullable HTMLTag do return null + + # Add aditionnal information on a child-token and return an additionnal HInfoBox on it + protected fun decorate_tag(v: HighlightVisitor, res: HTMLTag, token: Token): nullable HInfoBox do #debug("no decoration for {token.inspect}") #res.add_class("nc_error") + return null + end + + # Return a optional infobox + 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 accept_highlight_visitor(v) + redef fun make_tag(v) do var res = new HTMLTag("span") res.add_class("nc_cdef") var md = mclassdef - if md != null then - var a = new HTMLTag("a") - a.attr("id", md.to_s) - res.add(a) - end - v.enter res - super + if md != null then res.attr("id", md.to_s) + return res end - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null res.add_class("nc_def") var md = mclassdef - if md == null then return - var mc = md.mclass - res.attrs["title"] = mc.full_name - var mi = mc.intro - if md != mi then - res.attrs["link"] = mi.mmodule.name + ".html#" + mi.to_s - end + if md == null then return null + return md.infobox(v) end end redef class APropdef - redef fun accept_highlight_visitor(v) + redef fun make_tag(v) do var res = new HTMLTag("span") res.add_class("nc_pdef") var mpd mpd = mpropdef - if mpd != null then res.add(tag(mpd)) + if mpd != null then + #res.add(tag(mpd)) + res.attr("id", mpd.to_s) + end if self isa AAttrPropdef then mpd = mreadpropdef if mpd != null then res.add(tag(mpd)) mpd = mwritepropdef if mpd != null then res.add(tag(mpd)) end - v.enter res - super + return res end private fun tag(mpd: MPropDef): HTMLTag @@ -251,33 +762,14 @@ end redef class Token # Produce an HTMLTag with the correct contents and CSS classes # Subclasses can redefine it to decorate the tag - protected fun make_tag(v: HighlightVisitor): HTMLTag + redef fun make_tag(v): HTMLTag do var res = new HTMLTag("span") res.text(text) return res end - - # Use `empty_tag` to create the tag ; then fill it and add it to the html - redef fun accept_highlight_visitor(v) - do - var n = make_tag(v) - if n.attrs.is_empty and n.classes.is_empty then - for c in n.children do - v.token_head.add(c) - end - else if n.attrs.has_key("link") then - var a = new HTMLTag("a") - a.attrs["href"] = n.attrs["link"] - n.attrs.keys.remove("link") - a.add(n) - v.token_head.add(a) - else - v.token_head.add(n) - end - #debug("WRT: {v.token_head.classes} << '{text.escape_to_c}' ") - end end + redef class TokenKeyword redef fun make_tag(v) do @@ -290,156 +782,150 @@ redef class TokenOperator redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(res, self) res.add_class("nc_o") return res end end -redef class Variable - private fun decorate_tag(res: HTMLTag, token: Token) - do - if declared_type == null then return - res.attrs["title"] = name + ": " + declared_type.to_s - end -end - redef class AVarFormExpr - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") + if token != n_id then return null var variable = self.variable - if variable == null then return - variable.decorate_tag(res, token) + if variable == null then return null + res.add_class("nc_v") + return variable.infobox(v) end end redef class AVardeclExpr - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") + if token != n_id then return null var variable = self.variable - if variable == null then return - variable.decorate_tag(res, token) + if variable == null then return null + res.add_class("nc_v") + return variable.infobox(v) end end -redef class AForExpr - redef fun decorate_tag(res, token) +redef class AForGroup + redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") + if not token isa TId then return null var vs = variables - if vs == null then return - var idx = n_ids.index_of(token.as(TId)) + if vs == null then return null + res.add_class("nc_v") + var idx = n_ids.index_of(token) var variable = vs[idx] - variable.decorate_tag(res, token) + return variable.infobox(v) end end redef class AParam - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") + if token != n_id then return null var mp = mparameter - if mp == null then return - res.attrs["title"] = mp.name + ": " + mp.mtype.to_s + if mp == null then return null + var variable = self.variable + if variable == null then return null + res.add_class("nc_v") + return variable.infobox(v) end end redef class AAssertExpr - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_ast") + return null end end redef class ALabel - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do + if not token isa TId then return null res.add_class("nc_la") + return null end end redef class ASendExpr - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do - if callsite == null then return - var mpropdef = callsite.mpropdef - res.attrs["title"] = mpropdef.to_s + callsite.msignature.to_s - res.attrs["link"] = mpropdef.mclassdef.mmodule.name + ".html#" + mpropdef.to_s + var callsite = self.callsite + if callsite == null then return null + return callsite.infobox(v) end end redef class ANewExpr - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do - if callsite == null then return - var mpropdef = callsite.mpropdef - res.attrs["title"] = mpropdef.to_s + callsite.msignature.to_s - res.attrs["link"] = mpropdef.mclassdef.mmodule.name + ".html#" + mpropdef.to_s + var callsite = self.callsite + if callsite == null then return null + return callsite.infobox(v) end end redef class AAssignOp - redef fun decorate_tag(res, v) + redef fun decorate_tag(v, res, token) do var p = parent assert p isa AReassignFormExpr var callsite = p.reassign_callsite - if callsite == null then return - var mpropdef = callsite.mpropdef - res.attrs["title"] = mpropdef.to_s + callsite.msignature.to_s - res.attrs["link"] = mpropdef.mclassdef.mmodule.name + ".html#" + mpropdef.to_s + if callsite == null then return null + return callsite.infobox(v) end end redef class AModuleName - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do - parent.decorate_tag(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(res, token) + 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 assert p isa AModule var mm = p.mmodule - if mm == null then return - res.attrs["title"] = mm.full_name + if mm == null then return null + return mm.infobox(v) end end redef class AStdImport - redef fun decorate_tag(res, token) + 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 - res.attrs["title"] = mm.full_name - res.attrs["link"] = mm.name + ".html" + if mm == null then return null + return mm.infobox(v) end end - redef class AAttrPropdef - redef fun decorate_tag(res, token) + 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 if mpd == null then mpd = mpropdef - if mpd == null then return - var mp = mpd.mproperty - res.attrs["title"] = mp.full_name - if mp.intro != mpd then - mpd = mp.intro - res.attrs["link"] = mpd.mclassdef.mmodule.name + ".html#" + mpd.to_s - end + if mpd == null then return null + return mpd.infobox(v) end end @@ -447,127 +933,105 @@ redef class TId redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(res, self) res.add_class("nc_i") return res end end redef class AMethid - redef fun accept_highlight_visitor(v) + redef fun make_tag(v) do var res = new HTMLTag("span") res.add_class("nc_def") - var p = parent - if p isa AMethPropdef then - var mpd = p.mpropdef - if mpd != null then - var mp = mpd.mproperty - res.attr("title", mp.full_name) - if mp.intro != mpd then - mpd = mp.intro - var link = mpd.mclassdef.mmodule.name + ".html#" + mpd.to_s - var l = new HTMLTag("a") - l.attr("href", link) - v.enter l - end - end - end - v.enter res - super + return res end - redef fun decorate_tag(res, v) + redef fun decorate_tag(v, res, token) do + return null # nothing to decorate end + redef fun infobox(v) + do + var p = parent + if not p isa AMethPropdef then return null + var mpd = p.mpropdef + if mpd == null then return null + return mpd.infobox(v) + end end redef class TAttrid redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(res, self) res.add_class("nc_a") return res end end redef class AAttrFormExpr - redef fun decorate_tag(res, v) + redef fun decorate_tag(v, res, token) do + if not token isa TAttrid then return null var p = mproperty - if p == null then return - res.attrs["title"] = p.full_name - var pi = p.intro - res.attrs["link"] = pi.mclassdef.mmodule.name + ".html#" + pi.to_s + if p == null then return null + return p.intro.infobox(v) end end redef class TClassid redef fun make_tag(v) do var res = super - var p = parent - if p != null then p.decorate_tag(res, self) res.add_class("nc_t") return res end end redef class AType - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null var mt = mtype - if mt == null then return - var title = mt.to_s - if mt isa MNullableType then mt = mt.mtype - if mt isa MVirtualType or mt isa MParameterType then + if mt == null then return null + mt = mt.undecorate + if mt isa MFormalType then res.add_class("nc_vt") - else if mt isa MClassType then - title = mt.mclass.full_name - res.attrs["link"] = mt.mclass.intro.mmodule.name + ".html#" + mt.mclass.intro.to_s end - res.attrs["title"] = title + return mt.infobox(v) end end redef class AFormaldef - redef fun decorate_tag(res, token) + redef fun decorate_tag(v, res, token) do + if not token isa TClassid then return null res.add_class("nc_vt") - if mtype == null then return - res.attrs["title"] = "{mtype.to_s}: {bound.to_s}" + var mtype = self.mtype + if mtype == null then return null + return mtype.infobox(v) end end redef class ATypePropdef - redef fun decorate_tag(res, token) + 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 - var mp = mpropdef.mproperty - res.attrs["title"] = mp.full_name - var mi = mp.intro - if md != mi then - res.attrs["link"] = mi.mclassdef.mmodule.name + ".html#" + mi.to_s - end + if md == null then return null + return md.infobox(v) end end redef class TComment redef fun make_tag(v) do var res = super - if parent == null then + if is_loose then res.add_class("nc_c") - else - assert parent isa ADoc end return res end end redef class ADoc - redef fun accept_highlight_visitor(v) + redef fun make_tag(v) do var res = new HTMLTag("span") res.add_class("nc_d") - v.enter res - super + return res end end redef class TokenLiteral @@ -575,26 +1039,31 @@ redef class TokenLiteral do var res = super res.add_class("nc_l") - var p = parent - if p isa AStringFormExpr then p.decorate_tag(res, self) return res end end redef class ASuperstringExpr - redef fun accept_highlight_visitor(v) + redef fun make_tag(v) do var res = new HTMLTag("span") res.add_class("nc_ss") - v.enter res - super + return res end end redef class AStringFormExpr - redef fun decorate_tag(res, v) + redef fun decorate_tag(v, res, token) do - # Workarount to tag strings + # Workaround to tag strings res.classes.remove("nc_l") res.add_class("nc_s") + return super + end +end +redef class AExpr + redef fun decorate_tag(v, res, token) + do + var t = mtype + if t == null then return null + return t.infobox(v) end end -