X-Git-Url: http://nitlanguage.org diff --git a/src/ni_nitdoc.nit b/src/ni_nitdoc.nit index 2db5723..535614b 100644 --- a/src/ni_nitdoc.nit +++ b/src/ni_nitdoc.nit @@ -1,7 +1,5 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2008 Jean Privat -# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -14,38 +12,52 @@ # 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. module ni_nitdoc import model_utils -import abstract_compiler -import html +import modelize_property + +# The NitdocContext contains all the knowledge used for doc generation +class NitdocContext -class Nitdoc - private var toolcontext: ToolContext + private var toolcontext = new ToolContext private var model: Model - private var modelbuilder: ModelBuilder + private var mbuilder: ModelBuilder private var mainmodule: MModule + private var class_hierarchy: POSet[MClass] private var arguments: Array[String] - private var destinationdir: nullable String - private var sharedir: nullable 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 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_nodot = new OptionBool("Do not generate graphes with graphiviz", "--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") - init(toolcontext: ToolContext) do - # We need a model to collect stufs - self.toolcontext = toolcontext + init do self.arguments = toolcontext.option_context.rest toolcontext.option_context.options.clear toolcontext.option_context.add_option(opt_dir) toolcontext.option_context.add_option(opt_source) toolcontext.option_context.add_option(opt_sharedir) 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.process_options - process_options if arguments.length < 1 then toolcontext.option_context.usage @@ -53,24 +65,31 @@ class Nitdoc end model = new Model - modelbuilder = new ModelBuilder(model, toolcontext) - - # Here we load an process std modules - var mmodules = modelbuilder.parse_and_build([arguments.first]) + mbuilder = new ModelBuilder(model, toolcontext) + # Here we load an process all modules passed on the command line + var mmodules = mbuilder.parse(arguments) if mmodules.is_empty then return - modelbuilder.full_propdef_semantic_analysis - assert mmodules.length == 1 - self.mainmodule = mmodules.first + 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 + self.process_options end private fun process_options do if not opt_dir.value is null then - destinationdir = opt_dir.value + output_dir = opt_dir.value else - destinationdir = "nitdoc_directory" + output_dir = "doc" end if not opt_sharedir.value is null then - sharedir = opt_sharedir.value + share_dir = opt_sharedir.value else var dir = "NIT_DIR".environ if dir.is_empty then @@ -78,1258 +97,1497 @@ class Nitdoc else dir = "{dir}/share/nitdoc" end - sharedir = dir - if sharedir is null then + share_dir = dir + if share_dir is null then print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR" abort end - dir = "{sharedir.to_s}/scripts/js-facilities.js" - if sharedir is null then + dir = "{share_dir.to_s}/scripts/js-facilities.js" + if share_dir is null then print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR" abort end + + if opt_private.value then + min_visibility = none_visibility + else + min_visibility = protected_visibility + end end - if not opt_source.value is null then - source = "" - else - source = opt_source.value - 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 + sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/") + self.dot_dir = null + if not opt_nodot.value then self.dot_dir = output_dir.to_s + overview + fullindex + modules + classes + quicksearch_list end - fun start do - if arguments.length == 1 then - # Create destination dir if it's necessary - if not destinationdir.file_exists then destinationdir.mkdir - sys.system("cp -r {sharedir.to_s}/* {destinationdir.to_s}/") - overview - fullindex - 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 - fun overview do - var overviewpage = new NitdocOverview.with(modelbuilder.nmodules, self.opt_nodot.value, destinationdir.to_s) - overviewpage.save("{destinationdir.to_s}/index.html") + private fun fullindex do + var fullindex = new NitdocFullindex(self) + fullindex.save("{output_dir.to_s}/full-index.html") end - fun fullindex do - var fullindex = new NitdocFullindex.with(model.mmodules) - fullindex.save("{destinationdir.to_s}/full-index.html") + 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 - fun modules do - for mod in modelbuilder.nmodules do - var modulepage = new NitdocModules.with(mod) - modulepage.save("{destinationdir.to_s}/{mod.mmodule.name}.html") + 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 - fun classes do - for amodule in modelbuilder.nmodules do - for mclass, aclassdef in amodule.mclass2nclassdef do - mclass.amodule(modelbuilder.mmodule2nmodule) - mclass.mmethod(aclassdef.mprop2npropdef) - var classpage = new NitdocMClasses.with(mclass, aclassdef, source) - classpage.save("{destinationdir.to_s}/{mclass.name}.html") + private fun quicksearch_list do + var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js") + file.write("var entries = \{ ") + for mmodule in model.mmodules do + file.write("\"{mmodule.name}\": [") + file.write("\{txt: \"{mmodule.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.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 - # Generate QuickSearch file - fun quicksearch_list do - var file = new OFStream.open("{destinationdir.to_s}/quicksearch-list.js") - var content = new Buffer - content.append("var entries = \{ ") - for prop in model.mproperties do - if not prop isa MMethod then continue - content.append("\"{prop.name}\": [") - for propdef in prop.mpropdefs do - content.append("\{txt: \"{propdef.mproperty.full_name}\", url:\"{propdef.mproperty.link_anchor}\" \}") - if not propdef is prop.mpropdefs.last then content.append(", ") - end - content.append("]") - content.append(", ") +end + +# Nitdoc base page +abstract class NitdocPage + + var dot_dir: nullable String + var source: nullable String + var ctx: NitdocContext + + init(ctx: NitdocContext) do + self.ctx = ctx + end + + protected fun head do + append("") + append("") + 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 - for mclass in model.mclasses do - content.append("\"{mclass.name}\": [") - for mclassdef in mclass.mclassdefs do - content.append("\{txt: \"{mclassdef.mclass.full_name}\", url:\"{mclass.link_anchor}\" \}") - if not mclassdef is mclass.mclassdefs.last then content.append(", ") - end - content.append("]") - if not mclass is model.mclasses.last then content.append(", ") + 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 - content.append(" \};") - file.write(content.to_s) + 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 + append("
") + content + append("
") + footer + 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.mmodules 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 - var amodules: Array[AModule] + redef fun title do return "Overview" - # Init with Array[AModule] to get all ifnormations about each MModule containt in a program - # opt_nodot to inform about the graph gen - # destination: to know where will be saved dot files - init with(modules: Array[AModule], opt_nodot: Bool, destination: String) do - self.amodules = modules - self.opt_nodot = opt_nodot - self.destinationdir = destination + redef fun menu do + super + append("
  • Overview
  • ") + append("
  • Full Index
  • ") end - redef fun head do - super - add("title").text("Overview | Nit Standard Library") - end - - redef fun header do - open("header") - open("nav").add_class("main") - open("ul") - add("li").add_class("current").text("Overview") - open("li") - add_html("Full Index") - close("li") - open("li").attr("id", "liGitHub") - open("a").add_class("btn").attr("id", "logGitHub") - add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png") - close("a") - open("div").add_class("popover bottom") - add("div").add_class("arrow").text(" ") - open("div").add_class("githubTitle") - add("h3").text("Github Sign In") - close("div") - open("div") - add("label").attr("id", "lbloginGit").text("Username") - add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text") - open("label").attr("id", "logginMessage").text("Hello ") - open("a").attr("id", "githubAccount") - add("strong").attr("id", "nickName").text(" ") - close("a") - close("label") - close("div") - open("div") - add("label").attr("id", "lbpasswordGit").text("Password") - add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password") - open("div").attr("id", "listBranches") - add("label").attr("id", "lbBranches").text("Branch") - add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ") - close("div") - close("div") - open("div") - add("label").attr("id", "lbrepositoryGit").text("Repository") - add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text") - close("div") - open("div") - add("label").attr("id", "lbbranchGit").text("Branch") - add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text") - close("div") - open("div") - add("a").attr("id", "signIn").text("Sign In") - close("div") - close("div") - close("li") - close("ul") - close("nav") - close("header") - end - - redef fun body do - super - open("div").add_class("page") - open("div").add_class("content fullpage") - add("h1").text("Nit Standard Library") - open("article").add_class("overview") - add_html("

    Documentation for the standard library of Nit
    Version jenkins-component=stdlib-19
    Date: TODAY

    ") - close("article") - open("article").add_class("overview") - add("h2").text("Modules") - open("ul") - add_modules - close("ul") + redef fun content do + var footed = "" + if ctx.opt_custom_footer_text.value != null then footed = "footed" + 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 - close("article") - close("div") - close("div") - add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.") - end - - fun add_modules do - var ls = new List[nullable MModule] - for amodule in amodules do - var mmodule = amodule.mmodule.public_owner - if mmodule != null and not ls.has(mmodule) then - open("li") - add("a").attr("href", "{mmodule.name}.html").text("{mmodule.to_s} ") - add_html(amodule.comment) - close("li") - ls.add(mmodule) + append("
    ") + append("
    ") + end + + 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 - end - - fun process_generate_dot do + # 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 amodule in amodules do - op.append("\"{amodule.mmodule.name}\"[URL=\"{amodule.mmodule.name}.html\"];\n") - for mmodule2 in amodule.mmodule.in_importation.direct_greaters do - op.append("\"{amodule.mmodule.name}\"->\"{mmodule2.name}\";\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 +# The full index page class NitdocFullindex super NitdocPage - var mmodules: Array[MModule] - - init with(mmodules: Array[MModule]) do - self.mmodules = mmodules - opt_nodot = false - destinationdir = "" + init(ctx: NitdocContext) do + super(ctx) + self.dot_dir = null end - redef fun head do - super - add("title").text("Full Index | Nit Standard Library") - end - - redef fun header do - open("header") - open("nav").add_class("main") - open("ul") - open("li") - add_html("Overview") - close("li") - add("li").add_class("current").text("Full Index") - open("li").attr("id", "liGitHub") - open("a").add_class("btn").attr("id", "logGitHub") - add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png") - close("a") - open("div").add_class("popover bottom") - add("div").add_class("arrow").text(" ") - open("div").add_class("githubTitle") - add("h3").text("Github Sign In") - close("div") - open("div") - add("label").attr("id", "lbloginGit").text("Username") - add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text") - open("label").attr("id", "logginMessage").text("Hello ") - open("a").attr("id", "githubAccount") - add("strong").attr("id", "nickName").text(" ") - close("a") - close("label") - close("div") - open("div") - add("label").attr("id", "lbpasswordGit").text("Password") - add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password") - open("div").attr("id", "listBranches") - add("label").attr("id", "lbBranches").text("Branch") - add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ") - close("div") - close("div") - open("div") - add("label").attr("id", "lbrepositoryGit").text("Repository") - add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text") - close("div") - open("div") - add("label").attr("id", "lbbranchGit").text("Branch") - add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text") - close("div") - open("div") - add("a").attr("id", "signIn").text("Sign In") - close("div") - close("div") - close("li") - close("ul") - close("nav") - close("header") - end - - redef fun body do + redef fun title do return "Full Index" + + redef fun menu do super - open("div").add_class("page") - open("div").add_class("content fullpage") - add("h1").text("Full Index") - add_content - close("div") - close("div") - add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.") + append("
  • Overview
  • ") + append("
  • Full Index
  • ") end - fun add_content do + redef fun content do + var footed = "" + if ctx.opt_custom_footer_text.value != null then footed = "footed" + append("
    ") + append("

    Full Index

    ") module_column classes_column properties_column + append("
    ") end # Add to content modules column - fun module_column do - var ls = new List[nullable MModule] - var sorted = mmodules - var sorterp = new ComparableSorter[MModule] - sorterp.sort(sorted) - open("article").add_class("modules filterable") - add("h2").text("Modules") - open("ul") + 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.public_owner != null and not ls.has(mmodule.public_owner) then - ls.add(mmodule.public_owner) - open("li") - add("a").attr("href", "{mmodule.public_owner.name}.html").text(mmodule.public_owner.name) - close("li") - end + append("
    • ") + mmodule.html_link(self) + append("
    • ") end - close("ul") - close("article") + append("
    ") + append("
    ") end # Add to content classes modules - fun classes_column do - var sorted = mmodules.first.imported_mclasses.to_a - var sorterp = new ComparableSorter[MClass] - sorterp.sort(sorted) - open("article").add_class("classes filterable") - add("h2").text("Classes") - open("ul") - + 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 - open("li") - add("a").attr("href", "{mclass.name}.html").text(mclass.name) - close("li") + if mclass.visibility < ctx.min_visibility then continue + append("
    • ") + mclass.html_link(self) + append("
    • ") end - - close("ul") - close("article") + append("
    ") + append("
    ") end # Insert the properties column of fullindex page - fun properties_column do - open("article").add_class("properties filterable") - add("h2").text("Properties") - open("ul") - var sorted_imported = mmodules.first.imported_methods.to_a - var sorted_redef = mmodules.first.redef_methods.to_a - var sorterp = new ComparableSorter[MProperty] - sorterp.sort(sorted_imported) - sorterp.sort(sorted_redef) - - for method in sorted_imported do - if method.visibility is none_visibility or method.visibility is intrude_visibility then continue - open("li").add_class("intro") - add("span").attr("title", "introduction").text("I") - add_html(" ") - add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})") - close("li") - end - - for method in sorted_redef do - if method.visibility is none_visibility or method.visibility is intrude_visibility then continue - open("li").add_class("redef") - add("span").attr("title", "redefinition").text("R") - add_html(" ") - add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})") - close("li") - end - - close("ul") - close("article") + 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 end -class NitdocModules +# A module page +class NitdocModule super NitdocPage - var amodule: AModule - var modulename: String - init with(amodule: AModule) do - self.amodule = amodule - self.modulename = self.amodule.mmodule.name - opt_nodot = false - destinationdir = "" + private var mmodule: MModule + private var mbuilder: ModelBuilder + + init(mmodule: MModule, ctx: NitdocContext, dot_dir: nullable String) do + super(ctx) + self.mmodule = mmodule + self.mbuilder = ctx.mbuilder + self.dot_dir = dot_dir end - redef fun head do - super - add("title").text("{modulename} module | {amodule.short_comment}") - end - - redef fun header do - open("header") - open("nav").add_class("main") - open("ul") - open("li") - add_html("Overview") - close("li") - add("li").add_class("current").text(modulename) - open("li") - add_html("Full Index") - close("li") - open("li").attr("id", "liGitHub") - open("a").add_class("btn").attr("id", "logGitHub") - add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png") - close("a") - open("div").add_class("popover bottom") - add("div").add_class("arrow").text(" ") - open("div").add_class("githubTitle") - add("h3").text("Github Sign In") - close("div") - open("div") - add("label").attr("id", "lbloginGit").text("Username") - add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text") - open("label").attr("id", "logginMessage").text("Hello ") - open("a").attr("id", "githubAccount") - add("strong").attr("id", "nickName").text(" ") - close("a") - close("label") - close("div") - open("div") - add("label").attr("id", "lbpasswordGit").text("Password") - add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password") - open("div").attr("id", "listBranches") - add("label").attr("id", "lbBranches").text("Branch") - add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ") - close("div") - close("div") - open("div") - add("label").attr("id", "lbrepositoryGit").text("Repository") - add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text") - close("div") - open("div") - add("label").attr("id", "lbbranchGit").text("Branch") - add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text") - close("div") - open("div") - add("a").attr("id", "signIn").text("Sign In") - close("div") - close("div") - close("li") - close("ul") - close("nav") - close("header") - end - - redef fun body do - super - open("div").add_class("page") - menu - add_content - close("div") - add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.") + redef fun title do + if mbuilder.mmodule2nmodule.has_key(mmodule) then + var nmodule = mbuilder.mmodule2nmodule[mmodule] + return "{mmodule.name} module | {nmodule.short_comment}" + else + return "{mmodule.name} module" + end end - # Insert all tags in content part - fun add_content do - open("div").add_class("content") - add("h1").text(modulename) - add("div").add_class("subtitle").text("module {modulename}") - module_comment + redef fun menu do + super + append("
  • Overview
  • ") + append("
  • {mmodule.name}
  • ") + append("
  • Full Index
  • ") + end + + redef fun content do + sidebar + var footed = "" + if ctx.opt_custom_footer_text.value != null then footed = "footed" + append("
    ") + append("

    {mmodule.name}

    ") + append("
    ") + mmodule.html_signature(self) + append("
    ") + mmodule.html_full_comment(self) + process_generate_dot classes properties - close("div") - end - - # Insert module comment in the content - fun module_comment do - var doc = amodule.comment - open("div").attr("id", "description") - add("pre").add_class("text_label").text(doc) - add("textarea").add_class("edit").attr("rows", "1").attr("cols", "76").attr("id", "fileContent").text(" ") - add("a").attr("id", "cancelBtn").text("Cancel") - add("a").attr("id", "commitBtn").text("Commit") - add("pre").add_class("text_label").attr("id", "preSave").attr("type", "2") - close("div") - end - - fun menu do - var mmodule = amodule.mmodule - open("div").add_class("menu") - open("nav") - add("h3").text("Module Hierarchy").attr("style","cursor: pointer;") - if mmodule.in_importation.direct_greaters.length > 0 then - add_html("

    All dependencies

      ") - var sorted = mmodule.in_importation.direct_greaters.to_a - var sorterp = new ComparableSorter[MModule] - sorterp.sort(sorted) - for m in sorted do - if m == mmodule or mmodule == m.public_owner then continue - open("li") - add("a").attr("href", "{m.name}.html").text(m.name) - close("li") + append("
    ") + end + + 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 omodule.public_owner != null then continue + if mmodule.in_importation < omodule then + poset.add_node(omodule) + poset.add_edge(mmodule, omodule) + end end - add_html("") - end - if mmodule.in_importation.greaters.length > 0 then - add_html("

    All clients

      ") - var sorted = mmodule.in_importation.greaters.to_a - var sorterp = new ComparableSorter[MModule] - sorterp.sort(sorted) - for m in sorted do - if m == mmodule then continue - open("li") - add("a").attr("href", "{m.name}.html").text(m.name) - close("li") + 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 - add_html("
    ") - end - close("nav") - if mmodule.in_nesting.direct_greaters.length > 0 then - var sorted = mmodule.in_nesting.direct_greaters.to_a - var sorterp = new ComparableSorter[MModule] - sorterp.sort(sorted) - open("nav") - add("h3").text("Nested Modules").attr("style","cursor: pointer;") - open("ul") - for m in sorted do - open("li") - add("a").attr("href", "{m.name}.html").text(m.name) - close("li") + for omodule in poset[mmodule].direct_greaters do + op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n") end - close("ul") - - close("nav") end - close("div") + op.append("\}\n") + generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}") end - fun classes do - var sorted = new Array[MClass] - sorted.add_all(amodule.mmodule.mclasses.keys) - var sorterp = new ComparableSorter[MClass] - sorterp.sort(sorted) - open("div").add_class("module") - open("article").add_class("classes filterable") - add("h2").text("Classes") - open("ul") - for c in sorted do - var state = amodule.mmodule.mclasses[c] - var name = c.name - if state == c_is_intro or state == c_is_imported then - open("li").add_class("intro") - add("span").attr("title", "introduced in this module").text("I ") - else - open("li").add_class("redef") - add("span").attr("title", "refined in this module").text("R ") + private fun sidebar do + append("") end - fun properties do - var sorted_imported = amodule.mmodule.imported_methods.to_a - var sorted_redef = amodule.mmodule.redef_methods.to_a - var sorterp = new ComparableSorter[MProperty] - sorterp.sort(sorted_imported) - sorterp.sort(sorted_redef) - open("article").add_class("properties filterable") - add_html("

    Properties

    ") - open("ul") - for method in sorted_imported do - if method.visibility is none_visibility or method.visibility is intrude_visibility then continue - open("li").add_class("intro") - add("span").attr("title", "introduction").text("I") - add_html(" ") - add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})") - close("li") + 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 - for method in sorted_redef do - if method.visibility is none_visibility or method.visibility is intrude_visibility then continue - open("li").add_class("redef") - add("span").attr("title", "redefinition").text("R") - add_html(" ") - add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})") - close("li") + # display the class column + private fun classes do + var intro_mclasses = mmodule.intro_mclasses + var redef_mclasses = mmodule.redef_mclasses + var all_mclasses = new HashSet[MClass] + for m in mmodule.in_nesting.greaters do + all_mclasses.add_all(m.intro_mclasses) + all_mclasses.add_all(m.redef_mclasses) end + all_mclasses.add_all(intro_mclasses) + all_mclasses.add_all(redef_mclasses) - close("ul") - close("article") + var sorted = new Array[MClass] + sorted.add_all(all_mclasses) + var sorter = new MClassNameSorter + sorter.sort(sorted) + append("
    ") + append("
    ") + append("

    Classes

    ") + append("
      ") + for c in sorted do + if c.visibility < ctx.min_visibility then continue + if redef_mclasses.has(c) and c.intro_mmodule.public_owner != mmodule then + append("
    • ") + append("R ") + else + append("
    • ") + append("I ") + end + c.html_link(self) + append("
    • ") + end + append("
    ") + append("
    ") + append("
    ") end + # display the property column + private fun properties do + # get properties + var mpropdefs = new HashSet[MPropDef] + for m in mmodule.in_nesting.greaters do + for c in m.mclassdefs do mpropdefs.add_all(c.mpropdefs) + end + for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs) + var sorted = mpropdefs.to_a + var sorter = new MPropDefNameSorter + sorter.sort(sorted) + # display properties in one column + append("
    ") + append("

    Properties

    ") + append("
      ") + for mprop in sorted do + if mprop isa MAttributeDef then continue + if mprop.mproperty.visibility < ctx.min_visibility then continue + mprop.html_list_item(self) + end + append("
    ") + append("
    ") + end end -# Nit Standard Library -class NitdocMClasses +# A class page +class NitdocClass super NitdocPage - var mclass: MClass - var aclassdef: AClassdef - var stdclassdef: nullable AStdClassdef - var public_owner: nullable MModule + 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 with(mclass: MClass, aclassdef: AClassdef, source: nullable String) do + init(mclass: MClass, ctx: NitdocContext, dot_dir: nullable String, source: nullable String) do + super(ctx) self.mclass = mclass - self.aclassdef = aclassdef - if aclassdef isa AStdClassdef then self.stdclassdef = aclassdef - self.public_owner = mclass.intro_mmodule.public_owner - opt_nodot = false - destinationdir = "" + self.dot_dir = dot_dir self.source = source + # load properties + 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 + 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 + 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 head do - super - add("title").text("{self.mclass.name} class | Nit Standard Library") + redef fun title do + var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro] + if nclass isa AStdClassdef then + return "{mclass.name} class | {nclass.short_comment}" + else + return "{mclass.name} class" + end end - redef fun header do - open("header") - open("nav").add_class("main") - open("ul") - open("li") - add_html("Overview") - close("li") - open("li") + redef fun menu do + super + append("
  • Overview
  • ") + var public_owner = mclass.public_owner if public_owner is null then - add_html("{mclass.intro_mmodule.name}") + append("
  • ") + mclass.intro_mmodule.html_link(self) + append("
  • ") else - add_html("{public_owner.name}") - end - close("li") - add("li").add_class("current").text(mclass.name) - open("li") - add_html("Full Index") - close("li") - open("li").attr("id", "liGitHub") - open("a").add_class("btn").attr("id", "logGitHub") - add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png") - close("a") - open("div").add_class("popover bottom") - add("div").add_class("arrow").text(" ") - open("div").add_class("githubTitle") - add("h3").text("Github Sign In") - close("div") - open("div") - add("label").attr("id", "lbloginGit").text("Username") - add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text") - open("label").attr("id", "logginMessage").text("Hello ") - open("a").attr("id", "githubAccount") - add("strong").attr("id", "nickName").text(" ") - close("a") - close("label") - close("div") - open("div") - add("label").attr("id", "lbpasswordGit").text("Password") - add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password") - open("div").attr("id", "listBranches") - add("label").attr("id", "lbBranches").text("Branch") - add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ") - close("div") - close("div") - open("div") - add("label").attr("id", "lbrepositoryGit").text("Repository") - add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text") - close("div") - open("div") - add("label").attr("id", "lbbranchGit").text("Branch") - add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text") - close("div") - open("div") - add("a").attr("id", "signIn").text("Sign In") - close("div") - close("div") - close("li") - close("ul") - close("nav") - close("header") - end - - redef fun body do - super - open("div").add_class("page") - add_content - close("div") - add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.") + append("
  • ") + public_owner.html_link(self) + append("
  • ") + end + append("
  • {mclass.name}
  • ") + append("
  • Full Index
  • ") end - # Insert all tags in content part - fun add_content do - open("div").add_class("menu") + redef fun content do + append("") + var footed = "" + if ctx.opt_custom_footer_text.value != null then footed = "footed" + 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.signature}

    ") + append("
    ") + mclass.html_full_signature(self) + append("
    ") + # comment + var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro] + append("
    ") + process_generate_dot + append("
    ") + # 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.name}") + else + append("{owner.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.name}
      • ") + else + append("
      • {mmodule.name}: {nmodule.short_comment}
      • ") + end + end + append("
      ") + end + append("
    • ") + end + append("
    ") + append("
    ") + # properties + var prop_sorter = new MPropDefNameSorter var lmmodule = new List[MModule] - # Insert the subtitle part - add("h1").text(mclass.name) - open("div").add_class("subtitle") - if mclass.visibility is none_visibility then subtitle += "private " - subtitle += "{mclass.kind} {mclass.public_owner.name}::{mclass.name}" - add_html(subtitle) - close("div") - add_html("
    ") - # We add the class description - open("section").add_class("description") - if not stdclassdef is null and not stdclassdef.comment.is_empty then add_html("
    {stdclassdef.comment} 
    CancelCommit
    ")
    -		close("section")
    -		open("section").add_class("concerns")
    -		add("h2").add_class("section-header").text("Concerns")
    -		open("ul")
    -		for owner, childs in mclass.concerns do
    -			open("li")
    -			add_html("{owner.name}: {owner.amodule.short_comment}")
    -			if not childs is null then
    -				open("ul")
    -				for child in childs.as(not null) do add_html("
  • {child.name}: {child.amodule.short_comment}
  • ") - close("ul") + # 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 - close("li") - end - close("ul") - close("section") - # Insert virtual types if there is almost one - if mclass.virtual_types.length > 0 or (stdclassdef != null and stdclassdef.n_formaldefs.length > 0) then - open("section").add_class("types") - add("h2").text("Formal and Virtual Types") - if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop) - if stdclassdef.n_formaldefs.length > 0 then - for prop in stdclassdef.n_formaldefs do - open("article").attr("id", "FT_Object_{prop.collect_text}") - open("h3").add_class("signature").text("{prop.collect_text}: nullable ") - add_html("Object") - close("h3") - add_html("
    formal generic type
    ") - close("article") + # virtual types + prop_sorter.sort(local_vtypes) + for prop in local_vtypes do prop.html_full_desc(self) + 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) + 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) + 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) + end + end + 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 - close("section") - end - # Insert constructors if there is almost one - if mclass.constructors.length > 0 then - open("section").add_class("constructors") - add("h2").add_class("section-header").text("Constructors") - for prop in mclass.constructors do description(prop) - close("section") - end - open("section").add_class("methods") - add("h2").add_class("section-header").text("Methods") - for mmodule, mmethods in mclass.all_methods do - add_html("") - if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then - if mclass.has_mmodule(mmodule) then - add_html("

    {mmodule.name}: {mmodule.amodule.short_comment}

    ") - else - add_html("

    Methods refined in {mmodule.name}

    {mmodule.name}: {mmodule.amodule.short_comment}

    ") + 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 - for prop in mmethods do description(prop) - end - # Insert inherited methods - if mclass.inherited_methods.length > 0 then - add("h3").text("Inherited Methods") - for i_mclass, methods in mclass.inherited do - open("p") - add_html("Defined in {i_mclass.name}: ") - for method in methods do - add_html("{method.name}") - if method != methods.last then add_html(", ") + end + append("
    ") + 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 - close("p") end end - close("section") + op.append("\}\n") + generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}") end +end - # Insert description tags for 'prop' - fun description(prop: MProperty) do - open("article").add_class("fun public {if prop.is_redef then "redef" else ""}").attr("id", "{prop.anchor}") - var sign = prop.name - if prop.apropdef != null then sign += prop.apropdef.signature - add_html("

    {sign}

    ") - add_html("
    {if prop.is_redef then "redef" else ""} fun {prop.intro_mclassdef.namespace(mclass)}::{prop.name}
    ") +# +# Model redefs +# - open("div").add_class("description") - if prop.apropdef is null or prop.apropdef.comment == "" then - add_html("New Comment") - else - add_html("
    {prop.apropdef.comment}
    ") +redef class MModule + # URL to nitdoc page + 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 - add_html("CancelCommit
    ")
    -		open("p")
    -		if prop.local_class != mclass then add_html("inherited from {prop.local_class.intro_mmodule.name} ")
    -		#TODO display show code if doc github
    -		add_html("defined by the module {prop.intro_mclassdef.mmodule.name} {if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
    +		return url_cache.as(not null)
    +	end
    +	private var url_cache: nullable String
     
    -		for parent in mclass.parents do
    -			if prop isa MMethod then if parent.constructors.has(prop) then add_html(" Previously defined by: {parent.intro_mmodule.name} for {parent.name}.")
    +	# html anchor id to the module in a nitdoc page
    +	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
    -		close("p")
    -		close("div")
    -
    -		close("article")
    +		return anchor_cache.as(not null)
     	end
    +	private var anchor_cache: nullable String
     
    -end	
    -
    -class NitdocPage
    -	super HTMLPage
    -	
    -	var opt_nodot: Bool
    -	var destinationdir : String
    -	var source: nullable String
    -
    -	redef fun head do
    -		add("meta").attr("charset", "utf-8")
    -		add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
    -		add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
    -		add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
    -		add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
    +	# Return a link (html a tag) to the nitdoc module page
    +	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("{name}")
    +			else
    +				res.append("{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
     
    -	redef fun body do header
    -	fun header do end
    -
    -	# Generate a clickable graphviz image using a dot content
    -	fun generate_dot(dot: String, name: String, alt: String) do
    -		if opt_nodot then return
    -		var file = new OFStream.open("{self.destinationdir}/{name}.dot")
    -		file.write(dot)
    -		file.close
    -		sys.system("\{ test -f {self.destinationdir}/{name}.png && test -f {self.destinationdir}/{name}.s.dot && diff {self.destinationdir}/{name}.dot {self.destinationdir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {self.destinationdir}/{name}.dot {self.destinationdir}/{name}.s.dot && dot -Tpng -o{self.destinationdir}/{name}.png -Tcmapx -o{self.destinationdir}/{name}.map {self.destinationdir}/{name}.s.dot ; \}")
    -		open("article").add_class("graph")
    -		add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
    -		close("article")
    -		var fmap = new IFStream.open("{self.destinationdir}/{name}.map")
    -		add_html(fmap.read_all)
    -		fmap.close
    +	# Return the module signature decorated with html
    +	fun html_signature(page: NitdocPage) do
    +		page.append("module ")
    +		html_full_namespace(page)
    +		page.append("")
     	end
     
    -	# Add a (source) link fo a given location
    -	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 " (show code)"
    +	# Return the module full namespace decorated with html
    +	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
     
    -end
    -
    -redef class AModule
    -	private fun comment: String do
    -		var ret = ""
    -		if n_moduledecl is null or n_moduledecl.n_doc is null then ret
    -		if n_moduledecl.n_doc is null then return ""
    -		for t in n_moduledecl.n_doc.n_comment do
    -			ret += "{t.text.replace("# ", "")}"
    +	# Return the module full namespace decorated with html
    +	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
    -		return ret
    +		page.append("")
     	end
     
    -	private fun short_comment: String do
    -		var ret = ""
    -		if n_moduledecl != null and n_moduledecl.n_doc != null then
    -			var txt = n_moduledecl.n_doc.n_comment.first.text
    -			txt = txt.replace("# ", "")
    -			txt = txt.replace("\n", "")
    -			ret += txt
    +	# Return the full comment of the module decorated with html
    +	fun html_full_comment(page: NitdocPage) do
    +		if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
    +			page.append("
    ") + page.append("
    {page.ctx.mbuilder.mmodule2nmodule[self].full_comment}
    ") + page.append("
    ") end - return ret end end -redef class MModule - - super Comparable - redef type OTHER: MModule - redef fun <(other: OTHER): Bool do return self.name < other.name - - var amodule: nullable AModule - - # Get the list of all methods in a module - fun imported_methods: Set[MMethod] do - var methods = new HashSet[MMethod] - for mclass in imported_mclasses do - for method in mclass.intro_methods do - methods.add(method) - end +redef class MClass + # Return the module signature decorated with html + fun html_full_signature(page: NitdocPage) do + if visibility < public_visibility then page.append("{visibility.to_s} ") + page.append("{kind} ") + html_namespace(page) + end + + # name with formal parameter + # Foo[A, B] + private fun signature: String do + if arity > 0 then + return "{name}[{intro.parameter_names.join(", ")}]" + else + return name end - return methods end - # Get the list aof all refined methods in a module - fun redef_methods: Set[MMethod] do - var methods = new HashSet[MMethod] - for mclass in redef_mclasses do - for method in mclass.intro_methods do - methods.add(method) + # Return a link (html a tag) to the nitdoc class page + fun html_link(page: NitdocPage) do + if html_link_cache == null then + var res = new Buffer + res.append("{signature}") + html_link_cache = res.to_s end - return methods + page.append(html_link_cache.as(not null)) end + private var html_link_cache: nullable String + + # Return the class namespace decorated with html + fun html_namespace(page: NitdocPage) do + intro_mmodule.html_namespace(page) + page.append("::") + html_link(page) + page.append("") + end + + fun url: String do + return "class_{public_owner}_{name}.html" + end + + # Escape name for html output + redef fun name do return super.html_escape end redef class MProperty + # Return the property namespace decorated with html + fun html_namespace(page: NitdocPage) do + intro_mclassdef.mclass.html_namespace(page) + page.append("::") + intro.html_link(page) + page.append("") + end - super Comparable - redef type OTHER: MProperty - redef fun <(other: OTHER): Bool do return self.name < other.name + # Escape name for html output + redef fun name do return super.html_escape +end - var is_redef: Bool - var apropdef: nullable APropdef +redef class MType + fun html_link(page: NitdocPage) is abstract +end - redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility) - do - super - is_redef = false - end +redef class MClassType + redef fun html_link(page) do mclass.html_link(page) +end - fun local_class: MClass do - var classdef = self.intro_mclassdef - return classdef.mclass +redef class MNullableType + redef fun html_link(page) do + page.append("nullable ") + mtype.html_link(page) end +end - fun class_text: String do - return local_class.name +redef class MGenericType + redef fun html_link(page) do + page.append("{mclass.name}[") + for i in [0..arguments.length[ do + arguments[i].html_link(page) + if i < arguments.length - 1 then page.append(", ") + end + page.append("]") end +end - fun link_anchor: String do - return "{class_text}.html#{anchor}" +redef class MParameterType + redef fun html_link(page) do + var name = mclass.intro.parameter_names[rank] + page.append("{name}") end +end - fun anchor: String do - return "PROP_{c_name}" - 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 + fun html_namespace(page: NitdocPage) do + mmodule.html_full_namespace(page) + page.append("::") + mclass.html_link(page) + page.append("") + end end -redef class MClass +redef class MPropDef + 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 - super Comparable - redef type OTHER: MClass - redef fun <(other: OTHER): Bool do return self.name < other.name - - # Associate all MMethods to each MModule concerns - fun all_methods: HashMap[MModule, Set[MMethod]] do - var hm = new HashMap[MModule, Set[MMethod]] - for mmodule, childs in concerns do - if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod] - for prop in intro_methods do - if mmodule == prop.intro_mclassdef.mmodule then - prop.is_redef = false - hm[mmodule].add(prop) - end - end - for prop in redef_methods do - if mmodule == prop.intro_mclassdef.mmodule then - prop.is_redef = true - hm[mmodule].add(prop) - end - end + fun anchor: String do + if anchor_cache == null then + anchor_cache = "PROP_{mclassdef.mclass.public_owner.name}_{mproperty.name}" + end + return anchor_cache.as(not null) + end + private var anchor_cache: nullable String - if childs != null then - for child in childs do - if not hm.has_key(child) then hm[child] = new HashSet[MMethod] - for prop in intro_methods do - if child == prop.intro_mclassdef.mmodule then - prop.is_redef = false - hm[child].add(prop) - end - end - for prop in redef_methods do - if child == prop.intro_mclassdef.mmodule then - prop.is_redef = true - hm[child].add(prop) - end - end - end + # Return a link (html a tag) to the nitdoc class page + 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.name}") + else + res.append("{mproperty.name}") end + html_link_cache = res.to_s end - return hm + page.append(html_link_cache.as(not null)) end + private var html_link_cache: nullable String - fun public_owner: MModule do - var owner = intro_mmodule - if owner.public_owner is null then - return owner + # Return a list item for the mpropdef + private fun html_list_item(page: NitdocPage) do + if is_intro then + page.append("
  • ") + page.append("I ") else - return owner.public_owner.as(not null) + page.append("
  • ") + page.append("R ") end - end - - # Associate Amodule to all MModule concern by 'self' - fun amodule(amodules: HashMap[MModule, AModule]) do - for owner, childs in concerns do - if childs != null then for child in childs do child.amodule = amodules[child] - owner.amodule = amodules[owner] + html_link(page) + page.append("(") + mclassdef.mclass.html_link(page) + page.append(")") + page.append("
  • ") + end + + # Return a list item for the mpropdef + 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 - # Associate MClass to all MMethod include in 'inherited_methods' - fun inherited: HashMap[MClass, Set[MMethod]] do - var hm = new HashMap[MClass, Set[MMethod]] - for method in inherited_methods do - var mclass = method.intro_mclassdef.mclass - if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod] - hm[mclass].add(method) - end - return hm - end + private fun html_full_desc(page: NitdocClass) is abstract + private fun html_info(page: NitdocClass) is abstract - # Return true if MModule concern contain subMModule - fun has_mmodule(sub: MModule): Bool do - for mmodule, childs in concerns do - if childs is null then continue - if childs.has(sub) then return true - end - return false + fun full_name: String do + return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}" end - fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do - for const in constructors do - if mprop2npropdef.has_key(const)then - const.apropdef = mprop2npropdef[const].as(AMethPropdef) + private fun html_inheritance(page: NitdocClass) do + # definitions block + page.append("

    ") + page.ctx.mainmodule.linearize_mpropdefs(mproperty.mpropdefs) + var previous_defs = new Array[MPropDef] + var next_defs = new Array[MPropDef] + var self_passed = false + for def in mproperty.mpropdefs do + if def == self then + self_passed = true + continue + end + if not self_passed then + if def.mclassdef.mclass.in_hierarchy(page.ctx.mainmodule) < page.mclass then continue + if def.is_intro then continue + previous_defs.add(def) + else + if page.mclass.in_hierarchy(page.ctx.mainmodule) < def.mclassdef.mclass then continue + next_defs.add(def) end end - - for intro in intro_methods do - if mprop2npropdef.has_key(intro)then - if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef) + page.append("defined by ") + mclassdef.mmodule.html_full_namespace(page) + if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then + page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[self].location)}") + end + if not is_intro then + page.append(", introduced by ") + mproperty.intro.mclassdef.mclass.html_link(page) + if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then + page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[self].location)}") end end + if not previous_defs.is_empty then + page.append(", inherited from ") + for i in [0..previous_defs.length[ do + var def = previous_defs[i] + def.mclassdef.mclass.html_link(page) + if page.ctx.mbuilder.mpropdef2npropdef.has_key(def) then + page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[def].location)}") + end - for rd in redef_methods do - if mprop2npropdef.has_key(rd)then - if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef) + if i < previous_defs.length - 1 then page.append(", ") end end + if not next_defs.is_empty then + page.append(", redefined by ") + for i in [0..next_defs.length[ do + var def = next_defs[i] + def.mclassdef.mclass.html_link(page) + if page.ctx.mbuilder.mpropdef2npropdef.has_key(def) then + page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[def].location)}") + end + if i < next_defs.length - 1 then page.append(", ") + end + end + page.append(".

    ") + end + + private fun html_comment(page: NitdocClass) do + if not page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then return + var nprop = page.ctx.mbuilder.mpropdef2npropdef[self] + page.append("
    ") + if not is_intro and page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then + var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro] + page.append("

    from ") + mproperty.html_namespace(page) + page.append("

    ") + if intro_nprop.full_comment == "" then + page.append("No comment") + else + page.append("
    {intro_nprop.full_comment}
    ") + end + page.append("

    from ") + mclassdef.html_namespace(page) + page.append("

    ") + end + if nprop.full_comment == "" then + page.append("No comment") + else + page.append("
    {nprop.full_comment}
    ") + end + html_inheritance(page) + page.append("
    ") end - - fun link_anchor: String do - return "{name}.html" - end - end -redef class AStdClassdef - private fun comment: String do - var ret = "" - if n_doc != null then - for t in n_doc.n_comment do - var txt = t.text.replace("# ", "") - txt = txt.replace("#", "") - ret += "{txt}" - end +redef class MMethodDef + redef fun html_full_desc(page) do + var classes = new Array[String] + var is_redef = mproperty.intro_mclassdef.mclass != page.mclass + 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.name}") + msignature.html_signature(page) + page.append("

    ") + else + page.append("

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

    ") end - return ret + html_info(page) + html_comment(page) + page.append("
    ") end - private fun short_comment: String do - var ret = "" - if n_doc != null then - var txt = n_doc.n_comment.first.text - txt = txt.replace("# ", "") - txt = txt.replace("\n", "") - ret += txt + redef fun html_info(page) do + page.append("
    ") + if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ") + if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ") + if mproperty.is_init then + page.append("init ") + else + page.append("fun ") end - return ret + mproperty.html_namespace(page) + page.append("
    ") end end -redef class ASignature - redef fun to_s do - #TODO closures - var ret = "" - if not n_params.is_empty then - ret = "{ret}({n_params.join(", ")})" - end - if n_type != null and n_type.to_s != "" then ret += " {n_type.to_s}" - return ret +redef class MVirtualTypeDef + redef fun html_full_desc(page) do + var is_redef = mproperty.intro_mclassdef.mclass != page.mclass + 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.name}: ") + bound.html_link(page) + page.append("

    ") + html_info(page) + html_comment(page) + page.append("
    ") + end + + redef fun html_info(page) do + page.append("
    ") + if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ") + page.append("type ") + mproperty.html_namespace(page) + page.append("
    ") end end -redef class AParam - redef fun to_s do - var ret = "{n_id.text}" - if n_type != null then - ret = "{ret}: {n_type.to_s}" - if n_dotdotdot != null then ret = "{ret}..." +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 - return ret end -end -redef class AType - redef fun to_s do - var ret = "{n_id.text}" - if n_kwnullable != null then ret = "nullable {ret}" - if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]" - return ret + 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 APropdef - private fun short_comment: String is abstract - private fun signature: String is abstract - private fun comment: String is abstract +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 -redef class AAttrPropdef - redef fun short_comment do - var ret = "" - if n_doc != null then - var txt = n_doc.n_comment.first.text - txt = txt.replace("# ", "") - txt = txt.replace("\n", "") - ret += txt +# +# Nodes redefs +# + +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.n_comment.first.text.substring_from(2).replace("\n", "").html_escape end - return ret + return "" end -end -redef class AMethPropdef - redef fun short_comment do - var ret = "" - if n_doc != null then - var txt = n_doc.n_comment.first.text - txt = txt.replace("# ", "") - txt = txt.replace("\n", "") - ret += txt + private fun full_comment: String do + var res = new Buffer + if n_moduledecl != null and n_moduledecl.n_doc != null then + for t in n_moduledecl.n_doc.n_comment do + res.append(t.text.substring_from(1).html_escape) + end end - return ret + return res.to_s end +end - redef fun signature: String do - var sign = "" - if n_signature != null then sign = " {n_signature.to_s}" - return sign +redef class AStdClassdef + private fun short_comment: String do + if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape + return "" end - redef private fun comment: String do - var ret = "" + private fun full_comment: String do + var res = new Buffer if n_doc != null then - for t in n_doc.n_comment do - var txt = t.text.replace("# ", "") - txt = txt.replace("#", "") - ret += "{txt}" - end + for t in n_doc.n_comment do res.append(t.text.substring_from(1).html_escape) end - return ret + return res.to_s end end -redef class MClassDef - private fun namespace(mclass: MClass): String do - - if mmodule.public_owner is null then - return "{mmodule.full_name}::{mclass.name}" - else if mclass is self.mclass then - return "{mmodule.public_owner.name}::{mclass.name}" - else - return "{mmodule.public_owner.name}::{mclass.name}" - end +redef class APropdef + private fun short_comment: String do + if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape + return "" end -end -redef class Set[E] - fun last: E do - return to_a[length-1] + private fun full_comment: String do + var res = new Buffer + if n_doc != null then + for t in n_doc.n_comment do res.append(t.text.substring_from(1).html_escape) + end + return res.to_s end end -# Create a tool context to handle options and paths -var toolcontext = new ToolContext - -# Here we launch the nit index -var nitdoc = new Nitdoc(toolcontext) -nitdoc.start +var nitdoc = new NitdocContext +nitdoc.generate_nitdoc