# This file is part of NIT ( http://www.nitlanguage.org ).
#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
# 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
# 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 modelize_property
# The NitdocContext contains all the knowledge used for doc generation
class NitdocContext
- super ToolContext
+ private var toolcontext = new ToolContext
private var model: Model
private var mbuilder: ModelBuilder
private var mainmodule: MModule
private var opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text")
init do
- super
- self.arguments = option_context.rest
- option_context.options.clear
- option_context.add_option(opt_dir)
- option_context.add_option(opt_source)
- option_context.add_option(opt_sharedir)
- option_context.add_option(opt_nodot)
- option_context.add_option(opt_private)
- option_context.add_option(opt_custom_title)
- option_context.add_option(opt_custom_footer_text)
- option_context.add_option(opt_custom_overview_text)
- option_context.add_option(opt_custom_menu_items)
- process_options
+ 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
+ self.arguments = toolcontext.option_context.rest
if arguments.length < 1 then
- option_context.usage
+ print "usage: nitdoc [options] file..."
+ toolcontext.option_context.usage
exit(1)
end
model = new Model
- mbuilder = new ModelBuilder(model, self)
+ mbuilder = new ModelBuilder(model, toolcontext)
# Here we load an process all modules passed on the command line
- var mmodules = mbuilder.parse_and_build(arguments)
- mbuilder.full_propdef_semantic_analysis
+ var mmodules = mbuilder.parse(arguments)
if mmodules.is_empty then return
+ mbuilder.run_phases
if mmodules.length == 1 then
mainmodule = mmodules.first
mainmodule.set_imported_mmodules(mmodules)
end
self.class_hierarchy = mainmodule.flatten_mclass_hierarchy
+ self.process_options
end
- redef fun process_options do
- super
+ private fun process_options do
if not opt_dir.value is null then
output_dir = opt_dir.value
else
private fun quicksearch_list do
var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js")
- var content = new Buffer
- content.append("var entries = \{ ")
-
+ file.write("var entries = \{ ")
for mmodule in model.mmodules do
- content.append("\"{mmodule.name}\": [")
- content.append("\{txt: \"{mmodule.name}\", url:\"{mmodule.url}\" \},")
- content.append("],")
+ 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
- content.append("\"{mclass.name}\": [")
- content.append("\{txt: \"{mclass.name}\", url:\"{mclass.url}\" \},")
- content.append("],")
+ 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
name2mprops[mproperty.name].add_all(mproperty.mpropdefs)
end
for mproperty, mpropdefs in name2mprops do
- content.append("\"{mproperty}\": [")
+ file.write("\"{mproperty}\": [")
for mpropdef in mpropdefs do
- content.append("\{txt: \"{mpropdef.full_name}\", url:\"{mpropdef.url}\" \},")
+ file.write("\{txt: \"{mpropdef.full_name}\", url:\"{mpropdef.url}\" \},")
end
- content.append("],")
+ file.write("],")
end
-
- content.append(" \};")
- file.write(content.to_s)
+ file.write(" \};")
file.close
end
# Nitdoc base page
abstract class NitdocPage
- super Buffer
var dot_dir: nullable String
var source: nullable String
var ctx: NitdocContext
init(ctx: NitdocContext) do
- super
self.ctx = ctx
end
end
# Render the page as a html string
- fun render: String do
+ protected fun render do
append("<!DOCTYPE html>")
append("<head>")
head
append("</div>")
footer
append("</body>")
- return to_s
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
- var out = new OFStream.open(file)
- out.write(render)
- out.close
+ self.out = new OFStream.open(file)
+ render
+ self.out.close
end
+ private var out: nullable OFStream
end
# The overview page
end
end
# sort modules
- var sorter = new ComparableSorter[MModule]
+ var sorter = new MModuleNameSorter
self.mmodules.add_all(mmodules)
sorter.sort(self.mmodules)
end
# Add to content modules column
private fun module_column do
var sorted = ctx.mbuilder.model.mmodule_importation_hierarchy.to_a
- var sorter = new ComparableSorter[MModule]
+ var sorter = new MModuleNameSorter
sorter.sort(sorted)
append("<article class='modules filterable'>")
append("<h2>Modules</h2>")
# Add to content classes modules
private fun classes_column do
var sorted = ctx.mbuilder.model.mclasses
- var sorter = new ComparableSorter[MClass]
+ var sorter = new MClassNameSorter
sorter.sort(sorted)
append("<article class='modules filterable'>")
append("<h2>Classes</h2>")
# Insert the properties column of fullindex page
private fun properties_column do
var sorted = ctx.mbuilder.model.mproperties
- var sorter = new ComparableSorter[MProperty]
+ var sorter = new MPropertyNameSorter
sorter.sort(sorted)
append("<article class='modules filterable'>")
append("<h2>Properties</h2>")
end
var clients = new Array[MModule]
for dep in mmodule.in_importation.smallers do
+ if dep.name == "<main>" then continue
if dep == mmodule or dep.public_owner != null then continue
clients.add(dep)
end
private fun display_module_list(list: Array[MModule]) do
append("<ul>")
- var sorter = new ComparableSorter[MModule]
+ var sorter = new MModuleNameSorter
sorter.sort(list)
for m in list do
append("<li>")
var sorted = new Array[MClass]
sorted.add_all(all_mclasses)
- var sorter = new ComparableSorter[MClass]
+ var sorter = new MClassNameSorter
sorter.sort(sorted)
append("<div class='module'>")
append("<article class='classes filterable'>")
end
for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs)
var sorted = mpropdefs.to_a
- var sorter = new ComparableSorter[MPropDef]
+ var sorter = new MPropDefNameSorter
sorter.sort(sorted)
# display properties in one column
append("<article class='properties filterable'>")
end
end
# get inherited properties
- for mprop in mclass.inherited_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)
+ 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
- inherited.add(mpropdef)
end
end
end
private fun properties_column do
- var sorter = new ComparableSorter[MPropDef]
+ var sorter = new MPropDefNameSorter
append("<nav class='properties filterable'>")
append("<h3>Properties</h3>")
# virtual types
private fun inheritance_column do
var sorted = new Array[MClass]
- var sorterp = new ComparableSorter[MClass]
+ var sorterp = new MClassNameSorter
append("<nav>")
append("<h3>Inheritance</h3>")
var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a
append("</ul>")
append("</section>")
# properties
- var prop_sorter = new ComparableSorter[MPropDef]
- var sorterprop = new ComparableSorter[MProperty]
- var sorterc = new ComparableSorter[MClass]
+ var prop_sorter = new MPropDefNameSorter
var lmmodule = new List[MModule]
# virtual and formal types
var local_vtypes = new Array[MVirtualTypeDef]
prop_sorter.sort(mmethods)
append("<p>Defined in ")
c.html_link(self)
- append("}: ")
+ append(": ")
for i in [0..mmethods.length[ do
var mmethod = mmethods[i]
mmethod.html_link(self)
#
redef class MModule
- super Comparable
- redef type OTHER: MModule
- redef fun <(other: OTHER): Bool do return self.name < other.name
-
# URL to nitdoc page
fun url: String do
- var res = new Buffer
- res.append("module_")
- var mowner = public_owner
- if mowner != null then
- res.append("{public_owner.name}_")
+ 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
- res.append("{self.name}.html")
- return res.to_s
+ return url_cache.as(not null)
end
+ private var url_cache: nullable String
# html anchor id to the module in a nitdoc page
fun anchor: String do
- var res = new Buffer
- res.append("MOD_")
- var mowner = public_owner
- if mowner != null then
- res.append("{public_owner.name}_")
+ 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
- res.append(self.name)
- return res.to_s
+ return anchor_cache.as(not null)
end
+ private var anchor_cache: nullable String
# Return a link (html a tag) to the nitdoc module page
fun html_link(page: NitdocPage) do
- if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
- page.append("<a href='{url}' title='{page.ctx.mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>")
- else
- page.append("<a href='{url}'>{name}</a>")
+ if html_link_cache == null then
+ var res = new Buffer
+ if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
+ res.append("<a href='{url}' title='{page.ctx.mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>")
+ else
+ res.append("<a href='{url}'>{name}</a>")
+ 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
fun html_signature(page: NitdocPage) do
end
redef class MClass
- super Comparable
- redef type OTHER: MClass
- redef fun <(other: OTHER): Bool do return self.name < other.name
-
# Return the module signature decorated with html
fun html_full_signature(page: NitdocPage) do
if visibility < public_visibility then page.append("{visibility.to_s} ")
# Return a link (html a tag) to the nitdoc class page
fun html_link(page: NitdocPage) do
- page.append("<a href='{url}'")
- if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
- var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
- if nclass isa AStdClassdef then
- page.append(" title=\"{nclass.short_comment}\"")
+ if html_link_cache == null then
+ var res = new Buffer
+ res.append("<a href='{url}'")
+ if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
+ var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
+ if nclass isa AStdClassdef then
+ res.append(" title=\"{nclass.short_comment}\"")
+ end
end
+ res.append(">{signature}</a>")
+ html_link_cache = res.to_s
end
- page.append(">{signature}</a>")
+ 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
end
fun url: String do
- return "class_{public_owner}_{c_name}.html"
+ return "class_{public_owner}_{name}.html"
end
# Escape name for html output
end
redef class MProperty
- super Comparable
- redef type OTHER: MProperty
- redef fun <(other: OTHER): Bool do return self.name < other.name
-
# Return the property namespace decorated with html
fun html_namespace(page: NitdocPage) do
intro_mclassdef.mclass.html_namespace(page)
end
redef class MPropDef
- super Comparable
- redef type OTHER: MPropDef
- redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
+ 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
- fun url: String do return "{mclassdef.mclass.url}#{anchor}"
- fun anchor: String do return "PROP_{mclassdef.mclass.public_owner.name}_{c_name}"
+ 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
# Return a link (html a tag) to the nitdoc class page
fun html_link(page: NitdocPage) do
- if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
- var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
- page.append("<a href=\"{url}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>")
- else
- page.append("<a href=\"{url}\">{mproperty.name}</a>")
+ 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("<a href=\"{url}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>")
+ else
+ res.append("<a href=\"{url}\">{mproperty.name}</a>")
+ 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
private fun html_list_item(page: NitdocPage) do
if not page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then return
var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
page.append("<div class='description'>")
- if not is_intro then
+ 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("<p>from ")
mproperty.html_namespace(page)
redef class MMethodDef
redef fun html_full_desc(page) do
- if not page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then return
- var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
var classes = new Array[String]
var is_redef = mproperty.intro_mclassdef.mclass != page.mclass
if mproperty.is_init then
if is_redef then classes.add("redef")
classes.add(mproperty.visibility.to_s)
page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
- if nprop isa AAttrPropdef then
- if nprop.mreadpropdef == self then
- page.append("<h3 class='signature'>{mproperty.name}: ")
- nprop.html_signature(page)
- page.append("</h3>")
- else
- page.append("<h3 class='signature'>{mproperty.name}(value: ")
- nprop.html_signature(page)
- page.append(")</h3>")
- end
- else
- var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro]
+ if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
page.append("<h3 class='signature'>{mproperty.name}")
- intro_nprop.html_signature(page)
+ msignature.html_signature(page)
+ page.append("</h3>")
+ else
+ page.append("<h3 class='signature'>init")
+ msignature.html_signature(page)
page.append("</h3>")
end
html_info(page)
page.append("<div class='info'>")
if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ")
if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ")
- page.append("fun ")
+ if mproperty.is_init then
+ page.append("init ")
+ else
+ page.append("fun ")
+ end
mproperty.html_namespace(page)
page.append("</div>")
page.append("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
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
+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
#
end
redef class APropdef
- private fun short_comment: String is abstract
- private fun full_comment: String is abstract
- private fun html_signature(page: NitdocPage) is abstract
-end
-
-redef class AAttrPropdef
- redef fun short_comment do
- if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
- return ""
- end
-
- redef 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
-
- redef fun html_signature(page) do
- if n_type != null then n_type.mtype.html_link(page)
- end
-end
-
-redef class AMethPropdef
- redef fun short_comment do
- if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
- return ""
- end
-
- redef fun full_comment 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
-
- redef fun html_signature(page) do
- if n_signature != null then n_signature.html_link(page)
- end
-end
-
-redef class ATypePropdef
- redef fun short_comment do
+ 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 fun full_comment do
+ 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
-
- redef fun html_signature(page) do
- mpropdef.bound.html_link(page)
- end
-end
-
-redef class ASignature
- fun html_link(page: NitdocPage) do
- #TODO closures
- if not n_params.is_empty then
- page.append("(")
- for i in [0..n_params.length[ do
- n_params[i].html_link(page)
- if i < n_params.length - 1 then page.append(", ")
- end
- page.append(")")
- end
- if n_type != null then
- page.append(":")
- n_type.mtype.html_link(page)
- end
- end
-end
-
-redef class AParam
- fun html_link(page: NitdocPage) do
- page.append(n_id.text)
- if n_type != null then
- page.append(": ")
- n_type.mtype.html_link(page)
- if n_dotdotdot != null then page.append("...")
- end
- end
end
var nitdoc = new NitdocContext