X-Git-Url: http://nitlanguage.org diff --git a/src/doc/doc_pages.nit b/src/doc/doc_pages.nit index 844e79e..f8a1250 100644 --- a/src/doc/doc_pages.nit +++ b/src/doc/doc_pages.nit @@ -17,12 +17,15 @@ module doc_pages import toolcontext import doc_model +private import json::static redef class ToolContext private var opt_dir = new OptionString("output directory", "-d", "--dir") private var opt_source = new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source") private var opt_sharedir = new OptionString("directory containing nitdoc assets", "--sharedir") private var opt_shareurl = new OptionString("use shareurl instead of copy shared files", "--shareurl") + private var opt_no_attributes = new OptionBool("ignore the attributes", + "--no-attributes") private var opt_nodot = new OptionBool("do not generate graphes with graphviz", "--no-dot") private var opt_private = new OptionBool("also generate private API", "--private") @@ -45,7 +48,8 @@ redef class ToolContext super var opts = option_context - opts.add_option(opt_dir, opt_source, opt_sharedir, opt_shareurl, opt_nodot, opt_private) + opts.add_option(opt_dir, opt_source, opt_sharedir, opt_shareurl, + opt_no_attributes, opt_nodot, opt_private) opts.add_option(opt_custom_title, opt_custom_footer, opt_custom_intro, opt_custom_brand) opts.add_option(opt_github_upstream, opt_github_base_sha1, opt_github_gitdir) opts.add_option(opt_piwik_tracker, opt_piwik_site_id) @@ -82,6 +86,23 @@ redef class ToolContext end end end + + # Filter the entity based on the options specified by the user. + # + # Return `true` if the specified entity has to be included in the generated + # documentation + private fun filter_mclass(mclass: MClass): Bool do + return mclass.visibility >= min_visibility + end + + # Filter the entity based on the options specified by the user. + # + # Return `true` if the specified entity has to be included in the generated + # documentation + private fun filter_mproperty(mproperty: MProperty): Bool do + return mproperty.visibility >= min_visibility and + not (opt_no_attributes.value and mproperty isa MAttribute) + end end # The Nitdoc class explores the model and generate pages for each mentities found @@ -109,11 +130,7 @@ class Nitdoc 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 @@ -121,9 +138,9 @@ class Nitdoc 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 @@ -157,7 +174,7 @@ class Nitdoc private fun classes do for mclass in model.mclasses do - if mclass.visibility <= ctx.min_visibility then continue + if not ctx.filter_mclass(mclass) then continue var page = new NitdocClass(ctx, model, mainmodule, mclass) page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end @@ -165,7 +182,8 @@ class Nitdoc private fun properties do for mproperty in model.mproperties do - if mproperty.visibility <= ctx.min_visibility then continue + if not ctx.filter_mproperty(mproperty) then continue + if mproperty isa MInnerClass then continue var page = new NitdocProperty(ctx, model, mainmodule, mproperty) page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end @@ -186,9 +204,7 @@ 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 @@ -196,51 +212,71 @@ class QuickSearch 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 + if not ctx.filter_mclass(mclass) then continue + 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] + if not ctx.filter_mproperty(mproperty) then continue + 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 @@ -315,14 +351,16 @@ abstract class NitdocPage # Clickable graphviz image using dot format # return null if no graph for this page - fun tpl_graph(dot: FlatBuffer, name: String, title: nullable String): nullable TplArticle do + 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 @@ -330,11 +368,12 @@ abstract class NitdocPage 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 "" + var name_html = name.html_escape + content.add "" content.add map article.content = content return article @@ -347,7 +386,7 @@ abstract class NitdocPage var source = ctx.opt_source.value if source == null then var url = location.file.filename.simplify_path - return "View Source" + return "View Source" end # THIS IS JUST UGLY ! (but there is no replace yet) var x = source.split_with("%f") @@ -357,7 +396,7 @@ abstract class NitdocPage x = source.split_with("%L") source = x.join(location.line_end.to_s) source = source.simplify_path - return "View Source" + return "View Source" end # MProject description template @@ -365,8 +404,9 @@ abstract class NitdocPage var article = mproject.tpl_article article.subtitle = mproject.tpl_declaration article.content = mproject.tpl_definition - if mproject.mdoc != null then - article.content = mproject.mdoc.tpl_short_comment + var mdoc = mproject.mdoc_or_fallback + if mdoc != null then + article.content = mdoc.tpl_short_comment end return article end @@ -388,7 +428,7 @@ abstract class NitdocPage var intros = mmodule.intro_mclassdefs(ctx.min_visibility).to_a if not intros.is_empty then mainmodule.linearize_mclassdefs(intros) - var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}_intros", "Introduces") + var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}.intros", "Introduces") var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mclassdef in intros do intros_lst.add_li mclassdef.tpl_list_item @@ -401,7 +441,7 @@ abstract class NitdocPage var redefs = mmodule.redef_mclassdefs(ctx.min_visibility).to_a if not redefs.is_empty then mainmodule.linearize_mclassdefs(redefs) - var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}_redefs", "Redefines") + var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}.redefs", "Redefines") var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mclassdef in redefs do redefs_lst.add_li mclassdef.tpl_list_item @@ -430,7 +470,7 @@ abstract class NitdocPage redef_article.source_link = tpl_showsource(mclassdef.location) article.add_child redef_article # mpropdefs list - var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}_intros", "Introduces") + var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}.intros", "Introduces") var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in mclassdef.collect_intro_mpropdefs(ctx.min_visibility) do intros_lst.add_li mpropdef.tpl_list_item @@ -439,7 +479,7 @@ abstract class NitdocPage intros.content = intros_lst redef_article.add_child intros end - var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}_redefs", "Redefines") + var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}.redefs", "Redefines") var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in mclassdef.collect_redef_mpropdefs(ctx.min_visibility) do redefs_lst.add_li mpropdef.tpl_list_item @@ -478,7 +518,8 @@ abstract class NitdocPage else var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mprop.nitdoc_id}" - var lnk = new TplLink.with_title(def_url, mprop.name, "Go to introduction") + var lnk = new TplLink.with_title(def_url, mprop.nitdoc_name, + "Go to introduction") title.add "redef " title.add lnk end @@ -486,10 +527,10 @@ abstract class NitdocPage article.title_classes.add "signature" article.summary_title = "{mprop.nitdoc_name}" article.subtitle = main_mpropdef.tpl_namespace - if main_mpropdef.mdoc != null then - article.content = main_mpropdef.mdoc.tpl_comment + if main_mpropdef.mdoc_or_fallback != null then + article.content = main_mpropdef.mdoc_or_fallback.tpl_comment end - var subarticle = new TplArticle("{main_mpropdef.nitdoc_id}_redefs") + var subarticle = new TplArticle("{main_mpropdef.nitdoc_id}.redefs") # Add redef in same `MClass` if local_mpropdefs.length > 1 then for mpropdef in local_mpropdefs do @@ -511,7 +552,7 @@ abstract class NitdocPage end # Add linearization if lin.length > 1 then - var lin_article = new TplArticle("{main_mpropdef.nitdoc_id}_lin") + var lin_article = new TplArticle("{main_mpropdef.nitdoc_id}.lin") lin_article.title = "Inheritance" var lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in lin do @@ -637,7 +678,7 @@ class NitdocSearch private fun classes_list: Array[MClass] do var sorted = new Array[MClass] for mclass in model.mclasses do - if mclass.visibility < ctx.min_visibility then continue + if not ctx.filter_mclass(mclass) then continue sorted.add mclass end name_sorter.sort(sorted) @@ -648,9 +689,7 @@ class NitdocSearch private fun mprops_list: Array[MProperty] do var sorted = new Array[MProperty] for mproperty in model.mproperties do - if mproperty.visibility < ctx.min_visibility then continue - if mproperty isa MAttribute then continue - sorted.add mproperty + if ctx.filter_mproperty(mproperty) then sorted.add mproperty end name_sorter.sort(sorted) return sorted @@ -884,7 +923,7 @@ class NitdocModule # Graph var mmodules = new HashSet[MModule] - mmodules.add_all mmodule.in_nesting.direct_greaters + mmodules.add_all mmodule.nested_mmodules mmodules.add_all imports if clients.length < 10 then mmodules.add_all clients mmodules.add mmodule @@ -996,17 +1035,17 @@ class NitdocModule end end # build graph - var op = new FlatBuffer - var name = "dep_{mmodule.name}" - op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") + var op = new RopeBuffer + 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") @@ -1063,19 +1102,17 @@ class NitdocClass # Property list to display in sidebar fun tpl_sidebar_properties do - var kind_map = sort_by_kind(mclass_inherited_mprops) + var by_kind = new PropertiesByKind.with_elements(mclass_inherited_mprops) var summary = new TplList.with_classes(["list-unstyled"]) - 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) + by_kind.sort_groups(name_sorter) + for g in by_kind.groups do tpl_sidebar_list(g, summary) tpl_sidebar.boxes.add new TplSideBox.with_content("All properties", summary) end - private fun tpl_sidebar_list(name: String, mprops: Array[MProperty], summary: TplList) do + private fun tpl_sidebar_list(mprops: PropertyGroup[MProperty], summary: TplList) do if mprops.is_empty then return - name_sorter.sort(mprops) - var entry = new TplListItem.with_content(name) + var entry = new TplListItem.with_content(mprops.title) var list = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mprop in mprops do list.add_li tpl_sidebar_item(mprop) @@ -1090,8 +1127,9 @@ class NitdocClass classes.add "inherit" var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mprop.nitdoc_id}" - var lnk = new TplLink(def_url, mprop.name) - if mprop.intro.mdoc != null then lnk.title = mprop.intro.mdoc.short_comment + var lnk = new TplLink(def_url, mprop.nitdoc_name) + var mdoc = mprop.intro.mdoc_or_fallback + if mdoc != null then lnk.title = mdoc.short_comment var item = new Template item.add new TplLabel.with_classes(classes) item.add lnk @@ -1113,8 +1151,9 @@ class NitdocClass var section = new TplSection.with_title("top", tpl_title) section.subtitle = mclass.intro.tpl_declaration var article = new TplArticle("comment") - if mclass.mdoc != null then - article.content = mclass.mdoc.tpl_comment + var mdoc = mclass.mdoc_or_fallback + if mdoc != null then + article.content = mdoc.tpl_comment end section.add_child article return section @@ -1135,15 +1174,14 @@ class NitdocClass # parents var hparents = new HashSet[MClass] for c in mclass.in_hierarchy(mainmodule).direct_greaters do - if c.visibility < ctx.min_visibility then continue - hparents.add c + if ctx.filter_mclass(c) then hparents.add c end # ancestors var hancestors = new HashSet[MClass] for c in mclass.in_hierarchy(mainmodule).greaters do if c == mclass then continue - if c.visibility < ctx.min_visibility then continue + if not ctx.filter_mclass(c) then continue if hparents.has(c) then continue hancestors.add c end @@ -1151,15 +1189,14 @@ class NitdocClass # children var hchildren = new HashSet[MClass] for c in mclass.in_hierarchy(mainmodule).direct_smallers do - if c.visibility < ctx.min_visibility then continue - hchildren.add c + if ctx.filter_mclass(c) then hchildren.add c end # descendants var hdescendants = new HashSet[MClass] for c in mclass.in_hierarchy(mainmodule).smallers do if c == mclass then continue - if c.visibility < ctx.min_visibility then continue + if not ctx.filter_mclass(c) then continue if hchildren.has(c) then continue hdescendants.add c end @@ -1242,30 +1279,21 @@ class NitdocClass # properties var mprops = mmodules2mprops[mentity] - var kind_map = sort_by_kind(mprops) + var by_kind = new PropertiesByKind.with_elements(mprops) - # virtual types - for article in tpl_mproperty_articles(kind_map, "type") do - section.add_child article - end - # constructors - for article in tpl_mproperty_articles(kind_map, "init") do - section.add_child article - end - # methods - for article in tpl_mproperty_articles(kind_map, "fun") do - section.add_child article + for g in by_kind.groups do + for article in tpl_mproperty_articles(g) do + section.add_child article + end end parent.add_child section end end end - private fun tpl_mproperty_articles(kind_map: Map[String, Set[MProperty]], - kind_name: String): Sequence[TplArticle] do + private fun tpl_mproperty_articles(elts: Collection[MProperty]): + Sequence[TplArticle] do var articles = new List[TplArticle] - var elts = kind_map[kind_name].to_a - name_sorter.sort(elts) for elt in elts do var local_defs = mprops2mdefs[elt] # var all_defs = elt.mpropdefs @@ -1318,25 +1346,6 @@ class NitdocClass return map end - private fun sort_by_kind(mprops: Collection[MProperty]): Map[String, Set[MProperty]] do - var map = new HashMap[String, Set[MProperty]] - map["type"] = new HashSet[MProperty] - map["init"] = new HashSet[MProperty] - map["fun"] = new HashSet[MProperty] - for mprop in mprops do - if mprop isa MVirtualTypeProp then - map["type"].add mprop - else if mprop isa MMethod then - if mprop.is_init then - map["init"].add mprop - else - map["fun"].add mprop - end - end - end - return map - end - private fun mclass_inherited_mprops: Set[MProperty] do var res = new HashSet[MProperty] var local = mclass.local_mproperties(ctx.min_visibility) @@ -1377,9 +1386,9 @@ class NitdocClass end end - var op = new FlatBuffer - var name = "dep_{mclass.name}" - op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n") + var op = new RopeBuffer + 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] @@ -1390,18 +1399,18 @@ class NitdocClass 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") @@ -1409,6 +1418,74 @@ class NitdocClass end end +# Groups properties by kind. +private class PropertiesByKind + # The virtual types. + var virtual_types = new PropertyGroup[MVirtualTypeProp]("Virtual types") + + # The constructors. + var constructors = new PropertyGroup[MMethod]("Contructors") + + # The attributes. + var attributes = new PropertyGroup[MAttribute]("Attributes") + + # The methods. + var methods = new PropertyGroup[MMethod]("Methods") + + # The inner classes. + var inner_classes = new PropertyGroup[MInnerClass]("Inner classes") + + # All the groups. + # + # Sorted in the order they are displayed to the user. + var groups: SequenceRead[PropertyGroup[MProperty]] = [ + virtual_types, + constructors, + attributes, + methods, + inner_classes: PropertyGroup[MProperty]] + + # Add each the specified property to the appropriate list. + init with_elements(properties: Collection[MProperty]) do add_all(properties) + + # Add the specified property to the appropriate list. + fun add(property: MProperty) do + if property isa MMethod then + if property.is_init then + constructors.add property + else + methods.add property + end + else if property isa MVirtualTypeProp then + virtual_types.add property + else if property isa MAttribute then + attributes.add property + else if property isa MInnerClass then + inner_classes.add property + else + abort + end + end + + # Add each the specified property to the appropriate list. + fun add_all(properties: Collection[MProperty]) do + for p in properties do add(p) + end + + # Sort each group with the specified comparator. + fun sort_groups(comparator: Comparator) do + for g in groups do comparator.sort(g) + end +end + +# A Group of properties of the same kind. +private class PropertyGroup[E: MProperty] + super Array[E] + + # The title of the group, as displayed to the user. + var title: String +end + # A MProperty page class NitdocProperty super NitdocPage @@ -1467,7 +1544,12 @@ class NitdocProperty private fun tpl_properties(parent: TplSection) do # intro title - var section = new TplSection.with_title("intro", "Introduction") + var ns = mproperty.intro.mclassdef.mmodule.tpl_namespace + var section = new TplSection("intro") + var title = new Template + title.add "Introduction in " + title.add ns + section.title = title section.summary_title = "Introduction" section.add_child tpl_mpropdef_article(mproperty.intro) parent.add_child section @@ -1485,7 +1567,7 @@ class NitdocProperty parent.add_child new TplSection(mentity.nitdoc_id) else if mentity isa MModule then var ssection = new TplSection(mentity.nitdoc_id) - var title = new Template + title = new Template title.add "in " title.add mentity.tpl_namespace ssection.title = title