X-Git-Url: http://nitlanguage.org diff --git a/src/nitdoc.nit b/src/nitdoc.nit index 18e1d0c..7fd49a4 100644 --- a/src/nitdoc.nit +++ b/src/nitdoc.nit @@ -1,12 +1,10 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2008 Jean Privat -# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,1144 +12,1594 @@ # See the License for the specific language governing permissions and # limitations under the License. -# The main module of the nitdoc program -package nitdoc +# Documentation generator for the nit language. +# Generate API documentation in HTML format from nit source code. +module nitdoc + +import model_utils +import modelize_property +import markdown +import doc_template + +# The NitdocContext contains all the knowledge used for doc generation +class NitdocContext + + private var toolcontext = new ToolContext + private var mbuilder: ModelBuilder + private var mainmodule: MModule + private var output_dir: String + private var min_visibility: MVisibility + + private var opt_dir = new OptionString("output directory", "-d", "--dir") + private var opt_source = new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source") + private var opt_sharedir = new OptionString("directory containing nitdoc assets", "--sharedir") + private var opt_shareurl = new OptionString("use shareurl instead of copy shared files", "--shareurl") + private var opt_nodot = new OptionBool("do not generate graphes with graphviz", "--no-dot") + private var opt_private = new OptionBool("also generate private API", "--private") + + private var opt_custom_title = new OptionString("custom title for homepage", "--custom-title") + private var opt_custom_menu = new OptionString("custom items added in top menu (each item must be enclosed in 'li' tags)", "--custom-menu-items") + private var opt_custom_intro = new OptionString("custom intro text for homepage", "--custom-overview-text") + private var opt_custom_footer = new OptionString("custom footer text", "--custom-footer-text") + + private var opt_github_upstream = new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream") + private var opt_github_base_sha1 = new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1") + private var opt_github_gitdir = new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir") + + private var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker") + private var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id") + + init do + var opts = toolcontext.option_context + opts.add_option(opt_dir, opt_source, opt_sharedir, opt_shareurl, opt_nodot, opt_private) + opts.add_option(opt_custom_title, opt_custom_footer, opt_custom_intro, opt_custom_menu) + opts.add_option(opt_github_upstream, opt_github_base_sha1, opt_github_gitdir) + opts.add_option(opt_piwik_tracker, opt_piwik_site_id) + + var tpl = new Template + tpl.add "Usage: nitdoc [OPTION]... ...\n" + tpl.add "Generates HTML pages of API documentation from Nit source files." + toolcontext.tooldescription = tpl.write_to_string + toolcontext.process_options(args) + + self.process_options + self.parse(toolcontext.option_context.rest) + end -import syntax -private import utils -import abstracttool + private fun process_options do + if opt_private.value then + min_visibility = none_visibility + else + min_visibility = protected_visibility + end + var gh_upstream = opt_github_upstream.value + var gh_base_sha = opt_github_base_sha1.value + var gh_gitdir = opt_github_gitdir.value + if not gh_upstream == null or not gh_base_sha == null or not gh_gitdir == null then + if gh_upstream == null or gh_base_sha == null or gh_gitdir == null then + print "Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin" + abort + end + end + end + private fun parse(arguments: Array[String]) do + var model = new Model + mbuilder = new ModelBuilder(model, toolcontext) + var mmodules = mbuilder.parse(arguments) + if mmodules.is_empty then return + mbuilder.run_phases + if mmodules.length == 1 then + mainmodule = mmodules.first + else + mainmodule = new MModule(model, null, "
", new Location(null, 0, 0, 0, 0)) + mainmodule.set_imported_mmodules(mmodules) + end + end -# Store knowledge and facilities to generate files -class DocContext -special AbstractCompiler - # Destination directory - readable writable attr _dir: String = "." + private fun generate_nitdoc do + init_output_dir + overview + search + modules + classes + quicksearch_list + end - # Content of a generated file - attr _stage_context: StageContext = new StageContext(null) + private fun init_output_dir do + # location output dir + var output_dir = opt_dir.value + if output_dir == null then + output_dir = "doc" + end + self.output_dir = output_dir + # create destination dir if it's necessary + if not output_dir.file_exists then output_dir.mkdir + # locate share dir + var sharedir = opt_sharedir.value + if sharedir == null then + var dir = toolcontext.nit_dir + if dir == null then + print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR" + abort + end + sharedir = "{dir}/share/nitdoc" + if not sharedir.file_exists then + print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR" + abort + end + end + # copy shared files + if opt_shareurl.value == null then + sys.system("cp -r {sharedir.to_s}/* {output_dir.to_s}/") + else + sys.system("cp -r {sharedir.to_s}/resources/ {output_dir.to_s}/resources/") + end - # Add a string in the content - meth add(s: String) do - _stage_context.content.add(s) - _stage_context.validate = true end - # Add a string in the content iff some other string are added - meth stage(s: String) do _stage_context.content.add(s) - - # Create a new stage in the content - meth open_stage do _stage_context = new StageContext(_stage_context) + private fun overview do + var overviewpage = new NitdocOverview(self) + overviewpage.render.write_to_file("{output_dir.to_s}/index.html") + end - # Close the current stage in the content - meth close_stage - do - var s = _stage_context.parent - if _stage_context.validate then - s.content.add_all(_stage_context.content) - s.validate = true - end - assert s != null - _stage_context = s + private fun search do + var searchpage = new NitdocSearch(self) + searchpage.render.write_to_file("{output_dir.to_s}/search.html") end - # Write the content to a new file - meth write_to(filename: String) - do - print "Generate {filename}" - var f = new OFStream.open(filename) - for s in _stage_context.content do - f.write(s) + private fun modules do + for mmodule in mbuilder.model.mmodules do + if mmodule.name == "
" then continue + var modulepage = new NitdocModule(mmodule, self) + modulepage.render.write_to_file("{output_dir.to_s}/{mmodule.nitdoc_url}") end - f.close end - # Currently computed module - readable attr _module: nullable MMSrcModule - - # Is the current directory module computed as a simple modude ? - readable writable attr _inside_mode: Bool = false - - # Is the current module computed as a intruded one ? - readable writable attr _intrude_mode: Bool = false - - # Compued introducing entities (for the index) - attr _entities: Array[MMEntity] = new Array[MMEntity] - - # Register an entity (for the index) - meth register(e: MMEntity) - do - _entities.add(e) - if e isa MMSrcModule then - _module = e + private fun classes do + for mclass in mbuilder.model.mclasses do + var classpage = new NitdocClass(mclass, self) + classpage.render.write_to_file("{output_dir.to_s}/{mclass.nitdoc_url}") end end - # Start a new file - meth clear - do - _stage_context = new StageContext(null) + private fun quicksearch_list do + var quicksearch = new QuickSearch(self) + quicksearch.render.write_to_file("{output_dir.to_s}/quicksearch-list.js") end +end - # Generate common files (frames, index, overview) - meth extract_other_doc - do - _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) +# Nitdoc QuickSearch list generator +# +# Create a JSON object containing links to: +# * modules +# * mclasses +# * mpropdefs +# All entities are grouped by name to make the research easier. +class QuickSearch + + private var mmodules = new HashSet[MModule] + private var mclasses = new HashSet[MClass] + private var mpropdefs = new HashMap[String, Set[MPropDef]] + + init(ctx: NitdocContext) do + for mmodule in ctx.mbuilder.model.mmodules do + if mmodule.name == "
" then continue + mmodules.add mmodule + end + for mclass in ctx.mbuilder.model.mclasses do + if mclass.visibility < ctx.min_visibility then continue + mclasses.add mclass + end + for mproperty in ctx.mbuilder.model.mproperties do + if mproperty.visibility < ctx.min_visibility then continue + if mproperty isa MAttribute then continue + if not mpropdefs.has_key(mproperty.name) then + mpropdefs[mproperty.name] = new HashSet[MPropDef] end + mpropdefs[mproperty.name].add_all(mproperty.mpropdefs) 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 - end + fun render: Template do + var tpl = new Template + tpl.add "var nitdocQuickSearchRawList=\{ " + for mmodule in mmodules do + tpl.add "\"{mmodule.name}\":[" + tpl.add "\{txt:\"{mmodule.full_name}\",url:\"{mmodule.nitdoc_url}\"\}," + tpl.add "]," + end + for mclass in mclasses do + var full_name = mclass.intro.mmodule.full_name + tpl.add "\"{mclass.name}\":[" + tpl.add "\{txt:\"{full_name}\",url:\"{mclass.nitdoc_url}\"\}," + tpl.add "]," + end + for mproperty, mprops in mpropdefs do + tpl.add "\"{mproperty}\":[" + for mpropdef in mprops do + var full_name = mpropdef.mclassdef.mclass.full_name + tpl.add "\{txt:\"{full_name}\",url:\"{mpropdef.nitdoc_url}\"\}," end + tpl.add "]," end - add("\n") - write_to("{dir}/menu-frame.html") + tpl.add " \};" + return tpl + end +end - 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") - end - add("
\n") - write_to("{dir}/index-1.html") +# Nitdoc base page +# Define page structure and properties +abstract class NitdocPage - clear - add_header("Overview") - add("\n") - add("\n") - for m in modules do - add("\n") - end - add("
Overview of all Modules
{m.html_link(self)}{m.short_doc}
\n") - write_to("{dir}/overview.html") + private var ctx: NitdocContext + private var shareurl = "." - clear - add("\n\n\n\n\n") - write_to("{dir}/index.html") + init(ctx: NitdocContext) do + self.ctx = ctx + if ctx.opt_shareurl.value != null then shareurl = ctx.opt_shareurl.value.as(not null) end - meth 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  ") + # Render the page as a html template + fun render: Template do + var tpl = new TplNitdocPage + tpl.head = tpl_head + tpl.topmenu = tpl_topmenu + tpl.sidebar = tpl_sidebar + tpl.content = tpl_content + tpl.footer = tpl_footer + + tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl)) + if ctx.opt_github_upstream.value != null and ctx.opt_github_base_sha1.value != null then + tpl.body_attrs.add(new TagAttribute("data-github-upstream", ctx.opt_github_upstream.value)) + tpl.body_attrs.add(new TagAttribute("data-github-base-sha1", ctx.opt_github_base_sha1.value)) + end + var requirejs = new TplScript + requirejs.attrs.add(new TagAttribute("data-main", "{shareurl}/js/nitdoc")) + requirejs.attrs.add(new TagAttribute("src", "{shareurl}/js/lib/require.js")) + tpl.scripts.add requirejs + + # piwik tracking + var tracker_url = ctx.opt_piwik_tracker.value + var site_id = ctx.opt_piwik_site_id.value + if tracker_url != null and site_id != null then + tpl.scripts.add new TplPiwikScript(tracker_url, site_id) + end + return tpl + end + + # Page title string + fun tpl_title: String do + if ctx.opt_custom_title.value != null then + return ctx.opt_custom_title.value.to_s else - add("Intrude  ") + return "Nitdoc" end - add("
") end - # Sorter of entities in alphabetical order - attr _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity] + # Page template + fun tpl_head: TplHead do return new TplHead(tpl_title, shareurl) - # Sort entities in the alphabetical order - meth sort(array: Array[MMEntity]) - do - _sorter.sort(array) + # Top menu template + fun tpl_topmenu: TplTopMenu do + var topmenu = new TplTopMenu + var custom_elt = ctx.opt_custom_menu.value + if custom_elt != null then topmenu.add_raw(custom_elt) + return topmenu end - readable writable attr _owned_modules: Array[MMModule] = new Array[MMModule] + # Page sidebar template + # return null if no sidebar for this page + fun tpl_sidebar: nullable TplSidebar do return null - # Return the known_owner for current module - # if inside_mode is set, it could be a different result - meth known_owner_of(m: MMModule): MMModule - 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 - else - return res + # Page content template + fun tpl_content: Template is abstract + + # Page footer template + # return null if no footer for this page + fun tpl_footer: nullable TplFooter do + if ctx.opt_custom_footer.value != null then + return new TplFooter(ctx.opt_custom_footer.value.to_s) end + return null end - readable attr _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir") + # Clickable graphviz image using dot format + # return null if no graph for this page + fun tpl_graph(dot: FlatBuffer, name: String, alt: String): nullable TplGraph do + if ctx.opt_nodot.value then return null + var output_dir = ctx.output_dir + var file = new OFStream.open("{output_dir}/{name}.dot") + file.write(dot) + file.close + sys.system("\{ test -f {output_dir}/{name}.png && test -f {output_dir}/{name}.s.dot && diff {output_dir}/{name}.dot {output_dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {output_dir}/{name}.dot {output_dir}/{name}.s.dot && dot -Tpng -o{output_dir}/{name}.png -Tcmapx -o{output_dir}/{name}.map {output_dir}/{name}.s.dot ; \}") + var fmap = new IFStream.open("{output_dir}/{name}.map") + var map = fmap.read_all + fmap.close + return new TplGraph(name, alt, map) + end - redef meth perform_work(mods) + # A (source) link template for a given location + fun tpl_showsource(location: nullable Location): nullable String do - dir.mkdir + if location == null then return null + var source = ctx.opt_source.value + if source == null then return "({location.file.filename.simplify_path})" + # THIS IS JUST UGLY ! (but there is no replace yet) + var x = source.split_with("%f") + source = x.join(location.file.filename.simplify_path) + x = source.split_with("%l") + source = x.join(location.line_start.to_s) + x = source.split_with("%L") + source = x.join(location.line_end.to_s) + source = source.simplify_path + return " (source)" + end - for mod in modules do - assert mod isa MMSrcModule - mod.extract_module_doc(self) - end - self.extract_other_doc + # MClassDef description template + fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do + var article = mclassdef.tpl_article + article.content = new Template + if not mclassdef.is_intro then + # add intro synopsys + var intro = mclassdef.mclass.intro + var location = intro.location + var sourcelink = tpl_showsource(location) + var intro_def = intro.tpl_short_definition + intro_def.location = sourcelink + intro_def.github_area = tpl_github(intro.full_namespace, intro.mdoc, location) + article.content.add intro_def + end + # add mclassdef full description + var location = mclassdef.location + var sourcelink = tpl_showsource(location) + var prop_def = mclassdef.tpl_definition + prop_def.location = sourcelink + prop_def.github_area = tpl_github(mclassdef.full_namespace, mclassdef.mdoc, location) + article.content.add prop_def + return article end - init - do - super("nitdoc") - option_context.add_option(opt_dir) + # MPropDef description template + fun tpl_mpropdef_article(mpropdef: MPropDef): TplArticle do + var article = mpropdef.tpl_article + article.content = new Template + if not mpropdef.is_intro then + # add intro synopsys + var intro = mpropdef.mproperty.intro + var location = intro.location + var sourcelink = tpl_showsource(location) + var intro_def = intro.tpl_short_definition + intro_def.location = sourcelink + intro_def.github_area = tpl_github(intro.full_namespace, intro.mdoc, location) + article.content.add intro_def + end + # add mpropdef description + var location = mpropdef.location + var sourcelink = tpl_showsource(location) + var prop_def = mpropdef.tpl_definition + prop_def.location = sourcelink + prop_def.github_area = tpl_github(mpropdef.full_namespace, mpropdef.mdoc, location) + article.content.add prop_def + return article end - redef meth process_options - do - super - var d = opt_dir.value - if d != null then dir = d + # Github area (for Github comment edition plugin) + # return null if no github plugin for this page + fun tpl_github(namespace: String, mdoc: nullable MDoc, loc: nullable Location): nullable TplGithubArea do + if loc == null then return null + if ctx.opt_github_gitdir.value == null then return null + var gitdir = ctx.opt_github_gitdir.value.as(not null) + var location = loc.github(gitdir) + var comment: String + if mdoc != null then + comment = mdoc.full_comment + else + comment = "" + end + return new TplGithubArea(comment, namespace, location) end end -# Conditionnal part of the text content of a DocContext -class StageContext - # Content of the current stage - readable attr _content: Array[String] = new Array[String] - - # Is a normal string already added? - readable writable attr _validate: Bool = false - - # Parent stage is any - readable attr _parent: nullable StageContext = null +# The overview page +# Display a list of modules contained in program +class NitdocOverview + super NitdocPage + + private var mmodules = new Array[MModule] + + init(ctx: NitdocContext) do + super(ctx) + # get modules + var mmodules = new HashSet[MModule] + for mmodule in ctx.mbuilder.model.mmodule_importation_hierarchy do + if mmodule.name == "
" then continue + var owner = mmodule.public_owner + if owner != null then + mmodules.add(owner) + else + mmodules.add(mmodule) + end + end + # sort modules + var sorter = new MModuleNameSorter + self.mmodules.add_all(mmodules) + sorter.sort(self.mmodules) + end - init(parent: nullable StageContext) do _parent = parent -end + redef fun tpl_title do return "Overview | {super}" + redef fun tpl_topmenu do + var topmenu = super + topmenu.add_elt("#", "Overview", true) + topmenu.add_elt("search.html", "Search", false) + return topmenu + end -# Efficiently sort object with their to_s method -class AlphaSorter[E: Object] -special AbstractSorter[E] - redef meth compare(a, b) - do - var sa: String - var sb: String - var d = _dico - if d.has_key(a) then - sa = d[a] + redef fun tpl_content do + var tpl = new TplOverviewPage + # title + if ctx.opt_custom_title.value != null then + tpl.title = ctx.opt_custom_title.value.to_s else - sa = a.to_s - d[a] = sa - end - if d.has_key(b) then - sb = d[b] - else - sb = b.to_s - d[b] = sb + tpl.title = "Overview" + end + # intro text + if ctx.opt_custom_intro.value != null then + tpl.text = ctx.opt_custom_intro.value.to_s + end + # module list + for mmodule in mmodules do + if mmodule.mdoc != null then + var mtpl = new Template + mtpl.add mmodule.tpl_link + mtpl.add " " + mtpl.add mmodule.mdoc.short_comment + tpl.modules.add mtpl + end end - return sa <=> sb + # module graph + tpl.graph = tpl_dot + return tpl end - # Keep track of to_s values - attr _dico: HashMap[Object, String] = new HashMap[Object, String] - - init do end + # Genrate dot and template for module hierarchy + fun tpl_dot: nullable TplGraph do + # build poset with public owners + var poset = new POSet[MModule] + for mmodule in mmodules do + poset.add_node(mmodule) + for omodule in mmodules do + if mmodule == omodule then continue + if mmodule.in_importation < omodule then + poset.add_node(omodule) + poset.add_edge(mmodule, omodule) + end + end + end + # build graph + var op = new FlatBuffer + 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 mmodule in poset do + op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n") + for omodule in poset[mmodule].direct_greaters do + op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n") + end + end + op.append("\}\n") + return tpl_graph(op, "dep", "Modules hierarchy") + end end -# Generalization of metamodel entities -class MMEntity - # Return a link to - meth html_link(dctx: DocContext): String is abstract +# The search page +# Display a list of modules, classes and properties +class NitdocSearch + super NitdocPage - # Is the entity should appear in the generaed doc - meth need_doc(dctx: DocContext): Bool is abstract + init(ctx: NitdocContext) do super(ctx) - # Return a one liner description - meth short_doc: String do return " " + redef fun tpl_title do return "Search | {super}" - # The doc node from the AST - # Return null is none - meth doc: nullable ADoc do return null + redef fun tpl_topmenu do + var topmenu = super + topmenu.add_elt("index.html", "Overview", false) + topmenu.add_elt("#", "Search", true) + return topmenu + end - # Human redable location of the entity (module/class/property) - meth locate(dctx: DocContext): String do return "" + redef fun tpl_content do + var tpl = new TplSearchPage + # title + tpl.title = "Search" + # modules list + for mmodule in modules_list do + tpl.modules.add mmodule.tpl_link + end + # classes list + for mclass in classes_list do + tpl.classes.add mclass.tpl_link + end + # properties list + for mproperty in mprops_list do + var m = new Template + m.add mproperty.intro.tpl_link + m.add " (" + m.add mproperty.intro.mclassdef.mclass.tpl_link + m.add ")" + tpl.props.add m + end + return tpl + end - # Part of the prototype before the name (kind, modifiers, qualifier) - meth prototype_head(dctx: DocContext): String is abstract + # Extract mmodule list to display (sorted by name) + private fun modules_list: Array[MModule] do + var sorted = new Array[MModule] + for mmodule in ctx.mbuilder.model.mmodule_importation_hierarchy do + if mmodule.name == "
" then continue + sorted.add mmodule + end + var sorter = new MModuleNameSorter + sorter.sort(sorted) + return sorted + end - # Part of the property after the name (signature, modifiers) - meth prototype_body(dctx: DocContext): String do return "" + # Extract mclass list to display (sorted by name) + private fun classes_list: Array[MClass] do + var sorted = new Array[MClass] + for mclass in ctx.mbuilder.model.mclasses do + if mclass.visibility < ctx.min_visibility then continue + sorted.add mclass + end + var sorter = new MClassNameSorter + sorter.sort(sorted) + return sorted + end + + # Extract mproperty list to display (sorted by name) + private fun mprops_list: Array[MProperty] do + var sorted = new Array[MProperty] + for mproperty in ctx.mbuilder.model.mproperties do + if mproperty.visibility < ctx.min_visibility then continue + if mproperty isa MAttribute then continue + sorted.add mproperty + end + var sorter = new MPropertyNameSorter + sorter.sort(sorted) + return sorted + end end -redef class MMModule -special MMEntity - redef meth html_link(dctx) do - if dctx.module == self then - return "{self}" +# A module page +# Display the list of introduced and redefined classes in module +class NitdocModule + super NitdocPage + + private var mmodule: MModule + private var intro_mclassdefs: Set[MClassDef] + private var redef_mclassdefs: Set[MClassDef] + private var sorted_intro_mclassdefs: Array[MClassDef] + private var sorted_redef_mclassdefs: Array[MClassDef] + + init(mmodule: MModule, ctx: NitdocContext) do + self.mmodule = mmodule + var sorter = new MClassDefNameSorter + intro_mclassdefs = in_nesting_intro_mclassdefs(ctx.min_visibility) + sorted_intro_mclassdefs = intro_mclassdefs.to_a + sorter.sort sorted_intro_mclassdefs + redef_mclassdefs = in_nesting_redef_mclassdefs(ctx.min_visibility) + sorted_redef_mclassdefs = redef_mclassdefs.to_a + sorter.sort sorted_redef_mclassdefs + super(ctx) + end + + redef fun tpl_title do + if mmodule.mdoc != null then + return "{mmodule.nitdoc_name} module | {mmodule.mdoc.short_comment} | {super}" else - return "{self}" + return "{mmodule.nitdoc_name} module | {super}" end end - redef meth need_doc(dctx) do return true - redef meth prototype_head(dctx) do return "module " - attr _known_owner_of_cache: Map[MMModule, MMModule] = new HashMap[MMModule, MMModule] - meth 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) + redef fun tpl_topmenu do + var topmenu = super + topmenu.add_elt("index.html", "Overview", false) + topmenu.add_elt("#", "{mmodule.nitdoc_name}", true) + topmenu.add_elt("search.html", "Search", false) + return topmenu + end + + redef fun tpl_sidebar do + var sidebar = new TplSidebar + tpl_sidebar_classes(sidebar) + tpl_sidebar_inheritance(sidebar) + return sidebar + end + + # Classes to display on sidebar + fun tpl_sidebar_classes(sidebar: TplSidebar) do + var box = new TplSidebarBox("Class Definitions") + var group = new TplSidebarGroup("Introductions") + for mclassdef in sorted_intro_mclassdefs do + tpl_sidebar_item(mclassdef, group) + end + box.elts.add group + group = new TplSidebarGroup("Refinements") + for mclassdef in sorted_redef_mclassdefs do + if intro_mclassdefs.has(mclassdef.mclass.intro) then continue + tpl_sidebar_item(mclassdef, group) + end + box.elts.add group + sidebar.boxes.add box + end + + # Module inheritance to display on sidebar + fun tpl_sidebar_inheritance(sidebar: TplSidebar) do + var box = new TplSidebarBox("Module Hierarchy") + box.elts.add tpl_sidebar_group("Nested Modules", mmodule.in_nesting.direct_greaters.to_a) + var dependencies = new Array[MModule] + for dep in mmodule.in_importation.greaters do + if dep == mmodule or + dep.direct_owner == mmodule or + dep.public_owner == mmodule then continue + dependencies.add(dep) + end + if dependencies.length > 0 then + box.elts.add tpl_sidebar_group("All dependencies", dependencies) + end + var clients = new Array[MModule] + for dep in mmodule.in_importation.smallers do + if dep.name == "
" then continue + if dep == mmodule then continue + clients.add(dep) + end + if clients.length > 0 then + box.elts.add tpl_sidebar_group("All clients", clients) + end + sidebar.boxes.add box + end + + private fun tpl_sidebar_item(mclassdef: MClassDef, group: TplSidebarGroup) do + if mclassdef.is_intro then + group.add_bullet("I", "Introduced", mclassdef.tpl_link_anchor, ["intro"]) else - res = module.owner(self) + group.add_bullet("R", "Redefined", mclassdef.tpl_link_anchor, ["redef"]) end - _known_owner_of_cache[module] = res - return res end - # Return the most general module that own self - meth owner(from: MMModule): MMModule - 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 + private fun tpl_sidebar_group(name: String, elts: Array[MModule]): TplSidebarGroup do + var group = new TplSidebarGroup(name) + for elt in elts do + group.add_elt(elt.tpl_link, new Array[String]) end - return res + return group end - private meth known_owner_of_intern(module: MMModule, from: MMModule, as_owner: Bool): MMModule - 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 + redef fun tpl_content do + var class_sorter = new MClassNameSorter + var tpl = new TplModulePage + tpl.title = mmodule.nitdoc_name + tpl.subtitle = mmodule.tpl_signature + tpl.definition = mmodule.tpl_definition + var location = mmodule.location + tpl.definition.location = tpl_showsource(location) + tpl.definition.github_area = tpl_github(mmodule.full_namespace, mmodule.mdoc, location) + tpl.graph = tpl_dot + for mclassdef in sorted_intro_mclassdefs do tpl.intros.add tpl_mclassdef_article(mclassdef) + for mclassdef in sorted_redef_mclassdefs do + if intro_mclassdefs.has(mclassdef.mclass.intro) then continue + tpl.redefs.add tpl_mclassdef_article(mclassdef) + end + return tpl end -end + # Genrate dot hierarchy for class inheritance + fun tpl_dot: nullable TplGraph do + # build poset with public owners + var poset = new POSet[MModule] + for mmodule in self.mmodule.in_importation.poset do + if mmodule.name == "
" then continue + #if mmodule.public_owner != null then continue + if not mmodule.in_importation < self.mmodule and not self.mmodule.in_importation < mmodule and mmodule != self.mmodule then continue + poset.add_node(mmodule) + for omodule in mmodule.in_importation.poset do + if mmodule == omodule then continue + if omodule.name == "
" then continue + if not omodule.in_importation < self.mmodule and not self.mmodule.in_importation < omodule then continue + if omodule.in_importation < mmodule then + poset.add_node(omodule) + poset.add_edge(omodule, mmodule) + end + if mmodule.in_importation < omodule then + poset.add_node(omodule) + poset.add_edge(mmodule, omodule) + end + #if omodule.public_owner != null then continue + #if mmodule.in_importation < omodule then + #poset.add_node(omodule) + #poset.add_edge(mmodule, omodule) + #end + end + end + # build graph + var op = new FlatBuffer + var name = "dep_{mmodule.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 mmodule in poset do + if mmodule == self.mmodule then + op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n") + else + op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n") + end + for omodule in poset[mmodule].direct_greaters do + op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n") + end + end + op.append("\}\n") + return tpl_graph(op, name, "Dependency graph for module {mmodule.name}") + end -redef class MMLocalProperty -special MMEntity - # Anchor of the property description in the module html file - meth html_anchor: String - do - return "PROP_{local_class}_{cmangle(name)}" + private fun in_nesting_intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do + var res = new HashSet[MClassDef] + for mmodule in self.mmodule.in_nesting.greaters do + res.add_all mmodule.intro_mclassdefs(min_visibility) + end + return res end - redef meth html_link(dctx) - do - var m = module - if not need_doc(dctx) then m = global.intro.module - var m = dctx.known_owner_of(m) - if m == dctx.module then - return "{self}" - else - return "{self}" + private fun in_nesting_redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do + var res = new HashSet[MClassDef] + for mmodule in self.mmodule.in_nesting.greaters do + res.add_all mmodule.redef_mclassdefs(min_visibility) end + return res end - - # Kind of property (meth, attr, etc.) - meth kind: String is abstract +end - redef meth locate(dctx) - do - return "in {module.html_link(dctx)}::{local_class.html_link(dctx)}" +# A class page +# Display a list properties defined or redefined for this class +class NitdocClass + super NitdocPage + + private var mclass: MClass + private var inherited_mpropdefs: Set[MPropDef] + private var intro_mpropdefs: Set[MPropDef] + private var redef_mpropdefs: Set[MPropDef] + private var all_mpropdefs: Set[MPropDef] + + init(mclass: MClass, ctx: NitdocContext) do + self.mclass = mclass + super(ctx) + intro_mpropdefs = mclass_intro_mpropdefs + redef_mpropdefs = mclass_redef_mpropdefs + inherited_mpropdefs = in_nesting_inherited_mpropedefs + all_mpropdefs = new HashSet[MPropDef] + all_mpropdefs.add_all intro_mpropdefs + all_mpropdefs.add_all redef_mpropdefs + all_mpropdefs.add_all inherited_mpropdefs end - meth known_intro_class(dctx: DocContext): MMLocalClass - do - var mod = dctx.known_owner_of(global.intro.local_class.module) - var cla = mod[global.intro.local_class.global] - return cla + private fun in_nesting_inherited_mpropedefs: Set[MPropDef] do + var res = new HashSet[MPropDef] + var local = mclass.local_mproperties(ctx.min_visibility) + for mprop in mclass.inherited_mproperties(ctx.mainmodule, ctx.min_visibility) do + if local.has(mprop) then continue + if mprop isa MMethod and mprop.is_init then continue + res.add mprop.intro + end + return res end - redef meth prototype_head(dctx) - 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 ") - 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)}::") + private fun mclass_intro_mpropdefs: Set[MPropDef] do + var res = new HashSet[MPropDef] + for mclassdef in mclass.mclassdefs do + var owner = mclassdef.mmodule.public_owner + if owner == null then owner = mclassdef.mmodule + for mpropdef in mclassdef.mpropdefs do + if not mpropdef.is_intro then continue + if owner != mclass.public_owner then continue + var mprop = mpropdef.mproperty + if mprop isa MMethod and mprop.is_init and mclass.is_abstract then continue + if mprop.visibility < ctx.min_visibility then continue + res.add mpropdef end end - return res.to_s + return res end - redef meth prototype_body(dctx) - do - var res = new Buffer - res.append(signature.to_html(dctx)) - var s = self - if s.node != null then - if s.node isa ADeferredMethPropdef then - res.append(" is abstract") - else if s.node isa AInternMethPropdef then - res.append(" is intern") + private fun mclass_redef_mpropdefs: Set[MPropDef] do + var res = new HashSet[MPropDef] + for mclassdef in mclass.mclassdefs do + if mclassdef == mclass.intro then continue + var owner = mclassdef.mmodule.public_owner + if owner == null then owner = mclassdef.mmodule + for mpropdef in mclassdef.mpropdefs do + if owner == mclass.public_owner then continue + if mpropdef.mproperty.visibility < ctx.min_visibility then continue + res.add mpropdef end end - return res.to_s + return res end - redef meth need_doc(dctx) - 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 - end - if global.intro == self then - return true + redef fun tpl_title do + if mclass.mdoc != null then + return "{mclass.nitdoc_name} class | {mclass.mdoc.short_comment} | {super}" + else + return "{mclass.nitdoc_name} class | {super}" end - return doc != null end - redef meth short_doc - do - var d = doc - if d != null then - return d.short - else if global.intro == self then - return " " + redef fun tpl_topmenu do + var topmenu = super + var mmodule: MModule + if mclass.public_owner == null then + mmodule = mclass.intro_mmodule else - return global.intro.short_doc + mmodule = mclass.public_owner.as(not null) end + topmenu.add_elt("index.html", "Overview", false) + topmenu.add_elt("{mmodule.nitdoc_url}", "{mmodule.nitdoc_name}", false) + topmenu.add_elt("#", "{mclass.nitdoc_name}", true) + topmenu.add_elt("search.html", "Search", false) + return topmenu end - - redef meth doc - do - var n = node - if n == null or not node isa PPropdef then - return null - end - assert n isa PPropdef - var d = n.n_doc - if d == null then - return null - end - assert d isa ADoc - if d.n_comment.is_empty then - return null + + redef fun tpl_sidebar do + var sidebar = new TplSidebar + tpl_sidebar_properties(sidebar) + tpl_sidebar_inheritance(sidebar) + return sidebar + end + + # Property list to display in sidebar + fun tpl_sidebar_properties(sidebar: TplSidebar) do + var kind_map = sort_by_kind(all_mpropdefs) + var sorter = new MPropDefNameSorter + var box = new TplSidebarBox("Properties") + # virtual types + var elts = kind_map["type"].to_a + sorter.sort(elts) + var group = new TplSidebarGroup("Virtual Types") + for mprop in elts do + tpl_sidebar_item(mprop, group) + end + box.elts.add group + # constructors + elts = kind_map["init"].to_a + sorter.sort(elts) + group = new TplSidebarGroup("Constructors") + for mprop in elts do + tpl_sidebar_item(mprop, group) + end + box.elts.add group + # methods + elts = kind_map["fun"].to_a + sorter.sort(elts) + group = new TplSidebarGroup("Methods") + for mprop in elts do + tpl_sidebar_item(mprop, group) + end + box.elts.add group + sidebar.boxes.add box + end + + # Class inheritance to display in sidebar + fun tpl_sidebar_inheritance(sidebar: TplSidebar) do + var sorted = new Array[MClass] + var sorterp = new MClassNameSorter + var box = new TplSidebarBox("Inheritance") + var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a + if greaters.length > 1 then + ctx.mainmodule.linearize_mclasses(greaters) + box.elts.add tpl_sidebar_group("Superclasses", greaters) + end + var smallers = mclass.in_hierarchy(ctx.mainmodule).smallers.to_a + var direct_smallers = mclass.in_hierarchy(ctx.mainmodule).direct_smallers.to_a + if smallers.length <= 1 then + box.elts.add(new TplSidebarGroup("No Known Subclasses")) + else if smallers.length <= 100 then + ctx.mainmodule.linearize_mclasses(smallers) + box.elts.add tpl_sidebar_group("Subclasses", smallers) + else if direct_smallers.length <= 100 then + ctx.mainmodule.linearize_mclasses(direct_smallers) + box.elts.add tpl_sidebar_group("Direct Subclasses Only", direct_smallers) else - return d + box.elts.add(new TplSidebarGroup("Too much Subclasses to list")) end + sidebar.boxes.add box end -end -redef class MMMethod - redef meth kind do return if global.is_init then "init" else "meth" -end -redef class MMAttribute - redef meth kind do return "attr" -end -redef class MMTypeProperty - redef meth kind do return "type" -end -redef class MMSrcModule - # Extract and generate html file for the module - meth extract_module_doc(dctx: DocContext) - do - dctx.register(self) - - 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 + private fun tpl_sidebar_item(mprop: MPropDef, group: TplSidebarGroup) do + if mprop.is_intro and mprop.mclassdef.mclass == mclass then + group.add_bullet("I", "Introduced", mprop.tpl_link, ["intro"]) + else if mprop.is_intro and mprop.mclassdef.mclass != mclass then + group.add_bullet("H", "Inherited", mprop.tpl_link, ["inherit"]) + else + group.add_bullet("R", "Redefined", mprop.tpl_link, ["redef"]) + end + 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 + private fun tpl_sidebar_group(name: String, elts: Array[MClass]): TplSidebarGroup do + var group = new TplSidebarGroup(name) + for elt in elts do + if elt == mclass then continue + group.add_elt(elt.tpl_link, new Array[String]) end + return group end - meth extract_module_doc_inside(dctx: DocContext) - 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) + redef fun tpl_content do + var intro = mclass.intro + var tpl = new TplClassPage + tpl.title = "{mclass.nitdoc_name}{mclass.tpl_short_signature}" + tpl.subtitle = mclass.tpl_namespace_with_signature + tpl.definition = intro.tpl_definition + var location = intro.location + tpl.definition.location = tpl_showsource(location) + tpl.definition.github_area = tpl_github(intro.full_namespace, intro.mdoc, location) + tpl.graph = tpl_dot + + # properties + var prop_sorter = new MPropDefNameSorter + var kind_map = sort_by_kind(intro_mpropdefs) + + # virtual types + var elts = kind_map["type"].to_a + prop_sorter.sort(elts) + for elt in elts do tpl.types.add tpl_mpropdef_article(elt) + + # constructors + elts = kind_map["init"].to_a + prop_sorter.sort(elts) + for elt in elts do tpl.inits.add tpl_mpropdef_article(elt) + + # intro methods + elts = kind_map["fun"].to_a + prop_sorter.sort(elts) + for elt in elts do tpl.methods.add tpl_mpropdef_article(elt) + + # redef methods + kind_map = sort_by_kind(redef_mpropdefs) + var module_sorter = new MModuleNameSorter + var module_map = sort_by_mmodule(kind_map["fun"]) + var owner_map = sort_by_public_owner(module_map.keys) + var owners = owner_map.keys.to_a + module_sorter.sort owners + + var ctpl = new TplConcernList + var mtpl = new Template + for owner in owners do + # concerns list + var octpl = new TplConcernListElt + octpl.anchor = "#{owner.nitdoc_anchor}" + octpl.name = owner.nitdoc_name + if owner.mdoc != null then + octpl.comment = owner.mdoc.short_comment end - 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") - 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") - 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") - end - dctx.add("
\n") - - 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 + ctpl.elts.add octpl + # concern section + var otpl = new TplTopConcern + otpl.anchor = owner.nitdoc_anchor + otpl.concern = owner.tpl_link + mtpl.add otpl + + var mmodules = owner_map[owner].to_a + module_sorter.sort mmodules + var stpl = new TplConcernList + for mmodule in mmodules do + # concerns list + if mmodule != owner then + var mctpl = new TplConcernListElt + mctpl.anchor = "#{mmodule.nitdoc_anchor}" + mctpl.name = mmodule.nitdoc_name + if mmodule.mdoc != null then + mctpl.comment = mmodule.mdoc.short_comment end + stpl.elts.add mctpl + # concern section + var cctpl = new TplConcern + cctpl.anchor = mmodule.nitdoc_anchor + cctpl.concern = mmodule.tpl_link + if mmodule.mdoc != null then + cctpl.comment = mmodule.mdoc.short_comment + end + mtpl.add cctpl end + var mprops = module_map[mmodule].to_a + prop_sorter.sort mprops + for mprop in mprops do mtpl.add tpl_mpropdef_article(mprop) end + ctpl.elts.add stpl + end + if not owners.is_empty then + tpl.concerns = ctpl end + tpl.methods.add mtpl + return tpl + 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") + private fun sort_by_kind(mpropdefs: Set[MPropDef]): Map[String, Set[MPropDef]] do + var map = new HashMap[String, Set[MPropDef]] + map["type"] = new HashSet[MPropDef] + map["init"] = new HashSet[MPropDef] + map["fun"] = new HashSet[MPropDef] + for mpropdef in mpropdefs do + if mpropdef isa MVirtualTypeDef then + map["type"].add mpropdef + else if mpropdef isa MMethodDef then + if mpropdef.mproperty.is_init then + map["init"].add mpropdef + else + map["fun"].add mpropdef + end end - dctx.add("
Class Summary of {self}
{c.prototype_head(dctx)}{c.html_link(dctx)}{c.prototype_body(dctx)}
{c.short_doc}

\n") end + return map + end - if not new_classes.is_empty then - dctx.add("\n") - dctx.add("\n") - dctx.add("
Class Detail of {self}
\n") + private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do + var map = new HashMap[MModule, Set[MPropDef]] + for mpropdef in mpropdefs do + var mmodule = mpropdef.mclassdef.mmodule + if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef] + map[mmodule].add mpropdef + end + return map + end - for c in new_classes do - c.extract_class_doc(dctx) - end + private fun sort_by_public_owner(mmodules: Collection[MModule]): Map[MModule, Set[MModule]] do + var map = new HashMap[MModule, Set[MModule]] + for mmodule in mmodules do + var owner = mmodule + if mmodule.public_owner != null then owner = mmodule.public_owner.as(not null) + if not map.has_key(owner) then map[owner] = new HashSet[MModule] + map[owner].add mmodule end + return map + end - dctx.add("\n") + # Generate dot hierarchy for classes + fun tpl_dot: nullable TplGraph do + var pe = mclass.in_hierarchy(ctx.mainmodule) + var cla = new HashSet[MClass] + var sm = new HashSet[MClass] + var sm2 = new HashSet[MClass] + sm.add(mclass) + 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(pe.poset[x].direct_smallers) + end + var t = sm + sm = sm2 + sm2 = t + end + cla.add_all(pe.greaters) + + var op = new FlatBuffer + var name = "dep_{mclass.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 == mclass then + op.append("\"{c.name}\"[shape=box,margin=0.03];\n") + else + op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n") + end + for c2 in pe.poset[c].direct_greaters do + if not cla.has(c2) then continue + op.append("\"{c.name}\"->\"{c2.name}\";\n") + end + if not pe.poset[c].direct_smallers.is_empty then + var others = true + for c2 in pe.poset[c].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 + end + op.append("\}\n") + return tpl_graph(op, name, "Dependency graph for class {mclass.name}") end +end - redef meth short_doc - do - var d = doc - if d != null then - return d.short - else - return " " +# +# Model redefs +# + +redef class MModule + # Return the HTML escaped name of the module + private fun nitdoc_name: String do return name.html_escape + + private fun full_namespace: String do + if public_owner != null then + return "{public_owner.nitdoc_name}::{nitdoc_name}" end + return nitdoc_name end - redef meth doc - do - var n = node - if n.n_packagedecl == null then - return null - end - var np = n.n_packagedecl - assert np isa APackagedecl - var d = np.n_doc - if d == null then - return null - end - assert d isa ADoc - if d.n_comment.is_empty then - return null - else - return d + # URL to nitdoc page + # module_owner_name.html + private fun nitdoc_url: String do + var res = new FlatBuffer + res.append("module_") + var mowner = public_owner + if mowner != null then + res.append("{public_owner.name}_") end + res.append("{self.name}.html") + return res.to_s end -end -redef class ADoc - # Html transcription of the doc - meth to_html: String - do - var res = new Buffer - for c in n_comment do - res.append(c.text.substring_from(1)) + # html nitdoc_anchor id for the module in a nitdoc page + # MOD_owner_name + private fun nitdoc_anchor: String do + var res = new FlatBuffer + res.append("MOD_") + var mowner = public_owner + if mowner != null then + res.append("{public_owner.name}_") end + res.append(self.name) return res.to_s end - # Oneliner transcription of the doc - meth short: String - do - return n_comment.first.text.substring_from(1) + # Return a link (html a tag) to the nitdoc module page + private fun tpl_link: TplLink do + var tpl = new TplLink + tpl.href = nitdoc_url + tpl.text = nitdoc_name + if mdoc != null then + tpl.title = mdoc.short_comment + end + return tpl end -end -redef class MMLocalClass -special MMEntity - # Anchor of the class description in the module html file - meth html_anchor: String do return "CLASS_{self}" + # Return the module signature decorated with html + private fun tpl_signature: Template do + var tpl = new Template + tpl.add "module " + tpl.add tpl_full_namespace + tpl.add "" + return tpl + end - redef meth html_link(dctx) - do - var m = module - if not need_doc(dctx) then m = global.module - var m = dctx.known_owner_of(m) - if m == dctx.module then - return "{self}" + # Return the module full namespace decorated with html + private fun tpl_full_namespace: Template do + var tpl = new Template + tpl.add ("") + var mowner = public_owner + if mowner != null then + tpl.add public_owner.tpl_namespace + tpl.add "::" + end + tpl.add tpl_link + tpl.add "" + return tpl + end + + # Return the module full namespace decorated with html + private fun tpl_namespace: Template do + var tpl = new Template + tpl.add "" + var mowner = public_owner + if mowner != null then + tpl.add public_owner.tpl_namespace else - return "{self}" + tpl.add tpl_link end + tpl.add "" + return tpl end - redef meth short_doc do return global.intro.short_doc - - redef meth doc do return global.intro.doc + # Description with short comment + private fun tpl_short_definition: TplDefinition do + var tpl = new TplDefinition + tpl.namespace = tpl_namespace + if mdoc != null then + tpl.comment = mdoc.tpl_short_comment + end + return tpl + end - redef meth 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 + # Description with full comment + private fun tpl_definition: TplDefinition do + var tpl = new TplDefinition + tpl.namespace = tpl_namespace + if mdoc != null then + tpl.comment = mdoc.tpl_comment end - return false + return tpl end +end - redef meth locate(dctx) do return "in {module.html_link(dctx)}" +redef class MClass + # Return the HTML escaped name of the module + private fun nitdoc_name: String do return name.html_escape - meth known_intro(dctx: DocContext): MMLocalClass do return dctx.known_owner_of(global.intro.module)[global] + # URL to nitdoc page + # class_owner_name.html + private fun nitdoc_url: String do + return "class_{public_owner}_{name}.html" + end - redef meth prototype_head(dctx) - 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 + # html nitdoc_anchor id for the class in a nitdoc page + # MOD_owner_name + private fun nitdoc_anchor: String do + return "CLASS_{public_owner.name}_{name}" end - redef meth prototype_body(dctx) - 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("]") + # Return a link (with signature) to the nitdoc class page + private fun tpl_link: TplLink do + var tpl = new TplLink + tpl.href = nitdoc_url + tpl.text = "{nitdoc_name}{tpl_short_signature.write_to_string}" + if intro.mdoc != null then + tpl.title = intro.mdoc.short_comment end - return res.to_s + return tpl end - # Extract the doc of a class - meth extract_class_doc(dctx: DocContext) - 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 + # Return a short link (without signature) to the nitdoc class page + private fun tpl_short_link: TplLink do + var tpl = new TplLink + tpl.href = nitdoc_url + tpl.text = nitdoc_name + if intro.mdoc != null then + tpl.title = intro.mdoc.short_comment end - if not sup2.is_empty then - dctx.add("
Other direct subclasses in known modules:
{sup2.join(", ")}\n") + return tpl + end + + # Return a link (with signature) to the class nitdoc_anchor + private fun tpl_link_anchor: TplLink do + var tpl = new TplLink + tpl.href = "#{nitdoc_anchor}" + tpl.text = "{nitdoc_name}{tpl_short_signature.write_to_string}" + if intro.mdoc != null then + tpl.title = intro.mdoc.short_comment 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)) + return tpl + end + + # Return the generic signature of the class with bounds + private fun tpl_signature: Template do + var tpl = new Template + if arity > 0 then + tpl.add "[" + for i in [0..intro.parameter_names.length[ do + tpl.add "{intro.parameter_names[i]}: " + tpl.add intro.bound_mtype.arguments[i].tpl_link + if i < intro.parameter_names.length - 1 then tpl.add ", " end + tpl.add "]" end - if not sup2.is_empty then - dctx.add("
Refinements in known modules:
{sup2.join(", ")}\n") - end - dctx.add("
\n") + return tpl + end - var doc = doc - if doc != null then - dctx.add("
{doc.to_html}
\n") + # Return the generic signature of the class without bounds + private fun tpl_short_signature: String do + if arity > 0 then + return "[{intro.parameter_names.join(", ")}]" + else + return "" end + 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]) + # Return the class namespace decorated with html + private fun tpl_namespace: Template do + var tpl = new Template + tpl.add intro_mmodule.tpl_namespace + tpl.add "::" + tpl.add tpl_short_link + tpl.add "" + return tpl + end - dctx.add("

\n") + private fun tpl_namespace_with_signature: Template do + var tpl = new Template + tpl.add intro.tpl_modifiers + tpl.add intro.mmodule.tpl_namespace + tpl.add "::" + tpl.add nitdoc_name + tpl.add tpl_signature + return tpl end +end - meth pass_name(pass: Int): String - do - var names = once ["Virtual Types", "Consructors", "Methods", "Attributes"] - return names[pass] +redef class MProperty + # Escape name for html output + private fun nitdoc_name: String do return name.html_escape + + # Return the property namespace decorated with html + private fun tpl_namespace: Template do + var tpl = new Template + tpl.add intro_mclassdef.mclass.tpl_namespace + tpl.add intro_mclassdef.mclass.tpl_short_signature + tpl.add "::" + tpl.add intro.tpl_link + tpl.add "" + return tpl end - - meth accept_prop(p: MMLocalProperty, pass: Int): 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 - end - abort + + private fun tpl_signature: Template is abstract +end + +redef class MMethod + redef fun tpl_signature do return intro.msignature.tpl_signature +end + +redef class MVirtualTypeProp + redef fun tpl_signature do + var tpl = new Template + tpl.add ": " + tpl.add intro.bound.tpl_link + return tpl end +end - meth property_summary(dctx: DocContext, pass: Int): Array[MMLocalProperty] - do - var passname = pass_name(pass) - dctx.open_stage - dctx.stage("\n") - dctx.stage("\n") - - var new_props = new Array[MMLocalProperty] - 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) - end - if cla.is_empty then continue - cla = crhe.order.select_smallests(cla) - end +redef class MType + # Link to the type definition in the nitdoc page + private fun tpl_link: Template is abstract +end - new_props.add(p) - if p.global.intro == p then - dctx.register(p) - end - end - dctx.sort(new_props) - for p in new_props do - dctx.add("\n") - end - dctx.stage("
{passname} Summary of {self}
{p.prototype_head(dctx)}{p.html_link(dctx)}{p.prototype_body(dctx)}
    {p.short_doc}

\n") - - 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]] - var props = new Array[MMLocalClass] - 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 - end - dctx.open_stage - dctx.stage("\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.close_stage - - dctx.open_stage - dctx.stage("\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") - end - dctx.close_stage - end - - var mmap = new HashMap[MMModule, Array[MMLocalProperty]] - var props = new Array[MMLocalClass] - 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] - else - props = new Array[MMLocalProperty] - mmap[km] = props - 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) - end - end - # c.properties_inherited_from(dctx, self, pass) - end - 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") - 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") - dctx.close_stage +redef class MClassType + redef fun tpl_link do return mclass.tpl_link +end - dctx.close_stage - return new_props +redef class MNullableType + redef fun tpl_link do + var tpl = new Template + tpl.add "nullable " + tpl.add mtype.tpl_link + return tpl end +end - meth 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.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") - end - dctx.stage("
\n") - dctx.close_stage +redef class MGenericType + redef fun tpl_link: Template do + var tpl = new Template + tpl.add mclass.tpl_short_link + tpl.add "[" + for i in [0..arguments.length[ do + tpl.add arguments[i].tpl_link + if i < arguments.length - 1 then tpl.add ", " + end + tpl.add "]" + return tpl + end +end - dctx.open_stage - dctx.stage("
\n") - end - dctx.close_stage +redef class MParameterType + redef fun tpl_link do + var name = mclass.intro.parameter_names[rank] + var tpl = new TplLink + tpl.href = "{mclass.nitdoc_url}#FT_{name}" + tpl.text = name + tpl.title = "formal type" + return tpl + end +end + +redef class MVirtualType + redef fun tpl_link do return mproperty.intro.tpl_link +end - dctx.close_stage +redef class MClassDef + # Return the classdef namespace decorated with html + private fun tpl_namespace: Template do + var tpl = new Template + tpl.add mmodule.tpl_namespace + tpl.add "::" + tpl.add mclass.tpl_link + tpl.add "" + return tpl end - # Add rows for properties inheriterd to some heirs - meth 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)) - end + private fun full_namespace: String do + return "{mmodule.full_namespace}::{mclass.nitdoc_name}" + end + + private fun tpl_link_anchor: TplLink do return mclass.tpl_link_anchor + + private fun tpl_article: TplArticle do + var tpl = new TplArticle + tpl.id = mclass.nitdoc_anchor + tpl.classes.add_all(tpl_css_classes) + tpl.title = new Template + tpl.title.add mclass.tpl_short_link + tpl.title.add mclass.tpl_signature + tpl.subtitle = new Template + tpl.subtitle.add tpl_modifiers + tpl.subtitle.add tpl_namespace + tpl.content = new Template + tpl.content.add tpl_definition + return tpl + end + + private fun tpl_css_classes: Set[String] do + var set = new HashSet[String] + set.add_all mclass.intro.modifiers + set.add_all modifiers + return set + end + + private fun tpl_modifiers: Template do + var tpl = new Template + for modifier in modifiers do + if modifier == "public" then continue + tpl.add "{modifier} " 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) - end - dctx.add("in {s}{properties.join(", ")}\n") + return tpl + end + + private fun tpl_short_definition: TplDefinition do + var tpl = new TplDefinition + tpl.namespace = mmodule.tpl_full_namespace + if mdoc != null then + tpl.comment = mdoc.tpl_short_comment end + return tpl end -end -redef class MMSrcLocalClass - redef meth short_doc - do - var d = doc - if d != null then - return d.short - else if global.intro == self then - return " " - else - var bc = global.intro - return bc.short_doc + private fun tpl_definition: TplDefinition do + var tpl = new TplDefinition + tpl.namespace = mmodule.tpl_full_namespace + if mdoc != null then + tpl.comment = mdoc.tpl_comment end + return tpl + end +end + +redef class MPropDef + # Return the full qualified name of the mpropdef + # module::classdef::name + private fun tpl_namespace: Template do + var tpl = new Template + tpl.add mclassdef.tpl_namespace + tpl.add "::" + tpl.add mproperty.name + return tpl end - redef meth doc - do - var n = nodes.first - if not n isa AClassdef then - return null - end - assert n isa AClassdef - var d = n.n_doc - if d == null then - return null - end - assert d isa ADoc - if d.n_comment.is_empty then - return null - else - return d + private fun full_namespace: String do + return "{mclassdef.full_namespace}::{mproperty.nitdoc_name}" + end + + # URL into the nitdoc page + # class_owner_name.html#nitdoc_anchor + private fun nitdoc_url: String do + return "{mclassdef.mclass.nitdoc_url}#{nitdoc_anchor}" + end + + # html nitdoc_anchor id for the property in a nitdoc class page + # PROP_mclass_propertyname + private fun nitdoc_anchor: String do + return "PROP_{mclassdef.mclass.public_owner.nitdoc_name}_{mproperty.name.replace(" ", "_")}" + end + + # Return a link to property into the nitdoc class page + # nitdoc_name + private fun tpl_link: TplLink do + var tpl = new TplLink + tpl.href = nitdoc_url + tpl.text = mproperty.nitdoc_name + if mproperty.intro.mdoc != null then + tpl.title = mproperty.intro.mdoc.short_comment end + return tpl end - redef meth 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 + private fun tpl_article: TplArticle do + var tpl = new TplArticle + tpl.id = nitdoc_anchor + tpl.classes.add_all(tpl_css_classes) + tpl.title = new Template + tpl.title.add mproperty.nitdoc_name + tpl.title.add mproperty.tpl_signature + tpl.subtitle = new Template + tpl.subtitle.add tpl_modifiers + tpl.subtitle.add tpl_namespace + tpl.content = new Template + tpl.content.add tpl_definition + return tpl + end + + private fun tpl_css_classes: Set[String] do + var set = new HashSet[String] + set.add_all mproperty.intro.modifiers + set.add_all modifiers + return set + end + + private fun tpl_modifiers: Template do + var tpl = new Template + for modifier in modifiers do + if modifier == "public" then continue + tpl.add "{modifier} " end - if global.intro == self then - return true + return tpl + end + + private fun tpl_short_definition: TplDefinition do + var tpl = new TplDefinition + tpl.namespace = mclassdef.tpl_namespace + if mdoc != null then + tpl.comment = mdoc.tpl_short_comment end - for p in src_local_properties do - if p.need_doc(dctx) then - return true - end + return tpl + end + + private fun tpl_definition: TplDefinition do + var tpl = new TplDefinition + tpl.namespace = mclassdef.tpl_namespace + if mdoc != null then + tpl.comment = mdoc.tpl_comment end - return super + return tpl end end -redef class MMSignature - # Htlm transcription of the signature (with nested links) - meth to_html(dctx: DocContext): String - do - 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(", ") - res.append(self[i].html_link(dctx)) +redef class MSignature + private fun tpl_signature: Template do + var tpl = new Template + if not mparameters.is_empty then + tpl.add "(" + for i in [0..mparameters.length[ do + tpl.add mparameters[i].tpl_link + if i < mparameters.length - 1 then tpl.add ", " end - res.append(")") + tpl.add ")" end - if return_type != null then - res.append(": ") - res.append(return_type.html_link(dctx)) + if return_mtype != null then + tpl.add ": " + tpl.add return_mtype.tpl_link end - return res.to_s + return tpl end end -redef class MMType - # Htlm transcription of the type (with nested links) - meth html_link(dctx: DocContext): String do return to_s +redef class MParameter + private fun tpl_link: Template do + var tpl = new Template + tpl.add "{name}: " + tpl.add mtype.tpl_link + if is_vararg then tpl.add "..." + return tpl + end end -redef class MMTypeSimpleClass - redef meth html_link(dctx) do return local_class.html_link(dctx) +redef class Location + fun github(gitdir: String): String do + var base_dir = getcwd.join_path(gitdir).simplify_path + var file_loc = getcwd.join_path(file.filename).simplify_path + var gith_loc = file_loc.substring(base_dir.length + 1, file_loc.length) + return "{gith_loc}:{line_start},{column_start}--{line_end},{column_end}" + end end -redef class MMTypeGeneric - redef meth html_link(dctx) - do - var res = new Buffer - res.append(local_class.html_link(dctx)) - res.append("[") - res.append(params[0].html_link(dctx)) - for i in [1..params.length[ do - res.append(", ") - res.append(params[i].html_link(dctx)) - end - res.append("]") - return res.to_s +redef class MDoc + private fun short_comment: String do + return content.first.html_escape + end + + private fun full_comment: String do + return content.join("\n").html_escape + end + + private fun tpl_short_comment: TplShortComment do + return new TplShortComment(short_markdown) + end + + private fun tpl_comment: TplComment do + return new TplComment(full_markdown) end end -var c = new DocContext -c.exec_cmd_line +var nitdoc = new NitdocContext +nitdoc.generate_nitdoc +