X-Git-Url: http://nitlanguage.org diff --git a/src/highlight.nit b/src/highlight.nit index 4a2166c..66e1cd9 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -25,7 +25,8 @@ 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 = false is writable @@ -55,6 +56,15 @@ class HighlightVisitor # 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") @@ -145,14 +155,22 @@ class HighlightVisitor 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 line = 0 var c: nullable Token = first_token - var hv = new HighlightVisitor + var hv = self while c != null do var starting @@ -364,9 +382,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 @@ -387,56 +402,73 @@ 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 - # The module HTML page - fun href: String - do - return c_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 @@ -444,7 +476,7 @@ redef class MClassDef 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 @@ -452,125 +484,90 @@ 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 - - # The class HTML page (an anchor in the module page) - 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 - if msignature != null 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 - if static_mtype != null 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 - if bound != null 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 - - # The property HTML page (an anchor in the module page) - 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) - 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 - do - return (new HTMLTag("span")).text(name) - end end redef class MNullableType @@ -578,10 +575,10 @@ redef class MNullableType do return mtype.infobox(v) end - redef fun linkto + redef fun linkto(v) do var res = new HTMLTag("span") - res.append("nullable ").add(mtype.linkto) + res.append("nullable ").add(mtype.linkto(v)) return res end end @@ -591,10 +588,10 @@ redef class MNotNullType 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) + res.append("not null ").add(mtype.linkto(v)) return res end end @@ -605,7 +602,7 @@ redef class MNullType var res = new HInfoBox(v, to_s) return res end - redef fun linkto + redef fun linkto(v) do var res = new HTMLTag("span") res.append("null") @@ -614,7 +611,7 @@ redef class MNullType end redef class MSignature - redef fun linkto + redef fun linkto(v) do var res = new HTMLTag("span") var first = true @@ -628,14 +625,14 @@ 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 @@ -645,21 +642,19 @@ redef class CallSite 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 @@ -674,13 +669,9 @@ redef class Variable 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 @@ -702,6 +693,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 @@ -752,7 +763,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) @@ -846,6 +857,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 @@ -854,6 +866,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 @@ -874,7 +887,9 @@ 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 @@ -987,6 +1002,7 @@ redef class AFormaldef 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