X-Git-Url: http://nitlanguage.org?ds=sidebyside diff --git a/src/nitdoc.nit b/src/nitdoc.nit index 7777177..9196911 100644 --- a/src/nitdoc.nit +++ b/src/nitdoc.nit @@ -83,6 +83,15 @@ 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_private: OptionBool = new OptionBool("Generate the private API", "--private") + readable var _opt_nodot: OptionBool = new OptionBool("Do not generate graphes with graphviz", "--no-dot") + readable var _opt_sharedir: OptionString = new OptionString("Directory containing the nitdoc files", "--sharedir") + + readable var _opt_custom_menu_items: OptionString = new OptionString("Items displayed in menu before the 'Overview' item (Each item must be enclosed in 'li' tags)", "--custom-menu-items") + readable var _opt_custom_title: OptionString = new OptionString("Title displayed in the top of the Overview page and as suffix of all page names", "--custom-title") + readable var _opt_custom_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text") + readable var _opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text") + var sharedir: nullable String fun public_only: Bool do @@ -90,6 +99,12 @@ class DocContext return false end + fun with_private: Bool + do + if self._opt_private.value == true then return true + return false + end + # The current processed filename var filename: String @@ -102,6 +117,8 @@ class DocContext dir.mkdir + sys.system("cp -r '{sharedir.to_s}'/* {dir}/") + # Compute the set of direct owned nested modules var owns = new HashMap[MMModule, Array[MMModule]] for mod in modules do @@ -142,20 +159,35 @@ class DocContext m.tmhe_ = tmh.add(m, pub) end - var head = "\n" + - "\n" + - "" + var head = "" + + "\n" + + "\n" + + "\n" + + "" + + var custom_items = "" + if self._opt_custom_menu_items.value != null then custom_items = self._opt_custom_menu_items.value.as(not null) - var action_bar = "
\n" + var action_bar = "
\n" + + var custom_title = "Nitdoc" + if self._opt_custom_title.value != null then custom_title = self._opt_custom_title.value.as(not null) + + var overview_text = "" + if self._opt_custom_overview_text.value != null then overview_text = self._opt_custom_overview_text.value.as(not null) + var footer_text = "" + if self._opt_custom_footer_text.value != null then footer_text = self._opt_custom_footer_text.value.as(not null) + # generate the index self.filename = "index.html" clear - add("{head}\n") + add("") + add("{head}Overview | {custom_title}\n") add(action_bar) add("
") add("
") - add("

Modules

\n
    ") + add("

    {custom_title}

    \n
    {overview_text}

    Modules

      ") var modss = mainmod.mhe.greaters_and_self.to_a sort(modss) for mod in modss do @@ -179,10 +211,10 @@ class DocContext end end op.append("\}\n") - self.gen_dot(op.to_s, "dep") + self.gen_dot(op.to_s, "dep", "Modules hierarchy") add("
") - add("
") add("
") + add("") add("\n") write_to("{dir}/index.html") @@ -192,12 +224,15 @@ class DocContext assert mod isa MMSrcModule if not mod.require_doc(self) then continue self.filename = mod.html_name + action_bar = "
\n" clear - add("{head}Module {mod.name}\n") + add("") + add("{head}{mod.name} module | {custom_title}\n") add(action_bar) add("
") mod.file_page_doc(self) add("
") + add("") add("\n") write_to("{dir}/{mod.html_name}.html") end @@ -206,27 +241,38 @@ class DocContext for c in mainmod.local_classes do if not c.require_doc(self) then continue self.filename = c.html_name + action_bar = "
\n" clear - add("{head}Class {c.name}\n") + add("") + add("{head}{c.name} class | {custom_title}\n") add(action_bar) add("
") c.file_page_doc(self) add("
") + add("") add("\n") write_to("{dir}/{c.html_name}.html") end self.filename = "fullindex" + action_bar = "
\n" clear - add("{head}\n") + add("") + add("{head}Full Index | {custom_title}\n") add(action_bar) add("
") add("
") mainmod.file_index_page_doc(self) add("
") add("
") + add("") add("\n") write_to("{dir}/full-index.html") + + self.filename = "quicksearch-list" + clear + mainmod.file_quicksearch_list_doc(self) + write_to("{dir}/quicksearch-list.js") end @@ -235,11 +281,11 @@ class DocContext do var s = opt_source.value if s == null then - add("in #{l.file.filename}") + add("({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") @@ -251,13 +297,14 @@ class DocContext # Generate a clicable graphiz image using a dot content. # `name' refer to the filename (without extension) and the id name of the map. # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...) - fun gen_dot(dot: String, name: String) + 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 sys.system("\{ test -f {self.dir}/{name}.png && test -f {self.dir}/{name}.s.dot && diff {self.dir}/{name}.dot {self.dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {self.dir}/{name}.dot {self.dir}/{name}.s.dot && dot -Tpng -o{self.dir}/{name}.png -Tcmapx -o{self.dir}/{name}.map {self.dir}/{name}.s.dot ; \}") - self.add("
") + self.add("
\"{alt}\"/
") var fmap = new IFStream.open("{self.dir}/{name}.map") self.add(fmap.read_all) fmap.close @@ -269,8 +316,15 @@ class DocContext super("nitdoc") filename = "-unset-" option_context.add_option(opt_public) + option_context.add_option(opt_private) option_context.add_option(opt_dir) option_context.add_option(opt_source) + option_context.add_option(opt_nodot) + option_context.add_option(opt_sharedir) + option_context.add_option(opt_custom_title) + option_context.add_option(opt_custom_menu_items) + option_context.add_option(opt_custom_overview_text) + option_context.add_option(opt_custom_footer_text) end redef fun process_options @@ -278,6 +332,35 @@ 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 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 + + sharedir = opt_sharedir.value + if sharedir == null then + var dir = "NIT_DIR".environ + if dir.is_empty then + dir = "{sys.program_name.dirname}/../share/nitdoc" + if dir.file_exists then sharedir = dir + else + dir = "{dir}/share/nitdoc" + if dir.file_exists then sharedir = dir + end + if sharedir == null then + fatal_error(null, "Error: Cannot locate nitdoc shared files. Uses --sharedir or envvar NIT_DIR.") + end + dir = "{sharedir.to_s}/scripts/js-facilities.js" + if sharedir == null then + fatal_error(null, "Error: Invalid nitdoc shared files. Check --sharedir or envvar NIT_DIR.") + end + + end end redef fun handle_property_conflict(lc, impls) @@ -360,7 +443,7 @@ class AlphaSorter[E: Object] end # Generalization of metamodel entities -class MMEntity +interface MMEntity # Return a link to fun html_link(dctx: DocContext): String is abstract @@ -370,12 +453,44 @@ class MMEntity # The doc node from the AST # Return null is none fun doc: nullable ADoc do return null + + # Return a JSON entry for quicksearch list + fun json_entry(dctx: DocContext): String is abstract + + # Return the qualified name as string + fun qualified_name: String is abstract + end redef class MMModule super MMEntity redef fun html_link(dctx) do - return "{self}" + if short_doc == " " then + return "{self}" + else + return "{self}" + end + end + + fun html_anchor: String do + return "" + end + + fun html_link_to_anchor: String do + return "{self}" + end + + redef fun json_entry(dctx) do + return "\{txt:\"{self.qualified_name}\",url:\"{html_name}.html\"\}," + end + + redef fun qualified_name do + var buffer = new Buffer + for m in mnhe.smallers do + buffer.append("{m.html_name}::") + end + buffer.append("{self.name}") + return buffer.to_s end fun require_doc(dctx: DocContext): Bool @@ -552,7 +667,7 @@ redef class MMModule end end op.append("\}\n") - dctx.gen_dot(op.to_s, name.to_s) + dctx.gen_dot(op.to_s, name.to_s, "Dependency graph for module {name}") dctx.add("") var clas = new Array[MMLocalClass] @@ -573,6 +688,7 @@ redef class MMModule end if not keep then continue clas.add(self[g]) + lc.compute_super_classes for gp in lc.global_properties do if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import var lp = lc[gp] @@ -622,24 +738,22 @@ redef class MMModule var lpi = self[gp.intro.local_class.global][gp] if lps.has(lpi) then - dctx.add("
  • I {lpi} ({lpi.local_class})
  • \n") + dctx.add("
  • I {lpi.html_open_link(dctx)}{lpi.html_name} ({lpi.local_class})
  • \n") lps.remove(lpi) else - dctx.add("
  • I {lpi}") + dctx.add("
  • I {lpi.html_name}") dctx.add(" ({lpi.local_class})
  • \n") end if lps.length >= 1 then dctx.sort(lps) for lp in lps do - dctx.add("
  • R {lp} ({lp.local_class})
  • ") + dctx.add("
  • R {lp.html_open_link(dctx)}{lp.html_name} ({lp.local_class})
  • ") end end end dctx.stage("\n") dctx.close_stage - - - dctx.add("\n") + dctx.add("\n") dctx.add("\n") end @@ -710,37 +824,127 @@ redef class MMModule var lpi = self[gp.intro.local_class.global][gp] lps.remove(lpi) - dctx.add("
  • I {lpi} ({lpi.local_class})
  • \n") + dctx.add("
  • I {lpi.html_open_link(dctx)}{lpi.html_name} ({lpi.local_class})
  • \n") if lps.length >= 1 then dctx.sort(lps) for lp in lps do - dctx.add("
  • R {lp} ({lp.local_class})
  • \n") + dctx.add("
  • R {lp.html_open_link(dctx)}{lp.html_name} ({lp.local_class})
  • \n") end end end dctx.stage("\n") dctx.close_stage end + + # Fill the quicksearch list JSON object + fun file_quicksearch_list_doc(dctx: DocContext) + do + var entities = new HashMap[String, Array[MMEntity]] + var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]] + for m in mhe.greaters_and_self do + if not m.require_doc(dctx) then continue + var a = new Array[MMEntity] + a.add(m) + entities[m.html_name] = a + end + for g in global_classes do + var lc = self[g] + if not lc.require_doc(dctx) then continue + var a = new Array[MMEntity] + a.add(lc) + entities[lc.html_name] = a + for gp in lc.global_properties do + var lp = lc[gp] + if not lp.require_doc(dctx) then continue + if lp.kind == "var" then continue + if props.has_key(lp.global) then + if not props[lp.global].has(lp) then + props[lp.global].add(lp) + end + else + props[lp.global] = [lp] + end + end + end + + for k, v in props do + entities[k.short_name] = v + end + + var keys = entities.keys.to_a + var sorter = new AlphaSorter[String] + sorter.sort(keys) + + dctx.open_stage + dctx.stage("var entries = \{") + for key in keys do + dctx.add("\"{key}\": [") + for entity in entities[key] do + dctx.add(entity.json_entry(dctx)) + end + dctx.add("],") + end + dctx.stage("\};") + dctx.close_stage + end end +redef class MMGlobalProperty + # Return the short name of the property + fun short_name: String do + return self.intro.html_name + end +end + redef class MMLocalProperty super MMEntity # Anchor of the property description in the module html file fun html_anchor: String do - return "PROP_{local_class}_{cmangle(name)}" + return "PROP_{self.mmmodule.toplevel_owner}_{local_class}_{cmangle(name)}" + end + + redef fun json_entry(dctx) do + return "\{txt:\"{qualified_name}\",url:\"{local_class.html_name}.html#{html_anchor}\"\}," + end + + redef fun qualified_name do + return "{intro_module.qualified_name}::{local_class.html_name}::{html_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 != " " then + title += " #{short_doc}" + end + return "" + 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 "{self}" + var title = "{html_name}{signature.to_s}" + if short_doc != " " then + title += " #{short_doc}" + end + return "{html_name}" end fun html_link_special(dctx: DocContext, lc: MMLocalClass): String do if not require_doc(dctx) then print "not required {self}" - return "{self}" + var title = "{html_name}{signature_for(lc.get_type)}" + if short_doc != " " then + title += " #{short_doc}" + end + return "{html_name}" end # Kind of property (fun, attr, etc.) @@ -794,10 +998,10 @@ redef class MMLocalProperty return m == m.toplevel_owner end - # Return true if the global class must be documented according to the visibility configured + # Return true if the global property must be documented according to the visibility configured fun require_doc(dctx: DocContext): Bool do - if global.visibility_level == 3 then return false # Private + if global.visibility_level == 3 and not dctx.with_private then return false # Private if dctx.public_only then var m = intro_module if m != m.toplevel_owner then return false # Unexported @@ -823,7 +1027,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("
    \n") - dctx.add("

    {name}{signature.to_html(dctx, true)}

    \n") + dctx.add("

    {html_name}{signature.to_html(dctx, true)}

    \n") dctx.add("
    \n") #dctx.add("

    LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}

    ") @@ -846,9 +1050,9 @@ redef class MMLocalProperty dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}") end if is_redef then - dctx.add("::{mmmodule[intro_class.global][global].html_link(dctx)}") + dctx.add("::{mmmodule[intro_class.global][global].global.intro.html_link(dctx)}") else - dctx.add("::{name}") + dctx.add("::{html_name}") end dctx.add("
    ") @@ -925,7 +1129,7 @@ redef class MMLocalProperty if lp == tlp then continue dctx.add(" {lp.mmmodule.html_link(dctx)}") if lp.local_class.global != lc.global then - dctx.add(" for {lp.local_class.html_link(dctx)}") + dctx.add(" for {lp.local_class.html_link(dctx)} ") end n = lp.node @@ -994,13 +1198,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 @@ -1015,7 +1219,19 @@ redef class MMLocalClass redef fun html_link(dctx) do if not require_doc(dctx) then print "{dctx.filename}: not required {self}" - return "{self}" + if short_doc == " " then + return "{self}" + else + return "{self}" + end + end + + redef fun json_entry(dctx) do + return "\{txt:\"{qualified_name}\",url:\"{html_name}.html\"\}," + end + + redef fun qualified_name do + return "{intro_module.qualified_name}::{html_name}" end redef fun short_doc do return global.intro.short_doc @@ -1066,7 +1282,7 @@ redef class MMLocalClass # Return true if the global class must be documented according to the visibility configured fun require_doc(dctx: DocContext): Bool do - if global.visibility_level == 3 then return false # Private + if global.visibility_level == 3 and not dctx.with_private then return false # Private if dctx.public_only then var m = intro_module if m != m.toplevel_owner then return false # Unexported @@ -1133,7 +1349,7 @@ redef class MMLocalClass dctx.add("