X-Git-Url: http://nitlanguage.org
diff --git a/src/highlight.nit b/src/highlight.nit
index 59e5370..cf7c598 100644
--- a/src/highlight.nit
+++ b/src/highlight.nit
@@ -43,6 +43,27 @@ class HighlightVisitor
# The last line to generate, null if finish at the last line
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(entitiy: MEntity): nullable String do return null
+
init
do
html.add_class("nitcode")
@@ -108,14 +129,16 @@ class HighlightVisitor
if infobox == null and anode isa Token then
var pa = anode.parent
if pa != null then
- var c = anode
- if c isa TId or c isa TClassid or c isa TAttrid or c isa TokenLiteral or c isa TokenOperator or c isa TComment and pa isa ADoc then
- infobox = pa.decorate_tag(hv, tag, anode)
- end
+ 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 then
+ if messages != null and show_messages then
tag.css("border-bottom", "solid 2px red")
if infobox == null then
infobox = new HInfoBox(hv, "Messages")
@@ -131,14 +154,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
@@ -236,6 +267,7 @@ 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 */
.nitcode .foldable { display: block } /* for block productions*/
.nitcode .line{ display: block } /* for lines */
@@ -349,9 +381,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
@@ -372,56 +401,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
@@ -429,7 +475,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
@@ -437,125 +483,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
+ add_doc_to_infobox(res)
if mdoc != null then mdoc.fill_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
@@ -563,10 +574,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
@@ -576,10 +587,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
@@ -590,7 +601,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")
@@ -599,7 +610,7 @@ redef class MNullType
end
redef class MSignature
- redef fun linkto
+ redef fun linkto(v)
do
var res = new HTMLTag("span")
var first = true
@@ -613,14 +624,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
@@ -630,21 +641,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
@@ -659,13 +668,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
@@ -687,6 +692,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
@@ -737,7 +762,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)
@@ -757,8 +782,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
@@ -767,6 +790,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")
@@ -777,6 +801,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")
@@ -800,6 +825,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
@@ -812,6 +838,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
@@ -820,6 +847,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
@@ -828,6 +856,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
@@ -836,6 +865,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
@@ -856,13 +886,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
@@ -876,6 +909,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
@@ -885,6 +919,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
@@ -898,8 +933,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
@@ -929,8 +962,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
@@ -938,6 +969,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)
@@ -947,8 +979,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
@@ -956,6 +986,7 @@ 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.undecorate
@@ -968,7 +999,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
@@ -976,6 +1009,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
@@ -1005,8 +1039,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
@@ -1024,7 +1056,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