nitdoc: introduce RenderHTMLPhase
authorAlexandre Terrasa <alexandre@moz-code.org>
Wed, 4 Feb 2015 20:15:51 +0000 (21:15 +0100)
committerAlexandre Terrasa <alexandre@moz-code.org>
Wed, 4 Feb 2015 20:17:38 +0000 (21:17 +0100)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/doc/doc_pages.nit
src/doc/doc_phases/doc_html.nit [new file with mode: 0644]
src/doc/doc_phases/doc_phases.nit
src/nitdoc.nit

index e1fdc93..024e00a 100644 (file)
@@ -19,88 +19,6 @@ 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_custom_title = new OptionString("custom title for homepage", "--custom-title")
-       private var opt_custom_brand = new OptionString("custom link to external site", "--custom-brand")
-       private var opt_custom_intro = new OptionString("custom intro text for homepage", "--custom-overview-text")
-       private var opt_custom_footer = new OptionString("custom footer text", "--custom-footer-text")
-
-       private var opt_github_upstream = new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream")
-       private var opt_github_base_sha1 = new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1")
-       private var opt_github_gitdir = new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir")
-
-       private var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker")
-       private var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id")
-
-       private var output_dir: String
-       private var min_visibility: MVisibility
-
-       redef init do
-               super
-
-               var opts = option_context
-               opts.add_option(opt_dir, opt_source, opt_sharedir, opt_shareurl)
-               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)
-       end
-
-       redef fun process_options(args) do
-               super
-
-               # output dir
-               var output_dir = opt_dir.value
-               if output_dir == null then
-                       output_dir = "doc"
-               end
-               self.output_dir = output_dir
-               # github urls
-               var gh_upstream = opt_github_upstream.value
-               var gh_base_sha = opt_github_base_sha1.value
-               var gh_gitdir = opt_github_gitdir.value
-               if not gh_upstream == null or not gh_base_sha == null or not gh_gitdir == null then
-                       if gh_upstream == null or gh_base_sha == null or gh_gitdir == null then
-                               print "Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin"
-                               abort
-                       end
-               end
-       end
-end
-
-# The Nitdoc class explores the model and generate pages for each mentities found
-class Nitdoc
-       var ctx: ToolContext
-       var model: Model
-       var mainmodule: MModule
-
-       private fun init_output_dir do
-               # create destination dir if it's necessary
-               var output_dir = ctx.output_dir
-               if not output_dir.file_exists then output_dir.mkdir
-               # locate share dir
-               var sharedir = ctx.opt_sharedir.value
-               if sharedir == null then
-                       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"
-                               abort
-                       end
-               end
-               # copy shared files
-               if ctx.opt_shareurl.value == null then
-                       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.escape_to_sh}/resources/ {output_dir.to_s.escape_to_sh}/resources/")
-               end
-
-       end
-end
-
 # Nitdoc QuickSearch list generator
 #
 # Create a JSON object containing links to:
@@ -182,433 +100,3 @@ private class QuickSearchResult
                return "\{\"txt\":{txt.to_json},\"url\":{url.to_json}\}"
        end
 end
-
-# Nitdoc base page
-# Define page structure and properties
-abstract class NitdocPage
-
-       private var ctx: ToolContext
-       private var model: Model
-       private var mainmodule: MModule
-       private var name_sorter = new MEntityNameSorter
-
-       # Render the page as a html template
-       fun render: Template do
-               var shareurl = "."
-               if ctx.opt_shareurl.value != null then
-                       shareurl = ctx.opt_shareurl.value.as(not null)
-               end
-
-               # build page
-               var tpl = tpl_page
-               tpl.title = tpl_title
-               tpl.url = page_url
-               tpl.shareurl = shareurl
-               tpl.topmenu = tpl_topmenu
-               tpl_content
-               tpl.footer = ctx.opt_custom_footer.value
-               tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl))
-               tpl.sidebar = tpl_sidebar
-
-               # piwik tracking
-               var tracker_url = ctx.opt_piwik_tracker.value
-               var site_id = ctx.opt_piwik_site_id.value
-               if tracker_url != null and site_id != null then
-                       tpl.scripts.add new TplPiwikScript(tracker_url, site_id)
-               end
-               return tpl
-       end
-
-       # URL to this page.
-       fun page_url: String is abstract
-
-       # Build page template
-       fun tpl_page: TplPage is abstract
-
-       # Build page sidebar if any
-       fun tpl_sidebar: nullable TplSidebar do return null
-
-       # Build page title string
-       fun tpl_title: String do
-               if ctx.opt_custom_title.value != null then
-                       return ctx.opt_custom_title.value.to_s
-               end
-               return "Nitdoc"
-       end
-
-       # Build top menu template
-       fun tpl_topmenu: TplTopMenu do
-               var topmenu = new TplTopMenu(page_url)
-               var brand = ctx.opt_custom_brand.value
-               if brand != null then
-                       var tpl = new Template
-                       tpl.add "<span class='navbar-brand'>"
-                       tpl.add brand
-                       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
-       end
-
-       # Build page content template
-       fun tpl_content is abstract
-
-       # A (source) link template for a given location
-       fun tpl_showsource(location: nullable Location): nullable String
-       do
-               if location == null then return null
-               var source = ctx.opt_source.value
-               if source == null then
-                       var url = location.file.filename.simplify_path
-                       return "<a target='_blank' title='Show source' href=\"{url.html_escape}\">View Source</a>"
-               end
-               # THIS IS JUST UGLY ! (but there is no replace yet)
-               var x = source.split_with("%f")
-               source = x.join(location.file.filename.simplify_path)
-               x = source.split_with("%l")
-               source = x.join(location.line_start.to_s)
-               x = source.split_with("%L")
-               source = x.join(location.line_end.to_s)
-               source = source.simplify_path
-               return "<a target='_blank' title='Show source' href=\"{source.to_s.html_escape}\">View Source</a>"
-       end
-
-       # MProject description template
-       fun tpl_mproject_article(mproject: MProject): TplArticle do
-               var article = mproject.tpl_article
-               article.subtitle = mproject.tpl_declaration
-               article.content = mproject.tpl_definition
-               var mdoc = mproject.mdoc_or_fallback
-               if mdoc != null then
-                       article.content = mdoc.tpl_short_comment
-               end
-               return article
-       end
-
-       # MGroup description template
-       fun tpl_mgroup_article(mgroup: MGroup): TplArticle do
-               var article = mgroup.tpl_article
-               article.subtitle = mgroup.tpl_declaration
-               article.content = mgroup.tpl_definition
-               return article
-       end
-
-       # MModule description template
-       fun tpl_mmodule_article(mmodule: MModule): TplArticle do
-               var article = mmodule.tpl_article
-               article.subtitle = mmodule.tpl_declaration
-               article.content = mmodule.tpl_definition
-               return article
-       end
-
-       # MClassDef description template
-       fun tpl_mclass_article(mclass: MClass, mclassdefs: Array[MClassDef]): TplArticle do
-               var article = mclass.tpl_article
-               return article
-       end
-
-       # MClassDef description template
-       fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do
-               var article = mclassdef.tpl_article
-               if mclassdef.is_intro then article.content = null
-               article.source_link = tpl_showsource(mclassdef.location)
-               return article
-       end
-
-       # MProp description template
-       #
-       # `main_mpropdef`: The most important mpropdef to display
-       # `local_mpropdefs`: List of other locally defined mpropdefs to display
-       # `lin`: full linearization from local_mpropdefs to intro (displayed in redef tree)
-       fun tpl_mprop_article(main_mpropdef: MPropDef, local_mpropdefs: Array[MPropDef],
-          lin: Array[MPropDef]): TplArticle do
-               var mprop = main_mpropdef.mproperty
-               var article = new TplArticle(mprop.nitdoc_id)
-               var title = new Template
-               title.add mprop.tpl_icon
-               title.add "<span id='{main_mpropdef.nitdoc_id}'></span>"
-               if main_mpropdef.is_intro then
-                       title.add mprop.tpl_link
-                       title.add mprop.intro.tpl_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,
-                                       "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 = main_mpropdef.tpl_namespace
-               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")
-               # Add linearization
-               if lin.length > 1 then
-                       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
-                               lst.add_li mpropdef.tpl_inheritance_item
-                       end
-                       lin_article.content = lst
-                       subarticle.add_child lin_article
-               end
-               article.add_child subarticle
-               return article
-       end
-end
-
-# The overview page
-# Display a list of modules contained in program
-class NitdocOverview
-       super NitdocPage
-
-       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
-               if ctx.opt_custom_title.value != null then
-                       return ctx.opt_custom_title.value.to_s
-               else
-                       return "Overview"
-               end
-       end
-
-       redef fun page_url do return "index.html"
-end
-
-# The search page
-# Display a list of modules, classes and properties
-class NitdocSearch
-       super NitdocPage
-
-       private var page = new TplPage
-       redef fun tpl_page do return page
-
-       redef fun tpl_title do return "Index"
-
-       redef fun page_url do return "search.html"
-end
-
-# A group page
-# Display a flattened view of the group
-class NitdocGroup
-       super NitdocPage
-
-       private var mgroup: MGroup
-
-       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 mgroup.nitdoc_name
-
-       redef fun page_url do return mgroup.nitdoc_url
-
-       redef fun tpl_topmenu do
-               var topmenu = super
-               var mproject = mgroup.mproject
-               if not mgroup.is_root then
-                       topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
-               end
-               topmenu.add_link new TplLink(page_url, mproject.nitdoc_name)
-               return topmenu
-       end
-
-       # Class list to display in sidebar
-       fun tpl_sidebar_mclasses do
-               var mclasses = new HashSet[MClass]
-               mclasses.add_all intros
-               mclasses.add_all redefs
-               if mclasses.is_empty then return
-               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
-
-               var sorted = mclasses.to_a
-               name_sorter.sort(sorted)
-               for mclass in sorted do
-                       list.add_li tpl_sidebar_item(mclass)
-               end
-               tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
-       end
-
-       private fun tpl_sidebar_item(def: MClass): TplListItem do
-               var classes = def.intro.tpl_css_classes.to_a
-               if intros.has(def) then
-                       classes.add "intro"
-               else
-                       classes.add "redef"
-               end
-               var lnk = new Template
-               lnk.add new TplLabel.with_classes(classes)
-               lnk.add def.tpl_link
-               return new TplListItem.with_content(lnk)
-       end
-end
-
-# A module page
-# Display the list of introduced and redefined classes in module
-class NitdocModule
-       super NitdocPage
-
-       private var mmodule: MModule
-
-       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 mmodule.nitdoc_name
-       redef fun page_url do return mmodule.nitdoc_url
-
-       redef fun tpl_topmenu do
-               var topmenu = super
-               var mproject = mmodule.mgroup.mproject
-               topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
-               topmenu.add_link new TplLink(page_url, mmodule.nitdoc_name)
-               return topmenu
-       end
-
-       # Class list to display in sidebar
-       fun tpl_sidebar_mclasses do
-               var mclasses = new HashSet[MClass]
-               mclasses.add_all mmodule.filter_intro_mclasses(ctx.min_visibility)
-               mclasses.add_all mmodule.filter_redef_mclasses(ctx.min_visibility)
-               if mclasses.is_empty then return
-               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
-
-               var sorted = mclasses.to_a
-               name_sorter.sort(sorted)
-               for mclass in sorted do
-                       list.add_li tpl_sidebar_item(mclass)
-               end
-               tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
-       end
-
-       private fun tpl_sidebar_item(def: MClass): TplListItem do
-               var classes = def.intro.tpl_css_classes.to_a
-               if def.intro_mmodule == mmodule then
-                       classes.add "intro"
-               else
-                       classes.add "redef"
-               end
-               var lnk = new Template
-               lnk.add new TplLabel.with_classes(classes)
-               lnk.add def.tpl_link
-               return new TplListItem.with_content(lnk)
-       end
-end
-
-# A class page
-# Display a list properties defined or redefined for this class
-class NitdocClass
-       super NitdocPage
-
-       private var mclass: MClass
-
-       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 "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
-       redef fun page_url do return mclass.nitdoc_url
-
-       redef fun tpl_topmenu do
-               var topmenu = super
-               var mproject = mclass.intro_mmodule.mgroup.mproject
-               topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
-               topmenu.add_link new TplLink(page_url, mclass.nitdoc_name)
-               return topmenu
-       end
-
-       # Property list to display in sidebar
-       fun tpl_sidebar_properties do
-               var by_kind = new PropertiesByKind.with_elements(mclass_inherited_mprops)
-               var summary = new TplList.with_classes(["list-unstyled"])
-
-               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(mprops: PropertyGroup[MProperty], summary: TplList) do
-               if mprops.is_empty then return
-               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)
-               end
-               entry.append list
-               summary.elts.add entry
-       end
-
-       private fun tpl_sidebar_item(mprop: MProperty): TplListItem do
-               var classes = mprop.intro.tpl_css_classes.to_a
-               if not mprops2mdefs.has_key(mprop) then
-                       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 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
-                       return new TplListItem.with_content(item)
-               end
-               var defs = mprops2mdefs[mprop]
-               if defs.has(mprop.intro) then
-                       classes.add "intro"
-               else
-                       classes.add "redef"
-               end
-               var lnk = new Template
-               lnk.add new TplLabel.with_classes(classes)
-               lnk.add mprop.tpl_anchor
-               return new TplListItem.with_content(lnk)
-       end
-end
-
-# A MProperty page
-class NitdocProperty
-       super NitdocPage
-
-       private var mproperty: MProperty
-
-       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
-end
-
diff --git a/src/doc/doc_phases/doc_html.nit b/src/doc/doc_phases/doc_html.nit
new file mode 100644 (file)
index 0000000..a4bdeb0
--- /dev/null
@@ -0,0 +1,844 @@
+# This file is part of NIT ( http://www.nitlanguage.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
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Render the DocModel pages as HTML pages.
+#
+# FIXME this module is all f*cked up to maintain compatibility with
+# the original `doc_templates` and `doc_model` modules.
+# This will change in further refactorings.
+module doc_html
+
+import doc_structure
+import doc_hierarchies
+import doc_intros_redefs
+import doc_graphs
+
+redef class ToolContext
+
+       # File pattern used to link documentation to source code.
+       var opt_source = new OptionString("link for source (%f for filename, " +
+               "%l for first line, %L for last line)", "--source")
+
+       # Directory where the CSS and JS is stored.
+       var opt_sharedir = new OptionString("directory containing nitdoc assets", "--sharedir")
+
+       # Use a shareurl instead of copy shared files.
+       #
+       # This is usefull if you don't want to store the Nitdoc templates with your
+       # documentation.
+       var opt_shareurl = new OptionString("use shareurl instead of copy shared files", "--shareurl")
+
+       # Use a custom title for the homepage.
+       var opt_custom_title = new OptionString("custom title for homepage", "--custom-title")
+
+       # Display a custom brand or logo in the documentation top menu.
+       var opt_custom_brand = new OptionString("custom link to external site", "--custom-brand")
+
+       # Display a custom introduction text before the projects overview.
+       var opt_custom_intro = new OptionString("custom intro text for homepage", "--custom-overview-text")
+       # Display a custom footer on each documentation page.
+       #
+       # Generally used to display the documentation or product version.
+       var opt_custom_footer = new OptionString("custom footer text", "--custom-footer-text")
+
+       # Piwik tracker URL.
+       #
+       # If you want to monitor your visitors.
+       var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker")
+
+       # Piwik tracker site id.
+       var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id")
+
+       # These options are not currently used in Nitdoc.
+
+       # FIXME redo the plugin
+       var opt_github_upstream = new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream")
+       # FIXME redo the plugin
+       var opt_github_base_sha1 = new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1")
+       # FIXME redo the plugin
+       var opt_github_gitdir = new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir")
+
+       redef init do
+               super
+
+               option_context.add_option(
+                       opt_source, opt_sharedir, opt_shareurl, opt_custom_title,
+                       opt_custom_footer, opt_custom_intro, opt_custom_brand,
+                       opt_github_upstream, opt_github_base_sha1, opt_github_gitdir,
+                       opt_piwik_tracker, opt_piwik_site_id)
+       end
+
+       redef fun process_options(args) do
+               super
+               var upstream = opt_github_upstream
+               var base_sha = opt_github_base_sha1
+               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}, " +
+                               "{base_sha.names.first} and {git_dir.names.first} " +
+                               "are required to enable the GitHub plugin"
+                       exit 1
+               end
+       end
+end
+
+# Render the Nitdoc as a HTML website.
+class RenderHTMLPhase
+       super DocPhase
+
+       # Used to sort sidebar elements by name.
+       var name_sorter = new MEntityNameSorter
+
+       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}")
+               end
+       end
+
+       # Creates the output directory and imports assets files form `resources/`.
+       fun init_output_dir do
+               # create destination dir if it's necessary
+               var output_dir = ctx.output_dir
+               if not output_dir.file_exists then output_dir.mkdir
+               # locate share dir
+               var sharedir = ctx.opt_sharedir.value
+               if sharedir == null then
+                       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"
+                               abort
+                       end
+               end
+               # copy shared files
+               if ctx.opt_shareurl.value == null then
+                       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.escape_to_sh}/resources/ {output_dir.to_s.escape_to_sh}/resources/")
+               end
+
+       end
+
+       # A source link template for a given location
+       fun tpl_showsource(location: nullable Location): nullable String
+       do
+               if location == null then return null
+               var source = ctx.opt_source.value
+               if source == null then
+                       var url = location.file.filename.simplify_path
+                       return "<a target='_blank' title='Show source' href=\"{url.html_escape}\">View Source</a>"
+               end
+               # THIS IS JUST UGLY ! (but there is no replace yet)
+               var x = source.split_with("%f")
+               source = x.join(location.file.filename.simplify_path)
+               x = source.split_with("%l")
+               source = x.join(location.line_start.to_s)
+               x = source.split_with("%L")
+               source = x.join(location.line_end.to_s)
+               source = source.simplify_path
+               return "<a target='_blank' title='Show source' href=\"{source.to_s.html_escape}\">View Source</a>"
+       end
+end
+
+redef class DocPage
+
+       # Render the page as a html template.
+       private fun render(v: RenderHTMLPhase, doc: DocModel): TplPage do
+               var shareurl = "."
+               if v.ctx.opt_shareurl.value != null then
+                       shareurl = v.ctx.opt_shareurl.value.as(not null)
+               end
+
+               # 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)
+
+               # 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)
+               end
+               return tpl
+       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
+
+       # Build top menu template
+       fun tpl_topmenu(v: RenderHTMLPhase, doc: DocModel): TplTopMenu do
+               var topmenu = new TplTopMenu(page_url)
+               var brand = v.ctx.opt_custom_brand.value
+               if brand != null then
+                       var tpl = new Template
+                       tpl.add "<span class='navbar-brand'>"
+                       tpl.add brand
+                       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
+       end
+
+       # Build page content template
+       fun tpl_content(v: RenderHTMLPhase, doc: DocModel): TplSection is abstract
+end
+
+redef class OverviewPage
+       redef fun page_url do return "index.html"
+
+       redef fun tpl_title(v, doc) do
+               if v.ctx.opt_custom_title.value != null then
+                       return v.ctx.opt_custom_title.value.to_s
+               else
+                       return "Overview"
+               end
+       end
+
+       # TODO this should be done in StructurePhase.
+       redef fun tpl_content(v, doc) do
+               # intro text
+               var section = new TplSection.with_title("overview", tpl_title(v, doc))
+               var article = new TplArticle("intro")
+               if v.ctx.opt_custom_intro.value != null then
+                       article.content = v.ctx.opt_custom_intro.value.to_s
+               end
+               section.add_child article
+               # Projects list
+               var mprojects = doc.model.mprojects.to_a
+               var sorter = new MConcernRankSorter
+               sorter.sort mprojects
+               var ssection = new TplSection.with_title("projects", "Projects")
+               for mproject in mprojects do
+                       var sarticle = mproject.tpl_article
+                       sarticle.subtitle = mproject.tpl_declaration
+                       sarticle.content = mproject.tpl_definition
+                       var mdoc = mproject.mdoc_or_fallback
+                       if mdoc != null then
+                               sarticle.content = mdoc.tpl_short_comment
+                       end
+                       ssection.add_child sarticle
+               end
+               section.add_child ssection
+               return section
+       end
+
+       redef fun tpl_sidebar(v, doc) do return new TplSidebar
+end
+
+redef class SearchPage
+       redef fun page_url do return "search.html"
+       redef fun tpl_title(v, doc) do return "Index"
+
+       # TODO this should be done in StructurePhase.
+       redef fun tpl_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
+               end
+               # classes list
+               for mclass in classes_list(v, doc) do
+                       tpl.classes.add mclass.tpl_link
+               end
+               # properties list
+               for mproperty in mprops_list(v, doc) do
+                       var m = new Template
+                       m.add mproperty.intro.tpl_link
+                       m.add " ("
+                       m.add mproperty.intro.mclassdef.mclass.tpl_link
+                       m.add ")"
+                       tpl.props.add m
+               end
+               section.add_child tpl
+               return section
+       end
+
+       # Extract mmodule list to display (sorted by name)
+       private fun modules_list(v: RenderHTMLPhase, doc: DocModel): Array[MModule] do
+               var sorted = new Array[MModule]
+               for mmodule in doc.model.mmodule_importation_hierarchy do
+                       if mmodule.is_fictive or mmodule.is_test_suite then continue
+                       sorted.add mmodule
+               end
+               v.name_sorter.sort(sorted)
+               return sorted
+       end
+
+       # Extract mclass list to display (sorted by name)
+       private fun classes_list(v: RenderHTMLPhase, doc: DocModel): Array[MClass] do
+               var sorted = doc.mclasses.to_a
+               v.name_sorter.sort(sorted)
+               return sorted
+       end
+
+       # Extract mproperty list to display (sorted by name)
+       private fun mprops_list(v: RenderHTMLPhase, doc: DocModel): Array[MProperty] do
+               var sorted = doc.mproperties.to_a
+               v.name_sorter.sort(sorted)
+               return sorted
+       end
+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)
+end
+
+# FIXME all clases below are roughly copied from `doc_pages` and adapted to new
+# doc phases. This is to preserve the compatibility with the current
+# `doc_templates` module.
+
+redef class MGroupPage
+       redef fun tpl_topmenu(v, doc) do
+               var topmenu = super
+               var mproject = mentity.mproject
+               if not mentity.is_root then
+                       topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
+               end
+               topmenu.add_link new TplLink(page_url, mproject.nitdoc_name)
+               return topmenu
+       end
+
+       redef fun tpl_sidebar(v, doc) do
+               var sidebar = new TplSidebar
+               var mclasses = new HashSet[MClass]
+               mclasses.add_all intros
+               mclasses.add_all redefs
+               if mclasses.is_empty then return sidebar
+               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
+
+               var sorted = mclasses.to_a
+               v.name_sorter.sort(sorted)
+               for mclass in sorted do
+                       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
+               var classes = def.intro.tpl_css_classes.to_a
+               if intros.has(def) then
+                       classes.add "intro"
+               else
+                       classes.add "redef"
+               end
+               var lnk = new Template
+               lnk.add new TplLabel.with_classes(classes)
+               lnk.add def.tpl_link
+               return new TplListItem.with_content(lnk)
+       end
+end
+
+redef class MModulePage
+       redef fun tpl_topmenu(v, doc) do
+               var topmenu = 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
+       end
+
+       # Class list to display in sidebar
+       redef fun tpl_sidebar(v, doc) do
+               # TODO filter here?
+               var sidebar = new TplSidebar
+               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
+               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
+
+               var sorted = mclasses.to_a
+               v.name_sorter.sort(sorted)
+               for mclass in sorted do
+                       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
+               var classes = def.intro.tpl_css_classes.to_a
+               if def.intro_mmodule == self.mentity then
+                       classes.add "intro"
+               else
+                       classes.add "redef"
+               end
+               var lnk = new Template
+               lnk.add new TplLabel.with_classes(classes)
+               lnk.add def.tpl_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
+               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
+       end
+
+       redef fun tpl_sidebar(v, doc) do
+               var sidebar = new TplSidebar
+               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
+               if mprops.is_empty then return
+               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)
+               end
+               entry.append list
+               summary.elts.add entry
+       end
+
+       private fun tpl_sidebar_item(mprop: MProperty): TplListItem do
+               var classes = mprop.intro.tpl_css_classes.to_a
+               if not mprop_is_local(mprop) then
+                       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 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
+                       return new TplListItem.with_content(item)
+               end
+               if mpropdefs.has(mprop.intro) then
+                       classes.add "intro"
+               else
+                       classes.add "redef"
+               end
+               var lnk = new Template
+               lnk.add new TplLabel.with_classes(classes)
+               lnk.add mprop.tpl_anchor
+               return new TplListItem.with_content(lnk)
+       end
+
+       private fun mclass_inherited_mprops(v: RenderHTMLPhase, doc: DocModel): Set[MProperty] do
+               var res = new HashSet[MProperty]
+               var local = mentity.local_mproperties(v.ctx.min_visibility)
+               for mprop in mentity.inherited_mproperties(doc.mainmodule, v.ctx.min_visibility) do
+                       if local.has(mprop) then continue
+                       #if mprop isa MMethod and mprop.is_init then continue
+                       if mprop.intro.mclassdef.mclass.name == "Object" and
+                               (mprop.visibility == protected_visibility or
+                               mprop.intro.mclassdef.mmodule.name != "kernel") then continue
+                       res.add mprop
+               end
+               res.add_all local
+               return res
+       end
+
+       private fun mprop_is_local(mprop: MProperty): Bool do
+               for mpropdef in mprop.mpropdefs do
+                       if self.mpropdefs.has(mpropdef) then return true
+               end
+               return false
+       end
+end
+
+redef class MPropertyPage
+       redef fun tpl_topmenu(v, doc) do
+               var topmenu = 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
+       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
+       # Render this DocComposite as HTML.
+       #
+       # FIXME needed to maintain TplSection compatibility.
+       fun render(v: RenderHTMLPhase, doc: DocModel, page: MEntityPage, parent: TplSectionElt) is abstract
+end
+
+redef class DocRoot
+
+       # Start the rendering from root.
+       #
+       # FIXME needed to maintain TplSection compatibility.
+       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
+               # 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}"
+               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
+               end
+               render(v, doc, page, section)
+               return section
+       end
+
+       redef fun render(v, doc, page, parent) do
+               for child in children do
+                       child.render(v, doc, page, parent)
+               end
+       end
+end
+
+redef class ConcernSection
+       redef fun render(v, doc, page, parent) do
+               var section = new TplSection(mentity.nitdoc_id)
+               var mentity = self.mentity
+               # FIXME hideous hacks to avoid diff
+               if page.mentity isa MModule and mentity isa MModule then
+                       render_concern_mmodule(page, section, mentity)
+               else if page.mentity isa MClass and mentity isa MModule then
+                       render_concern_other(page, section, mentity)
+               else if page.mentity isa MProperty and mentity isa MModule then
+                       render_concern_other(page, section, mentity)
+               end
+               for child in children do
+                       child.render(v, doc, page, section)
+               end
+               parent.add_child section
+       end
+
+       private fun render_concern_mmodule(page: MEntityPage, section: TplSection, mmodule: MModule) do
+               var title = new Template
+               if mmodule == page.mentity then
+                       title.add "in "
+                       section.summary_title = "in {mmodule.nitdoc_name}"
+               else
+                       title.add "from "
+                       section.summary_title = "from {mmodule.nitdoc_name}"
+               end
+               title.add mmodule.tpl_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
+               section.title = title
+               section.summary_title = "in {mmodule.nitdoc_name}"
+       end
+end
+
+redef class IntroArticle
+       redef fun render(v, doc, page, parent) do
+               var article = new TplArticle("intro")
+               var mentity = self.mentity
+               if mentity isa MModule then
+                       article.source_link = v.tpl_showsource(mentity.location)
+               else if mentity isa MClassDef then
+                       article.source_link = v.tpl_showsource(mentity.location)
+               else if mentity isa MPropDef then
+                       article.source_link = v.tpl_showsource(mentity.location)
+               end
+               # article.subtitle = mentity.tpl_declaration
+               # FIXME diff hack
+               if mentity isa MProperty then
+                       # intro title
+                       var ns = mentity.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"
+                       var intro = mentity.intro.tpl_article
+                       intro.source_link = v.tpl_showsource(mentity.intro.location)
+                       section.add_child intro
+                       parent.add_child section
+               else
+                       article.content = mentity.tpl_definition
+                       parent.add_child article
+               end
+       end
+end
+
+redef class ConcernsArticle
+       redef fun render(v, doc, page, parent) do
+               # FIXME diff hack
+               var title = "concerns"
+               if page.mentity isa MProperty then title = "Concerns"
+               parent.add_child new TplArticle.
+                       with_content(title, "Concerns", concerns.to_tpl)
+       end
+end
+
+redef class DefinitionArticle
+       redef fun render(v, doc, page, parent) do
+               var article: TplArticle
+               var mentity = self.mentity
+               # FIXME hideous hacks...
+               if mentity isa MModule then
+                       article = mentity.tpl_article
+                       article.subtitle = mentity.tpl_declaration
+                       article.content = mentity.tpl_definition
+               else if mentity isa MClass then
+                       article = make_mclass_article(v, page)
+               else if mentity isa MClassDef then
+                       article = make_mclassdef_article(v, page)
+                       article.source_link = v.tpl_showsource(mentity.location)
+               else if mentity isa MPropDef and page.mentity isa MClass then
+                       article = make_mpropdef_article(v, doc, page)
+               else
+                       article = mentity.tpl_article
+                       article.subtitle = mentity.tpl_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)
+               end
+               parent.add_child article
+       end
+
+       # FIXME avoid diff while preserving TplArticle compatibility.
+
+       private fun make_mclass_article(v: RenderHTMLPhase, page: MEntityPage): TplArticle do
+               var article = mentity.tpl_article
+               article.subtitle = mentity.tpl_namespace
+               article.content = null
+               return article
+       end
+
+       private fun make_mclassdef_article(v: RenderHTMLPhase, page: MEntityPage): TplArticle do
+               var mclassdef = mentity.as(MClassDef)
+               var article = mentity.tpl_article
+               if mclassdef.is_intro and mclassdef.mmodule != page.mentity then
+                       article = mentity.tpl_short_article
+               end
+               var title = new Template
+               title.add "in "
+               title.add mclassdef.mmodule.tpl_namespace
+               article.subtitle = title
+               return article
+       end
+
+       private fun make_mpropdef_article(v: RenderHTMLPhase, doc: DocModel, page: MEntityPage): TplArticle
+       do
+               var mpropdef = mentity.as(MPropDef)
+               var mprop = mpropdef.mproperty
+               var article = new TplArticle(mprop.nitdoc_id)
+               var title = new Template
+               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
+               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,
+                                       "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
+               end
+               # TODO move in its own phase? let's see after doc_template refactoring.
+               # Add linearization
+               var all_defs = new HashSet[MPropDef]
+               for local_def in local_defs(page.as(MClassPage), mprop) do
+                       all_defs.add local_def
+                       var smpropdef = local_def
+                       while not smpropdef.is_intro do
+                               smpropdef = smpropdef.lookup_next_definition(
+                                       doc.mainmodule, smpropdef.mclassdef.bound_mtype)
+                               all_defs.add smpropdef
+                       end
+               end
+               var lin = all_defs.to_a
+               doc.mainmodule.linearize_mpropdefs(lin)
+               if lin.length > 1 then
+                       var lin_article = new TplArticle("{mpropdef.nitdoc_id}.lin")
+                       lin_article.title = "Inheritance"
+                       var lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
+                       for smpropdef in lin do
+                               lst.add_li smpropdef.tpl_inheritance_item
+                       end
+                       lin_article.content = lst
+                       article.add_child lin_article
+               end
+               return article
+       end
+
+       # Filter `page.mpropdefs` for this `mpropertie`.
+       #
+       # FIXME compatability with current templates.
+       private fun local_defs(page: MClassPage, mproperty: MProperty): HashSet[MPropDef] do
+               var mpropdefs = new HashSet[MPropDef]
+               for mpropdef in page.mpropdefs do
+                       if mpropdef.mproperty == mproperty then
+                               mpropdefs.add mpropdef
+                       end
+               end
+               return mpropdefs
+       end
+end
+
+redef class IntrosRedefsListArticle
+       redef fun render(v, doc, page, parent) do
+               if mentities.is_empty then return
+               var title = list_title
+               # FIXME diff hack
+               var id = "intros"
+               if title == "Redefines" then id = "redefs"
+               var article = new TplArticle.with_title("{mentity.nitdoc_id}.{id}", title)
+               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
+               for mentity in mentities do
+                       list.add_li mentity.tpl_list_item
+               end
+               article.content = list
+               parent.add_child article
+       end
+end
+
+# FIXME compatibility with doc_templates.
+redef class ImportationListSection
+       redef fun render(v, doc, page, parent) do
+               var section = new TplSection.with_title("dependencies", "Dependencies")
+               for child in children do
+                       child.render(v, doc, page, section)
+               end
+               parent.add_child section
+       end
+end
+
+# FIXME compatibility with doc_templates.
+redef class InheritanceListSection
+       redef fun render(v, doc, page, parent) do
+               var section = new TplSection.with_title("inheritance", "Inheritance")
+               for child in children do
+                       child.render(v, doc, page, section)
+               end
+               parent.add_child section
+       end
+end
+
+# FIXME compatibility with doc_templates.
+redef class HierarchyListArticle
+       redef fun render(v, doc, page, parent) do
+               if mentities.is_empty then return
+               var title = list_title
+               var id = list_title.to_lower
+               var article = new TplArticle.with_title(id, title)
+               var list = new TplList.with_classes(["list-unstyled", "list-definition"])
+               for mentity in mentities do
+                       list.elts.add mentity.tpl_list_item
+               end
+               article.content = list
+               parent.add_child article
+       end
+end
+
+redef class GraphArticle
+       redef fun render(v, doc, page, parent) do
+               var output_dir = v.ctx.output_dir
+               var path = output_dir / id
+               var path_sh = path.escape_to_sh
+               var file = new OFStream.open("{path}.dot")
+               file.write(dot)
+               file.close
+               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 article = new TplArticle("graph")
+               var alt = ""
+               # FIXME diff hack
+               # if title != null then
+                       # article.title = title
+                       # alt = "alt='{title.html_escape}'"
+               # end
+               article.css_classes.add "text-center"
+               var content = new Template
+               var name_html = id.html_escape
+               content.add "<img src='{name_html}.png' usemap='#{name_html}' style='margin:auto' {alt}/>"
+               content.add map
+               article.content = content
+               parent.add_child article
+       end
+end
index 144b97c..0d4bef0 100644 (file)
@@ -17,7 +17,4 @@
 # See `DocPhase`.
 module doc_phases
 
-import doc_structure
-import doc_hierarchies
-import doc_graphs
-import doc_intros_redefs
+import doc_html
index 7c1dd33..f09837c 100644 (file)
@@ -40,7 +40,8 @@ private class Nitdoc
                        new StructurePhase(toolcontext, doc),
                        new InheritanceListsPhase(toolcontext, doc),
                        new IntroRedefListPhase(toolcontext, doc),
-                       new GraphPhase(toolcontext, doc): DocPhase]
+                       new GraphPhase(toolcontext, doc),
+                       new RenderHTMLPhase(toolcontext, doc): DocPhase]
 
                for phase in phases do
                        toolcontext.info("# {phase.class_name}", 1)