nitdoc: add --no-dot to skip graph generation
[nit.git] / src / nitdoc.nit
index a0b7a44..23cf6b8 100644 (file)
@@ -83,6 +83,7 @@ class DocContext
        readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
        readable var _opt_source: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
        readable var _opt_public: OptionBool = new OptionBool("Generate only the public API", "--public")
+       readable var _opt_nodot: OptionBool = new OptionBool("Do not generate graphes with graphviz", "--no-dot")
 
        fun public_only: Bool
        do
@@ -243,11 +244,11 @@ class DocContext
        do
                var s = opt_source.value
                if s == null then
-                       add("in #{l.file.filename}")
+                       add("in #{l.file.filename.simplify_path}")
                else
                        # THIS IS JUST UGLY ! (but there is no replace yet)
                        var x = s.split_with("%f")
-                       s = x.join(l.file.filename)
+                       s = x.join(l.file.filename.simplify_path)
                        x = s.split_with("%l")
                        s = x.join(l.line_start.to_s)
                        x = s.split_with("%L")
@@ -261,6 +262,7 @@ class DocContext
        # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...)
        fun gen_dot(dot: String,  name: String, alt: String)
        do
+               if opt_nodot.value then return
                var f = new OFStream.open("{self.dir}/{name}.dot")
                f.write(dot)
                f.close
@@ -279,6 +281,7 @@ class DocContext
                option_context.add_option(opt_public)
                option_context.add_option(opt_dir)
                option_context.add_option(opt_source)
+               option_context.add_option(opt_nodot)
        end
 
        redef fun process_options
@@ -286,6 +289,15 @@ class DocContext
                super
                var d = opt_dir.value
                if d != null then dir = d
+
+               if not opt_nodot.value then
+                       # Test if dot is runable
+                       var res = sys.system("sh -c dot </dev/null >/dev/null 2>&1")
+                       if res != 0 then
+                               stderr.write "--no-dot implied since `dot' is not available. Try to install graphviz.\n"
+                               opt_nodot.value = true
+                       end
+               end
        end
 
        redef fun handle_property_conflict(lc, impls)
@@ -298,6 +310,42 @@ class DocContext
        end
 end
 
+redef class String
+       # Replace all occurence of pattern ith string
+       fun replace(p: Pattern, string: String): String
+       do
+               return self.split_with(p).join(string)
+       end
+
+       # Escape the following characters < > & and " with their html counterpart
+       fun html_escape: String
+       do
+               var ret = self
+               if ret.has('&') then ret = ret.replace('&', "&amp;")
+               if ret.has('<') then ret = ret.replace('<', "&lt;")
+               if ret.has('>') then ret = ret.replace('>', "&gt;")
+               if ret.has('"') then ret = ret.replace('"', "&quot;")
+               return ret
+       end
+
+       # Remove "/./", "//" and "bla/../"
+       fun simplify_path: String
+       do
+               var a = self.split_with("/")
+               var a2 = new Array[String]
+               for x in a do
+                       if x == "." then continue
+                       if x == "" and not a2.is_empty then continue
+                       if x == ".." and not a2.is_empty then
+                               a2.pop
+                               continue
+                       end
+                       a2.push(x)
+               end
+               return a2.join("/")
+       end
+end
+
 # A virtual module is used to work as an implicit main module that combine unrelated modules
 # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled
 class MMVirtualModule
@@ -383,7 +431,7 @@ end
 redef class MMModule
        super MMEntity
        redef fun html_link(dctx) do 
-               return "<a href=\"{html_name}.html\">{self}</a>"
+               return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
        end
 
        fun require_doc(dctx: DocContext): Bool
@@ -630,16 +678,16 @@ redef class MMModule
                        var lpi = self[gp.intro.local_class.global][gp]
                
                        if lps.has(lpi) then
-                               dctx.add("<li class='intro'><span title='introduction in an other module'>I</span>&nbsp;<a href=\"{lpi.local_class.html_name}.html#{lpi.html_anchor}\">{lpi}&nbsp;({lpi.local_class})</a></li>\n")
+                               dctx.add("<li class='intro'><span title='introduction in an other module'>I</span>&nbsp;{lpi.html_open_link(dctx)}{lpi.html_name}&nbsp;({lpi.local_class})</a></li>\n")
                                lps.remove(lpi)
                        else
-                               dctx.add("<li class='intro'><span title='introduction in this module'>I</span>&nbsp;{lpi}")
+                               dctx.add("<li class='intro'><span title='introduction in this module'>I</span>&nbsp;{lpi.html_name}")
                                dctx.add("&nbsp;({lpi.local_class})</li>\n")
                        end
                        if lps.length >= 1 then
                                dctx.sort(lps)
                                for lp in lps do
-                                       dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;<a href=\"{lp.local_class.html_name}.html#{lp.html_anchor}\">{lp}&nbsp;({lp.local_class})</a></li>")
+                                       dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;{lp.html_open_link(dctx)}{lp.html_name}&nbsp;({lp.local_class})</a></li>")
                                end
                        end
                end
@@ -716,11 +764,11 @@ redef class MMModule
                        var lpi = self[gp.intro.local_class.global][gp]
                        
                        lps.remove(lpi)
-                               dctx.add("<li class='intro'><span title='introduction'>I</span>&nbsp;<a href=\"{lpi.local_class.html_name}.html#{lpi.html_anchor}\">{lpi}&nbsp;({lpi.local_class})</a></li>\n")
+                               dctx.add("<li class='intro'><span title='introduction'>I</span>&nbsp;{lpi.html_open_link(dctx)}{lpi.html_name}&nbsp;({lpi.local_class})</a></li>\n")
                        if lps.length >= 1 then
                                dctx.sort(lps)
                                for lp in lps do
-                                       dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;<a href=\"{lp.local_class.html_name}.html#{lp.html_anchor}\">{lp}&nbsp;({lp.local_class})</a></li>\n")
+                                       dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;{lp.html_open_link(dctx)}{lp.html_name}&nbsp;({lp.local_class})</a></li>\n")
                                end
                        end
                end
@@ -737,16 +785,39 @@ redef class MMLocalProperty
                return "PROP_{local_class}_{cmangle(name)}"
        end
 
+       fun html_open_link(dctx: DocContext): String
+       do
+               if not require_doc(dctx) then print "not required {self}"
+               var title = "{html_name}{signature.to_s}"
+               if short_doc != "&nbsp;" then
+                       title += " #{short_doc}"
+               end
+               return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">"
+       end
+
+       fun html_name: String
+       do
+               return self.name.to_s.html_escape
+       end
+
        redef fun html_link(dctx)
        do
                if not require_doc(dctx) then print "not required {self}"
-               return "<a href=\"{local_class.html_name}.html#{html_anchor}\">{self}</a>"
+               var title = "{html_name}{signature.to_s}"
+               if short_doc != "&nbsp;" then
+                       title += " #{short_doc}"
+               end
+               return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
        end
 
        fun html_link_special(dctx: DocContext, lc: MMLocalClass): String
        do
                if not require_doc(dctx) then print "not required {self}"
-               return "<a href=\"{lc.html_name}.html#{html_anchor}\">{self}</a>"
+               var title = "{html_name}{signature_for(lc.get_type)}"
+               if short_doc != "&nbsp;" then
+                       title += " #{short_doc}"
+               end
+               return "<a href=\"{lc.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
        end
 
        # Kind of property (fun, attr, etc.)
@@ -829,7 +900,7 @@ redef class MMLocalProperty
                var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner
 
                dctx.add("<article id=\"{html_anchor}\" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\">\n")
-               dctx.add("<h3 class=\"signature\">{name}{signature.to_html(dctx, true)}</h3>\n")
+               dctx.add("<h3 class=\"signature\">{html_name}{signature.to_html(dctx, true)}</h3>\n")
                dctx.add("<div class=\"info\">\n")
                #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
 
@@ -854,7 +925,7 @@ redef class MMLocalProperty
                if is_redef then
                        dctx.add("::{mmmodule[intro_class.global][global].html_link(dctx)}")
                else
-                       dctx.add("::{name}")
+                       dctx.add("::{html_name}")
                end
                dctx.add("</div>")
 
@@ -962,42 +1033,6 @@ redef class MMTypeProperty
        redef fun kind do return "type"
 end
 
-redef class Symbol
-       # Replace < and > with html entities
-       redef fun to_s
-       do
-               var ret = super.to_s
-
-               if(ret.has('<')) then
-                       var parts = ret.split_with("<")
-                       ret = ""
-
-                       for i in [0..parts.length[ do
-                               ret += parts[i]
-
-                               if(i < parts.length - 1) then
-                                       ret += "&lt;"
-                               end
-                       end
-               end
-
-               if(ret.has('>')) then
-                       var parts = ret.split_with(">")
-                       ret = ""
-
-                       for i in [0..parts.length[ do
-                               ret += parts[i]
-
-                               if(i < parts.length - 1) then
-                                       ret += "&gt;"
-                               end
-                       end
-               end
-               
-               return ret
-       end
-end
-
 redef class MMSrcModule
        redef fun short_doc
        do
@@ -1036,13 +1071,13 @@ redef class ADoc
                for c in n_comment do
                        res.append(c.text.substring_from(1))
                end
-               return res.to_s
+               return res.to_s.html_escape
        end
 
        # Oneliner transcription of the doc
        fun short: String
        do
-               return n_comment.first.text.substring_from(1)
+               return n_comment.first.text.substring_from(1).html_escape
        end
 end
 
@@ -1057,7 +1092,7 @@ redef class MMLocalClass
        redef fun html_link(dctx)
        do
                if not require_doc(dctx) then print "{dctx.filename}: not required {self}"
-               return "<a href=\"{html_name}.html\">{self}</a>"
+               return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
        end
 
        redef fun short_doc do return global.intro.short_doc