# 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
# Is 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
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
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
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
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
+ #assert stack.is_empty
+ #assert stack2.is_empty
end
# Return a default CSS content related to CSS classes used in the `html` tree.
.popover { max-width: 800px !important; }
"""
end
+
+ # Additional content to inject in the <head> tag
+ # Note: does not include `css_content`; handle it yourself.
+ fun head_content: String
+ do
+ return """<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">\n"""
+ end
+
+ # Additional content to inject just before the closing </body> tag
+ fun foot_content: String
+ do
+ return """
+<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
+<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
+<script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>"""
+ end
end
redef class HTMLTag
return res
end
+ # The module HTML page
fun href: String
do
return name + ".html"
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
return res
end
+ # The class HTML page (an anchor in the module page)
fun href: String
do
return mmodule.href + "#" + to_s
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
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
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
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
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}")
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 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
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
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)
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
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)
return t.infobox(v)
end
end
-