+ return tpl_graph(op, name, null)
+ 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
+
+ private var mproperty: MProperty
+ private var concerns: ConcernsTree is noinit
+ private var mmodules2mdefs: Map[MModule, Set[MPropDef]] is noinit
+
+ init do
+ self.mproperty = mproperty
+ self.mmodules2mdefs = sort_by_mmodule(collect_mpropdefs)
+ self.concerns = model.concerns_tree(mmodules2mdefs.keys)
+ self.concerns.sort_with(new MConcernRankSorter)
+ end
+
+ private fun collect_mpropdefs: Set[MPropDef] do
+ var res = new HashSet[MPropDef]
+ for mpropdef in mproperty.mpropdefs do
+ if not mpropdef.is_intro then res.add mpropdef
+ end
+ return res
+ end
+
+ private var page = new TplPage
+ redef fun tpl_page do return page
+
+ private var sidebar = new TplSidebar
+ redef fun tpl_sidebar do return sidebar
+
+ redef fun tpl_title do
+ return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}"
+ end
+
+ redef fun page_url do return mproperty.nitdoc_url
+
+ redef fun tpl_topmenu do
+ var topmenu = super
+ var mmodule = mproperty.intro_mclassdef.mmodule
+ var mproject = mmodule.mgroup.mproject
+ var mclass = mproperty.intro_mclassdef.mclass
+ topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
+ topmenu.add_link new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}")
+ topmenu.add_link new TplLink(page_url, mproperty.nitdoc_name)
+ return topmenu
+ end
+
+ private fun tpl_intro: TplSection do
+ var title = new Template
+ title.add mproperty.nitdoc_name
+ title.add mproperty.intro.tpl_signature
+ var section = new TplSection.with_title("top", title)
+ section.subtitle = mproperty.tpl_namespace
+ section.summary_title = mproperty.nitdoc_name
+ return section
+ end
+
+ private fun tpl_properties(parent: TplSection) do
+ # intro title
+ 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
+
+ # concerns
+ if concerns.is_empty then return
+ parent.add_child new TplArticle.with_content("Concerns", "Concerns", concerns.to_tpl)
+
+ # redef list
+ var lst = concerns.to_a
+ for mentity in lst do
+ if mentity isa MProject then
+ parent.add_child new TplSection(mentity.nitdoc_id)
+ else if mentity isa MGroup then
+ parent.add_child new TplSection(mentity.nitdoc_id)
+ else if mentity isa MModule then
+ var ssection = new TplSection(mentity.nitdoc_id)
+ title = new Template
+ title.add "in "
+ title.add mentity.tpl_namespace
+ ssection.title = title
+ ssection.summary_title = "in {mentity.nitdoc_name}"
+
+ # properties
+ var mpropdefs = mmodules2mdefs[mentity].to_a
+ name_sorter.sort(mpropdefs)
+ for mpropdef in mpropdefs do
+ ssection.add_child tpl_mpropdef_article(mpropdef)
+ end
+ parent.add_child ssection
+ end
+ end
+ end
+
+ redef fun tpl_content do
+ var top = tpl_intro
+ tpl_properties(top)
+ tpl_page.add_section top
+ end
+
+ private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do
+ var map = new HashMap[MModule, Set[MPropDef]]
+ for mpropdef in mpropdefs do
+ var mmodule = mpropdef.mclassdef.mmodule
+ if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef]
+ map[mmodule].add mpropdef
+ end
+ return map