X-Git-Url: http://nitlanguage.org diff --git a/src/nitdoc.nit b/src/nitdoc.nit index a0cf48c..bdaa346 100644 --- a/src/nitdoc.nit +++ b/src/nitdoc.nit @@ -24,10 +24,11 @@ import abstracttool # Store knowledge and facilities to generate files class DocContext -special AbstractCompiler + super AbstractCompiler # Destination directory - readable writable var _dir: String = "." - + readable writable var _dir: String = "doc" + # GitHub Repo name + var github_repo: nullable String = null # Content of a generated file var _stage_context: StageContext = new StageContext(null) @@ -65,191 +66,302 @@ special AbstractCompiler f.close end - # Currently computed module - readable var _module: nullable MMSrcModule - - # Is the current directory module computed as a simple modude ? - readable writable var _inside_mode: Bool = false + # Start a new file + fun clear + do + _stage_context = new StageContext(null) + end - # Is the current module computed as a intruded one ? - readable writable var _intrude_mode: Bool = false + # Sorter of entities in alphabetical order + var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity] - # Compued introducing entities (for the index) - var _entities: Array[MMEntity] = new Array[MMEntity] + # Sort entities in the alphabetical order + fun sort(array: Array[MMEntity]) + do + _sorter.sort(array) + end - # Register an entity (for the index) - fun register(e: MMEntity) + 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") + readable var _opt_github_repo_name: OptionString = new OptionString("GitHub repo name, example: --github MyRepoName", "--github") + var sharedir: nullable String + + fun public_only: Bool do - _entities.add(e) - if e isa MMSrcModule then - _module = e - end + if self._opt_public.value == true then return true + return false end - # Start a new file - fun clear + fun with_private: Bool do - _stage_context = new StageContext(null) + if self._opt_private.value == true then return true + return false end - # Generate common files (frames, index, overview) - fun extract_other_doc + # The current processed filename + var filename: String + + # The main virtual module + var mainmod: nullable MMVirtualModule + + redef fun perform_work(mods) do - info("Generating other files",1) - _module = null - inside_mode = false - intrude_mode = false - clear - add("\n") - add("Overview
\n") - add("Index
\n") - var modules = modules.to_a - sort(modules) - - var rootdirs = new Array[MMDirectory] - for m in modules do - var md = m.directory - if md.parent == null and not rootdirs.has(md) then - rootdirs.add(md) + mainmod = new MMVirtualModule(self, mods) + + 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 + owns[mod] = new Array[MMModule]# [mod] + end + for mod in modules do + if mod == mainmod then continue + var d = mod.directory + loop + var o = d.owner + if o != null and o != mod then + owns[o].add(mod) + end + var dp = d.parent + if dp == null or dp == d then break + d = dp end end - var done = new Array[MMModule] - for root in rootdirs do - var dirstack = [root] - var curdir = root - add("{root.name}
\n") - var indent = "  " - while not dirstack.is_empty do - var redo = false - for m in modules do - if done.has(m) then continue - var md = m.directory - if md.owner == m and md.parent == curdir then - # It's a directory module - add("{indent}{m}
\n") - curdir = md - dirstack.push(curdir) - indent = "  " * dirstack.length - redo = true - break # restart to preserve alphabetic order - else if md == curdir then - if md.owner == m then - add("{indent}{m}
\n") - else - add("{indent}{m}
\n") - end - done.add(m) - redo = true - end - end - if not redo then - dirstack.pop - if not dirstack.is_empty then - curdir = dirstack[dirstack.length-1] - indent = "  " * dirstack.length - end + # Builds the various module hierarchies + var mnh = new PartialOrder[MMModule] # nested module hierarchy + var tmh = new PartialOrder[MMModule] # top module import hierrchy + var ms = mainmod.mhe.linear_extension.reversed + for m in ms do + if ms == mainmod then continue + m.mnhe_ = mnh.add(m, owns[m]) + var pub = new Array[MMModule] + for m2 in m.mhe.greaters do + if m2.toplevel_owner != m2 and m2.toplevel_owner != m.toplevel_owner then continue + if m.mnhe <= m2 then continue + if m.visibility_for(m2) <= 0 then + # nothing + else if m.visibility_for(m2) == 1 then + else + pub.add(m2) end end + m.tmhe_ = tmh.add(m, pub) end - add("\n") - write_to("{dir}/menu-frame.html") + 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 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_header("Index") - add("
\n") - sort(_entities) - for e in _entities do - add("
{e.html_link(self)} - {e.prototype_head(self)} {e}{e.prototype_body(self)} {e.locate(self)}
{e.short_doc}\n") + add("") + add("{head}Overview | {custom_title}\n") + add(action_bar) + add("
") + add("
") + add("

{custom_title}

\n
{overview_text}

Modules

    ") + var modss = mainmod.mhe.greaters_and_self.to_a + sort(modss) + for mod in modss do + if not mod.is_toplevel then continue + if not mod.require_doc(self) then continue + assert mod isa MMSrcModule + add("
  • {mod.html_link(self)} {mod.short_doc}
  • ") + end - add("
\n") - write_to("{dir}/index-1.html") + add("") - clear - add_header("Overview") - add("\n") - add("\n") - for m in modules do - add("\n") + var op = new Buffer + op.append("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") + for mod in modss do + if not mod.is_toplevel then continue + if not mod.require_doc(self) then continue + op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n") + for mod2 in mod.tmhe.direct_greaters do + if not modss.has(mod2) then continue + op.append("\"{mod.name}\"->\"{mod2.name}\";\n") + end end - add("
Overview of all Modules
{m.html_link(self)}{m.short_doc}
\n") - write_to("{dir}/overview.html") + op.append("\}\n") + self.gen_dot(op.to_s, "dep", "Modules hierarchy") + add("") + add("") + add("") + addGithubInformation + addCommitForm + add("\n") + write_to("{dir}/index.html") + # Generate page for modules + for mod in modules do + if mod == mainmod then continue + assert mod isa MMSrcModule + if not mod.require_doc(self) then continue + self.filename = mod.html_name + action_bar = "
\n" + clear + add("") + add("{head}{mod.name} module | {custom_title}\n") + add(action_bar) + add("
") + mod.file_page_doc(self) + add("
") + add("") + addGithubInformation + addCommitForm + add("\n") + write_to("{dir}/{mod.html_name}.html") + end + + # Generate pages for global classes + 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("") + add("{head}{c.name} class | {custom_title}\n") + add(action_bar) + add("
") + c.file_page_doc(self) + add("
") + add("") + addGithubInformation + addCommitForm + add("\n") + write_to("{dir}/{c.html_name}.html") + end + + self.filename = "fullindex" + action_bar = "
\n" clear - add("\n\n\n\n\n") - write_to("{dir}/index.html") - end + add("") + add("{head}Full Index | {custom_title}\n") + add(action_bar) + add("
") + add("
") + mainmod.file_index_page_doc(self) + add("
") + add("
") + add("") + addGithubInformation + addCommitForm + add("\n") + write_to("{dir}/full-index.html") - fun add_header(title: String) - do - add("{title}\n\n") - add("
\n") - add("Overview  Index  With Frames\n") - add("
") - add("Visibility: ") - if (not inside_mode and not intrude_mode) or module == null then - add("Public  ") - else - add("Public  ") - end - if inside_mode or module == null then - add("Inside  ") - else if module.directory.owner != module then - add("Inside  ") - else - add("Inside  ") - end - if intrude_mode or module == null then - add("Intrude  ") - else - add("Intrude  ") - end - add("
") + self.filename = "quicksearch-list" + clear + mainmod.file_quicksearch_list_doc(self) + write_to("{dir}/quicksearch-list.js") + end + + # Add or not a tag for the github repository + fun addGithubInformation do + if not github_repo == null then add("
") end - # Sorter of entities in alphabetical order - var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity] + # Add all tags for the commit form + fun addCommitForm do + add("
Signed-Off
Commit

YesNo
\n\n") + end - # Sort entities in the alphabetical order - fun sort(array: Array[MMEntity]) + # Add a (source) link fo a given location + fun show_source(l: Location) do - _sorter.sort(array) + var s = opt_source.value + if s == null then + 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.simplify_path) + x = s.split_with("%l") + s = x.join(l.line_start.to_s) + x = s.split_with("%L") + s = x.join(l.line_end.to_s) + add(" (show code)") + end end - readable writable var _owned_modules: Array[MMModule] = new Array[MMModule] - - # Return the known_owner for current module - # if inside_mode is set, it could be a different result - fun known_owner_of(m: MMModule): MMModule + # Return source link for a given location + fun get_source(l: Location): String do - var module = module - if module == null then return m - var res = module.known_owner_of(m) - if not inside_mode and not intrude_mode and res.directory.owner == module then - return module + var s = opt_source.value + if s == null then + return l.file.filename.simplify_path else - return res + # THIS IS JUST UGLY ! (but there is no replace yet) + var x = s.split_with("%f") + 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") + s = x.join(l.line_end.to_s) + return s end end - readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir") - - redef fun perform_work(mods) + # 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, alt: String) do - dir.mkdir - - for mod in modules do - assert mod isa MMSrcModule - mod.extract_module_doc(self) - end - self.extract_other_doc + 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("
\"{alt}\"/
") + var fmap = new IFStream.open("{self.dir}/{name}.map") + self.add(fmap.read_all) + fmap.close end init do keep_ast = true 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) + option_context.add_option(opt_github_repo_name) end redef fun process_options @@ -257,7 +369,72 @@ special AbstractCompiler 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 + var git = opt_github_repo_name.value + if not git == null then github_repo = git + end + + redef fun handle_property_conflict(lc, impls) + do + # THIS IS SO UGLY! See MMVirtualModule + if lc.mmmodule == self.mainmod then + return # We just accept, so one in impls is arbitrary inherited + end + super + 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 + super MMModule + init(ctx: MMContext, mods: Array[MMModule]) + do + # We need to compute the whole metamodel since there is no mmbuilder to do it + super(" main".to_symbol, mods.first.directory, ctx, new Location(null,0,0,0,0)) + ctx.add_module(self, mods) + for m in mods do + self.add_super_module(m, 1) + end + self.import_global_classes + self.import_local_classes + for c in self.local_classes do + c.compute_super_classes + end + for c in self.local_classes do + c.compute_ancestors + end + end + redef fun require_doc(dctx) do return false end # Conditionnal part of the text content of a DocContext @@ -277,7 +454,7 @@ end # Efficiently sort object with their to_s method class AlphaSorter[E: Object] -special AbstractSorter[E] + super AbstractSorter[E] redef fun compare(a, b) do var sa: String @@ -305,13 +482,10 @@ special AbstractSorter[E] end # Generalization of metamodel entities -class MMEntity +interface MMEntity # Return a link to fun html_link(dctx: DocContext): String is abstract - # Is the entity should appear in the generaed doc - fun need_doc(dctx: DocContext): Bool is abstract - # Return a one liner description fun short_doc: String do return " " @@ -319,164 +493,505 @@ class MMEntity # Return null is none fun doc: nullable ADoc do return null - # Human redable location of the entity (module/class/property) - fun locate(dctx: DocContext): String do return "" - - # Part of the prototype before the name (kind, modifiers, qualifier) - fun prototype_head(dctx: DocContext): String is abstract + # Return a JSON entry for quicksearch list + fun json_entry(dctx: DocContext): String is abstract - # Part of the property after the name (signature, modifiers) - fun prototype_body(dctx: DocContext): String do return "" + # Return the qualified name as string + fun qualified_name: String is abstract + end redef class MMModule -special MMEntity + super MMEntity redef fun html_link(dctx) do - if dctx.module == self then - return "{self}" - else - return "{self}" + if short_doc == " " then + return "{self}" + else + return "{self}" end end - redef fun need_doc(dctx) do return true - redef fun prototype_head(dctx) do return "module " + + fun html_anchor: String do + return "" + end + + fun html_link_to_anchor: String do + return "{self}" + end - var _known_owner_of_cache: Map[MMModule, MMModule] = new HashMap[MMModule, MMModule] - fun known_owner_of(module: MMModule): MMModule - do - if _known_owner_of_cache.has_key(module) then return _known_owner_of_cache[module] - var res = module - if mhe < module and visibility_for(module) != 0 then - res = known_owner_of_intern(module, self, false) - else - res = module.owner(self) + 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 - _known_owner_of_cache[module] = res - return res + buffer.append("{self.name}") + return buffer.to_s end - # Return the most general module that own self - fun owner(from: MMModule): MMModule + fun require_doc(dctx: DocContext): Bool do - var res = self - var d: nullable MMDirectory = directory - while d != null and d != from.directory do - var o = d.owner - if o != null and o.mhe <= res then res = o - d = d.parent - end - return res + if dctx.public_only and not is_toplevel then return false + return true end - private fun known_owner_of_intern(module: MMModule, from: MMModule, as_owner: Bool): MMModule + # Return true if the module is a top-level owner or a top-level module + fun is_toplevel: Bool do - if module == self then return self - var candidates = new Array[MMModule] - for m in explicit_imported_modules do - if from.visibility_for(m) == 0 then continue - if not m.mhe <= module then continue - candidates.add(m.known_owner_of_intern(module, from, true)) - end - assert not candidates.is_empty - var max = candidates.first - for m in candidates do - if max.mhe < m then max = m - end - if as_owner and max.directory.owner == self then - return self - else - return max - end + var pd = directory.parent + return pd == null or (pd.owner == null and directory.owner == self) end -end + # Element in the module nesting tree + fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null) + var mnhe_: nullable PartialOrderElement[MMModule] = null -redef class MMLocalProperty -special MMEntity - # Anchor of the property description in the module html file - fun html_anchor: String + # Element in the top level module importation hierarchy + fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null) + var tmhe_: nullable PartialOrderElement[MMModule] = null + + fun toplevel_owner: MMModule do - return "PROP_{local_class}_{cmangle(name)}" + var m = self + loop + var ds = m.mnhe.direct_smallers + if ds.length == 0 then return m + if ds.length == 1 then m = ds.first else abort + end end - redef fun html_link(dctx) + fun html_name: String do - var m = module - if not need_doc(dctx) then m = global.intro.module - m = dctx.known_owner_of(m) - if m == dctx.module then - return "{self}" - else - return "{self}" - end + return "{name}" end - - # Kind of property (fun, attr, etc.) - fun kind: String is abstract - redef fun locate(dctx) + fun direct_owner: nullable MMModule do - return "in {module.html_link(dctx)}::{local_class.html_link(dctx)}" + var d = directory + while d.owner == self do d = d.parent.as(not null) + return d.owner end - fun known_intro_class(dctx: DocContext): MMLocalClass + # Fill the body for the page associated to the module + fun file_page_doc(dctx: DocContext) do - var mod = dctx.known_owner_of(global.intro.local_class.module) - var cla = mod[global.intro.local_class.global] - return cla + dctx.add("
\n") + + var mods = new Array[MMModule] + mods = self.mhe.greaters.to_a + dctx.sort(mods) + + dctx.open_stage + dctx.stage("\n") + dctx.close_stage + + if not dctx.public_only then + mods = self.mnhe.direct_greaters.to_a + dctx.sort(mods) + dctx.open_stage + dctx.stage("\n") + dctx.close_stage + end + + dctx.add("
") # metadata + + dctx.add("
\n") + dctx.add("

{name}

\n") + dctx.add("
module ") + for m in mnhe.smallers do + dctx.add("{m.html_link(dctx)}::") + end + dctx.add("{self.name}
\n") + dctx.add("
") + dctx.add("
\n") + + var doc = doc + if doc != null then + dctx.add("
\n") + dctx.add("
{doc.to_html}
\n") + dctx.add("\n") + dctx.add("CancelCommit") + dctx.add("
")
+			dctx.add("
\n") + end + + var op = new Buffer + op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") + var ms = new Array[nullable MMModule] + do + var m0: nullable MMModule = self + while m0 != null do + m0 = m0.direct_owner + ms.add(m0) + end + end + var cla = new HashSet[MMModule] + cla.add(self) + for m0 in self.mhe.greaters do + if not m0.require_doc(dctx) then continue + if self.visibility_for(m0) <= 1 then continue # private or hidden + if self.mnhe <= m0 then continue # do not want nested stuff + if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting + cla.add(m0) + end + for m0 in self.mhe.smallers do + if not m0.require_doc(dctx) then continue + if m0.visibility_for(self) <= 1 then continue # private or hidden + if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting + cla.add(m0) + end + for m0 in self.mnhe.smallers do + cla.add(m0) + end + ms = ms.reversed + for m0 in ms do + if m0 != null then + op.append("subgraph \"cluster_{m0.name}\"\{\n") + end + for c in cla do + if c.direct_owner != m0 then continue + if c == self then + op.append("\"{c.name}\"[shape=box,margin=0.03];\n") + else + op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n") + end + end + if m0 != null then + op.append("\"{m0.name}\"[URL=\"{m0.html_name}.html\"];\n") + for c in m0.mhe.direct_greaters do + if not cla.has(c) then continue + op.append("\"{m0.name}\"->\"{c.name}\";\n") + end + end + end + for m0 in ms do + # Close the nesting subgraph + if m0 != null then + op.append("\}\n") + end + end + for c in cla do + for c2 in c.tmhe.direct_greaters do + if not cla.has(c2) then continue + op.append("\"{c.name}\"->\"{c2.name}\";\n") + end + end + op.append("\}\n") + dctx.gen_dot(op.to_s, name.to_s, "Dependency graph for module {name}") + dctx.add("
") + + var clas = new Array[MMLocalClass] + var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]] + var gprops = new Array[MMLocalProperty] + do + var m = self + for g in m.global_classes do + var lc = m[g] + if not lc.require_doc(dctx) then continue + var im = g.intro.mmmodule + if self.visibility_for(im) <= 1 then continue # private import or invisible import + var keep = false + for lc2 in lc.crhe.greaters_and_self do + if not lc2 isa MMSrcLocalClass then continue + if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen + keep = true + 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] + var mp = lp.local_class.mmmodule + if not self.mnhe <= mp then continue # not introduced/redefined here/stolen + lp = self[g][gp] + if not lp.require_doc(dctx) 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] + gprops.add(lp.global.intro) + end + end + end + end + dctx.add("
\n") + dctx.open_stage + dctx.stage("
\n") + dctx.stage("

Classes

\n") + dctx.sort(clas) + dctx.stage("
    \n") + for lc in clas do + if self.mnhe <= lc.global.intro.mmmodule then + dctx.add("
  • I ") + else + dctx.add("
  • R ") + end + dctx.add("{lc.html_link(dctx)}
  • \n") + end + dctx.stage("
\n") + dctx.close_stage + + dctx.open_stage + dctx.stage("
\n") + dctx.stage("

Properties

\n") + dctx.sort(gprops) + dctx.stage("
    \n") + for lgp in gprops do + var gp = lgp.global + var lps = props[gp] + + if gp.intro isa MMAttribute then continue + + var lpi = self[gp.intro.local_class.global][gp] + + if lps.has(lpi) then + dctx.add("
  • I {lpi.html_open_link(dctx)}{lpi.html_name} ({lpi.local_class})
  • \n") + lps.remove(lpi) + else + 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.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") end - redef fun prototype_head(dctx) + # Fill the body for the page associated to the full index + fun file_index_page_doc(dctx: DocContext) do - var res = new Buffer - var intro_class = known_intro_class(dctx) - var is_redef = local_class != intro_class - if is_redef then res.append("redef ") - if global.visibility_level == 2 then - res.append("protected ") - else if global.visibility_level == 3 then - res.append("private ") + dctx.add("

Full Index

\n") + + var clas = new Array[MMLocalClass] + var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]] + var gprops = new Array[MMLocalProperty] + var mods = new Array[MMModule] + for m in mhe.greaters_and_self do + if not m.require_doc(dctx) then continue + mods.add(m) + end + for g in global_classes do + var lc = self[g] + if not lc.require_doc(dctx) then continue + clas.add(lc) + for gp in lc.global_properties do + var lp = lc[gp] + if not lp.require_doc(dctx) 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] + gprops.add(lp.global.intro) + end + end end - res.append(kind) - if is_redef then - var gp = global.intro - if intro_class.global != local_class.global then - res.append(" {module[intro_class.global].html_link(dctx)}::") - else if intro_class.module != module then - res.append(" {intro_class.module.html_link(dctx)}::") + dctx.open_stage + dctx.stage("
\n") + dctx.stage("

Modules

\n") + dctx.sort(mods) + dctx.stage("
\n") + dctx.close_stage + + dctx.open_stage + dctx.stage("
\n") + dctx.stage("

Classes

\n") + dctx.sort(clas) + dctx.stage("
\n") + dctx.close_stage + + dctx.open_stage + dctx.stage("
\n") + dctx.stage("

Properties

\n") + dctx.sort(gprops) + dctx.stage("
\n") + dctx.close_stage end - redef fun prototype_body(dctx) + # Fill the quicksearch list JSON object + fun file_quicksearch_list_doc(dctx: DocContext) do - var res = new Buffer - res.append(signature.to_html(dctx, true)) - var s = self - if s isa MMMethod then - if s.is_abstract then - res.append(" is abstract") - else if s.is_intern then - res.append(" is intern") + 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 - return res.to_s + + 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 fun need_doc(dctx) +redef class MMLocalProperty + super MMEntity + # Anchor of the property description in the module html file + fun html_anchor: String + do + 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 global.visibility_level >= 3 or self isa MMAttribute then - if not dctx.intrude_mode then return false - if dctx.module.visibility_for(module) == 0 then return false + 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 - if global.intro == self then - return true + 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}" + 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}" + var title = "{html_name}{signature_for(lc.get_type)}" + if short_doc != " " then + title += " #{short_doc}" end - return doc != null + return "{html_name}" end + # Kind of property (fun, attr, etc.) + fun kind: String is abstract + redef fun short_doc do var d = doc @@ -505,142 +1020,195 @@ special MMEntity return d end end -end -redef class MMMethod - redef fun kind do return if global.is_init then "init" else "fun" -end -redef class MMAttribute - redef fun kind do return "var" -end -redef class MMTypeProperty - redef fun kind do return "type" -end -redef class MMSrcModule - # Extract and generate html file for the module - fun extract_module_doc(dctx: DocContext) + # The most specific module in the nesting hierarchy that exports the intro of self + fun intro_module: MMModule do - dctx.info("Generating HTML for module {name}",1) - dctx.register(self) + var m = global.intro.mmmodule + var mo = m.direct_owner + while mo != null and mo.visibility_for(m) >= 2 do + m = mo + mo = m.direct_owner + end + return m + end - dctx.clear - extract_module_doc_inside(dctx) - dctx.write_to("{dctx.dir}/{name}.html") - - dctx.intrude_mode = true - dctx.clear - extract_module_doc_inside(dctx) - dctx.write_to("{dctx.dir}/{name}__.html") - dctx.intrude_mode = false + # Is the intro of self exported by the top-level module ? + fun is_toplevel: Bool + do + var m = intro_module + return m == m.toplevel_owner + end - if directory.owner == self then - dctx.inside_mode = true - dctx.clear - extract_module_doc_inside(dctx) - dctx.write_to("{dctx.dir}/{name}_.html") - dctx.inside_mode = false + # 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 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 end + return true end - fun extract_module_doc_inside(dctx: DocContext) + # Document the global property in the global class lc + fun full_documentation(dctx: DocContext, lc: MMLocalClass) do - dctx.add_header("Module {self}") - dctx.add("

Module {self}

\n
") - var s = "" - var d: nullable MMDirectory = directory - while d == null do - if d.owner != null and (d.owner != self or dctx.inside_mode or dctx.intrude_mode) then - s = "{d.owner.html_link(dctx)}::{s}" - end - d = d.parent - end - dctx.add("{s}
{prototype_head(dctx)}{self}{prototype_body(dctx)}
\n") - - var strs = new Array[String] - var intrude_modules = new Array[MMModule] - var public_modules = new Array[MMModule] - var private_modules = new Array[MMModule] - var owned_modules = dctx.owned_modules - owned_modules.clear - for m in mhe.greaters do - var v = visibility_for(m) - if not dctx.inside_mode and not dctx.intrude_mode and m.directory.owner == self then - if v >= 2 then owned_modules.add(m) - continue - end - if v == 3 then - intrude_modules.add(m) - else if v == 2 then - public_modules.add(m) - else if v == 1 then - private_modules.add(m) - end + var visibility: String + if global.visibility_level == 1 then + visibility = "public" + else if global.visibility_level == 2 then + visibility = "protected" + else if global.visibility_level == 3 then + visibility = "private" + else + abort + end + + var intro_class = global.intro.local_class + 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("

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

\n") + dctx.add("
\n") + + if is_redef then + dctx.add("redef ") end - if not intrude_modules.is_empty then - var mods = mhe.order.select_smallests(intrude_modules) - for i in mods do strs.add(i.html_link(dctx)) - dctx.add("
Intruded modules:
{strs.join(", ")}\n") + if not is_toplevel then + dctx.add("(unexported) ") end - if not public_modules.is_empty then - strs.clear - var mods = mhe.order.select_smallests(public_modules) - for i in mods do strs.add(i.html_link(dctx)) - dctx.add("
Imported modules:
{strs.join(", ")}\n") + if global.visibility_level == 2 then + dctx.add("protected ") + else if global.visibility_level == 3 then + dctx.add("private ") end - if not private_modules.is_empty then - strs.clear - var mods = mhe.order.select_smallests(private_modules) - for i in mods do strs.add(i.html_link(dctx)) - dctx.add("
Privatly imported modules:
{strs.join(", ")}\n") + dctx.add(kind) + dctx.add(" {intro_class.mmmodule.toplevel_owner.name}") + if intro_class.global == lc.global then + dctx.add("::{lc.name}") + else + dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}") + end + if is_redef then + dctx.add("::{mmmodule[intro_class.global][global].global.intro.html_link(dctx)}") + else + dctx.add("::{html_name}") end - dctx.add("
\n") + dctx.add("") + dctx.add("
") + dctx.add("
") - var doc = doc - if doc != null then dctx.add("
{doc.to_html}
\n") - - var new_classes = new Array[MMLocalClass] - for c in local_classes do - if c.need_doc(dctx) then - new_classes.add(c) - if c.global.intro == c then - dctx.register(c) - end - else - for m in owned_modules do - if m.global_classes.has(c.global) then - var mc = m[c.global] - if mc.need_doc(dctx) then - new_classes.add(c) - break - end - end - end - end + # Collect all refinement of the global property in the same global property + var lps = new Array[MMLocalProperty] + for l in prhe.greaters_and_self do + lps.add(l) end - if not new_classes.is_empty then - dctx.sort(new_classes) - dctx.add("\n") - dctx.add("\n") - for c in new_classes do - dctx.add("\n") + var introdoc = false + if global.intro.doc != null then + for lp in lps do + if lp.doc == null then introdoc = true end - dctx.add("
Class Summary of {self}
{c.prototype_head(dctx)}{c.html_link(dctx)}{c.prototype_body(dctx)}
{c.short_doc}

\n") end + if introdoc then + dctx.add("
{global.intro.doc.to_html}
") + dctx.add("") + dctx.add("CancelCommit") + dctx.add("
")
+		end
+
+		var tlmods = new Array[MMModule]
+		for lp in lps do
+			var bm = lp.mmmodule.toplevel_owner
+			var lcm = lc.global.intro.mmmodule
+			if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner
+			if not tlmods.has(bm) then tlmods.add(bm)
+		end
+
+		for tm in tlmods do
+			# Document the top level property for the current top level module
+			var tlp
+			if tm.global_classes.has(lc.global) then
+				tlp = tm[lc.global][self.global]
+				#assert lps.has(tlp) # FIXME What this line is used for?
+			else if tm.global_classes.has(self.local_class.global) then
+				# Self is the inherited property. Process it
+				tlp = tm[self.local_class.global][self.global]
+				assert lps.has(tlp)
+			else
+				# We skip this module since the props defined by the module is  
+				continue
+			end
 
-		if not new_classes.is_empty then 
-			dctx.add("\n")
-			dctx.add("\n")
-			dctx.add("
Class Detail of {self}
\n") + var tlcm = lc.global.intro.mmmodule.toplevel_owner + if not tlcm.mhe <= tm then + dctx.add("

In module {tm.html_link(dctx)} :

") + end - for c in new_classes do - c.extract_class_doc(dctx) + var doc = tlp.doc + var n = tlp.node + if doc != null and (not introdoc or global.intro.doc != doc) then + if n != null then + var l = n.location + dctx.add("
{doc.to_html}
") + end + else if not is_redef then + if n != null then + var l = n.location + dctx.add("New comment\n") + end end - end + dctx.add("") + dctx.add("CancelCommit") + dctx.add("
")
+			dctx.add("

") + if tlp.local_class.global != lc.global then + dctx.add("inherited from {tlp.local_class.html_link(dctx)} ") + end + if tm != tlp.mmmodule then + dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ") + end + if n != null then + var l = n.location + dctx.show_source(l) + end + + dctx.open_stage + dctx.stage(". previously defined by:") + for lp in lps do + var tl = lp.mmmodule.toplevel_owner + if tl != tm then continue + 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)} ") + end - dctx.add("\n") + n = lp.node + if n != null then + var l = n.location + dctx.show_source(l) + end + end + dctx.close_stage + dctx.add("

") + end + dctx.add("
") + dctx.add("") end +end +redef class MMMethod + redef fun kind do return if global.is_init then "init" else "fun" +end +redef class MMAttribute + redef fun kind do return "var" +end +redef class MMTypeProperty + redef fun kind do return "type" +end +redef class MMSrcModule redef fun short_doc do var d = doc @@ -654,10 +1222,10 @@ redef class MMSrcModule redef fun doc do var n = node - if n.n_packagedecl == null then + if n.n_moduledecl == null then return null end - var np = n.n_packagedecl + var np = n.n_moduledecl var d = np.n_doc if d == null then return null @@ -678,376 +1246,408 @@ 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 redef class MMLocalClass -special MMEntity + super MMEntity + # Anchor of the class description in the module html file fun html_anchor: String do return "CLASS_{self}" + fun html_name: String do return "{self}" + redef fun html_link(dctx) do - var m = module - if not need_doc(dctx) then m = global.module - m = dctx.known_owner_of(m) - if m == dctx.module then - return "{self}" - else - return "{self}" + if not require_doc(dctx) then print "{dctx.filename}: not required {self}" + if short_doc == " " then + return "{self}" + else + return "{self}" end end - redef fun short_doc do return global.intro.short_doc - - redef fun doc do return global.intro.doc + redef fun json_entry(dctx) do + return "\{txt:\"{qualified_name}\",url:\"{html_name}.html\"\}," + end - redef fun need_doc(dctx) do - if module == dctx.module then - for m in dctx.owned_modules do - if m.global_classes.has(global) then - var c = m[global] - if c.need_doc(dctx) then return true - end - end - end - return false + redef fun qualified_name do + return "{intro_module.qualified_name}::{html_name}" end - redef fun locate(dctx) do return "in {module.html_link(dctx)}" + redef fun short_doc do return global.intro.short_doc - fun known_intro(dctx: DocContext): MMLocalClass do return dctx.known_owner_of(global.intro.module)[global] + redef fun doc do return global.intro.doc - redef fun prototype_head(dctx) + fun kind: String do - var res = new Buffer - var ki = known_intro(dctx) - var is_redef = ki != self - if is_redef then res.append("redef ") - if global.visibility_level == 3 then res.append("private ") - res.append("class ") - if is_redef then res.append("{ki.module.html_link(dctx)}::") - return res.to_s + if global.is_interface then + return "interface" + else if global.is_abstract then + return "abstract class" + else if global.is_enum then + return "enum" + else + return "class" + end end - redef fun prototype_body(dctx) + # The most specific module in the nesting hierarchy that exports the intro of self + fun intro_module: MMModule do - var res = new Buffer - if arity > 0 then - res.append("[") - for i in [0..arity[ do - var t = get_formal(i) - res.append(t.name.to_s) - res.append(": ") - res.append(t.bound.html_link(dctx)) - end - res.append("]") + var m = global.intro.mmmodule + var mo = m.direct_owner + while mo != null and mo.visibility_for(m) >= 2 do + m = mo + mo = m.direct_owner end - return res.to_s + return m end - # Extract the doc of a class - fun extract_class_doc(dctx: DocContext) + fun menu_link(dctx: DocContext, p: MMLocalProperty) do - dctx.add("

{self}

{module.html_link(dctx)}::
{prototype_head(dctx)}{self}{prototype_body(dctx)}\n") - dctx.add("
\n") - dctx.add("
\n") - - var sup2 = new Array[String] - var intro_module = dctx.known_owner_of(global.module) - if intro_module != module then - dctx.add("
Refine {self} from:
{intro_module.html_link(dctx)}\n") - sup2.clear - var mods = new Array[MMModule] - for c in crhe.greaters do - if c.need_doc(dctx) then - var km = dctx.known_owner_of(c.module) - if km != module and km != intro_module and not mods.has(km) then - mods.add(km) - end - end - end - for c in crhe.linear_extension do - if mods.has(c.module) then sup2.add(c.module.html_link(dctx)) - end - if not sup2.is_empty then dctx.add("
Previous refinements in:
{sup2.join(", ")}\n") - end - if not cshe.greaters.is_empty then - sup2.clear - var clas = new Array[MMLocalClass] - for c in cshe.direct_greaters do - sup2.add(c.html_link(dctx)) - end - dctx.add("
Direct superclasses:
{sup2.join(", ")}\n") - sup2.clear - for c in cshe.linear_extension do - if c != self then sup2.add(c.html_link(dctx)) - end - dctx.add("
All superclasses:
{sup2.join(", ")}\n") - end - if not cshe.direct_smallers.is_empty then - sup2.clear - for c in cshe.direct_smallers do - sup2.add(c.html_link(dctx)) - end - dctx.add("
Direct subclasses:
{sup2.join(", ")}\n") - end - sup2.clear - for c in crhe.smallers do - c.compute_super_classes - for c2 in c.module.local_classes do - if not c2 isa MMConcreteClass then continue - c2.compute_super_classes - c2.compute_ancestors - c2.inherit_global_properties - end - for c2 in c.cshe.direct_smallers do - if c2.global.intro == c2 then - sup2.add("{c2.html_link(dctx)}") - end - end - end - if not sup2.is_empty then - dctx.add("
Other direct subclasses in known modules:
{sup2.join(", ")}\n") - end - sup2.clear - for c in crhe.order do - if not module.mhe <= c.module and c.need_doc(dctx) then - sup2.add(c.module.html_link(dctx)) + if p.local_class.global != self.global then + if p.global.intro.local_class.name == "Object".to_symbol then return + if p.global.is_init or p isa MMTypeProperty then + dctx.add("
  • H {p.html_link_special(dctx, self)}
  • \n") + else + dctx.add("
  • H {p.html_link(dctx)}
  • \n") end + else if p.global.intro.local_class.global == self.global then + dctx.add("
  • I {p.html_link_special(dctx, self)}
  • \n") + else + dctx.add("
  • R {p.html_link_special(dctx, self)}
  • \n") end - if not sup2.is_empty then - dctx.add("
    Refinements in known modules:
    {sup2.join(", ")}\n") - end - dctx.add("
    \n") - - var doc = doc - if doc != null then - dctx.add("
    {doc.to_html}
    \n") - end - - var details = new Array[Array[MMLocalProperty]] - for i in [0..4[ do details.add(property_summary(dctx, i)) - for i in [0..4[ do property_detail(dctx, i, details[i]) - - dctx.add("

    \n") end - fun pass_name(pass: Int): String - do - var names = once ["Virtual Types", "Consructors", "Methods", "Attributes"] - return names[pass] - end - - fun accept_prop(p: MMLocalProperty, pass: Int): Bool + # Return true if the global class must be documented according to the visibility configured + fun require_doc(dctx: DocContext): Bool do - if pass == 0 then - return p isa MMTypeProperty - else if pass == 1 then - return p.global.is_init - else if pass == 2 then - return p isa MMMethod and not p.global.is_init - else if pass == 3 then - return p isa MMAttribute + 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 end - abort + return true end - fun property_summary(dctx: DocContext, pass: Int): Array[MMLocalProperty] + # Fill the body for the page associated to the global class + fun file_page_doc(dctx: DocContext) do - var passname = pass_name(pass) - dctx.open_stage - dctx.stage("\n") - dctx.stage("\n") + dctx.add("
    \n") - var new_props = new Array[MMLocalProperty] + var props = new Array[MMLocalProperty] + var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]] + var inhs = new Array[MMLocalClass] for g in global_properties do - if not accept_prop(g.intro, pass) then continue - if module.visibility_for(g.intro.module) < g.visibility_level then continue var p = self[g] - if p.local_class != self or not p.need_doc(dctx) then - var cla = new Array[MMLocalClass] - for m in dctx.owned_modules do - if not m.global_classes.has(global) then continue - var c = m[global] - if not c isa MMConcreteClass then continue - if not c.has_global_property(g) then continue - var p2 = c[g] - if p2.local_class != c or not p2.need_doc(dctx) then continue - cla.add(c) + if not p.require_doc(dctx) then continue + if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then + props.add(p) + else + var lc = mmmodule[p.local_class.global] + if inh.has_key(lc) then + inh[lc].add(p) + else + inh[lc] = [p] + inhs.add(lc) end - if cla.is_empty then continue - cla = crhe.order.select_smallests(cla) + props.add(p) end + end + dctx.sort(props) - new_props.add(p) - if p.global.intro == p then - dctx.register(p) + dctx.add("
    \n") + dctx.stage("\n") + dctx.close_stage + dctx.open_stage + dctx.stage("

    Constructors

    \n
    {passname} Summary of {self}
    {p.prototype_head(dctx)}{p.html_link(dctx)}{p.prototype_body(dctx)}
        {p.short_doc}

    \n") - + dctx.stage("\n") + dctx.close_stage dctx.open_stage - dctx.stage("\n") - if pass != 1 then - # skip pass 1 because constructors are not inherited - var cmap = new HashMap[MMLocalClass, Array[MMLocalProperty]] - var mmap = new HashMap[MMModule, Array[MMLocalProperty]] - for c in che.greaters do - if c isa MMSrcLocalClass then - var km = dctx.known_owner_of(c.module) - var kc = km[c.global] - if kc == self then continue - var props: Array[MMLocalProperty] - if km == module then - if cmap.has_key(kc) then - props = cmap[kc] - else - props = new Array[MMLocalProperty] - cmap[kc] = props - end - else - if mmap.has_key(km) then - props = mmap[km] - else - props = new Array[MMLocalProperty] - mmap[km] = props - end - end - for g in c.global_properties do - var p = c[g] - if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then - props.add(kc[g]) - end - end - end + dctx.stage("

    Methods

    \n\n") - for c in cshe.linear_extension do - if not cmap.has_key(c) then continue - var props = cmap[c] - if props.is_empty then continue - dctx.sort(props) - var properties = new Array[String] - for p in props do properties.add(p.html_link(dctx)) - dctx.add("\n") + end + dctx.stage("\n") + dctx.close_stage + dctx.add("\n") + + dctx.add("\n") - for m in module.mhe.linear_extension do - if not mmap.has_key(m) then continue - var props = mmap[m] - if props.is_empty then continue - dctx.sort(props) - var properties = new Array[String] - for p in props do properties.add(p.html_link(dctx)) - dctx.add("\n") + dctx.add("\n") + else if cshe.direct_smallers.length <= 100 then + dctx.add("

    Direct Subclasses Only

    \n\n") + else + dctx.add("

    Too much Subclasses to list

    \n") end + dctx.add("\n") + + dctx.add("\n") + - var mmap = new HashMap[MMModule, Array[MMLocalProperty]] - for c in crhe.order do - if module.mhe <= c.module or dctx.owned_modules.has(c.module) or not c isa MMSrcLocalClass then continue - var km = dctx.known_owner_of(c.module) - if module.mhe <= km then continue - var kc = km[c.global] - var props: Array[MMLocalProperty] - if mmap.has_key(km) then - props = mmap[km] + dctx.add("
    \n") + dctx.add("

    {name}

    \n") + dctx.add("
    ") + if global.visibility_level == 2 then + abort + else if global.visibility_level == 3 then + dctx.add("private ") + else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then + dctx.add("(unexported) ") + end + dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}
    ") + dctx.add("
    ") + dctx.add("
    \n") + var doc = doc + if doc != null then + var l = doc.location + dctx.add("
    {doc.to_html}
    \n") + dctx.add("") + dctx.add("CancelCommit") + dctx.add("
    ")
    +		end
    +
    +		var cla = new HashSet[MMLocalClass]
    +		var sm = new HashSet[MMLocalClass]
    +		var sm2 = new HashSet[MMLocalClass]
    +		sm.add(self)
    +		while cla.length + sm.length < 10 and sm.length > 0 do
    +			cla.add_all(sm)
    +			sm2.clear
    +			for x in sm do
    +				sm2.add_all(x.cshe.direct_smallers)
    +			end
    +			var t = sm
    +			sm = sm2
    +			sm2 = t
    +		end
    +		cla.add_all(cshe.greaters_and_self)
    +
    +		var op = new Buffer
    +		var name = "class_{name}"
    +		op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
    +		for c in cla do
    +			if c == self then
    +				op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
     			else
    -				props = new Array[MMLocalProperty]
    -				mmap[km] = props
    +				op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
    +			end
    +			for c2 in c.cshe.direct_greaters do
    +				if not cla.has(c2) then continue
    +				op.append("\"{c.name}\"->\"{c2.name}\";\n")
     			end
    -			for g in c.global_properties do
    -				var p = c[g]
    -				if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
    -					var kp = kc[g]
    -					if not props.has(kp) then props.add(kp)
    +			if not c.cshe.direct_smallers.is_empty then
    +				var others = true
    +				for c2 in c.cshe.direct_smallers do
    +					if cla.has(c2) then others = false
    +				end
    +				if others then
    +					op.append("\"{c.name}...\"[label=\"\"];\n")
    +					op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
     				end
     			end
    -			# c.properties_inherited_from(dctx, self, pass)
     		end
    +		op.append("\}\n")
    +		dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph for class {name}")
    +		dctx.add("
    \n") + + # Concerns table dctx.open_stage - dctx.stage("
    \n") - for c in crhe.order do - var m = c.module - if not mmap.has_key(m) then continue - var props = mmap[m] - if props.is_empty then continue - dctx.sort(props) - var properties = new Array[String] - for p in props do properties.add(p.html_link(dctx)) - dctx.add("\n") + dctx.stage("
    \n") + dctx.stage("

    Concerns

    \n") + + var mods = new Array[MMModule] + mods.add(global.intro.mmmodule.toplevel_owner) + for lc in crhe.greaters do + if not lc isa MMSrcLocalClass then continue + var m = lc.mmmodule.toplevel_owner + if not mods.has(m) then mods.add(m) end - dctx.close_stage - dctx.stage("
    Inherited {passname}
    from {c.html_link(dctx)}{properties.join(", ")}
    Imported {passname}
    from {m.html_link(dctx)}{properties.join(", ")}
    Added {passname} in known modules
    in {m.html_link(dctx)}{properties.join(", ")}


    \n") + + var intro = global.intro.mmmodule + var short_doc + dctx.add("\n") + dctx.stage("\n") dctx.close_stage + dctx.open_stage + dctx.stage("
    \n") + dctx.stage("

    Formal and Virtual Types

    \n") + for i in [0..arity[ do + var f = get_formal(i) + f.full_documentation(dctx, self) + end + for p in props do + if not p isa MMTypeProperty then continue + p.full_documentation(dctx, self) + end + dctx.stage("
    \n") dctx.close_stage - return new_props - end - fun property_detail(dctx: DocContext, pass: Int, new_props: Array[MMLocalProperty]) - do - var passname = pass_name(pass) dctx.open_stage - dctx.stage("\n") - dctx.stage("\n") - dctx.stage("
    {passname} Detail of {self}
    \n") + dctx.stage("
    \n") + dctx.stage("

    Constructors

    \n") + for p in props do + if not p.global.is_init_for(self) then continue + p.full_documentation(dctx, self) + end + dctx.stage("
    \n") + dctx.close_stage dctx.open_stage - for p in new_props do - dctx.add("

    {p}

    {p.module.html_link(dctx)}::{p.local_class.html_link(dctx)}::
    {p.prototype_head(dctx)} {p.name}{p.prototype_body(dctx)}

    \n") - dctx.add("
    ") - var doc = p.doc - if doc != null then - dctx.add("
    {doc.to_html}
    \n") + dctx.stage("
    \n") + dctx.stage("

    Methods

    \n") + var redefs = new HashMap[MMModule, HashMap[MMModule, Array[MMMethod]]] + for p in props do + if p.global.is_init then continue + if p.local_class.global != self.global then continue + if not p isa MMMethod then continue + # Top level module + var toplevel_module = p.mmmodule.toplevel_owner + if not redefs.has_key(toplevel_module) then + redefs[toplevel_module] = new HashMap[MMModule, Array[MMMethod]] + end + # Nested module + var nested_module = p.mmmodule + if not redefs[toplevel_module].has_key(nested_module) then + redefs[toplevel_module][nested_module] = new Array[MMMethod] + end + # Props + redefs[toplevel_module][nested_module].add(p) + + # Redefs + if p.mmmodule.toplevel_owner != p.intro_module then + toplevel_module = p.intro_module + nested_module = p.global.intro.mmmodule + + if not redefs.has_key(toplevel_module) then + redefs[toplevel_module] = new HashMap[MMModule, Array[MMMethod]] + end + if not redefs[toplevel_module].has_key(nested_module) then + redefs[toplevel_module][nested_module] = new Array[MMMethod] + end + + redefs[toplevel_module][nested_module].add(p.global.intro.as(MMMethod)) end - dctx.stage("
    \n") - dctx.close_stage - - dctx.open_stage - dctx.stage("
    \n") end - dctx.close_stage - - dctx.close_stage - end - - # Add rows for properties inheriterd to some heirs - fun properties_inherited_from(dctx: DocContext, heir: MMLocalClass, pass: Int) - do - var properties = new Array[String] - for g in global_properties do - var p = self[g] - if p.local_class == self and p.need_doc(dctx) and accept_prop(p, pass) then - properties.add(p.html_link(dctx)) + + # Display toplevel blocks + for m in mods do + if not redefs.has_key(m) then continue + dctx.add(m.html_anchor) + if m != global.intro.mmmodule.toplevel_owner then + dctx.add("

    Methods refined in {m.html_link(dctx)}

    ") + end + + # Display nested module blocks + for lc in crhe.linear_extension.reversed do + if lc.mmmodule.toplevel_owner != m then continue + var nm = lc.mmmodule + if not redefs[m].has_key(nm) then continue + dctx.add(nm.html_anchor) + if nm != global.intro.mmmodule then + short_doc = "" + if nm.short_doc != " " then short_doc = ": {nm.short_doc}" + dctx.add("

    {nm.html_name}{short_doc}

    \n") + end + + var pps = redefs[m][nm] + dctx.sort(pps) + for p in pps do + p.full_documentation(dctx, self) + end end end - if not properties.is_empty then - var s: String - if heir.global == global then - s = module.html_link(dctx) - else - s = self.html_link(dctx) + + if not inhs.is_empty then + dctx.open_stage + dctx.stage("

    Inherited Methods

    \n") + for lc in inhs do + dctx.open_stage + dctx.stage("

    Defined in {lc.html_link(dctx)}:") + + var ims = new Array[MMMethod] + for p in inh[lc] do + if p.global.is_init then continue + if not p isa MMMethod then continue + ims.add(p) + end + + var i = 0 + for p in ims do + dctx.add(" {p.html_link(dctx)}") + if i < ims.length - 1 then dctx.add(",") + i += 1 + end + + dctx.stage("

    ") + dctx.close_stage end - dctx.add("in {s}{properties.join(", ")}\n") + dctx.close_stage end + dctx.add("\n") + dctx.close_stage + dctx.add(" \n") end end @@ -1081,23 +1681,6 @@ redef class MMSrcLocalClass return d end end - - redef fun need_doc(dctx) - do - if global.visibility_level >= 3 then - if not dctx.intrude_mode then return false - if dctx.module.visibility_for(module) == 0 then return false - end - if global.intro == self then - return true - end - for p in src_local_properties do - if p.need_doc(dctx) then - return true - end - end - return super - end end redef class MMSignature @@ -1107,10 +1690,14 @@ redef class MMSignature var res = new Buffer if arity > 0 then res.append("(") - res.append(self[0].html_link(dctx)) - for i in [1..arity[ do - res.append(", ") + for i in [0..arity[ do + if i > 0 then res.append(", ") + res.append(self.params[i].name.to_s) + res.append(": ") res.append(self[i].html_link(dctx)) + if self.vararg_rank == i then + res.append("...") + end end res.append(")") end @@ -1157,5 +1744,33 @@ redef class MMTypeGeneric end end +redef class MMTypeFormalParameter + fun html_anchor: String + do + return "FT_{local_class}_{cmangle(name)}" + end + redef fun html_link(dctx) + do + return "{name}" + end + fun full_documentation(dctx: DocContext, lc: MMLocalClass) + do + dctx.add("
    \n") + dctx.add("

    {name}: {bound.html_link(dctx)}

    \n") + dctx.add("
    ") + dctx.add("formal generic type") + dctx.add("
    ") + dctx.add("
    ") + end +end + +redef class MMNullableType + redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx) +end + +redef class MMVirtualType + redef fun html_link(dctx) do return property.html_link(dctx) +end + var c = new DocContext c.exec_cmd_line