Merge: Kill `model_utils`
authorJean Privat <jean@pryen.org>
Sat, 30 May 2015 12:15:42 +0000 (08:15 -0400)
committerJean Privat <jean@pryen.org>
Sat, 30 May 2015 12:15:42 +0000 (08:15 -0400)
This PR removes the so hated module `model_utils`:

* Useful services are kept in a new module `model::model_collect` that allows to collect and filter things from a `Model`.
* Clients are migrated from `model_utils` to `model_collect` one by one. Misc functionalities used only by one client are moved in the client.

Fixes #710

Pull-Request: #1376
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

1  2 
src/doc/doc_base.nit
src/doc/doc_phases/doc_html.nit
src/doc/doc_phases/doc_intros_redefs.nit
src/doc/doc_phases/doc_poset.nit
src/doc/doc_phases/doc_structure.nit
src/doc/html_templates/html_model.nit
src/metrics/nullables_metrics.nit
src/model/model.nit
src/nitserial.nit
tests/sav/nituml_args3.res
tests/sav/nituml_args4.res

diff --combined src/doc/doc_base.nit
@@@ -16,7 -16,6 +16,6 @@@
  module doc_base
  
  import toolcontext
- import model_utils
  import model_ext
  
  # The model of a Nitdoc documentation.
@@@ -75,15 -74,6 +74,15 @@@ class DocPag
        var root = new DocRoot
  
        redef fun to_s do return title
 +
 +      # Pretty prints the content of this page.
 +      fun pretty_print: Writable do
 +              var res = new Template
 +              res.addn "page: {title}"
 +              res.addn ""
 +              root.pretty_print_in(res)
 +              return res
 +      end
  end
  
  # `DocPage` elements that can be nested in another.
@@@ -100,19 -90,6 +99,19 @@@ abstract class DocComposit
        # Parent element.
        var parent: nullable DocComposite = null is writable
  
 +      # Element uniq id.
 +      #
 +      # The `id` is used as name for the generated element (if any).
 +      # Because multiple elements can be generated in the same container
 +      # it should be uniq.
 +      #
 +      # The `id` can also be used to establish links between elements
 +      # (HTML links, HTML anchors, vim links, etc.).
 +      var id: String is writable
 +
 +      # Item title if any.
 +      var title: nullable String
 +
        # Does `self` have a `parent`?
        fun is_root: Bool do return parent == null
  
        # Children are ordered, this order can be changed by the `DocPhase`.
        var children = new Array[DocComposite]
  
 -      # Does `self` have `children`?
 -      fun is_empty: Bool do return children.is_empty
 +      # Is `self` not displayed in the page.
 +      #
 +      # By default, empty elements are hidden.
 +      fun is_hidden: Bool do return children.is_empty
 +
 +      # Title used in table of content if any.
 +      var toc_title: nullable String is writable, lazy do return title
 +
 +      # Is `self` hidden in the table of content?
 +      var is_toc_hidden: Bool is writable, lazy do
 +              return toc_title == null or is_hidden
 +      end
  
        # Add a `child` to `self`.
        #
                if parent == null then return 0
                return parent.depth + 1
        end
 +
 +      # Pretty prints this composite recursively.
 +      fun pretty_print: Writable do
 +              var res = new Template
 +              pretty_print_in(res)
 +              return res
 +      end
 +
 +      # Appends the Pretty print of this composite in `res`.
 +      private fun pretty_print_in(res: Template) do
 +              res.add "#" * depth
 +              res.addn " {id}"
 +              for child in children do child.pretty_print_in(res)
 +      end
  end
  
  # The `DocComposite` element that contains all the other.
  # The root uses a specific subclass to provide different a different behavior
  # than other `DocComposite` elements.
  class DocRoot
 +      noautoinit
        super DocComposite
  
 +      redef var id = "<root>"
 +      redef var title = "<root>"
 +
        # No op for `RootSection`.
        redef fun parent=(p) do end
  end
@@@ -302,8 -302,8 +302,8 @@@ redef class MModulePag
                # TODO filter here?
                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)
+               mclasses.add_all mentity.collect_intro_mclasses(v.ctx.min_visibility)
+               mclasses.add_all mentity.collect_redef_mclasses(v.ctx.min_visibility)
                if mclasses.is_empty then return
                var list = new UnorderedList
                list.css_classes.add "list-unstyled list-labeled"
@@@ -372,7 -372,7 +372,7 @@@ redef class MClassPag
                if not mprop_is_local(mprop) then
                        classes.add "inherit"
                        var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url
 -                      var def_url = "{cls_url}#article:{mprop.nitdoc_id}.definition"
 +                      var def_url = "{cls_url}#{mprop.nitdoc_id}.definition"
                        var lnk = new Link(def_url, mprop.html_name)
                        var mdoc = mprop.intro.mdoc_or_fallback
                        if mdoc != null then lnk.title = mdoc.short_comment
                end
                var def = select_mpropdef(mprop)
                var anc = def.html_link_to_anchor
 -              anc.href = "#article:{def.nitdoc_id}.definition"
 +              anc.href = "#{def.nitdoc_id}.definition"
                var lnk = new Template
                lnk.add new DocHTMLLabel.with_classes(classes)
                lnk.add anc
  
        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
+               var local = mentity.collect_local_mproperties(v.ctx.min_visibility)
+               for mprop in mentity.collect_inherited_mproperties(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
@@@ -471,7 -471,7 +471,7 @@@ redef class MEntitySectio
                        title.add mentity.html_signature
                        html_title = title
                        html_subtitle = mentity.html_namespace
 -                      toc_title = mentity.html_name
 +                      html_toc_title = mentity.html_name
                end
                super
        end
@@@ -484,16 -484,16 +484,16 @@@ redef class ConcernSectio
                var mentity = self.mentity
                if page isa MGroupPage then
                        html_title = null
 -                      toc_title = mentity.html_name
 +                      html_toc_title = mentity.html_name
                        is_toc_hidden = false
                else if page.mentity isa MModule and mentity isa MModule then
                        var title = new Template
                        if mentity == page.mentity then
                                title.add "in "
 -                              toc_title = "in {mentity.html_name}"
 +                              html_toc_title = "in {mentity.html_name}"
                        else
                                title.add "from "
 -                              toc_title = "from {mentity.html_name}"
 +                              html_toc_title = "from {mentity.html_name}"
                        end
                        title.add mentity.html_namespace
                        html_title = title
                        title.add "in "
                        title.add mentity.html_namespace
                        html_title = title
 -                      toc_title = "in {mentity.html_name}"
 +                      html_toc_title = "in {mentity.html_name}"
                end
                super
        end
@@@ -532,7 -532,7 +532,7 @@@ redef class DefinitionArticl
                        title.add mentity.html_icon
                        title.add mentity.html_namespace
                        html_title = title
 -                      toc_title = mentity.html_name
 +                      html_toc_title = mentity.html_name
                        if mentity isa MModule then
                                html_source_link = v.html_source_link(mentity.location)
                        end
                        title.add mentity.mmodule.html_namespace
                        html_title = mentity.html_declaration
                        html_subtitle = title
 -                      toc_title = "in {mentity.html_name}"
 +                      html_toc_title = "in {mentity.html_name}"
                        html_source_link = v.html_source_link(mentity.location)
                        if page isa MEntityPage and mentity.is_intro and mentity.mmodule != page.mentity then
                                is_short_comment = true
                                title.add mentity.html_declaration
                                html_title = title
                                html_subtitle = mentity.html_namespace
 -                              toc_title = mentity.html_name
 +                              html_toc_title = mentity.html_name
                        else
                                var title = new Template
                                title.add "in "
                                title.add mentity.mclassdef.html_link
                                html_title = title
 -                              toc_title = "in {mentity.mclassdef.html_name}"
 +                              html_toc_title = "in {mentity.mclassdef.html_name}"
                        end
                        html_source_link = v.html_source_link(mentity.location)
                end
@@@ -576,7 -576,7 +576,7 @@@ redef class HomeArticl
        redef fun init_html_render(v, doc, page) do
                if v.ctx.opt_custom_title.value != null then
                        self.html_title = v.ctx.opt_custom_title.value.to_s
 -                      self.toc_title = v.ctx.opt_custom_title.value.to_s
 +                      self.html_toc_title = v.ctx.opt_custom_title.value.to_s
                end
                self.content = v.ctx.opt_custom_intro.value
                super
@@@ -586,7 -586,7 +586,7 @@@ en
  redef class GraphArticle
        redef fun init_html_render(v, doc, page) do
                var output_dir = v.ctx.output_dir
 -              var path = output_dir / id
 +              var path = output_dir / graph_id
                var path_sh = path.escape_to_sh
                var file = new FileWriter.open("{path}.dot")
                file.write(dot)
@@@ -18,6 -18,7 +18,7 @@@
  module doc_intros_redefs
  
  import doc_structure
+ import model::model_collect
  
  # Computes intro / redef mentity list for each DefinitionArticle.
  class IntroRedefListPhase
@@@ -54,33 -55,52 +55,33 @@@ redef class DefinitionArticl
  
        # TODO this should move to MEntity?
        private fun build_mmodule_list(v: IntroRedefListPhase, doc: DocModel, mmodule: MModule) do
 -              var section = new IntrosRedefsSection(mentity)
 -              var group = new PanelGroup("List")
 +              var section = new TabbedGroup("{mentity.nitdoc_id}.intros_redefs")
 +              section.toc_title = "Intros / Redefs"
 +              var group = new PanelGroup("list.group", "List")
-               var intros = mmodule.intro_mclassdefs(v.ctx.min_visibility).to_a
+               var intros = mmodule.collect_intro_mclassdefs(v.ctx.min_visibility).to_a
                doc.mainmodule.linearize_mclassdefs(intros)
 -              group.add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
 +              group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.intros", "Introduces", intros)
-               var redefs = mmodule.redef_mclassdefs(v.ctx.min_visibility).to_a
+               var redefs = mmodule.collect_redef_mclassdefs(v.ctx.min_visibility).to_a
                doc.mainmodule.linearize_mclassdefs(redefs)
 -              group.add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
 +              group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.redefs", "Redefines", redefs)
                section.add_child group
                add_child(section)
        end
  
        # TODO this should move to MEntity?
        private fun build_mclassdef_list(v: IntroRedefListPhase, doc: DocModel, mclassdef: MClassDef) do
 -              var section = new IntrosRedefsSection(mentity)
 -              var group = new PanelGroup("List")
 +              var section = new TabbedGroup("{mentity.nitdoc_id}.intros_redefs")
 +              section.toc_title = "Intros / Redefs"
 +              var group = new PanelGroup("list.group", "List")
                var intros = mclassdef.collect_intro_mpropdefs(v.ctx.min_visibility).to_a
                # FIXME avoid diff changes
                # v.ctx.mainmodule.linearize_mpropdefs(intros)
 -              group.add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
 +              group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.intros", "Introduces", intros)
                var redefs = mclassdef.collect_redef_mpropdefs(v.ctx.min_visibility).to_a
                # FIXME avoid diff changes
                # v.ctx.mainmodule.linearize_mpropdefs(redefs)
 -              group.add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
 +              group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.redefs", "Redefines", redefs)
                section.add_child group
                add_child(section)
        end
 -
 -end
 -
 -# Section that contains the intros and redefs lists.
 -class IntrosRedefsSection
 -      super TabbedGroup
 -      super MEntitySection
 -end
 -
 -# An article that displays a list of introduced / refined mentities.
 -#
 -# FIXME diff hack
 -# This can merged with InheritanceListArticle in a more generic class.
 -class IntrosRedefsListArticle
 -      super MEntityArticle
 -
 -      # Title displayed as header of the list.
 -      var list_title: String
 -
 -      # Intro mentities to list.
 -      var mentities: Array[MEntity]
  end
@@@ -16,6 -16,7 +16,7 @@@
  module doc_poset
  
  import doc_pages
+ import model::model_collect
  
  # This phase computes importation and inheritance POSet for pages.
  class POSetPhase
@@@ -46,7 -47,7 +47,7 @@@ redef class MModulePag
        # Imported modules that should appear in the documentation.
        var imports = new HashSet[MModule]
  
 -      # Clients modules that shjould appear in the documentation.
 +      # Clients modules that should appear in the documentation.
        var clients = new HashSet[MModule]
  
        redef fun build_poset(v, doc) do
                end
                # make poset
                var mmodules = new HashSet[MModule]
-               mmodules.add_all mentity.nested_mmodules
+               var mgroup = mentity.mgroup
+               if mgroup != null and mgroup.default_mmodule == mentity then
+                       mmodules.add_all mgroup.mmodules
+               end
                mmodules.add_all imports
                if clients.length < 10 then mmodules.add_all clients
                mmodules.add mentity
@@@ -47,15 -47,15 +47,15 @@@ en
  
  redef class OverviewPage
        redef fun apply_structure(v, doc) do
 -              var article = new HomeArticle
 +              var article = new HomeArticle("home.article", "Home")
                root.add_child article
                # Projects list
                var mprojects = doc.model.mprojects.to_a
                var sorter = new MConcernRankSorter
                sorter.sort mprojects
 -              var section = new ProjectsSection
 +              var section = new DocSection("projects.section", "Projects")
                for mproject in mprojects do
 -                      section.add_child new DefinitionArticle(mproject)
 +                      section.add_child new DefinitionArticle("{mproject.nitdoc_id}.definition", mproject)
                end
                article.add_child section
        end
@@@ -69,18 -69,18 +69,18 @@@ redef class SearchPag
                v.name_sorter.sort(mclasses)
                var mprops = doc.mproperties.to_a
                v.name_sorter.sort(mprops)
 -              root.add_child new IndexArticle(mmodules, mclasses, mprops)
 +              root.add_child new IndexArticle("index.article", mmodules, mclasses, mprops)
        end
  end
  
  redef class MGroupPage
        redef fun apply_structure(v, doc) do
 -              var section = new MEntitySection(mentity)
 +              var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
                if mentity.is_root then
 -                      section.add_child new IntroArticle(mentity.mproject)
 +                      section.add_child new IntroArticle("{mentity.mproject.nitdoc_id}.intro", mentity.mproject)
                else
 -                      section.add_child new IntroArticle(mentity)
 +                      section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                end
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                concerns.sort_with(v.concerns_sorter)
                mentity.mproject.booster_rank = 0
                mentity.booster_rank = 0
 -              section.add_child new ConcernsArticle(mentity, concerns)
 +              section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                for mentity in concerns do
 -                      var ssection = new ConcernSection(mentity)
 +                      var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
 -                              ssection.add_child new DefinitionArticle(mentity)
 +                              ssection.add_child new DefinitionArticle("{mentity.nitdoc_id}.definition", mentity)
                        end
                        section.add_child ssection
                end
@@@ -103,9 -103,9 +103,9 @@@ en
  
  redef class MModulePage
        redef fun apply_structure(v, doc) do
 -              var section = new MEntitySection(mentity)
 +              var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
 -              section.add_child new IntroArticle(mentity)
 +              section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                # FIXME avoid diff
                mentity.mgroup.mproject.booster_rank = 0
                mentity.mgroup.booster_rank = 0
                mentity.booster_rank = 0
 -              section.add_child new ConcernsArticle(mentity, concerns)
 +              section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                # reference list
                for mentity in concerns do
 -                      var ssection = new ConcernSection(mentity)
 +                      var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
                                var mclasses = mclasses_for_mmodule(mentity).to_a
                                v.name_sorter.sort(mclasses)
                                for mclass in mclasses do
 -                                      var article = new DefinitionListArticle(mclass)
 +                                      var article = new DefinitionListArticle(
 +                                              "{mclass.intro.nitdoc_id}.definition-list", mclass)
                                        var mclassdefs = mclassdefs_for(mclass).to_a
                                        if not mclassdefs.has(mclass.intro) then
 -                                              article.add_child(new DefinitionArticle(mclass.intro))
 +                                              article.add_child(new DefinitionArticle(
 +                                                      "{mclass.intro.nitdoc_id}.definition", mclass.intro))
                                        end
                                        doc.mainmodule.linearize_mclassdefs(mclassdefs)
                                        for mclassdef in mclassdefs do
 -                                              article.add_child(new DefinitionArticle(mclassdef))
 +                                              article.add_child(new DefinitionArticle(
 +                                                      "{mclassdef.nitdoc_id}.definition", mclassdef))
                                        end
                                        ssection.add_child article
                                end
@@@ -168,9 -165,9 +168,9 @@@ en
  
  redef class MClassPage
        redef fun apply_structure(v, doc) do
 -              var section = new MEntitySection(mentity)
 +              var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
 -              section.add_child new IntroArticle(mentity)
 +              section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                # FIXME diff hack
                mentity.intro_mmodule.mgroup.mproject.booster_rank = 0
                mentity.intro_mmodule.mgroup.booster_rank = 0
                mentity.intro_mmodule.booster_rank = 0
 -              var constructors = new ConstructorsSection(mentity)
 +              var constructors = new DocSection("{mentity.nitdoc_id}.constructors", "Constructors")
                var minit = mentity.root_init
                if minit != null then
 -                      constructors.add_child new DefinitionArticle(minit)
 +                      constructors.add_child new DefinitionArticle("{minit.nitdoc_id}.definition", minit)
                end
                section.add_child constructors
 -              section.add_child new ConcernsArticle(mentity, concerns)
 +              section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                for mentity in concerns do
 -                      var ssection = new ConcernSection(mentity)
 +                      var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
                                var mprops = mproperties_for(mentity)
                                var by_kind = new PropertiesByKind.with_elements(mprops)
                                                for mpropdef in mpropdefs_for(mprop, mentity) do
                                                        if mpropdef isa MMethodDef and mpropdef.mproperty.is_init then
                                                                if mpropdef == minit then continue
 -                                                              constructors.add_child new DefinitionArticle(mpropdef)
 +                                                              constructors.add_child new DefinitionArticle(
 +                                                                      "{mpropdef.nitdoc_id}.definition", mpropdef)
                                                        else
 -                                                              ssection.add_child new DefinitionArticle(mpropdef)
 +                                                              ssection.add_child new DefinitionArticle(
 +                                                                      "{mpropdef.nitdoc_id}.definition", mpropdef)
                                                        end
                                                end
                                        end
@@@ -243,9 -238,9 +243,9 @@@ en
  
  redef class MPropertyPage
        redef fun apply_structure(v, doc) do
 -              var section = new MEntitySection(mentity)
 +              var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
 -              section.add_child new IntroArticle(mentity)
 +              section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                # FIXME diff hack
                mentity.intro.mclassdef.mmodule.mgroup.mproject.booster_rank = 0
                mentity.intro.mclassdef.mmodule.mgroup.booster_rank = 0
                mentity.intro.mclassdef.mmodule.booster_rank = 0
 -              section.add_child new ConcernsArticle(mentity, concerns)
 +              section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                for mentity in concerns do
 -                      var ssection = new ConcernSection(mentity)
 +                      var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
                                # Add mproperties
                                var mpropdefs = mpropdefs_for(mentity).to_a
                                v.name_sorter.sort(mpropdefs)
                                for mpropdef in mpropdefs do
 -                                      ssection.add_child new DefinitionArticle(mpropdef)
 +                                      ssection.add_child new DefinitionArticle(
 +                                              "{mpropdef.nitdoc_id}.definition", mpropdef)
                                end
                        end
                        section.add_child ssection
@@@ -294,26 -288,30 +294,26 @@@ en
  # A group of sections that can be displayed together in a tab panel.
  class PanelGroup
        super DocSection
 -
 -      # The title of this group.
 -      var group_title: String
  end
  
  # A DocComposite element about a MEntity.
  class MEntityComposite
        super DocComposite
  
 +      redef fun title do return mentity.nitdoc_name
 +
        # MEntity documented by this page element.
        var mentity: MEntity
  end
  
 -# A list of constructors.
 -class ConstructorsSection
 -      super MEntitySection
 -end
 -
  # A Section about a Concern.
  #
  # Those sections are used to build the page summary.
  class ConcernSection
        super MEntityComposite
        super DocSection
 +
 +      redef fun is_toc_hidden do return is_hidden
  end
  
  # An article about a Mentity.
@@@ -324,17 -322,6 +324,17 @@@ abstract class MEntityArticl
        super DocArticle
  end
  
 +# An article that displays a list of mentities.
 +class MEntitiesListArticle
 +      super DocArticle
 +
 +      # MEntities to display.
 +      var mentities: Array[MEntity]
 +
 +      redef fun is_hidden do return mentities.is_empty
 +end
 +
 +
  # A section about a Mentity.
  #
  # Used to regroup content about a MEntity.
@@@ -349,9 -336,6 +349,9 @@@ en
  class IntroArticle
        super MEntityComposite
        super DocArticle
 +
 +      redef var is_hidden = false
 +      redef var is_toc_hidden = true
  end
  
  # An article that display a ConcernsTreee as a list.
@@@ -360,11 -344,9 +360,11 @@@ class ConcernsArticl
  
        # Concerns to list in this article.
        var concerns: ConcernsTree
 +
 +      redef fun is_hidden do return concerns.is_empty
  end
  
 -# An article that displaus a list of definition belonging to a MEntity.
 +# An article that displays a list of definition belonging to a MEntity.
  class DefinitionListArticle
        super TabbedGroup
        super MEntityArticle
@@@ -373,8 -355,6 +373,8 @@@ en
  # An article that display the definition text of a MEntity.
  class DefinitionArticle
        super MEntityArticle
 +
 +      redef var is_hidden = false
  end
  
  # The main project article.
@@@ -382,6 -362,11 +382,6 @@@ class HomeArticl
        super DocArticle
  end
  
 -# The project list.
 -class ProjectsSection
 -      super DocArticle
 -end
 -
  # An article that display an index of mmodules, mclasses and mproperties.
  class IndexArticle
        super DocArticle
  
        # List of mproperties to display.
        var mprops: Array[MProperty]
 +
 +      redef fun is_hidden do
 +              return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
 +      end
  end
+ # Concerns ranking
+ # Sort MConcerns based on the module importation hierarchy ranking
+ # see also: `MConcern::concern_rank` and `MConcern::booster_rank`
+ #
+ # Comparison is made with the formula:
+ #
+ # ~~~nitish
+ # a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
+ # ~~~
+ #
+ # If both `a` and `b` have the same ranking,
+ # ordering is based on lexicographic comparison of `a.name` and `b.name`
+ class MConcernRankSorter
+       super Comparator
+       redef type COMPARED: MConcern
+       redef fun compare(a, b) do
+               if a.concern_rank == b.concern_rank then
+                       return a.name <=> b.name
+               end
+               return a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_rank
+       end
+ end
+ redef class MConcern
+       # Boost a MConcern rank
+       # see: `MConcernRankSorter`
+       # Use a positive booster to push down a result in the list
+       # A negative booster can be used to push up the result
+       var booster_rank: Int = 0 is writable
+       # Concern ranking used for ordering
+       # see: `MConcernRankSorter`
+       # Rank can be positive or negative
+       fun concern_rank: Int is abstract
+ end
+ redef class MProject
+       redef var concern_rank is lazy do
+               var max = 0
+               for mgroup in mgroups do
+                       var mmax = mgroup.concern_rank
+                       if mmax > max then max = mmax
+               end
+               return max + 1
+       end
+ end
+ redef class MGroup
+       redef var concern_rank is lazy do
+               var max = 0
+               for mmodule in mmodules do
+                       var mmax = mmodule.concern_rank
+                       if mmax > max then max = mmax
+               end
+               return max + 1
+       end
+ end
+ redef class MModule
+       redef var concern_rank is lazy do
+               var max = 0
+               for p in in_importation.direct_greaters do
+                       var pmax = p.concern_rank
+                       if pmax > max then max = pmax
+               end
+               return max + 1
+       end
+ end
@@@ -20,6 -20,7 +20,7 @@@ import doc_dow
  import html_components
  import html::bootstrap
  import ordered_tree
+ import model::model_collect
  
  redef class MEntity
        # URL of this entity’s Nitdoc page.
@@@ -330,8 -331,8 +331,8 @@@ redef class MClassDe
        redef fun 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
+               for m in mclass.intro.collect_modifiers do set.add m.to_cmangle
+               for m in collect_modifiers do set.add m.to_cmangle
                return set.to_a
        end
  end
@@@ -421,8 -422,8 +422,8 @@@ redef class MPropDe
        redef fun 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
+               for m in mproperty.intro.collect_modifiers do set.add m.to_cmangle
+               for m in collect_modifiers do set.add m.to_cmangle
                return set.to_a
        end
  end
@@@ -686,7 -687,7 +687,7 @@@ redef class MConcer
        private fun html_concern_item: ListItem do
                var lnk = html_link
                var tpl = new Template
 -              tpl.add new Link.with_title("#concern:{nitdoc_id}", lnk.text, lnk.title)
 +              tpl.add new Link.with_title("#{nitdoc_id}.concern", lnk.text, lnk.title)
                var comment = html_short_comment
                if comment != null then
                        tpl.add ": "
@@@ -93,7 -93,7 +93,7 @@@ class CNBN
  
        redef fun collect(mclasses) do
                for mclass in mclasses do
-                       var all = mclass.all_mattributes(mainmodule, min_visibility)
+                       var all = mclass.collect_accessible_mattributes(min_visibility)
                        for mattr in all do
                                if mattr.is_nullable then values.inc(mclass)
                        end
        end
  end
  
+ redef class MAttribute
+       # Is this attribute nullable for sure?
+       #
+       # This mean that its introduction is declarred with a nullable static type
+       # since attributes are invariant this will work on most cases
+       # attributes with static type anchored with a virtual type are not "nullable for-sure"
+       # because this type can be redefined in subclasses
+       private fun is_nullable: Bool do return intro.static_mtype isa MNullableType
+ end
  
  private class NullableSends
        super Visitor
                        end
                        t = t.anchor_to(self.nclassdef.mclassdef.mmodule, self.nclassdef.mclassdef.bound_mtype)
                        if t isa MNullableType then
 -                              var name = n.callsite.mproperty.name
 -                              if name == "==" or name == "!=" or name == "is_same_instance" then
 +                              var p = n.callsite.mproperty
 +                              if p.is_null_safe then
                                        self.nullable_eq_sends += 1
                                else
                                        self.nullable_sends += 1
diff --combined src/model/model.nit
@@@ -206,9 -206,6 +206,9 @@@ redef class MModul
        # The primitive type `Int`
        var int_type: MClassType = self.get_primitive_class("Int").mclass_type is lazy
  
 +      # The primitive type `Byte`
 +      var byte_type: MClassType = self.get_primitive_class("Byte").mclass_type is lazy
 +
        # The primitive type `Char`
        var char_type: MClassType = self.get_primitive_class("Char").mclass_type is lazy
  
@@@ -517,6 -514,18 +517,18 @@@ class MClas
  
        # Is there a `new` factory to allow the pseudo instantiation?
        var has_new_factory = false is writable
+       # Is `self` a standard or abstract class kind?
+       var is_class: Bool is lazy do return kind == concrete_kind or kind == abstract_kind
+       # Is `self` an interface kind?
+       var is_interface: Bool is lazy do return kind == interface_kind
+       # Is `self` an enum kind?
+       var is_enum: Bool is lazy do return kind == enum_kind
+       # Is `self` and abstract class?
+       var is_abstract: Bool is lazy do return kind == abstract_kind
  end
  
  
@@@ -2156,10 -2165,6 +2168,10 @@@ class MMetho
        do
                return self.is_init
        end
 +
 +      # A specific method that is safe to call on null.
 +      # Currently, only `==`, `!=` and `is_same_instance` are safe
 +      fun is_null_safe: Bool do return name == "==" or name == "!=" or name == "is_same_instance"
  end
  
  # A global attribute
diff --combined src/nitserial.nit
@@@ -27,7 -27,6 +27,6 @@@ module nitseria
  
  import frontend
  import rapid_type_analysis
- import model_utils
  import template
  
  # A Nit module
@@@ -94,7 -93,7 +93,7 @@@ en
  
  redef class MClassType
        redef fun is_visible_from(mmodule) do
 -              return mmodule.is_visible(mclass.intro_mmodule, public_visibility)
 +              return mmodule.is_visible(mclass.intro_mmodule, mclass.visibility)
        end
  end
  
@@@ -12,7 -12,7 +12,7 @@@ digraph G 
                                        fontsize = 8
                                ]
  Object [
-  label = "{interface\nObject||+ object_id(): Int\l+ is_same_type(other: Object): Bool\l+ is_same_instance(other: nullable Object): Bool\l+ ==(other: nullable Object): Bool\l+ !=(other: nullable Object): Bool\l+ output()\l+ output_class_name()\l+ hash(): Int\l+ sys(): Sys\l}"
+  label = "{interface\nObject||+ object_id(): Int\l+ is_same_type(other: Object): Bool\l+ is_same_instance(other: nullable Object): Bool\l+ ==(other: nullable Object): Bool\l+ !=(other: nullable Object): Bool\l+ output()\l+ output_class_name()\l+ hash(): Int\l+ sys(): Sys\l+ init()\l}"
  ]
  
  Sys [
@@@ -36,7 -36,7 +36,7 @@@ Cloneable 
  Object -> Cloneable [dir=back arrowtail=open style=dashed];
  
  Numeric [
 - label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
 + label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ to_b(): Byte\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
  ]
  Comparable -> Numeric [dir=back arrowtail=open style=dashed];
  
@@@ -50,12 -50,6 +50,12 @@@ Float 
  ]
  Numeric -> Float [dir=back arrowtail=open style=dashed];
  
 +Byte [
 + label = "{Byte||+ %(i: Byte): Byte\l+ lshift(i: Int): Byte\l+ \<\<(i: Int): Byte\l+ rshift(i: Int): Byte\l+ \>\>(i: Int): Byte\l}"
 +]
 +Discrete -> Byte [dir=back arrowtail=open style=dashed];
 +Numeric -> Byte [dir=back arrowtail=open style=dashed];
 +
  Int [
   label = "{Int||+ %(i: Int): Int\l+ lshift(i: Int): Int\l+ \<\<(i: Int): Int\l+ rshift(i: Int): Int\l+ \>\>(i: Int): Int\l+ ascii(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
  ]
@@@ -12,7 -12,7 +12,7 @@@ digraph G 
                                        fontsize = 8
                                ]
  Object [
-  label = "{interface\nObject||+ object_id(): Int\l+ is_same_type(other: Object): Bool\l+ is_same_instance(other: nullable Object): Bool\l+ ==(other: nullable Object): Bool\l+ !=(other: nullable Object): Bool\l+ output()\l+ output_class_name()\l+ hash(): Int\l+ sys(): Sys\l}"
+  label = "{interface\nObject||+ object_id(): Int\l+ is_same_type(other: Object): Bool\l+ is_same_instance(other: nullable Object): Bool\l+ ==(other: nullable Object): Bool\l+ !=(other: nullable Object): Bool\l+ output()\l+ output_class_name()\l+ hash(): Int\l+ sys(): Sys\l+ init()\l}"
  ]
  
  Sys [
@@@ -36,7 -36,7 +36,7 @@@ Cloneable 
  Object -> Cloneable [dir=back arrowtail=open style=dashed];
  
  Numeric [
 - label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
 + label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ to_b(): Byte\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
  ]
  Comparable -> Numeric [dir=back arrowtail=open style=dashed];
  
@@@ -49,12 -49,6 +49,12 @@@ Float 
   label = "{Float||+ is_approx(other: Float, precision: Float): Bool\l}"
  ]
  Numeric -> Float [dir=back arrowtail=open style=dashed];
 +
 +Byte [
 + label = "{Byte||+ %(i: Byte): Byte\l+ lshift(i: Int): Byte\l+ \<\<(i: Int): Byte\l+ rshift(i: Int): Byte\l+ \>\>(i: Int): Byte\l}"
 +]
 +Discrete -> Byte [dir=back arrowtail=open style=dashed];
 +Numeric -> Byte [dir=back arrowtail=open style=dashed];
  
  Int [
   label = "{Int||+ %(i: Int): Int\l+ lshift(i: Int): Int\l+ \<\<(i: Int): Int\l+ rshift(i: Int): Int\l+ \>\>(i: Int): Int\l+ ascii(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"