src/doc: introduce `html_comment` and `html_short_comment` services
[nit.git] / src / doc / doc_phases / doc_html.nit
index e4865e5..3329fc5 100644 (file)
@@ -23,6 +23,7 @@ import doc_structure
 import doc_hierarchies
 import doc_intros_redefs
 import doc_graphs
+import html_templates
 
 redef class ToolContext
 
@@ -86,7 +87,7 @@ redef class ToolContext
                var git_dir = opt_github_gitdir
                var opts = [upstream.value, base_sha.value, git_dir.value]
                if not opts.has_only(null) and opts.has(null) then
-                       print "Error: Options {upstream.names.first}, " +
+                       print "Option Error: options {upstream.names.first}, " +
                                "{base_sha.names.first} and {git_dir.names.first} " +
                                "are required to enable the GitHub plugin"
                        exit 1
@@ -104,7 +105,7 @@ class RenderHTMLPhase
        redef fun apply do
                init_output_dir
                for page in doc.pages do
-                       page.render(self, doc).write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
+                       page.render(self, doc).write_to_file("{ctx.output_dir.to_s}/{page.html_url}")
                end
        end
 
@@ -119,7 +120,7 @@ class RenderHTMLPhase
                        var dir = ctx.nit_dir
                        sharedir = dir/"share/nitdoc"
                        if not sharedir.file_exists then
-                               print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
+                               print "Error: cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
                                abort
                        end
                end
@@ -156,52 +157,41 @@ end
 redef class DocPage
 
        # Render the page as a html template.
-       private fun render(v: RenderHTMLPhase, doc: DocModel): TplPage do
+       private fun render(v: RenderHTMLPhase, doc: DocModel): Writable do
                var shareurl = "."
                if v.ctx.opt_shareurl.value != null then
                        shareurl = v.ctx.opt_shareurl.value.as(not null)
                end
 
+               # init page options
+               self.shareurl = shareurl
+               self.footer = v.ctx.opt_custom_footer.value
+               self.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl))
+
                # build page
-               var tpl = new TplPage
-               tpl.title = tpl_title(v, doc)
-               tpl.url = page_url
-               tpl.shareurl = shareurl
-               tpl.topmenu = tpl_topmenu(v, doc)
-               tpl.add_section tpl_content(v, doc)
-               tpl.footer = v.ctx.opt_custom_footer.value
-               tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl))
-               tpl.sidebar = tpl_sidebar(v, doc)
+               init_title(v, doc)
+               init_sidebar(v, doc)
+               init_topmenu(v, doc)
+               init_content(v, doc)
 
                # piwik tracking
                var tracker_url = v.ctx.opt_piwik_tracker.value
                var site_id = v.ctx.opt_piwik_site_id.value
                if tracker_url != null and site_id != null then
-                       tpl.scripts.add new TplPiwikScript(tracker_url, site_id)
+                       self.scripts.add new TplPiwikScript(tracker_url, site_id)
                end
-               return tpl
+               return self
        end
 
        # FIXME diff hack
        # all properties below are roughly copied from `doc_pages`
 
-       # URL to this page.
-       fun page_url: String is abstract
-
-       # Build page sidebar if any
-       fun tpl_sidebar(v: RenderHTMLPhase, doc: DocModel): nullable TplSidebar do return null
-
        # Build page title string
-       fun tpl_title(v: RenderHTMLPhase, doc: DocModel): String do
-               if v.ctx.opt_custom_title.value != null then
-                       return v.ctx.opt_custom_title.value.to_s
-               end
-               return "Nitdoc"
-       end
+       fun init_title(v: RenderHTMLPhase, doc: DocModel) is abstract
 
-       # Build top menu template
-       fun tpl_topmenu(v: RenderHTMLPhase, doc: DocModel): TplTopMenu do
-               var topmenu = new TplTopMenu(page_url)
+       # Build top menu template if any.
+       fun init_topmenu(v: RenderHTMLPhase, doc: DocModel) do
+               topmenu = new DocTopMenu
                var brand = v.ctx.opt_custom_brand.value
                if brand != null then
                        var tpl = new Template
@@ -210,30 +200,38 @@ redef class DocPage
                        tpl.add "</span>"
                        topmenu.brand = tpl
                end
-               topmenu.add_link new TplLink("index.html", "Overview")
-               topmenu.add_link new TplLink("search.html", "Index")
-               return topmenu
+               var title = "Overview"
+               if v.ctx.opt_custom_title.value != null then
+                       title = v.ctx.opt_custom_title.value.to_s
+               end
+               topmenu.add_li new ListItem(new Link("index.html", title))
+               topmenu.add_li new ListItem(new Link("search.html", "Index"))
+               topmenu.active_item = topmenu.items.first
+       end
+
+       # Build page sidebar if any.
+       fun init_sidebar(v: RenderHTMLPhase, doc: DocModel) do
+               sidebar = new TplSidebar
        end
 
-       # Build page content template
-       fun tpl_content(v: RenderHTMLPhase, doc: DocModel): TplSection is abstract
+       # Build page content template.
+       fun init_content(v: RenderHTMLPhase, doc: DocModel) do end
 end
 
 redef class OverviewPage
-       redef fun page_url do return "index.html"
+       redef var html_url = "index.html"
 
-       redef fun tpl_title(v, doc) do
+       redef fun init_title(v, doc) do
+               title = "Overview"
                if v.ctx.opt_custom_title.value != null then
-                       return v.ctx.opt_custom_title.value.to_s
-               else
-                       return "Overview"
+                       title = v.ctx.opt_custom_title.value.to_s
                end
        end
 
        # TODO this should be done in StructurePhase.
-       redef fun tpl_content(v, doc) do
+       redef fun init_content(v, doc) do
                # intro text
-               var section = new TplSection.with_title("overview", tpl_title(v, doc))
+               var section = new TplSection.with_title("overview", title)
                var article = new TplArticle("intro")
                if v.ctx.opt_custom_intro.value != null then
                        article.content = v.ctx.opt_custom_intro.value.to_s
@@ -246,50 +244,57 @@ redef class OverviewPage
                var ssection = new TplSection.with_title("projects", "Projects")
                for mproject in mprojects do
                        var sarticle = mproject.tpl_article
-                       sarticle.subtitle = mproject.tpl_declaration
+                       sarticle.subtitle = mproject.html_declaration
                        sarticle.content = mproject.tpl_definition
-                       var mdoc = mproject.mdoc_or_fallback
-                       if mdoc != null then
-                               sarticle.content = mdoc.tpl_short_comment
+                       var comment = mproject.html_short_comment
+                       if comment != null then
+                               sarticle.content = comment
                        end
                        ssection.add_child sarticle
                end
                section.add_child ssection
-               return section
+               self.add_section section
        end
 
-       redef fun tpl_sidebar(v, doc) do return new TplSidebar
+       redef fun init_sidebar(v, doc) do sidebar = new TplSidebar
 end
 
 redef class SearchPage
-       redef fun page_url do return "search.html"
-       redef fun tpl_title(v, doc) do return "Index"
+       redef var html_url = "search.html"
+       redef fun init_title(v, doc) do title = "Index"
+
+       redef fun init_topmenu(v, doc) do
+               super
+               topmenu.active_item = topmenu.items.last
+       end
+
+       redef fun init_sidebar(v, doc) do end
 
        # TODO this should be done in StructurePhase.
-       redef fun tpl_content(v, doc) do
+       redef fun init_content(v, doc) do
                var tpl = new TplSearchPage("search_all")
                var section = new TplSection("search")
                # title
                tpl.title = "Index"
                # modules list
                for mmodule in modules_list(v, doc) do
-                       tpl.modules.add mmodule.tpl_link
+                       tpl.modules.add mmodule.html_link
                end
                # classes list
                for mclass in classes_list(v, doc) do
-                       tpl.classes.add mclass.tpl_link
+                       tpl.classes.add mclass.html_link
                end
                # properties list
                for mproperty in mprops_list(v, doc) do
                        var m = new Template
-                       m.add mproperty.intro.tpl_link
+                       m.add mproperty.intro.html_link
                        m.add " ("
-                       m.add mproperty.intro.mclassdef.mclass.tpl_link
+                       m.add mproperty.intro.mclassdef.mclass.html_link
                        m.add ")"
                        tpl.props.add m
                end
                section.add_child tpl
-               return section
+               self.add_section section
        end
 
        # Extract mmodule list to display (sorted by name)
@@ -319,9 +324,9 @@ redef class SearchPage
 end
 
 redef class MEntityPage
-       redef fun page_url do return mentity.nitdoc_url
-       redef fun tpl_title(v, doc) do return mentity.nitdoc_name
-       redef fun tpl_content(v, doc) do return root.start_rendering(v, doc, self)
+       redef var html_url is lazy do return mentity.nitdoc_url
+       redef fun init_title(v, doc) do title = mentity.html_name
+       redef fun init_content(v, doc) do add_section root.start_rendering(v, doc, self)
 end
 
 # FIXME all clases below are roughly copied from `doc_pages` and adapted to new
@@ -329,22 +334,22 @@ end
 # `doc_templates` module.
 
 redef class MGroupPage
-       redef fun tpl_topmenu(v, doc) do
-               var topmenu = super
+       redef fun init_topmenu(v, doc) do
+               super
                var mproject = mentity.mproject
                if not mentity.is_root then
-                       topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
+                       topmenu.add_li new ListItem(new Link(mproject.nitdoc_url, mproject.html_name))
                end
-               topmenu.add_link new TplLink(page_url, mproject.nitdoc_name)
-               return topmenu
+               topmenu.add_li new ListItem(new Link(html_url, mproject.html_name))
+               topmenu.active_item = topmenu.items.last
        end
 
-       redef fun tpl_sidebar(v, doc) do
-               var sidebar = new TplSidebar
+       redef fun init_sidebar(v, doc) do
+               super
                var mclasses = new HashSet[MClass]
                mclasses.add_all intros
                mclasses.add_all redefs
-               if mclasses.is_empty then return sidebar
+               if mclasses.is_empty then return
                var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
 
                var sorted = mclasses.to_a
@@ -353,7 +358,6 @@ redef class MGroupPage
                        list.add_li tpl_sidebar_item(mclass)
                end
                sidebar.boxes.add new TplSideBox.with_content("All classes", list)
-               return sidebar
        end
 
        private fun tpl_sidebar_item(def: MClass): TplListItem do
@@ -365,28 +369,28 @@ redef class MGroupPage
                end
                var lnk = new Template
                lnk.add new TplLabel.with_classes(classes)
-               lnk.add def.tpl_link
+               lnk.add def.html_link
                return new TplListItem.with_content(lnk)
        end
 end
 
 redef class MModulePage
-       redef fun tpl_topmenu(v, doc) do
-               var topmenu = super
+       redef fun init_topmenu(v, doc) do
+               super
                var mproject = mentity.mproject
-               topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
-               topmenu.add_link new TplLink(mentity.nitdoc_url, mentity.nitdoc_name)
-               return topmenu
+               topmenu.add_li new ListItem(new Link(mproject.nitdoc_url, mproject.html_name))
+               topmenu.add_li new ListItem(new Link(mentity.nitdoc_url, mentity.html_name))
+               topmenu.active_item = topmenu.items.last
        end
 
        # Class list to display in sidebar
-       redef fun tpl_sidebar(v, doc) do
+       redef fun init_sidebar(v, doc) do
                # TODO filter here?
-               var sidebar = new TplSidebar
+               super
                var mclasses = new HashSet[MClass]
                mclasses.add_all mentity.filter_intro_mclasses(v.ctx.min_visibility)
                mclasses.add_all mentity.filter_redef_mclasses(v.ctx.min_visibility)
-               if mclasses.is_empty then return sidebar
+               if mclasses.is_empty then return
                var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
 
                var sorted = mclasses.to_a
@@ -395,7 +399,6 @@ redef class MModulePage
                        list.add_li tpl_sidebar_item(mclass)
                end
                sidebar.boxes.add new TplSideBox.with_content("All classes", list)
-               return sidebar
        end
 
        private fun tpl_sidebar_item(def: MClass): TplListItem do
@@ -407,34 +410,29 @@ redef class MModulePage
                end
                var lnk = new Template
                lnk.add new TplLabel.with_classes(classes)
-               lnk.add def.tpl_link
+               lnk.add def.html_link
                return new TplListItem.with_content(lnk)
        end
 end
 
 redef class MClassPage
 
-       redef fun tpl_title(v, doc) do
-               return "{mentity.nitdoc_name}{mentity.tpl_signature.write_to_string}"
-       end
-
-       redef fun tpl_topmenu(v, doc) do
-               var topmenu = super
+       redef fun init_topmenu(v, doc) do
+               super
                var mproject = mentity.intro_mmodule.mgroup.mproject
-               topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
-               topmenu.add_link new TplLink(page_url, mentity.nitdoc_name)
-               return topmenu
+               topmenu.add_li new ListItem(new Link(mproject.nitdoc_url, mproject.html_name))
+               topmenu.add_li new ListItem(new Link(html_url, mentity.html_name))
+               topmenu.active_item = topmenu.items.last
        end
 
-       redef fun tpl_sidebar(v, doc) do
-               var sidebar = new TplSidebar
+       redef fun init_sidebar(v, doc) do
+               super
                var by_kind = new PropertiesByKind.with_elements(mclass_inherited_mprops(v, doc))
                var summary = new TplList.with_classes(["list-unstyled"])
 
                by_kind.sort_groups(v.name_sorter)
                for g in by_kind.groups do tpl_sidebar_list(g, summary)
                sidebar.boxes.add new TplSideBox.with_content("All properties", summary)
-               return sidebar
        end
 
        private fun tpl_sidebar_list(mprops: PropertyGroup[MProperty], summary: TplList) do
@@ -454,7 +452,7 @@ redef class MClassPage
                        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.nitdoc_name)
+                       var lnk = new TplLink(def_url, mprop.html_name)
                        var mdoc = mprop.intro.mdoc_or_fallback
                        if mdoc != null then lnk.title = mdoc.short_comment
                        var item = new Template
@@ -469,7 +467,7 @@ redef class MClassPage
                end
                var lnk = new Template
                lnk.add new TplLabel.with_classes(classes)
-               lnk.add mprop.tpl_anchor
+               lnk.add mprop.html_link_to_anchor
                return new TplListItem.with_content(lnk)
        end
 
@@ -497,22 +495,20 @@ redef class MClassPage
 end
 
 redef class MPropertyPage
-       redef fun tpl_topmenu(v, doc) do
-               var topmenu = super
+       redef fun init_title(v, doc) do
+               title = "{mentity.html_name}{mentity.html_short_signature.write_to_string}"
+       end
+
+       redef fun init_topmenu(v, doc) do
+               super
                var mmodule = mentity.intro_mclassdef.mmodule
                var mproject = mmodule.mgroup.mproject
                var mclass = mentity.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, mentity.nitdoc_name)
-               return topmenu
+               topmenu.add_li new ListItem(new Link(mproject.nitdoc_url, mproject.html_name))
+               topmenu.add_li new ListItem(new Link(mclass.nitdoc_url, mclass.html_name))
+               topmenu.add_li new ListItem(new Link(html_url, mentity.html_name))
+               topmenu.active_item = topmenu.items.last
        end
-
-       redef fun tpl_title(v, doc) do
-               return "{mentity.nitdoc_name}{mentity.tpl_signature.write_to_string}"
-       end
-
-       redef fun tpl_sidebar(v, doc) do return new TplSidebar
 end
 
 redef class DocComposite
@@ -530,18 +526,16 @@ redef class DocRoot
        fun start_rendering(v: RenderHTMLPhase, doc: DocModel, page: MEntityPage): TplSection do
                var section = new TplSection("top")
                var mentity = page.mentity
-               section.title = mentity.nitdoc_name
-               section.subtitle = mentity.tpl_declaration
+               section.title = mentity.html_name
+               section.subtitle = mentity.html_declaration
                # FIXME ugly hack to avoid diff
                if mentity isa MGroup and mentity.is_root then
-                       section.title = mentity.mproject.nitdoc_name
-                       section.subtitle = mentity.mproject.tpl_declaration
-               else if mentity isa MClass then
-                       section.title = "{mentity.nitdoc_name}{mentity.tpl_signature.write_to_string}"
+                       section.title = mentity.mproject.html_name
+                       section.subtitle = mentity.mproject.html_declaration
                else if mentity isa MProperty then
-                       section.title = "{mentity.nitdoc_name}{mentity.intro.tpl_signature.write_to_string}"
-                       section.subtitle = mentity.tpl_namespace
-                       section.summary_title = mentity.nitdoc_name
+                       section.title = "{mentity.html_name}{mentity.intro.html_signature.write_to_string}"
+                       section.subtitle = mentity.html_namespace
+                       section.summary_title = mentity.html_name
                end
                render(v, doc, page, section)
                return section
@@ -576,21 +570,27 @@ redef class ConcernSection
                var title = new Template
                if mmodule == page.mentity then
                        title.add "in "
-                       section.summary_title = "in {mmodule.nitdoc_name}"
+                       section.summary_title = "in {mmodule.html_name}"
                else
                        title.add "from "
-                       section.summary_title = "from {mmodule.nitdoc_name}"
+                       section.summary_title = "from {mmodule.html_name}"
                end
-               title.add mmodule.tpl_namespace
+               title.add mmodule.html_namespace
                section.title = title
        end
 
        private fun render_concern_other(page: MEntityPage, section: TplSection, mmodule: MModule) do
                var title = new Template
                title.add "in "
-               title.add mmodule.tpl_namespace
+               title.add mmodule.html_namespace
                section.title = title
-               section.summary_title = "in {mmodule.nitdoc_name}"
+               section.summary_title = "in {mmodule.html_name}"
+       end
+end
+
+redef class MEntitySection
+       redef fun render(v, doc, page, parent) do
+               for child in children do child.render(v, doc, page, parent)
        end
 end
 
@@ -605,11 +605,11 @@ redef class IntroArticle
                else if mentity isa MPropDef then
                        article.source_link = v.tpl_showsource(mentity.location)
                end
-               # article.subtitle = mentity.tpl_declaration
+               # article.subtitle = mentity.html_declaration
                # FIXME diff hack
                if mentity isa MProperty then
                        # intro title
-                       var ns = mentity.intro.mclassdef.mmodule.tpl_namespace
+                       var ns = mentity.intro.mclassdef.mmodule.html_namespace
                        var section = new TplSection("intro")
                        var title = new Template
                        title.add "Introduction in "
@@ -644,7 +644,7 @@ redef class DefinitionArticle
                # FIXME hideous hacks...
                if mentity isa MModule then
                        article = mentity.tpl_article
-                       article.subtitle = mentity.tpl_declaration
+                       article.subtitle = mentity.html_declaration
                        article.content = mentity.tpl_definition
                else if mentity isa MClass then
                        article = make_mclass_article(v, page)
@@ -655,13 +655,10 @@ redef class DefinitionArticle
                        article = make_mpropdef_article(v, doc, page)
                else
                        article = mentity.tpl_article
-                       article.subtitle = mentity.tpl_declaration
+                       article.subtitle = mentity.html_declaration
                        if mentity isa MPropDef then
                                article.source_link = v.tpl_showsource(mentity.location)
                        end
-                       if not mentity isa MVirtualTypeProp then
-                               # article.content = mentity.tpl_comment
-                       end
                end
                for child in children do
                        child.render(v, doc, page, article)
@@ -673,7 +670,7 @@ redef class DefinitionArticle
 
        private fun make_mclass_article(v: RenderHTMLPhase, page: MEntityPage): TplArticle do
                var article = mentity.tpl_article
-               article.subtitle = mentity.tpl_namespace
+               article.subtitle = mentity.html_namespace
                article.content = null
                return article
        end
@@ -686,7 +683,7 @@ redef class DefinitionArticle
                end
                var title = new Template
                title.add "in "
-               title.add mclassdef.mmodule.tpl_namespace
+               title.add mclassdef.mmodule.html_namespace
                article.subtitle = title
                return article
        end
@@ -700,22 +697,23 @@ redef class DefinitionArticle
                title.add mprop.tpl_icon
                title.add "<span id='{mpropdef.nitdoc_id}'></span>"
                if mpropdef.is_intro then
-                       title.add mprop.tpl_link
-                       title.add mprop.intro.tpl_signature
+                       title.add mprop.html_link
+                       title.add mprop.intro.html_signature
                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.nitdoc_name,
+                       var lnk = new TplLink.with_title(def_url, mprop.html_name,
                                        "Go to introduction")
                        title.add "redef "
                        title.add lnk
                end
                article.title = title
                article.title_classes.add "signature"
-               article.summary_title = "{mprop.nitdoc_name}"
-               article.subtitle = mpropdef.tpl_namespace
-               if mpropdef.mdoc_or_fallback != null then
-                       article.content = mpropdef.mdoc_or_fallback.tpl_comment
+               article.summary_title = "{mprop.html_name}"
+               article.subtitle = mpropdef.html_namespace
+               var comment = mpropdef.html_comment
+               if comment != null then
+                       article.content = comment
                end
                # TODO move in its own phase? let's see after doc_template refactoring.
                # Add linearization
@@ -842,3 +840,317 @@ redef class GraphArticle
                parent.add_child article
        end
 end
+
+redef class Location
+       # Github url based on this location
+       fun github(gitdir: String): String do
+               var base_dir = getcwd.join_path(gitdir).simplify_path
+               var file_loc = getcwd.join_path(file.filename).simplify_path
+               var gith_loc = file_loc.substring(base_dir.length + 1, file_loc.length)
+               return "{gith_loc}:{line_start},{column_start}--{line_end},{column_end}"
+       end
+end
+
+redef class MEntity
+       # A template article that briefly describe the entity
+       fun tpl_short_article: TplArticle do
+               var tpl = tpl_article
+               var comment = html_short_comment
+               if comment != null then
+                       tpl.content = comment
+               end
+               return tpl
+       end
+
+       # A template article that describe the entity
+       fun tpl_article: TplArticle do
+               var tpl = new TplArticle.with_title(nitdoc_id, tpl_title)
+               tpl.title_classes.add "signature"
+               tpl.subtitle = html_namespace
+               tpl.summary_title = html_name
+               return tpl
+       end
+
+       # A template definition of the mentity
+       # include name, sysnopsys, comment and namespace
+       fun tpl_definition: TplDefinition is abstract
+
+       # A li element that can go in a list
+       fun tpl_list_item: TplListItem do
+               var lnk = new Template
+               lnk.add new TplLabel.with_classes(tpl_css_classes)
+               lnk.add html_link
+               var comment = html_short_comment
+               if comment != null then
+                       lnk.add ": "
+                       lnk.add comment
+               end
+               return new TplListItem.with_content(lnk)
+       end
+
+       var tpl_css_classes = new Array[String]
+
+       # Box title for this mentity
+       fun tpl_title: Template do
+               var title = new Template
+               title.add tpl_icon
+               title.add html_namespace
+               return title
+       end
+
+       # Icon that will be displayed before the title
+       fun tpl_icon: TplIcon do
+               var icon = new TplIcon.with_icon("tag")
+               icon.css_classes.add_all(tpl_css_classes)
+               return icon
+       end
+end
+
+redef class MConcern
+       # Return a li element for `self` that can be displayed in a concern list
+       private fun tpl_concern_item: TplListItem do
+               var lnk = new Template
+               lnk.add html_link_to_anchor
+               var comment = html_short_comment
+               if comment != null then
+                       lnk.add ": "
+                       lnk.add comment
+               end
+               return new TplListItem.with_content(lnk)
+       end
+end
+
+redef class MProject
+       redef fun tpl_definition do
+               var tpl = new TplDefinition
+                       var comment = html_comment
+               if comment != null then
+                       tpl.comment = comment
+               end
+               return tpl
+       end
+
+       redef fun tpl_css_classes do return ["public"]
+end
+
+redef class MGroup
+       redef fun tpl_definition do
+               var tpl = new TplDefinition
+               var comment = html_comment
+               if comment != null then
+                       tpl.comment = comment
+               end
+               return tpl
+       end
+end
+
+redef class MModule
+       redef fun tpl_definition do
+               var tpl = new TplClassDefinition
+               var comment = html_comment
+               if comment != null then
+                       tpl.comment = comment
+               end
+               return tpl
+       end
+
+       redef fun tpl_css_classes do return ["public"]
+end
+
+redef class MClass
+       redef fun tpl_definition do return intro.tpl_definition
+
+       redef fun tpl_title do
+               var title = new Template
+               title.add tpl_icon
+               title.add html_link
+               return title
+       end
+
+       redef fun tpl_icon do return intro.tpl_icon
+       redef fun tpl_css_classes do return intro.tpl_css_classes
+end
+
+redef class MClassDef
+       redef fun tpl_article do
+               var tpl = new TplArticle(nitdoc_id)
+               tpl.summary_title = "in {mmodule.html_name}"
+               tpl.title = html_declaration
+               tpl.title_classes.add "signature"
+               var title = new Template
+               title.add "in "
+               title.add mmodule.html_namespace
+               tpl.subtitle = title
+               var comment = html_comment
+               if comment != null then
+                       tpl.content = comment
+               end
+               return tpl
+       end
+
+       redef fun tpl_title do
+               var title = new Template
+               title.add tpl_icon
+               title.add html_link
+               return title
+       end
+
+       redef fun tpl_definition do
+               var tpl = new TplClassDefinition
+               var comment = html_comment
+               if comment != null then
+                       tpl.comment = comment
+               end
+               return tpl
+       end
+
+       redef fun tpl_css_classes do
+               var set = new HashSet[String]
+               if is_intro then set.add "intro"
+               for m in mclass.intro.modifiers do set.add m.to_cmangle
+               for m in modifiers do set.add m.to_cmangle
+               return set.to_a
+       end
+
+       fun tpl_modifiers: Template do
+               var tpl = new Template
+               for modifier in modifiers do
+                       if modifier == "public" then continue
+                       tpl.add "{modifier.html_escape} "
+               end
+               return tpl
+       end
+end
+
+redef class MProperty
+       redef fun tpl_title do return intro.tpl_title
+       redef fun tpl_icon do return intro.tpl_icon
+       redef fun tpl_css_classes do return intro.tpl_css_classes
+end
+
+redef class MPropDef
+       redef fun tpl_article do
+               var tpl = new TplArticle(nitdoc_id)
+               tpl.summary_title = "in {mclassdef.html_name}"
+               var title = new Template
+               title.add "in "
+               title.add mclassdef.html_link
+               tpl.title = title
+               tpl.subtitle = html_declaration
+               var comment = html_comment
+               if comment != null then
+                       tpl.content = comment
+               end
+               return tpl
+       end
+
+       redef fun tpl_definition do
+               var tpl = new TplDefinition
+               var comment = html_comment
+               if comment != null then
+                       tpl.comment = comment
+               end
+               return tpl
+       end
+
+       redef fun tpl_css_classes do
+               var set = new HashSet[String]
+               if is_intro then set.add "intro"
+               for m in mproperty.intro.modifiers do set.add m.to_cmangle
+               for m in modifiers do set.add m.to_cmangle
+               return set.to_a
+       end
+
+       fun tpl_modifiers: Template do
+               var tpl = new Template
+               for modifier in modifiers do
+                       if modifier == "public" then continue
+                       tpl.add "{modifier.html_escape} "
+               end
+               return tpl
+       end
+
+       redef fun tpl_list_item do
+               var lnk = new Template
+               lnk.add new TplLabel.with_classes(tpl_css_classes.to_a)
+               var atext = html_link.text
+               var ahref = "{mclassdef.mclass.nitdoc_url}#{mproperty.nitdoc_id}"
+               var atitle = html_link.title
+               var anchor = new Link.with_title(ahref, atext, atitle)
+               lnk.add anchor
+               var comment = html_short_comment
+               if comment != null then
+                       lnk.add ": "
+                       lnk.add comment
+               end
+               return new TplListItem.with_content(lnk)
+       end
+
+       fun tpl_inheritance_item: TplListItem do
+               var lnk = new Template
+               lnk.add new TplLabel.with_classes(tpl_css_classes.to_a)
+               lnk.add mclassdef.mmodule.html_namespace
+               lnk.add "::"
+               var atext = mclassdef.html_link.text
+               var ahref = "{mclassdef.mclass.nitdoc_url}#{mproperty.nitdoc_id}"
+               var atitle = mclassdef.html_link.title
+               var anchor = new Link.with_title(ahref, atext, atitle)
+               lnk.add anchor
+               var comment = html_short_comment
+               if comment != null then
+                       lnk.add ": "
+                       lnk.add comment
+               end
+               var li = new TplListItem.with_content(lnk)
+               li.css_classes.add "signature"
+               return li
+       end
+end
+
+redef class ConcernsTree
+
+       private var seen = new HashSet[MConcern]
+
+       redef fun add(p, e) do
+               if seen.has(e) then return
+               seen.add e
+               super(p, e)
+       end
+
+       fun to_tpl: TplList do
+               var lst = new TplList.with_classes(["list-unstyled", "list-definition"])
+               for r in roots do
+                       var li = r.tpl_concern_item
+                       lst.add_li li
+                       build_list(r, li)
+               end
+               return lst
+       end
+
+       private fun build_list(e: MConcern, li: TplListItem) do
+               if not sub.has_key(e) then return
+               var subs = sub[e]
+               var lst = new TplList.with_classes(["list-unstyled", "list-definition"])
+               for e2 in subs do
+                       if e2 isa MGroup and e2.is_root then
+                               build_list(e2, li)
+                       else
+                               var sli = e2.tpl_concern_item
+                               lst.add_li sli
+                               build_list(e2, sli)
+                       end
+               end
+               li.append lst
+       end
+end
+
+redef class MInnerClassDef
+       redef fun tpl_definition do
+               var tpl = new TplClassDefinition
+               var comment = html_comment
+               if comment != null then
+                       tpl.comment = comment
+               end
+               return tpl
+       end
+end