X-Git-Url: http://nitlanguage.org diff --git a/src/nitdoc.nit b/src/nitdoc.nit index 7fd49a4..3c3539f 100644 --- a/src/nitdoc.nit +++ b/src/nitdoc.nit @@ -12,1594 +12,255 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Documentation generator for the nit language. -# Generate API documentation in HTML format from nit source code. +# Generator of static API documentation 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 +import doc::static -# The NitdocContext contains all the knowledge used for doc generation -class NitdocContext +redef class ToolContext - private var toolcontext = new ToolContext - private var mbuilder: ModelBuilder - private var mainmodule: MModule - private var output_dir: String - private var min_visibility: MVisibility + # Nitdoc generation phase + var docphase: Phase = new Nitdoc(self, null) - 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") + # Directory where the Nitdoc is rendered + var opt_dir = new OptionString("Output directory", "-d", "--dir") - 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") + # Do not generate documentation for attributes + var opt_no_attributes = new OptionBool("Ignore the attributes", "--no-attributes") - 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") + # Do not generate documentation for private properties + var opt_private = new OptionBool("Also generate private API", "--private") - 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") + # Use a shareurl instead of copy shared files + # + # This is usefull if you don't want to store the Nitdoc templates with your + # documentation. + var opt_shareurl = new OptionString("Use shareurl instead of copy shared files", "--shareurl") - 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) + # Use a custom title for the homepage + var opt_custom_title = new OptionString("Custom title for homepage", "--custom-title") - 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) + # Display a custom brand or logo in the documentation top menu + var opt_custom_brand = new OptionString("Custom link to external site", "--custom-brand") - self.process_options - self.parse(toolcontext.option_context.rest) - end + # Display a custom introduction text before the packages overview + var opt_custom_intro = new OptionString("Custom intro text for homepage", "--custom-overview-text") - 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 + # Display a custom footer on each documentation page + # + # Generally used to display the documentation or product version. + var opt_custom_footer = new OptionString("Custom footer text", "--custom-footer-text") - 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 - - private fun generate_nitdoc do - init_output_dir - overview - search - modules - classes - quicksearch_list - end + # Piwik tracker URL + # + # If you want to monitor your visitors. + var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: `nitlanguage.org/piwik/`)", "--piwik-tracker") - 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 + # Piwik tracker site id + var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id") - end + # Do not generate dot/graphviz diagrams + var opt_nodot = new OptionBool("Do not generate graphs with graphviz", "--no-dot") - private fun overview do - var overviewpage = new NitdocOverview(self) - overviewpage.render.write_to_file("{output_dir.to_s}/index.html") - end + # Do not include highlighted code + var opt_nocode = new OptionBool("Do not generate code with nitlight", "--no-code") - private fun search do - var searchpage = new NitdocSearch(self) - searchpage.render.write_to_file("{output_dir.to_s}/search.html") - end + # File pattern used to link documentation to source code. + var opt_source = new OptionString("Format to link source code (%f for filename, " + + "%l for first line, %L for last line) only works with option --no-code", "--source") - 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 - end + # Disable HTML rendering + var opt_norender = new OptionBool("DO not render any HTML", "--no-render") - 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 + # Test mode + # + # Display test data and remove the progress bar + var opt_test = new OptionBool("Output test data", "--test") - private fun quicksearch_list do - var quicksearch = new QuickSearch(self) - quicksearch.render.write_to_file("{output_dir.to_s}/quicksearch-list.js") + redef init do + super + option_context.add_option( + opt_dir, opt_no_attributes, opt_private, + opt_share_dir, opt_shareurl, opt_custom_title, + opt_custom_footer, opt_custom_intro, opt_custom_brand, + opt_piwik_tracker, opt_piwik_site_id, + opt_nodot, opt_nocode, opt_source, opt_norender, opt_test) end end -# 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 +redef class DocModel - 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 - - 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 - tpl.add " \};" - return tpl + # Generate a documentation page + fun gen_page(page: DocPage, output_dir: String) do + page.apply_structure(self) + page.render(self).write_to_file("{output_dir}/{page.html_url}") end end -# Nitdoc base page -# Define page structure and properties -abstract class NitdocPage - - private var ctx: NitdocContext - private var shareurl = "." - - init(ctx: NitdocContext) do - self.ctx = ctx - if ctx.opt_shareurl.value != null then shareurl = ctx.opt_shareurl.value.as(not null) - end - - # 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 - return "Nitdoc" - end - end - - # Page template - fun tpl_head: TplHead do return new TplHead(tpl_title, shareurl) - - # 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 - - # Page sidebar template - # return null if no sidebar for this page - fun tpl_sidebar: nullable TplSidebar do return null - - # 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 +# Nitdoc phase explores the model and generate pages for each mentity found +private class Nitdoc + super Phase - # 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 - - # A (source) link template for a given location - fun tpl_showsource(location: nullable Location): nullable String + redef fun process_mainmodule(mainmodule, mmodules) do - 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 - - # 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 - - # 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 - - # 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 = "" + var modelbuilder = toolcontext.modelbuilder + var model = modelbuilder.model + + var min_visibility = private_visibility + if not toolcontext.opt_private.value then min_visibility = protected_visibility + var accept_attribute = true + if toolcontext.opt_no_attributes.value then accept_attribute = false + + var catalog = new Catalog(toolcontext.modelbuilder) + catalog.build_catalog(mainmodule.model.mpackages) + + var filter = new ModelFilter( + min_visibility, + accept_attribute = accept_attribute, + accept_fictive = true, + accept_generated = true, + accept_test = false, + accept_redef = true, + accept_extern = true, + accept_empty_doc = true, + accept_example = true, + accept_broken = false) + + var doc = new DocModel(model, mainmodule, modelbuilder, catalog, filter) + + model.nitdoc_md_processor = doc.md_processor + doc.no_dot = toolcontext.opt_nodot.value + doc.no_code = toolcontext.opt_nocode.value + doc.code_url = toolcontext.opt_source.value + doc.share_url = toolcontext.opt_shareurl.value + doc.custom_brand = toolcontext.opt_custom_brand.value + doc.custom_title = toolcontext.opt_custom_title.value + doc.custom_footer = toolcontext.opt_custom_footer.value + doc.custom_intro = toolcontext.opt_custom_intro.value + doc.tracker_url = toolcontext.opt_piwik_tracker.value + doc.piwik_site_id = toolcontext.opt_piwik_site_id.value + + # Prepare output dir + var test_mode = toolcontext.opt_test.value + var no_render = toolcontext.opt_norender.value + var output_dir = toolcontext.opt_dir.value or else "doc" + + if not no_render then + output_dir.mkdir + + # Copy assets + var share_dir = toolcontext.opt_share_dir.value or else "{toolcontext.share_dir}/nitdoc" + sys.system("cp -r -- {share_dir.escape_to_sh}/* {output_dir.escape_to_sh}/") + end + + # Collect model to document + var mpackages = model.collect_mpackages(filter) + var mgroups = model.collect_mgroups(filter) + var nmodules = model.collect_mmodules(filter) + var mclasses = model.collect_mclasses(filter) + var mprops = model.collect_mproperties(filter) + + var mentities = new Array[MEntity] + mentities.add_all mpackages + mentities.add_all mgroups + mentities.add_all nmodules + mentities.add_all mclasses + mentities.add_all mprops + + var persons = doc.catalog.persons + var tags = doc.catalog.tag2proj.keys + + # Prepare progress bar + var count = 0 + var pages = 1 # count homepage + pages += mentities.length + pages += persons.length + pages += tags.length + + print "Generating documentation pages..." + var progress = new TermProgress(pages, 0) + if not test_mode then progress.display + + # Make pages + count += 1 + if not test_mode then progress.update(count, "homepage") + if not no_render then doc.gen_page(new PageHome("Overview"), output_dir) + + for mentity in mentities do + count += 1 + if not test_mode then progress.update(count, "page {count}/{pages}") + if not no_render then doc.gen_page(new PageMEntity(mentity), output_dir) + end + for name, person in persons do + count += 1 + if not test_mode then progress.update(count, "page {count}/{pages}") + if not no_render then doc.gen_page(new PagePerson(person), output_dir) + end + for tag in tags do + count += 1 + if not test_mode then progress.update(count, "page {count}/{pages}") + if not no_render then doc.gen_page(new PageTag(tag), output_dir) + end + + if not test_mode then print "" # finalise progress + if not no_render then + doc.create_index_file("{output_dir}/quicksearch-list.js") + print "Documentation produced in `{output_dir}`" + end + + if test_mode then + print "Generated {count}/{pages} pages" + print " PageHome: 1" + print " PageMPackage: {mpackages.length}" + print " PageMGroup: {mgroups.length}" + print " PageMModule: {nmodules.length}" + print " PageMClass: {mclasses.length}" + print " PageMProperty: {mprops.length}" + print " PagePerson: {persons.length}" + print " PageTag: {tags.length}" end - return new TplGithubArea(comment, namespace, location) end end -# The overview page -# Display a list of modules contained in program -class NitdocOverview - super NitdocPage - - private var mmodules = new Array[MModule] +redef class Catalog - 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 + # Build the catalog from `mpackages` + fun build_catalog(mpackages: Array[MPackage]) do + # Compute the poset + for p in mpackages do + var g = p.root + assert g != null + modelbuilder.scan_group(g) end - # sort modules - var sorter = new MModuleNameSorter - self.mmodules.add_all(mmodules) - sorter.sort(self.mmodules) - 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 - - 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 - tpl.title = "Overview" + # Build the catalog + for mpackage in mpackages do + package_page(mpackage) + git_info(mpackage) + mpackage_stats(mpackage) 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 - # module graph - tpl.graph = tpl_dot - return tpl - 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 -# The search page -# Display a list of modules, classes and properties -class NitdocSearch - super NitdocPage - - init(ctx: NitdocContext) do super(ctx) - - redef fun tpl_title do return "Search | {super}" - - redef fun tpl_topmenu do - var topmenu = super - topmenu.add_elt("index.html", "Overview", false) - topmenu.add_elt("#", "Search", true) - return topmenu - end - - 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 - - # 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 - - # 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 - -# 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 "{mmodule.nitdoc_name} module | {super}" - end - end - - 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 - group.add_bullet("R", "Redefined", mclassdef.tpl_link_anchor, ["redef"]) - end - end - - 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 group - 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 - - # 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 - - 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 - - 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 -end - -# 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 - - 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 - - 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 - end - - 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 - end - - 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 - end - - redef fun tpl_topmenu do - var topmenu = super - var mmodule: MModule - if mclass.public_owner == null then - mmodule = mclass.intro_mmodule - else - 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 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 - box.elts.add(new TplSidebarGroup("Too much Subclasses to list")) - end - sidebar.boxes.add box - end - - 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 - - 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 - - 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 - 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 - - 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 - end - return map - end - - 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 - - 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 - - # 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 - -# -# 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 - - # 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 - - # 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 - - # 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 - - # 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 - - # 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 - tpl.add tpl_link - end - tpl.add "" - return tpl - end - - # 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 - - # 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 tpl - end -end - -redef class MClass - # Return the HTML escaped name of the module - private fun nitdoc_name: String do return name.html_escape - - # URL to nitdoc page - # class_owner_name.html - private fun nitdoc_url: String do - return "class_{public_owner}_{name}.html" - end - - # 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 - - # 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 tpl - 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 - 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 - 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 - return tpl - end - - # 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 - - # 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 - - 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 - -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 - - 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 - -redef class MType - # Link to the type definition in the nitdoc page - private fun tpl_link: Template is abstract -end - -redef class MClassType - redef fun tpl_link do return mclass.tpl_link -end - -redef class MNullableType - redef fun tpl_link do - var tpl = new Template - tpl.add "nullable " - tpl.add mtype.tpl_link - return tpl - end -end - -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 - -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 - -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 - - 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 - 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 - - 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 - - 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 - - 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 - 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 - 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 tpl - end -end - -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 - tpl.add ")" - end - if return_mtype != null then - tpl.add ": " - tpl.add return_mtype.tpl_link - end - return tpl - end -end - -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 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 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 nitdoc = new NitdocContext -nitdoc.generate_nitdoc - +# build toolcontext +var toolcontext = new ToolContext +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 + +# process options +toolcontext.process_options(args) +var arguments = toolcontext.option_context.rest + +# build model +var model = new Model +var mbuilder = new ModelBuilder(model, toolcontext) +var mmodules = mbuilder.parse_full(arguments) + +# process +if mmodules.is_empty then return +print "Parsing code..." +mbuilder.run_phases +toolcontext.run_global_phases(mmodules)