X-Git-Url: http://nitlanguage.org diff --git a/src/nitdoc.nit b/src/nitdoc.nit index c9691bf..3c3539f 100644 --- a/src/nitdoc.nit +++ b/src/nitdoc.nit @@ -12,1877 +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 - -# The NitdocContext contains all the knowledge used for doc generation -class NitdocContext - - private var toolcontext = new ToolContext - private var model: Model - private var mbuilder: ModelBuilder - private var mainmodule: MModule - private var class_hierarchy: POSet[MClass] - private var arguments: Array[String] - private var output_dir: nullable String - private var dot_dir: nullable String - private var share_dir: nullable String - private var source: nullable String - private var min_visibility: MVisibility - - private var github_upstream: nullable String - private var github_basesha1: nullable String - private var github_gitdir: nullable String - - private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir") - private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source") - private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir") - private var opt_shareurl = new OptionString("Do not copy shared files, link JS and CSS file to share url instead", "--shareurl") - private var opt_nodot = new OptionBool("Do not generate graphes with graphviz", "--no-dot") - private var opt_private: OptionBool = new OptionBool("Generate the private API", "--private") - - private var opt_custom_title: OptionString = new OptionString("Title displayed in the top of the Overview page and as suffix of all page names", "--custom-title") - private var opt_custom_menu_items: OptionString = new OptionString("Items displayed in menu before the 'Overview' item (Each item must be enclosed in 'li' tags)", "--custom-menu-items") - private var opt_custom_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text") - private var opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text") - - private var opt_github_upstream: OptionString = new OptionString("The branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream") - private var opt_github_base_sha1: OptionString = new OptionString("The sha1 of the base commit used to create pull request", "--github-base-sha1") - private var opt_github_gitdir: OptionString = new OptionString("The git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir") - - private var opt_piwik_tracker: OptionString = new OptionString("The URL of the Piwik tracker (ex: nitlanguage.org/piwik/)", "--piwik-tracker") - private var opt_piwik_site_id: OptionString = new OptionString("The site ID in Piwik tracker", "--piwik-site-id") - - init do - toolcontext.option_context.add_option(opt_dir) - toolcontext.option_context.add_option(opt_source) - toolcontext.option_context.add_option(opt_sharedir, opt_shareurl) - toolcontext.option_context.add_option(opt_nodot) - toolcontext.option_context.add_option(opt_private) - toolcontext.option_context.add_option(opt_custom_title) - toolcontext.option_context.add_option(opt_custom_footer_text) - toolcontext.option_context.add_option(opt_custom_overview_text) - toolcontext.option_context.add_option(opt_custom_menu_items) - toolcontext.option_context.add_option(opt_github_upstream) - toolcontext.option_context.add_option(opt_github_base_sha1) - toolcontext.option_context.add_option(opt_github_gitdir) - toolcontext.option_context.add_option(opt_piwik_tracker) - toolcontext.option_context.add_option(opt_piwik_site_id) - toolcontext.process_options - self.arguments = toolcontext.option_context.rest - - if arguments.length < 1 then - print "usage: nitdoc [options] file..." - toolcontext.option_context.usage - exit(1) - end - self.process_options - - model = new Model - mbuilder = new ModelBuilder(model, toolcontext) - # Here we load and process all modules passed on the command line - var mmodules = mbuilder.parse(arguments) - if mmodules.is_empty then return - mbuilder.run_phases - - if mmodules.length == 1 then - mainmodule = mmodules.first - else - # We need a main module, so we build it by importing all modules - mainmodule = new MModule(model, null, "
", new Location(null, 0, 0, 0, 0)) - mainmodule.set_imported_mmodules(mmodules) - end - self.class_hierarchy = mainmodule.flatten_mclass_hierarchy - end - - private fun process_options do - if opt_dir.value != null then - output_dir = opt_dir.value - else - output_dir = "doc" - end - if opt_sharedir.value != null then - share_dir = opt_sharedir.value - else - var dir = "NIT_DIR".environ - if dir.is_empty then - dir = "{sys.program_name.dirname}/../share/nitdoc" - else - dir = "{dir}/share/nitdoc" - end - share_dir = dir - if share_dir == null then - print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR" - abort - end - end - 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 - else - self.github_upstream = gh_upstream - self.github_basesha1 = gh_base_sha - self.github_gitdir = gh_gitdir - end - end - source = opt_source.value - end - - fun generate_nitdoc do - # Create destination dir if it's necessary - if not output_dir.file_exists then output_dir.mkdir - if opt_shareurl.value == null then - sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/") - else - sys.system("cp -r {share_dir.to_s}/ZeroClipboard.swf {output_dir.to_s}/") - sys.system("cp -r {share_dir.to_s}/resources/ {output_dir.to_s}/resources/") - end - self.dot_dir = null - if not opt_nodot.value then self.dot_dir = output_dir.to_s - overview - search - modules - classes - quicksearch_list - end - - private fun overview do - var overviewpage = new NitdocOverview(self, dot_dir) - overviewpage.save("{output_dir.to_s}/index.html") - end - - private fun search do - var searchpage = new NitdocSearch(self) - searchpage.save("{output_dir.to_s}/search.html") - end - - private fun modules do - for mmodule in model.mmodules do - if mmodule.name == "
" then continue - var modulepage = new NitdocModule(mmodule, self, dot_dir) - modulepage.save("{output_dir.to_s}/{mmodule.url}") - end - end - - private fun classes do - for mclass in mbuilder.model.mclasses do - var classpage = new NitdocClass(mclass, self, dot_dir, source) - classpage.save("{output_dir.to_s}/{mclass.url}") - end - end - - private fun quicksearch_list do - var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js") - file.write("var nitdocQuickSearchRawList = \{ ") - for mmodule in model.mmodules do - if mmodule.name == "
" then continue - file.write("\"{mmodule.name}\": [") - file.write("\{txt: \"{mmodule.full_name}\", url:\"{mmodule.url}\" \},") - file.write("],") - end - for mclass in model.mclasses do - if mclass.visibility < min_visibility then continue - file.write("\"{mclass.name}\": [") - file.write("\{txt: \"{mclass.full_name}\", url:\"{mclass.url}\" \},") - file.write("],") - end - var name2mprops = new HashMap[String, Set[MPropDef]] - for mproperty in model.mproperties do - if mproperty.visibility < min_visibility then continue - if mproperty isa MAttribute then continue - if not name2mprops.has_key(mproperty.name) then name2mprops[mproperty.name] = new HashSet[MPropDef] - name2mprops[mproperty.name].add_all(mproperty.mpropdefs) - end - for mproperty, mpropdefs in name2mprops do - file.write("\"{mproperty}\": [") - for mpropdef in mpropdefs do - file.write("\{txt: \"{mpropdef.full_name}\", url:\"{mpropdef.url}\" \},") - end - file.write("],") - end - file.write(" \};") - file.close - end - -end - -# Nitdoc base page -abstract class NitdocPage - - var dot_dir: nullable String - var source: nullable String - var ctx: NitdocContext - 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 - - protected fun head do - append("") - append("") - append("") - append("") - append("") - append("") - var title = "" - if ctx.opt_custom_title.value != null then - title = " | {ctx.opt_custom_title.value.to_s}" - end - append("{self.title}{title}") - end - - protected fun menu do - if ctx.opt_custom_menu_items.value != null then - append(ctx.opt_custom_menu_items.value.to_s) - end - end - - protected fun title: String is abstract - - protected fun header do - append("
") - append("") - append("
") - end - - protected fun content is abstract - - protected fun footer do - if ctx.opt_custom_footer_text.value != null then - append("
{ctx.opt_custom_footer_text.value.to_s}
") - end - end - - # Generate a clickable graphviz image using a dot content - protected fun generate_dot(dot: String, name: String, alt: String) do - var output_dir = dot_dir - if output_dir == null then return - 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 ; \}") - append("
") - append("{alt}") - append("
") - var fmap = new IFStream.open("{output_dir}/{name}.map") - append(fmap.read_all) - fmap.close - end - - # Add a (source) link for a given location - protected fun show_source(l: Location): String - do - if source == null then - return "({l.file.filename.simplify_path})" - else - # THIS IS JUST UGLY ! (but there is no replace yet) - var x = source.split_with("%f") - source = x.join(l.file.filename.simplify_path) - x = source.split_with("%l") - source = x.join(l.line_start.to_s) - x = source.split_with("%L") - source = x.join(l.line_end.to_s) - return " (source)" - end - end - - # Render the page as a html string - protected fun render do - append("") - append("") - head - append("") - append("") - header - var footed = "" - if ctx.opt_custom_footer_text.value != null then footed = "footed" - append("
") - content - append("
") - footer - append("") - - # 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 - append("") - append("") - append("") - end - append("") - end - - # Append a string to the page - fun append(s: String) do out.write(s) - - # Save html page in the specified file - fun save(file: String) do - self.out = new OFStream.open(file) - render - self.out.close - end - private var out: nullable OFStream -end - -# The overview page -class NitdocOverview - super NitdocPage - private var mbuilder: ModelBuilder - private var mmodules = new Array[MModule] - - init(ctx: NitdocContext, dot_dir: nullable String) do - super(ctx) - self.mbuilder = ctx.mbuilder - self.dot_dir = dot_dir - # get modules - var mmodules = new HashSet[MModule] - for mmodule in 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 - - redef fun title do return "Overview" - - redef fun menu do - super - append("
  • Overview
  • ") - append("
  • Search
  • ") - end - - redef fun content do - append("
    ") - var title = "Overview" - if ctx.opt_custom_title.value != null then - title = ctx.opt_custom_title.value.to_s - end - append("

    {title}

    ") - var text = "" - if ctx.opt_custom_overview_text.value != null then - text = ctx.opt_custom_overview_text.value.to_s - end - append("
    {text}
    ") - append("
    ") - # module list - append("

    Modules

    ") - append("
      ") - for mmodule in mmodules do - if mbuilder.mmodule2nmodule.has_key(mmodule) then - var amodule = mbuilder.mmodule2nmodule[mmodule] - append("
    • ") - mmodule.html_link(self) - append(" {amodule.short_comment}
    • ") - end - end - append("
    ") - # module graph - process_generate_dot - append("
    ") - append("
    ") - end +import doc::static - private fun process_generate_dot 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 Buffer - op.append("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") - for mmodule in poset do - op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n") - for omodule in poset[mmodule].direct_greaters do - op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n") - end - end - op.append("\}\n") - generate_dot(op.to_s, "dep", "Modules hierarchy") - end -end +redef class ToolContext -# The search page -class NitdocSearch - super NitdocPage + # Nitdoc generation phase + var docphase: Phase = new Nitdoc(self, null) - init(ctx: NitdocContext) do - super(ctx) - self.dot_dir = null - end + # Directory where the Nitdoc is rendered + var opt_dir = new OptionString("Output directory", "-d", "--dir") - redef fun title do return "Search" + # Do not generate documentation for attributes + var opt_no_attributes = new OptionBool("Ignore the attributes", "--no-attributes") - redef fun menu do - super - append("
  • Overview
  • ") - append("
  • Search
  • ") - end + # Do not generate documentation for private properties + var opt_private = new OptionBool("Also generate private API", "--private") - redef fun content do - append("
    ") - append("

    {title}

    ") - module_column - classes_column - properties_column - append("
    ") - end - - # Add to content modules column - private fun module_column do - var sorted = ctx.mbuilder.model.mmodule_importation_hierarchy.to_a - var sorter = new MModuleNameSorter - sorter.sort(sorted) - append("
    ") - append("

    Modules

    ") - append("
      ") - for mmodule in sorted do - if mmodule.name == "
      " then continue - append("
    • ") - mmodule.html_link(self) - append("
    • ") - end - append("
    ") - append("
    ") - end - - # Add to content classes modules - private fun classes_column do - var sorted = ctx.mbuilder.model.mclasses - var sorter = new MClassNameSorter - sorter.sort(sorted) - append("
    ") - append("

    Classes

    ") - append("
      ") - for mclass in sorted do - if mclass.visibility < ctx.min_visibility then continue - append("
    • ") - mclass.html_link(self) - append("
    • ") - end - append("
    ") - append("
    ") - end - - # Insert the properties column of fullindex page - private fun properties_column do - var sorted = ctx.mbuilder.model.mproperties - var sorter = new MPropertyNameSorter - sorter.sort(sorted) - append("
    ") - append("

    Properties

    ") - append("
      ") - for mproperty in sorted do - if mproperty.visibility < ctx.min_visibility then continue - if mproperty isa MAttribute then continue - append("
    • ") - mproperty.intro.html_link(self) - append(" (") - mproperty.intro.mclassdef.mclass.html_link(self) - append(")
    • ") - end - append("
    ") - append("
    ") - end + # 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") -end + # Use a custom title for the homepage + var opt_custom_title = new OptionString("Custom title for homepage", "--custom-title") -# A module page -class NitdocModule - super NitdocPage + # Display a custom brand or logo in the documentation top menu + var opt_custom_brand = new OptionString("Custom link to external site", "--custom-brand") - private var mmodule: MModule - private var mbuilder: ModelBuilder - private var local_mclasses = new HashSet[MClass] - private var intro_mclasses = new HashSet[MClass] - private var redef_mclasses = new HashSet[MClass] + # Display a custom introduction text before the packages overview + var opt_custom_intro = new OptionString("Custom intro text for homepage", "--custom-overview-text") - init(mmodule: MModule, ctx: NitdocContext, dot_dir: nullable String) do - super(ctx) - self.mmodule = mmodule - self.mbuilder = ctx.mbuilder - self.dot_dir = dot_dir - # get local mclasses - for m in mmodule.in_nesting.greaters do - for mclassdef in m.mclassdefs do - if mclassdef.mclass.visibility < ctx.min_visibility then continue - if mclassdef.is_intro then - intro_mclasses.add(mclassdef.mclass) - else - if mclassdef.mclass.mpropdefs_in_module(self).is_empty then continue - redef_mclasses.add(mclassdef.mclass) - end - local_mclasses.add(mclassdef.mclass) - 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") - redef fun title do - if mbuilder.mmodule2nmodule.has_key(mmodule) and not mbuilder.mmodule2nmodule[mmodule].short_comment.is_empty then - var nmodule = mbuilder.mmodule2nmodule[mmodule] - return "{mmodule.html_name} module | {nmodule.short_comment}" - else - return "{mmodule.html_name} module" - end - 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") - redef fun menu do - super - append("
  • Overview
  • ") - append("
  • {mmodule.html_name}
  • ") - append("
  • Search
  • ") - end + # Piwik tracker site id + var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id") - redef fun content do - append("") - append("
    ") - module_doc - append("
    ") - end + # Do not generate dot/graphviz diagrams + var opt_nodot = new OptionBool("Do not generate graphs with graphviz", "--no-dot") - private fun classes_column do - var sorter = new MClassNameSorter - var sorted = new Array[MClass] - sorted.add_all(intro_mclasses) - sorted.add_all(redef_mclasses) - sorter.sort(sorted) - if not sorted.is_empty then - append("") - end - end + # Do not include highlighted code + var opt_nocode = new OptionBool("Do not generate code with nitlight", "--no-code") - private fun importation_column do - append("") - 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 display_module_list(list: Array[MModule]) do - append("
      ") - var sorter = new MModuleNameSorter - sorter.sort(list) - for m in list do - append("
    • ") - m.html_link(self) - append("
    • ") - end - append("
    ") - end + # Disable HTML rendering + var opt_norender = new OptionBool("DO not render any HTML", "--no-render") - private fun module_doc do - # title - append("

    {mmodule.html_name}

    ") - append("
    ") - mmodule.html_signature(self) - append("
    ") - # comment - mmodule.html_comment(self) - process_generate_dot - # classes - var class_sorter = new MClassNameSorter - # intro - if not intro_mclasses.is_empty then - var sorted = new Array[MClass] - sorted.add_all(intro_mclasses) - class_sorter.sort(sorted) - append("
    ") - append("

    Introduced classes

    ") - for mclass in sorted do mclass.html_full_desc(self) - append("
    ") - end - # redefs - var redefs = new Array[MClass] - for mclass in redef_mclasses do if not intro_mclasses.has(mclass) then redefs.add(mclass) - class_sorter.sort(redefs) - if not redefs.is_empty then - append("
    ") - append("

    Refined classes

    ") - for mclass in redefs do mclass.html_full_desc(self) - append("
    ") - end - end + # Test mode + # + # Display test data and remove the progress bar + var opt_test = new OptionBool("Output test data", "--test") - private fun process_generate_dot 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 Buffer - 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.url}\"];\n") - end - for omodule in poset[mmodule].direct_greaters do - op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n") - end - end - op.append("\}\n") - generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}") - end -end - -# A class page -class NitdocClass - super NitdocPage - - private var mclass: MClass - private var vtypes = new HashSet[MVirtualTypeDef] - private var consts = new HashSet[MMethodDef] - private var meths = new HashSet[MMethodDef] - private var inherited = new HashSet[MPropDef] - - init(mclass: MClass, ctx: NitdocContext, dot_dir: nullable String, source: nullable String) do - super(ctx) - self.mclass = mclass - self.dot_dir = dot_dir - self.source = source - # load properties - var locals = new HashSet[MProperty] - for mclassdef in mclass.mclassdefs do - for mpropdef in mclassdef.mpropdefs do - if mpropdef.mproperty.visibility < ctx.min_visibility then continue - if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef) - if mpropdef isa MMethodDef then - if mpropdef.mproperty.is_init then - consts.add(mpropdef) - else - meths.add(mpropdef) - end - end - locals.add(mpropdef.mproperty) - end - end - # get inherited properties - for pclass in mclass.in_hierarchy(ctx.mainmodule).greaters do - if pclass == mclass then continue - for pclassdef in pclass.mclassdefs do - for mprop in pclassdef.intro_mproperties do - var mpropdef = mprop.intro - if mprop.visibility < ctx.min_visibility then continue # skip if not correct visibiility - if locals.has(mprop) then continue # skip if local - if mclass.name != "Object" and mprop.intro_mclassdef.mclass.name == "Object" and (mprop.visibility <= protected_visibility or mprop.intro_mclassdef.mmodule.public_owner == null or mprop.intro_mclassdef.mmodule.public_owner.name != "standard") then continue # skip toplevels - if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef) - if mpropdef isa MMethodDef then - if mpropdef.mproperty.is_init then - consts.add(mpropdef) - else - meths.add(mpropdef) - end - end - inherited.add(mpropdef) - end - end - end - end - - redef fun title do - var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro] - if nclass isa AStdClassdef then - return "{mclass.html_name} class | {nclass.short_comment}" - else - return "{mclass.html_name} class" - end - end - - redef fun menu do + redef init do super - append("
  • Overview
  • ") - var public_owner = mclass.public_owner - if public_owner == null then - append("
  • ") - mclass.intro_mmodule.html_link(self) - append("
  • ") - else - append("
  • ") - public_owner.html_link(self) - append("
  • ") - end - append("
  • {mclass.html_name}
  • ") - append("
  • Search
  • ") - end - - redef fun content do - append("") - append("
    ") - class_doc - append("
    ") - end - - private fun properties_column do - var sorter = new MPropDefNameSorter - append("") - end - - private fun inheritance_column do - var sorted = new Array[MClass] - var sorterp = new MClassNameSorter - append("") - end - - private fun class_doc do - # title - append("

    {mclass.html_name}{mclass.html_short_signature}

    ") - append("
    ") - if mclass.visibility < public_visibility then append("{mclass.visibility.to_s} ") - append("{mclass.kind.to_s} ") - mclass.html_namespace(self) - append("{mclass.html_short_signature}
    ") - # comment - mclass.html_comment(self) - process_generate_dot - # concerns - var concern2meths = new ArrayMap[MModule, Array[MMethodDef]] - var sorted_meths = new Array[MMethodDef] - var sorted = new Array[MModule] - sorted_meths.add_all(meths) - ctx.mainmodule.linearize_mpropdefs(sorted_meths) - for meth in meths do - if inherited.has(meth) then continue - var mmodule = meth.mclassdef.mmodule - if not concern2meths.has_key(mmodule) then - sorted.add(mmodule) - concern2meths[mmodule] = new Array[MMethodDef] - end - concern2meths[mmodule].add(meth) - end - var sections = new ArrayMap[MModule, Array[MModule]] - for mmodule in concern2meths.keys do - var owner = mmodule.public_owner - if owner == null then owner = mmodule - if not sections.has_key(owner) then sections[owner] = new Array[MModule] - if owner != mmodule then sections[owner].add(mmodule) - end - append("
    ") - append("

    Concerns

    ") - append("
      ") - for owner, mmodules in sections do - var nowner = ctx.mbuilder.mmodule2nmodule[owner] - append("
    • ") - if nowner.short_comment.is_empty then - append("{owner.html_name}") - else - append("{owner.html_name}: {nowner.short_comment}") - end - if not mmodules.is_empty then - append("
        ") - for mmodule in mmodules do - var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule] - if nmodule.short_comment.is_empty then - append("
      • {mmodule.html_name}
      • ") - else - append("
      • {mmodule.html_name}: {nmodule.short_comment}
      • ") - end - end - append("
      ") - end - append("
    • ") - end - append("
    ") - append("
    ") - # properties - var prop_sorter = new MPropDefNameSorter - var lmmodule = new List[MModule] - var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro] - # virtual and formal types - var local_vtypes = new Array[MVirtualTypeDef] - for vt in vtypes do if not inherited.has(vt) then local_vtypes.add(vt) - if local_vtypes.length > 0 or mclass.arity > 0 then - append("
    ") - append("

    Formal and Virtual Types

    ") - # formal types - if mclass.arity > 0 and nclass isa AStdClassdef then - for ft, bound in mclass.parameter_types do - append("
    ") - append("

    {ft}: ") - bound.html_link(self) - append("

    ") - append("
    formal generic type
    ") - append("
    ") - end - end - # virtual types - prop_sorter.sort(local_vtypes) - for prop in local_vtypes do prop.html_full_desc(self, self.mclass) - append("
    ") - end - # constructors - var local_consts = new Array[MMethodDef] - for const in consts do if not inherited.has(const) then local_consts.add(const) - prop_sorter.sort(local_consts) - if local_consts.length > 0 then - append("
    ") - append("

    Constructors

    ") - for prop in local_consts do prop.html_full_desc(self, self.mclass) - append("
    ") - end - # methods - if not concern2meths.is_empty then - append("
    ") - append("

    Methods

    ") - for owner, mmodules in sections do - append("") - if owner != mclass.intro_mmodule and owner != mclass.public_owner then - var nowner = ctx.mbuilder.mmodule2nmodule[owner] - append("

    Methods refined in ") - owner.html_link(self) - append("

    ") - append("

    ") - owner.html_link(self) - if not nowner.short_comment.is_empty then - append(": {nowner.short_comment}") - end - append("

    ") - end - if concern2meths.has_key(owner) then - var mmethods = concern2meths[owner] - prop_sorter.sort(mmethods) - for prop in mmethods do prop.html_full_desc(self, self.mclass) - end - for mmodule in mmodules do - append("") - var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule] - if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then - append("

    ") - mmodule.html_link(self) - if not nmodule.short_comment.is_empty then - append(": {nmodule.short_comment}") - end - append("

    ") - end - var mmethods = concern2meths[mmodule] - prop_sorter.sort(mmethods) - for prop in mmethods do prop.html_full_desc(self, self.mclass) - end - end - append("
    ") - end - # inherited properties - if inherited.length > 0 then - var sorted_inherited = new Array[MPropDef] - sorted_inherited.add_all(inherited) - ctx.mainmodule.linearize_mpropdefs(sorted_inherited) - var classes = new ArrayMap[MClass, Array[MPropDef]] - for mmethod in sorted_inherited.reversed do - var mclass = mmethod.mclassdef.mclass - if not classes.has_key(mclass) then classes[mclass] = new Array[MPropDef] - classes[mclass].add(mmethod) - end - append("
    ") - append("

    Inherited Properties

    ") - for c, mmethods in classes do - prop_sorter.sort(mmethods) - append("

    Defined in ") - c.html_link(self) - append(": ") - for i in [0..mmethods.length[ do - var mmethod = mmethods[i] - mmethod.html_link(self) - if i <= mmethods.length - 1 then append(", ") - end - append("

    ") - end - append("
    ") - end - end - - private fun process_generate_dot do - var pe = ctx.class_hierarchy[mclass] - 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 Buffer - 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.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") - generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}") - end -end - -# -# Model redefs -# - -redef class MModule - # Return the HTML escaped name of the module - private fun html_name: String do return name.html_escape - - # URL to nitdoc page - # module_owner_name.html - private fun url: String do - if url_cache == null then - var res = new Buffer - res.append("module_") - var mowner = public_owner - if mowner != null then - res.append("{public_owner.name}_") - end - res.append("{self.name}.html") - url_cache = res.to_s - end - return url_cache.as(not null) - end - private var url_cache: nullable String - - # html anchor id for the module in a nitdoc page - # MOD_owner_name - private fun anchor: String do - if anchor_cache == null then - var res = new Buffer - res.append("MOD_") - var mowner = public_owner - if mowner != null then - res.append("{public_owner.name}_") - end - res.append(self.name) - anchor_cache = res.to_s - end - return anchor_cache.as(not null) - end - private var anchor_cache: nullable String - - # Return a link (html a tag) to the nitdoc module page - # html_name - private fun html_link(page: NitdocPage) do - if html_link_cache == null then - var res = new Buffer - if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then - res.append("{html_name}") - else - res.append("{html_name}") - end - html_link_cache = res.to_s - end - page.append(html_link_cache.as(not null)) - end - private var html_link_cache: nullable String - - # Return the module signature decorated with html - # module html_full_namespace - private fun html_signature(page: NitdocPage) do - page.append("module ") - html_full_namespace(page) - page.append("") - end - - # Return the module full namespace decorated with html - # public_owner.html_namespace::html_link - private fun html_full_namespace(page: NitdocPage) do - page.append("") - var mowner = public_owner - if mowner != null then - public_owner.html_namespace(page) - page.append("::") - end - html_link(page) - page.append("") - end - - # Return the module full namespace decorated with html - # public_owner.html_namespace - private fun html_namespace(page: NitdocPage) do - page.append("") - var mowner = public_owner - if mowner != null then - public_owner.html_namespace(page) - else - html_link(page) - end - page.append("") - end - - # Return the full comment of the module decorated with html - private fun html_comment(page: NitdocPage) do - page.append("
    ") - if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then - var nmodule = page.ctx.mbuilder.mmodule2nmodule[self] - if page.ctx.github_gitdir != null then - var loc = nmodule.doc_location.github(page.ctx.github_gitdir.as(not null)) - page.append("") - end - if nmodule.full_comment == "" then - page.append("

    ") - page.append("no comment for ") - else - page.append("

    {nmodule.full_markdown}
    ") - page.append("

    ") - end - page.append("definition in ") - self.html_full_namespace(page) - page.append(" {page.show_source(nmodule.location)}

    ") - end - page.append("
    ") - end - - private fun has_mclassdef_for(mclass: MClass): Bool do - for mmodule in self.in_nesting.greaters do - for mclassdef in mmodule.mclassdefs do - if mclassdef.mclass == mclass then return true - end - end - return false - end - - private fun has_mclassdef(mclassdef: MClassDef): Bool do - for mmodule in self.in_nesting.greaters do - for oclassdef in mmodule.mclassdefs do - if mclassdef == oclassdef then return true - end - end - return false - end -end - -redef class MClass - # Return the HTML escaped name of the module - private fun html_name: String do return name.html_escape - - # URL to nitdoc page - # class_owner_name.html - private fun url: String do - return "class_{public_owner}_{name}.html" - end - - # html anchor id for the class in a nitdoc page - # MOD_owner_name - private fun anchor: String do - if anchor_cache == null then - anchor_cache = "CLASS_{public_owner.name}_{name}" - end - return anchor_cache.as(not null) - end - private var anchor_cache: nullable String - - # Return a link (with signature) to the nitdoc class page - # html_name(signature) - private fun html_link(page: NitdocPage) do - if html_link_cache == null then - var res = new Buffer - res.append("{html_name}{html_short_signature}") - html_link_cache = res.to_s - end - page.append(html_link_cache.as(not null)) - end - private var html_link_cache: nullable String - - # Return a short link (without signature) to the nitdoc class page - # html_name - private fun html_short_link(page: NitdocPage) do - if html_short_link_cache == null then - var res = new Buffer - res.append("{html_name}") - html_short_link_cache = res.to_s - end - page.append(html_short_link_cache.as(not null)) - end - private var html_short_link_cache: nullable String - - # Return a link (with signature) to the class anchor - # html_name - private fun html_link_anchor(page: NitdocPage) do - if html_link_anchor_cache == null then - var res = new Buffer - res.append("{html_name}{html_short_signature}") - html_link_anchor_cache = res.to_s - end - page.append(html_link_anchor_cache.as(not null)) - end - private var html_link_anchor_cache: nullable String - - # Return the generic signature of the class with bounds - # [E: MType, F: MType] - private fun html_signature(page: NitdocPage) do - if arity > 0 then - page.append("[") - for i in [0..intro.parameter_names.length[ do - page.append("{intro.parameter_names[i]}: ") - intro.bound_mtype.arguments[i].html_link(page) - if i < intro.parameter_names.length - 1 then page.append(", ") - end - page.append("]") - end - end - - # Return the generic signature of the class without bounds - # [E, F] - private fun html_short_signature: String do - if arity > 0 then - return "[{intro.parameter_names.join(", ")}]" - else - return "" - end - end - - # Return the class namespace decorated with html - # intro_module::html_short_link - private fun html_namespace(page: NitdocPage) do - intro_mmodule.html_namespace(page) - page.append("::") - html_short_link(page) - page.append("") - end - - # Return a list item for the mclass - #
  • html_link
  • - private fun html_sidebar_item(page: NitdocModule) do - if page.mmodule.in_nesting.greaters.has(intro.mmodule) then - page.append("
  • ") - page.append("I") - html_link_anchor(page) - else if page.mmodule.has_mclassdef_for(self) then - page.append("
  • ") - page.append("R") - html_link_anchor(page) - else - page.append("
  • ") - page.append("H") - html_link(page) - end - page.append("
  • ") - end - - private fun html_full_desc(page: NitdocModule) do - var is_redef = not page.mmodule.in_nesting.greaters.has(intro.mmodule) - var redefs = mpropdefs_in_module(page) - if not is_redef or not redefs.is_empty then - var classes = new Array[String] - classes.add(kind.to_s) - if is_redef then classes.add("redef") - classes.add(visibility.to_s) - page.append("
    ") - page.append("

    ") - page.append("") - html_short_link(page) - html_signature(page) - page.append("

    ") - html_info(page) - html_comment(page) - page.append("
    ") - end - end - - private fun html_info(page: NitdocModule) do - page.append("
    ") - if visibility < public_visibility then page.append("{visibility.to_s} ") - if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then page.append("redef ") - page.append("{kind} ") - html_namespace(page) - page.append("{html_short_signature}
    ") - end - - private fun html_comment(page: NitdocPage) do - page.append("
    ") - if page isa NitdocModule then - page.mmodule.linearize_mclassdefs(mclassdefs) - # comments for each mclassdef contained in current mmodule - for mclassdef in mclassdefs do - if not mclassdef.is_intro and not page.mmodule.mclassdefs.has(mclassdef) then continue - if page.ctx.mbuilder.mclassdef2nclassdef.has_key(mclassdef) then - var nclass = page.ctx.mbuilder.mclassdef2nclassdef[mclassdef] - if nclass isa AStdClassdef then - if page.ctx.github_gitdir != null then - var loc = nclass.doc_location.github(page.ctx.github_gitdir.as(not null)) - page.append("") - end - if nclass.full_comment == "" then - page.append("

    ") - page.append("no comment for ") - else - page.append("

    {nclass.full_markdown}
    ") - page.append("

    ") - end - if mclassdef.is_intro then - page.append("introduction in ") - else - page.append("refinement in ") - end - mclassdef.mmodule.html_full_namespace(page) - page.append(" {page.show_source(nclass.location)}

    ") - end - end - end - else - # comments for intro - if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then - var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro] - if nclass isa AStdClassdef then - if page.ctx.github_gitdir != null then - var loc = nclass.doc_location.github(page.ctx.github_gitdir.as(not null)) - page.append("") - end - if nclass.full_comment == "" then - page.append("

    ") - page.append("no comment for ") - else - page.append("

    {nclass.full_markdown}
    ") - page.append("

    ") - end - page.append("introduction in ") - intro.mmodule.html_full_namespace(page) - page.append(" {page.show_source(nclass.location)}

    ") - end - end - end - page.append("
    ") - end - - private fun mpropdefs_in_module(page: NitdocModule): Array[MPropDef] do - var res = new Array[MPropDef] - page.mmodule.linearize_mclassdefs(mclassdefs) - for mclassdef in mclassdefs do - if not page.mmodule.mclassdefs.has(mclassdef) then continue - if mclassdef.is_intro then continue - for mpropdef in mclassdef.mpropdefs do - if mpropdef.mproperty.visibility < page.ctx.min_visibility then continue - if mpropdef isa MAttributeDef then continue - res.add(mpropdef) - end - end - return res - end -end - -redef class MProperty - # Escape name for html output - private fun html_name: String do return name.html_escape - - # Return the property namespace decorated with html - # intro_module::intro_class::html_link - private fun html_namespace(page: NitdocPage) do - intro_mclassdef.mclass.html_namespace(page) - page.append(intro_mclassdef.mclass.html_short_signature) - page.append("::") - intro.html_link(page) - page.append("") - end -end - -redef class MType - # Link to the type definition in the nitdoc page - private fun html_link(page: NitdocPage) is abstract -end - -redef class MClassType - redef fun html_link(page) do mclass.html_link(page) -end - -redef class MNullableType - redef fun html_link(page) do - page.append("nullable ") - mtype.html_link(page) - end -end - -redef class MGenericType - redef fun html_link(page) do - page.append("{mclass.html_name}[") - for i in [0..arguments.length[ do - arguments[i].html_link(page) - if i < arguments.length - 1 then page.append(", ") - end - page.append("]") + 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 -redef class MParameterType - redef fun html_link(page) do - var name = mclass.intro.parameter_names[rank] - page.append("{name}") - end -end - -redef class MVirtualType - redef fun html_link(page) do mproperty.intro.html_link(page) -end - -redef class MClassDef - # Return the classdef namespace decorated with html - private fun html_namespace(page: NitdocPage) do - mmodule.html_full_namespace(page) - page.append("::") - mclass.html_link(page) - page.append("") - end -end - -redef class MPropDef - # Return the full qualified name of the mpropdef - # module::classdef::name - private fun full_name: String do - return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}" - end - - # URL into the nitdoc page - # class_owner_name.html#anchor - private fun url: String do - if url_cache == null then - url_cache = "{mclassdef.mclass.url}#{anchor}" - end - return url_cache.as(not null) - end - private var url_cache: nullable String - - # html anchor id for the property in a nitdoc class page - # PROP_mclass_propertyname - private fun anchor: String do - if anchor_cache == null then - anchor_cache = "PROP_{mclassdef.mclass.public_owner.name}_{mproperty.name.replace(" ", "_")}" - end - return anchor_cache.as(not null) - end - private var anchor_cache: nullable String - - # Return a link to property into the nitdoc class page - # html_name - private fun html_link(page: NitdocPage) do - if html_link_cache == null then - var res = new Buffer - if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then - var nprop = page.ctx.mbuilder.mpropdef2npropdef[self] - res.append("{mproperty.html_name}") - else - res.append("{mproperty.html_name}") - end - html_link_cache = res.to_s - end - page.append(html_link_cache.as(not null)) - end - private var html_link_cache: nullable String - - # Return a list item for the mpropdef - #
  • html_link
  • - private fun html_sidebar_item(page: NitdocClass) do - if is_intro and mclassdef.mclass == page.mclass then - page.append("
  • ") - page.append("I") - else if is_intro and mclassdef.mclass != page.mclass then - page.append("
  • ") - page.append("H") - else - page.append("
  • ") - page.append("R") - end - html_link(page) - page.append("
  • ") - end +redef class DocModel - private fun html_full_desc(page: NitdocPage, ctx: MClass) is abstract - private fun html_info(page: NitdocPage, ctx: MClass) is abstract - - private fun html_comment(page: NitdocPage) do - page.append("
    ") - if not is_intro then - if page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then - var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro] - if page.ctx.github_gitdir != null then - var loc = intro_nprop.doc_location.github(page.ctx.github_gitdir.as(not null)) - page.append("") - end - if intro_nprop.full_comment.is_empty then - page.append("

    ") - page.append("no comment for ") - else - page.append("

    {intro_nprop.full_markdown}
    ") - page.append("

    ") - end - page.append("introduction in ") - mproperty.intro.mclassdef.html_namespace(page) - page.append(" {page.show_source(intro_nprop.location)}

    ") - end - end - if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then - var nprop = page.ctx.mbuilder.mpropdef2npropdef[self] - if page.ctx.github_gitdir != null then - var loc = nprop.doc_location.github(page.ctx.github_gitdir.as(not null)) - page.append("") - end - if nprop.full_comment == "" then - page.append("

    ") - page.append("no comment for ") - else - page.append("

    {nprop.full_markdown}
    ") - page.append("

    ") - end - if is_intro then - page.append("introduction in ") - else - page.append("redefinition in ") - end - mclassdef.html_namespace(page) - page.append(" {page.show_source(nprop.location)}

    ") - end - page.append("
    ") + # 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 -redef class MMethodDef - redef fun html_full_desc(page, ctx) do - var classes = new Array[String] - var is_redef = mproperty.intro_mclassdef.mclass != ctx - if mproperty.is_init then - classes.add("init") - else - classes.add("fun") - end - if is_redef then classes.add("redef") - classes.add(mproperty.visibility.to_s) - page.append("
    ") - if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then - page.append("

    ") - page.append("{mproperty.html_name}") - msignature.html_signature(page) - page.append("

    ") - else - page.append("

    ") - page.append("init") - msignature.html_signature(page) - page.append("

    ") - end - html_info(page, ctx) - html_comment(page) - page.append("
    ") - end +# Nitdoc phase explores the model and generate pages for each mentity found +private class Nitdoc + super Phase - redef fun html_info(page, ctx) do - page.append("
    ") - if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ") - if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ") - if mproperty.is_init then - page.append("init ") - else - page.append("fun ") - end - mproperty.html_namespace(page) - page.append("
    ") - end -end - -redef class MVirtualTypeDef - redef fun html_full_desc(page, ctx) do - var is_redef = mproperty.intro_mclassdef.mclass != ctx - var classes = new Array[String] - classes.add("type") - if is_redef then classes.add("redef") - classes.add(mproperty.visibility.to_s) - page.append("
    ") - page.append("

    {mproperty.html_name}: ") - bound.html_link(page) - page.append("

    ") - html_info(page, ctx) - html_comment(page) - page.append("
    ") - end - - redef fun html_info(page, ctx) do - page.append("
    ") - if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ") - page.append("type ") - mproperty.html_namespace(page) - page.append("
    ") - end -end - -redef class MSignature - private fun html_signature(page: NitdocPage) do - if not mparameters.is_empty then - page.append("(") - for i in [0..mparameters.length[ do - mparameters[i].html_link(page) - if i < mparameters.length - 1 then page.append(", ") - end - page.append(")") - end - if return_mtype != null then - page.append(": ") - return_mtype.html_link(page) - end - end - - private fun untyped_signature(page: NitdocPage): String do - var res = new Buffer - if not mparameters.is_empty then - res.append("(") - for i in [0..mparameters.length[ do - res.append(mparameters[i].name) - if i < mparameters.length - 1 then res.append(", ") - end - res.append(")") - end - return res.to_s - end -end - -redef class MParameter - private fun html_link(page: NitdocPage) do - page.append("{name}: ") - mtype.html_link(page) - if is_vararg then page.append("...") - end -end - -# -# Nodes redefs -# - -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 ADoc - private fun short_comment: String do - return n_comment.first.text.substring_from(2).replace("\n", "").html_escape - end - - private fun full_comment: String do - var res = new Buffer - for t in n_comment do - var text = t.text - text = text.substring_from(1) - if text.first == ' ' then text = text.substring_from(1) - res.append(text.html_escape) + redef fun process_mainmodule(mainmodule, mmodules) + do + 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 - var str = res.to_s - return str.substring(0, str.length - 1) end end -redef class AModule - private fun short_comment: String do - if n_moduledecl != null and n_moduledecl.n_doc != null then - return n_moduledecl.n_doc.short_comment - end - return "" - end - - private fun full_comment: String do - if n_moduledecl != null and n_moduledecl.n_doc != null then - return n_moduledecl.n_doc.full_comment - end - return "" - end +redef class Catalog - private fun full_markdown: String do - if n_moduledecl != null and n_moduledecl.n_doc != null then - return n_moduledecl.n_doc.full_markdown.html + # 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 - return "" - end - - # The doc location or the first line of the block if doc node is null - private fun doc_location: Location do - if n_moduledecl != null and n_moduledecl.n_doc != null then - return n_moduledecl.n_doc.location + # Build the catalog + for mpackage in mpackages do + package_page(mpackage) + git_info(mpackage) + mpackage_stats(mpackage) end - var l = location - return new Location(l.file, l.line_start, l.line_start, l.column_start, l.column_start) end end -redef class AStdClassdef - private fun short_comment: String do - if n_doc != null then return n_doc.short_comment - return "" - end - - private fun full_comment: String do - if n_doc != null then return n_doc.full_comment - return "" - end - - private fun full_markdown: String do - if n_doc != null then return n_doc.full_markdown.html - return "" - end - - # The doc location or the first line of the block if doc node is null - private fun doc_location: Location do - if n_doc != null then return n_doc.location - var l = location - return new Location(l.file, l.line_start, l.line_start, l.column_start, l.column_start) - end -end - -redef class APropdef - private fun short_comment: String do - if n_doc != null then return n_doc.short_comment - return "" - end - - private fun full_comment: String do - if n_doc != null then return n_doc.full_comment - return "" - end - - private fun full_markdown: String do - if n_doc != null then return n_doc.full_markdown.html - return "" - end - - private fun doc_location: Location do - if n_doc != null then return n_doc.location - var l = location - return new Location(l.file, l.line_start, l.line_start, l.column_start, l.column_start) - - 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)