model: use the robust `intro_mmodule` instead of `intro.mmodule`.
[nit.git] / src / highlight.nit
index cd400bd..b2f856e 100644 (file)
 # 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 `<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
@@ -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.
@@ -167,9 +194,9 @@ class HighlightVisitor
                return """
 .nitcode a { color: inherit; cursor:pointer; }
 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
-pre.nitcode .foldable { display: block } /* for block productions*/
-pre.nitcode .line{ display: block } /* for lines */
-pre.nitcode .line:hover{ background-color: #FFFFE0; } /* current line */
+.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 */
@@ -322,6 +349,7 @@ redef class MModule
                return res
        end
 
+       # The module HTML page
        fun href: String
        do
                return name + ".html"
@@ -345,12 +373,14 @@ redef class MClassDef
                        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("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)
 
+               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
@@ -377,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
@@ -397,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
@@ -423,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
@@ -474,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
@@ -498,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
@@ -553,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}")
@@ -598,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
@@ -857,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)
@@ -934,4 +993,3 @@ redef class AExpr
                return t.infobox(v)
        end
 end
-