import toolcontext
import doc_model
+private import json::static
redef class ToolContext
private var opt_dir = new OptionString("output directory", "-d", "--dir")
# The Nitdoc class explores the model and generate pages for each mentities found
class Nitdoc
+ var ctx: ToolContext
var model: Model
var mainmodule: MModule
- var ctx: ToolContext
-
- init(ctx: ToolContext, model: Model, mainmodule: MModule) do
- self.ctx = ctx
- self.model = model
- self.mainmodule = mainmodule
- end
fun generate do
init_output_dir
var sharedir = ctx.opt_sharedir.value
if sharedir == null then
var dir = ctx.nit_dir
- if dir == null then
- print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
- abort
- end
- sharedir = "{dir}/share/nitdoc"
+ sharedir = dir/"share/nitdoc"
if not sharedir.file_exists then
print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
abort
end
# copy shared files
if ctx.opt_shareurl.value == null then
- sys.system("cp -r {sharedir.to_s}/* {output_dir.to_s}/")
+ sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/")
else
- sys.system("cp -r {sharedir.to_s}/resources/ {output_dir.to_s}/resources/")
+ sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/resources/ {output_dir.to_s.escape_to_sh}/resources/")
end
end
private fun properties do
for mproperty in model.mproperties do
if mproperty.visibility <= ctx.min_visibility then continue
+ if mproperty isa MInnerClass then continue
+ if mproperty isa MAttribute then continue
var page = new NitdocProperty(ctx, model, mainmodule, mproperty)
page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
end
# All entities are grouped by name to make the research easier.
class QuickSearch
- private var mmodules = new HashSet[MModule]
- private var mclasses = new HashSet[MClass]
- private var mpropdefs = new HashMap[String, Set[MPropDef]]
+ private var table = new QuickSearchTable
+
+ var ctx: ToolContext
+ var model: Model
- init(ctx: ToolContext, model: Model) do
+ init do
for mmodule in model.mmodules do
if mmodule.is_fictive then continue
- mmodules.add mmodule
+ add_result_for(mmodule.name, mmodule.full_name, mmodule.nitdoc_url)
end
for mclass in model.mclasses do
if mclass.visibility < ctx.min_visibility then continue
- mclasses.add mclass
+ add_result_for(mclass.name, mclass.full_name, mclass.nitdoc_url)
end
for mproperty in model.mproperties do
if mproperty.visibility < ctx.min_visibility then continue
if mproperty isa MAttribute then continue
- if not mpropdefs.has_key(mproperty.name) then
- mpropdefs[mproperty.name] = new HashSet[MPropDef]
+ for mpropdef in mproperty.mpropdefs do
+ var full_name = mpropdef.mclassdef.mclass.full_name
+ var cls_url = mpropdef.mclassdef.mclass.nitdoc_url
+ var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}"
+ add_result_for(mproperty.name, full_name, def_url)
end
- mpropdefs[mproperty.name].add_all(mproperty.mpropdefs)
end
end
+ private fun add_result_for(query: String, txt: String, url: String) do
+ table[query].add new QuickSearchResult(txt, url)
+ end
+
fun render: Template do
var tpl = new Template
- tpl.add "var nitdocQuickSearchRawList=\{ "
- for mmodule in mmodules do
- tpl.add "\"{mmodule.name}\":["
- tpl.add "\{txt:\"{mmodule.full_name}\",url:\"{mmodule.nitdoc_url}\"\},"
- tpl.add "],"
- end
- for mclass in mclasses do
- var full_name = mclass.intro.mmodule.full_name
- tpl.add "\"{mclass.name}\":["
- tpl.add "\{txt:\"{full_name}\",url:\"{mclass.nitdoc_url}\"\},"
- tpl.add "],"
- end
- for mproperty, mprops in mpropdefs do
- tpl.add "\"{mproperty}\":["
- for mpropdef in mprops do
- var full_name = mpropdef.mclassdef.mclass.full_name
- var cls_url = mpropdef.mclassdef.mclass.nitdoc_url
- var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}"
- tpl.add "\{txt:\"{full_name}\",url:\"{def_url}\"\},"
- end
- tpl.add "],"
- end
- tpl.add " \};"
+ var buffer = new RopeBuffer
+ tpl.add buffer
+ buffer.append "var nitdocQuickSearchRawList="
+ table.append_json buffer
+ buffer.append ";"
return tpl
end
end
+# The result map for QuickSearch.
+private class QuickSearchTable
+ super JsonMapRead[String, QuickSearchResultList]
+ super HashMap[String, QuickSearchResultList]
+
+ redef fun provide_default_value(key) do
+ var v = new QuickSearchResultList
+ self[key] = v
+ return v
+ end
+end
+
+# A QuickSearch result list.
+private class QuickSearchResultList
+ super JsonSequenceRead[QuickSearchResult]
+ super Array[QuickSearchResult]
+end
+
+# A QuickSearch result.
+private class QuickSearchResult
+ super Jsonable
+
+ # The text of the link.
+ var txt: String
+
+ # The destination of the link.
+ var url: String
+
+ redef fun to_json do
+ return "\{\"txt\":{txt.to_json},\"url\":{url.to_json}\}"
+ end
+end
+
# Nitdoc base page
# Define page structure and properties
abstract class NitdocPage
private var mainmodule: MModule
private var name_sorter = new MEntityNameSorter
- init(ctx: ToolContext, model: Model, mainmodule: MModule) do
- self.ctx = ctx
- self.model = model
- self.mainmodule = mainmodule
- end
-
# Render the page as a html template
fun render: Template do
var shareurl = "."
fun tpl_graph(dot: Buffer, name: String, title: nullable String): nullable TplArticle do
if ctx.opt_nodot.value then return null
var output_dir = ctx.output_dir
- var file = new OFStream.open("{output_dir}/{name}.dot")
+ var path = output_dir / name
+ var path_sh = path.escape_to_sh
+ var file = new OFStream.open("{path}.dot")
file.write(dot)
file.close
- sys.system("\{ test -f {output_dir}/{name}.png && test -f {output_dir}/{name}.s.dot && diff {output_dir}/{name}.dot {output_dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {output_dir}/{name}.dot {output_dir}/{name}.s.dot && dot -Tpng -o{output_dir}/{name}.png -Tcmapx -o{output_dir}/{name}.map {output_dir}/{name}.s.dot ; \}")
- var fmap = new IFStream.open("{output_dir}/{name}.map")
+ sys.system("\{ test -f {path_sh}.png && test -f {path_sh}.s.dot && diff -- {path_sh}.dot {path_sh}.s.dot >/dev/null 2>&1 ; \} || \{ cp -- {path_sh}.dot {path_sh}.s.dot && dot -Tpng -o{path_sh}.png -Tcmapx -o{path_sh}.map {path_sh}.s.dot ; \}")
+ var fmap = new IFStream.open("{path}.map")
var map = fmap.read_all
fmap.close
var alt = ""
if title != null then
article.title = title
- alt = "alt='{title}'"
+ alt = "alt='{title.html_escape}'"
end
article.css_classes.add "text-center"
var content = new Template
- content.add "<img src='{name}.png' usemap='#{name}' style='margin:auto' {alt}/>"
+ var name_html = name.html_escape
+ content.add "<img src='{name_html}.png' usemap='#{name_html}' style='margin:auto' {alt}/>"
content.add map
article.content = content
return article
private var mgroup: MGroup
- private var concerns: ConcernsTree
- private var intros: Set[MClass]
- private var redefs: Set[MClass]
+ private var concerns: ConcernsTree is noinit
+ private var intros: Set[MClass] is noinit
+ private var redefs: Set[MClass] is noinit
- init(ctx: ToolContext, model: Model, mainmodule: MModule, mgroup: MGroup) do
- super
- self.mgroup = mgroup
+ init do
self.concerns = model.concerns_tree(mgroup.collect_mmodules)
self.concerns.sort_with(new MConcernRankSorter)
self.intros = mgroup.in_nesting_intro_mclasses(ctx.min_visibility)
super NitdocPage
private var mmodule: MModule
- private var concerns: ConcernsTree
- private var mclasses2mdefs: Map[MClass, Set[MClassDef]]
- private var mmodules2mclasses: Map[MModule, Set[MClass]]
+ private var concerns: ConcernsTree is noinit
+ private var mclasses2mdefs: Map[MClass, Set[MClassDef]] is noinit
+ private var mmodules2mclasses: Map[MModule, Set[MClass]] is noinit
- init(ctx: ToolContext, model: Model, mainmodule: MModule, mmodule: MModule) do
- super
- self.mmodule = mmodule
+ init do
var mclassdefs = new HashSet[MClassDef]
mclassdefs.add_all mmodule.intro_mclassdefs(ctx.min_visibility)
mclassdefs.add_all mmodule.redef_mclassdefs(ctx.min_visibility)
end
# build graph
var op = new RopeBuffer
- 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")
+ var name = "dep_module_{mmodule.nitdoc_id}"
+ op.append("digraph \"{name.escape_to_dot}\" \{ 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")
+ op.append("\"{mmodule.name.escape_to_dot}\"[shape=box,margin=0.03];\n")
else
- op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n")
+ op.append("\"{mmodule.name.escape_to_dot}\"[URL=\"{mmodule.nitdoc_url.escape_to_dot}\"];\n")
end
for omodule in poset[mmodule].direct_greaters do
- op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
+ op.append("\"{mmodule.name.escape_to_dot}\"->\"{omodule.name.escape_to_dot}\";\n")
end
end
op.append("\}\n")
super NitdocPage
private var mclass: MClass
- private var concerns: ConcernsTree
- private var mprops2mdefs: Map[MProperty, Set[MPropDef]]
- private var mmodules2mprops: Map[MModule, Set[MProperty]]
+ private var concerns: ConcernsTree is noinit
+ private var mprops2mdefs: Map[MProperty, Set[MPropDef]] is noinit
+ private var mmodules2mprops: Map[MModule, Set[MProperty]] is noinit
- init(ctx: ToolContext, model: Model, mainmodule: MModule, mclass: MClass) do
- super
- self.mclass = mclass
+ init do
var mpropdefs = new HashSet[MPropDef]
mpropdefs.add_all mclass.intro_mpropdefs(ctx.min_visibility)
mpropdefs.add_all mclass.redef_mpropdefs(ctx.min_visibility)
tpl_sidebar_list("Virtual types", kind_map["type"].to_a, summary)
tpl_sidebar_list("Constructors", kind_map["init"].to_a, summary)
tpl_sidebar_list("Methods", kind_map["fun"].to_a, summary)
+ if not kind_map["inner"].is_empty then
+ tpl_sidebar_list("Inner classes", kind_map["inner"].to_a, summary)
+ end
tpl_sidebar.boxes.add new TplSideBox.with_content("All properties", summary)
end
for article in tpl_mproperty_articles(kind_map, "fun") do
section.add_child article
end
+ # inner classes
+ for article in tpl_mproperty_articles(kind_map, "inner") do
+ section.add_child article
+ end
parent.add_child section
end
end
map["type"] = new HashSet[MProperty]
map["init"] = new HashSet[MProperty]
map["fun"] = new HashSet[MProperty]
+ map["inner"] = new HashSet[MProperty]
for mprop in mprops do
if mprop isa MVirtualTypeProp then
map["type"].add mprop
else
map["fun"].add mprop
end
+ else if mprop isa MInnerClass then
+ map["inner"].add mprop
end
end
return map
end
var op = new RopeBuffer
- 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")
+ var name = "dep_class_{mclass.nitdoc_id}"
+ op.append("digraph \"{name.escape_to_dot}\" \{ 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")
var classes = poset.to_a
var todo = new Array[MClass]
var done = new HashSet[MClass]
if done.has(c) then continue
done.add c
if c == mclass then
- op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
+ op.append("\"{c.name.escape_to_dot}\"[shape=box,margin=0.03];\n")
else
- op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n")
+ op.append("\"{c.name.escape_to_dot}\"[URL=\"{c.nitdoc_url.escape_to_dot}\"];\n")
end
var smallers = poset[c].direct_smallers
if smallers.length < 10 then
for c2 in smallers do
- op.append("\"{c2.name}\"->\"{c.name}\";\n")
+ op.append("\"{c2.name.escape_to_dot}\"->\"{c.name.escape_to_dot}\";\n")
end
todo.add_all smallers
else
- op.append("\"...\"->\"{c.name}\";\n")
+ op.append("\"...\"->\"{c.name.escape_to_dot}\";\n")
end
end
op.append("\}\n")
super NitdocPage
private var mproperty: MProperty
- private var concerns: ConcernsTree
- private var mmodules2mdefs: Map[MModule, Set[MPropDef]]
+ private var concerns: ConcernsTree is noinit
+ private var mmodules2mdefs: Map[MModule, Set[MPropDef]] is noinit
- init(ctx: ToolContext, model: Model, mainmodule: MModule, mproperty: MProperty) do
- super
+ init do
self.mproperty = mproperty
self.mmodules2mdefs = sort_by_mmodule(collect_mpropdefs)
self.concerns = model.concerns_tree(mmodules2mdefs.keys)