X-Git-Url: http://nitlanguage.org diff --git a/src/highlight.nit b/src/highlight.nit index 5430321..00ed2bf 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -12,10 +12,9 @@ # 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 @@ -28,19 +27,29 @@ class HighlightVisitor # 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 + 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 init do html.add_class("nitcode") end + # The entry-point of the highlighting. + # Will fill `html` with the generated HTML content. fun enter_visit(n: ANode) do n.parentize_tokens @@ -48,12 +57,42 @@ class HighlightVisitor htmlize(s.first_token.as(not null), s.last_token.as(not null)) 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 + 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 + end + end + var messages = anode.location.messages + if messages != null 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 + # Produce HTML between two tokens protected fun htmlize(first_token, last_token: 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 @@ -71,11 +110,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, 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 @@ -84,7 +121,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 +137,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, 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 @@ -113,17 +149,8 @@ class HighlightVisitor 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 @@ -156,8 +183,8 @@ class HighlightVisitor c = n end - assert stack.is_empty - assert stack2.is_empty + #assert stack.is_empty + #assert stack2.is_empty end # Return a default CSS content related to CSS classes used in the `html` tree. @@ -200,6 +227,22 @@ class HighlightVisitor .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 HTMLTag @@ -306,6 +349,7 @@ redef class MModule return res end + # The module HTML page fun href: String do return name + ".html" @@ -335,6 +379,8 @@ redef class MClassDef if mdoc == null then mdoc = mclass.intro.mdoc if mdoc != null then mdoc.fill_infobox(res) + 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 @@ -361,6 +407,7 @@ redef class MClassDef return res end + # The class HTML page (an anchor in the module page) fun href: String do return mmodule.href + "#" + to_s @@ -381,11 +428,11 @@ redef class MPropDef var res = new HInfoBox(v, to_s) res.href = href if self isa MMethodDef then - res.new_field("fun").append(mproperty.name).add msignature.linkto + if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto else if self isa MAttributeDef then - res.new_field("fun").append(mproperty.name).add static_mtype.linkto + if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto else if self isa MVirtualTypeDef then - res.new_field("add").append(mproperty.name).add bound.linkto + if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto else res.new_field("wat?").append(mproperty.name) end @@ -407,6 +454,7 @@ redef class MPropDef 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 @@ -458,13 +506,11 @@ 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 return res end redef fun linkto do - var name = mclass.intro.parameter_names[rank] return (new HTMLTag("span")).text(name) end end @@ -482,6 +528,33 @@ redef class MNullableType end end +redef class MNotNullType + redef fun infobox(v) + do + return mtype.infobox(v) + end + redef fun linkto + do + var res = new HTMLTag("span") + res.append("not null ").add(mtype.linkto) + 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 + do + var res = new HTMLTag("span") + res.append("null") + return res + end +end + redef class MSignature redef fun linkto do @@ -537,6 +610,7 @@ 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}") @@ -582,6 +656,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 @@ -650,9 +725,9 @@ end redef class AVarFormExpr redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") var variable = self.variable if variable == null then return null + res.add_class("nc_v") return variable.infobox(v) end end @@ -660,9 +735,9 @@ end redef class AVardeclExpr redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") var variable = self.variable if variable == null then return null + res.add_class("nc_v") return variable.infobox(v) end end @@ -671,9 +746,9 @@ redef class AForExpr redef fun decorate_tag(v, res, token) do if not token isa TId then return null - res.add_class("nc_v") var vs = variables if vs == null then return null + res.add_class("nc_v") var idx = n_ids.index_of(token) var variable = vs[idx] return variable.infobox(v) @@ -683,11 +758,11 @@ end redef class AParam redef fun decorate_tag(v, res, token) do - res.add_class("nc_v") var mp = mparameter 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 @@ -841,8 +916,8 @@ redef class AType do 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) @@ -918,4 +993,3 @@ redef class AExpr return t.infobox(v) end end -