ni_nitdoc: rethinked mmodule page.
authorAlexandre Terrasa <alexandre@moz-code.org>
Wed, 14 Aug 2013 01:10:26 +0000 (21:10 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Wed, 14 Aug 2013 13:47:14 +0000 (09:47 -0400)
Module page now displays intro classes, redef classes and refined classes (in this order)
Added a side bar block to quick list classes (sorted by kind)
Nested modules are always dislayed
Modified comment display for better readability

Also cleaned trailing spaces in main.css

Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

share/nitdoc/styles/main.css
src/ni_nitdoc.nit

index 42ed6ae..c9b2b99 100644 (file)
@@ -31,6 +31,10 @@ pre {
        overflow: auto;\r
 }\r
 \r
+pre.noComment {\r
+       background: transparent;\r
+}\r
+\r
 hr {\r
        background-color: #DDD;\r
        height: 1px;\r
@@ -288,7 +292,7 @@ header nav.main ul li.current {
        padding-left: 10px;\r
 }\r
 \r
-nav ul li span, \r
+nav ul li span,\r
 .content article ul li span {\r
        color: #CCC;\r
        text-align: center;\r
@@ -341,6 +345,16 @@ article .info .code {
        float: right;\r
 }\r
 \r
+.description .inheritance {\r
+       text-align: right;\r
+       margin: 2px;\r
+\r
+}\r
+\r
+.classes pre, .methods pre {\r
+       margin-bottom: 0;\r
+}\r
+\r
 .content .concern-doc {\r
        border-top: 3px dotted #CCC;\r
        padding: 5px 0;\r
@@ -410,7 +424,7 @@ nav.main input[type=text] {
 }\r
 \r
 nav.main form {\r
-       float: right;   \r
+       float: right;\r
 }\r
 \r
 .filter a {\r
@@ -445,15 +459,15 @@ nav h3 a.fold {
 }\r
 \r
 /* Icons */\r
-.type.public { background-image: url('../resources/icons/vtype_public.png')}\r
-.type.protected { background-image: url('../resources/icons/vtype_protected.png')}\r
-.type.private { background-image: url('../resources/icons/vtype_private.png')}\r
-.init.public { background-image: url('../resources/icons/const_public.png')}\r
-.init.protected { background-image: url('../resources/icons/const_protected.png')}\r
-.init.private { background-image: url('../resources/icons/const_private.png')}\r
-.fun.public { background-image: url('../resources/icons/meth_public.png')}\r
-.fun.protected { background-image: url('../resources/icons/meth_protected.png')}\r
-.fun.private { background-image: url('../resources/icons/meth_private.png')}\r
+.type.public, .interface.public, .abstract.class.public { background-image: url('../resources/icons/vtype_public.png')}\r
+.type.protected, .interface.protected, .abstract.class.protected { background-image: url('../resources/icons/vtype_protected.png')}\r
+.type.private, .interface.private, .abstract.class.private { background-image: url('../resources/icons/vtype_private.png')}\r
+.init.public, .enum.public { background-image: url('../resources/icons/const_public.png')}\r
+.init.protected, .enum.protected { background-image: url('../resources/icons/const_protected.png')}\r
+.init.private, .enum.private { background-image: url('../resources/icons/const_private.png')}\r
+.fun.public, .class.public, .extern.public { background-image: url('../resources/icons/meth_public.png')}\r
+.fun.protected, .class.protected, .extern.protected { background-image: url('../resources/icons/meth_protected.png')}\r
+.fun.private, .class.private, .extern.private { background-image: url('../resources/icons/meth_private.png')}\r
 \r
 /* Quick Search */\r
 \r
@@ -544,8 +558,8 @@ textarea {
        display: none; /*--masqué par défaut--*/\r
        background: #fff;\r
        padding: 20px;\r
-       margin-bottom: 400px;   \r
-       font-size: 1.2em;       \r
+       margin-bottom: 400px;\r
+       font-size: 1.2em;\r
 }\r
 \r
 #modalQuestion{\r
@@ -606,8 +620,8 @@ img.btn_close, img.btnCloseQuestion {
        float: right;\r
        margin: -35px -35px 0 0;\r
 }\r
-fieldset { \r
-    margin-bottom: 20px; \r
+fieldset {\r
+    margin-bottom: 20px;\r
 }\r
 \r
 #login, #password, #commitMessage, #repoCommit, #branchName{\r
@@ -624,7 +638,7 @@ fieldset {
        max-width: 100%;\r
        display: block;\r
        margin-bottom: 20px;\r
-       background: #fff; \r
+       background: #fff;\r
 }\r
 \r
 .connect-button a{\r
@@ -638,18 +652,18 @@ fieldset {
        background-image:     -ms-linear-gradient(top, #92C929, #1d7900); /* IE10 */\r
        background-image:      -o-linear-gradient(top, #92C929, #1d7900); /* Opera 11.10+ */\r
        background-image:         linear-gradient(top, #92C929, #1d7900);\r
-       filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#92C929', EndColorStr='#1d7900'); /* IE6–IE9 */    \r
+       filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#92C929', EndColorStr='#1d7900'); /* IE6–IE9 */\r
        border-radius: 4px;\r
-       -moz-border-radius: 4px; \r
-       -webkit-border-radius: 4px;  \r
-       -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;    \r
+       -moz-border-radius: 4px;\r
+       -webkit-border-radius: 4px;\r
+       -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;\r
        box-shadow: 0px 2px 4px rgba(0,0,0, .2);\r
        -moz-box-shadow: 0px 2px 4px rgba(0,0,0, .2);\r
-       -webkit-box-shadow: 0px 2px 4px rgba(0,0,0, .2);    \r
+       -webkit-box-shadow: 0px 2px 4px rgba(0,0,0, .2);\r
        text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);\r
        -moz-text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);\r
-       -webkit-text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);    \r
-       border: 1px solid #1d7900;    \r
+       -webkit-text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);\r
+       border: 1px solid #1d7900;\r
        color: #fff;\r
        font-weight: 300;\r
        font-size: 14px;\r
@@ -813,13 +827,13 @@ fieldset {
        color: black;\r
        font-style: normal;\r
        margin-left: 10px;\r
-       margin-top: 10px;       \r
+       margin-top: 10px;\r
 }\r
 #logginMessage , #lbBranches{\r
        color: black;\r
        font-style: normal;\r
        text-align: center;\r
-       margin-top: 10px;       \r
+       margin-top: 10px;\r
 }\r
 \r
 #divlogIn{\r
@@ -837,18 +851,18 @@ fieldset {
        background-image:     -ms-linear-gradient(top, #92C929, #1d7900); /* IE10 */\r
        background-image:      -o-linear-gradient(top, #92C929, #1d7900); /* Opera 11.10+ */\r
        background-image:         linear-gradient(top, #92C929, #1d7900);\r
-       filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#92C929', EndColorStr='#1d7900'); /* IE6–IE9 */    \r
+       filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#92C929', EndColorStr='#1d7900'); /* IE6–IE9 */\r
        border-radius: 4px;\r
-       -moz-border-radius: 4px; \r
-       -webkit-border-radius: 4px;  \r
-       -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;    \r
+       -moz-border-radius: 4px;\r
+       -webkit-border-radius: 4px;\r
+       -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;\r
        box-shadow: 0px 2px 4px rgba(0,0,0, .2);\r
        -moz-box-shadow: 0px 2px 4px rgba(0,0,0, .2);\r
-       -webkit-box-shadow: 0px 2px 4px rgba(0,0,0, .2);    \r
+       -webkit-box-shadow: 0px 2px 4px rgba(0,0,0, .2);\r
        text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);\r
        -moz-text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);\r
-       -webkit-text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);    \r
-       border: 1px solid #1d7900;    \r
+       -webkit-text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.2);\r
+       border: 1px solid #1d7900;\r
        color: #fff;\r
        font-weight: 300;\r
        font-size: 14px;\r
@@ -857,7 +871,7 @@ fieldset {
 }\r
 \r
 #passwordGit, #loginGit, #repositoryGit, #branchGit, #signedOff{\r
-       height:30px; \r
+       height:30px;\r
        width: 200px;\r
 }\r
 \r
@@ -867,7 +881,7 @@ fieldset {
 \r
 #dropBranches{\r
        font-size: 12px;\r
-       height:20px; \r
+       height:20px;\r
 }\r
 \r
 #listBranches, #divGitHubRepoDisplay{\r
@@ -875,7 +889,7 @@ fieldset {
        display:block;\r
        text-align:center;\r
        margin-top: 15px;\r
-       margin-bottom: 15px;    \r
+       margin-bottom: 15px;\r
 }\r
 \r
 .githubTitle{\r
@@ -886,10 +900,6 @@ fieldset {
        display: none;\r
 }\r
 \r
-.noComment {\r
-       padding: 5px;\r
-}\r
-\r
 a.newComment {\r
        padding: 5px;\r
        color: #0D8921;\r
index 6eeccd9..c7525dc 100644 (file)
@@ -489,12 +489,37 @@ class NitdocModule
 
        private var mmodule: MModule
        private var mbuilder: ModelBuilder
+       private var local_mclasses = new HashSet[MClass]
+       private var intro_mclasses = new HashSet[MClass]
+       private var redef_mclasses = new HashSet[MClass]
+       private var inherited_mclasses = new HashSet[MClass]
 
        init(mmodule: MModule, ctx: NitdocContext, dot_dir: nullable String) do
                super(ctx)
                self.mmodule = mmodule
                self.mbuilder = ctx.mbuilder
                self.dot_dir = dot_dir
+               # get local mclasses
+               for m in mmodule.in_nesting.greaters do
+                       for mclassdef in m.mclassdefs do
+                               if mclassdef.mclass.visibility < ctx.min_visibility then continue
+                               if mclassdef.is_intro then
+                                       intro_mclasses.add(mclassdef.mclass)
+                               else
+                                       redef_mclasses.add(mclassdef.mclass)
+                               end
+                               local_mclasses.add(mclassdef.mclass)
+                       end
+               end
+               # get inherited mclasses
+               for m in mmodule.in_importation.greaters do
+                       if m == mmodule then continue
+                       for mclassdef in m.mclassdefs do
+                               if mclassdef.mclass.visibility < ctx.min_visibility then continue
+                               if local_mclasses.has(mclassdef.mclass) then continue
+                               inherited_mclasses.add(mclassdef.mclass)
+                       end
+               end
        end
 
        redef fun title do
@@ -514,59 +539,34 @@ class NitdocModule
        end
 
        redef fun content do
-               sidebar
+               append("<div class='menu'>")
+               classes_column
+               importation_column
+               append("</div>")
                var footed = ""
                if ctx.opt_custom_footer_text.value != null then footed = "footed"
                append("<div class='content {footed}'>")
-               append("<h1>{mmodule.name}</h1>")
-               append("<div class='subtitle info'>")
-               mmodule.html_signature(self)
-               append("</div>")
-               mmodule.html_full_comment(self)
-               process_generate_dot
-               classes
-               properties
+               module_doc
                append("</div>")
        end
 
-       private fun process_generate_dot do
-               # build poset with public owners
-               var poset = new POSet[MModule]
-               for mmodule in self.mmodule.in_importation.poset do
-                       if mmodule.name == "<main>" then continue
-                       if mmodule.public_owner != null then continue
-                       if not mmodule.in_importation < self.mmodule and not self.mmodule.in_importation < mmodule and mmodule != self.mmodule then continue
-                       poset.add_node(mmodule)
-                       for omodule in mmodule.in_importation.poset do
-                               if mmodule == omodule then continue
-                               if omodule.name == "<main>" then continue
-                               if omodule.public_owner != null then continue
-                               if mmodule.in_importation < omodule then
-                                       poset.add_node(omodule)
-                                       poset.add_edge(mmodule, omodule)
-                               end
-                       end
-               end
-               # build graph
-               var op = new Buffer
-               var name = "dep_{mmodule.name}"
-               op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
-               for mmodule in poset do
-                       if mmodule == self.mmodule then
-                               op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n")
-                       else
-                               op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
-                       end
-                       for omodule in poset[mmodule].direct_greaters do
-                               op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
-                       end
+       private fun classes_column do
+               var sorter = new MClassNameSorter
+               var sorted = new Array[MClass]
+               sorted.add_all(local_mclasses)
+               sorter.sort(sorted)
+               if not sorted.is_empty then
+                       append("<nav class='properties filterable'>")
+                       append("<h3>Classes</h3>")
+                       append("<h4>Classes</h4>")
+                       append("<ul>")
+                       for mclass in sorted do mclass.html_sidebar_item(self)
+                       append("</ul>")
+                       append("</nav>")
                end
-               op.append("\}\n")
-               generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
        end
 
-       private fun sidebar do
-               append("<div class='menu'>")
+       private fun importation_column do
                append("<nav>")
                append("<h3>Module Hierarchy</h3>")
                var dependencies = new Array[MModule]
@@ -574,6 +574,10 @@ class NitdocModule
                        if dep == mmodule or dep.public_owner != null then continue
                        dependencies.add(dep)
                end
+               if mmodule.in_nesting.direct_greaters.length > 0 then
+                       append("<h4>Nested Modules</h4>")
+                       display_module_list(mmodule.in_nesting.direct_greaters.to_a)
+               end
                if dependencies.length > 0 then
                        append("<h4>All dependencies</h4>")
                        display_module_list(dependencies)
@@ -589,15 +593,6 @@ class NitdocModule
                        display_module_list(clients)
                end
                append("</nav>")
-               if ctx.min_visibility < protected_visibility then
-                       if mmodule.in_nesting.direct_greaters.length > 0 then
-                               append("<nav>")
-                               append("<h3>Nested Modules</h3>")
-                               display_module_list(mmodule.in_nesting.direct_greaters.to_a)
-                               append("</nav>")
-                       end
-               end
-               append("</div>")
        end
 
        private fun display_module_list(list: Array[MModule]) do
@@ -612,65 +607,106 @@ class NitdocModule
                append("</ul>")
        end
 
-       # display the class column
-       private fun classes do
-               var intro_mclasses = mmodule.intro_mclasses
-               var redef_mclasses = mmodule.redef_mclasses
-               var all_mclasses = new HashSet[MClass]
-               for m in mmodule.in_nesting.greaters do
-                       all_mclasses.add_all(m.intro_mclasses)
-                       all_mclasses.add_all(m.redef_mclasses)
+       private fun module_doc do
+               # title
+               append("<h1>{mmodule.name}</h1>")
+               append("<div class='subtitle info'>")
+               mmodule.html_signature(self)
+               append("</div>")
+               # comment
+               var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
+               append("<section class='description'>")
+               if not nmodule.full_comment.is_empty then append("<pre>{nmodule.full_comment}</pre>")
+               process_generate_dot
+               append("</section>")
+               # classes
+               var class_sorter = new MClassNameSorter
+               # intro
+               if not intro_mclasses.is_empty then
+                       var sorted = new Array[MClass]
+                       sorted.add_all(intro_mclasses)
+                       class_sorter.sort(sorted)
+                       append("<section class='classes'>")
+                       append("<h2 class='section-header'>Introduced classes</h2>")
+                       for mclass in sorted do mclass.html_full_desc(self)
+                       append("</section>")
                end
-               all_mclasses.add_all(intro_mclasses)
-               all_mclasses.add_all(redef_mclasses)
-
-               var sorted = new Array[MClass]
-               sorted.add_all(all_mclasses)
-               var sorter = new MClassNameSorter
-               sorter.sort(sorted)
-               append("<div class='module'>")
-               append("<article class='classes filterable'>")
-               append("<h2>Classes</h2>")
-               append("<ul>")
-               for c in sorted do
-                       if c.visibility < ctx.min_visibility then continue
-                       if redef_mclasses.has(c) and c.intro_mmodule.public_owner != mmodule then
-                               append("<li class='redef'>")
-                               append("<span title='refined in this module'>R </span>")
-                       else
-                               append("<li class='intro'>")
-                               append("<span title='introduced in this module'>I </span>")
+               # redefs
+               var redefs = new Array[MClass]
+               for mclass in redef_mclasses do if not intro_mclasses.has(mclass) then redefs.add(mclass)
+               class_sorter.sort(redefs)
+               if not redefs.is_empty then
+                       append("<section class='classes'>")
+                       append("<h2 class='section-header'>Refined classes</h2>")
+                       for mclass in redefs do mclass.html_full_desc(self)
+                       append("</section>")
+               end
+               # inherited properties
+               var inherited = new Array[MClass]
+               inherited.add_all(inherited_mclasses)
+               if inherited_mclasses.length > 0 then
+                       var modules2classes = new ArrayMap[MModule, Array[MClass]]
+                       for mclass in inherited_mclasses do
+                               if not modules2classes.has_key(mclass.intro_mmodule) then modules2classes[mclass.intro_mmodule] = new Array[MClass]
+                               modules2classes[mclass.intro_mmodule].add(mclass)
                        end
-                       c.html_link(self)
-                       append("</li>")
+                       append("<section class='classes'>")
+                       append("<h2 class='section-header'>Inherited Classes</h2>")
+                       var mmodules = new Array[MModule]
+                       mmodules.add_all(modules2classes.keys)
+                       var msorter = new MModuleNameSorter
+                       msorter.sort(mmodules)
+                       for m in mmodules do
+                               var mclasses = modules2classes[m]
+                               class_sorter.sort(mclasses)
+                               append("<p>Defined in ")
+                               m.html_link(self)
+                               append(": ")
+                               for i in [0..mclasses.length[ do
+                                       var mclass = mclasses[i]
+                                       mclass.html_link(self)
+                                       if i <= mclasses.length - 1 then append(", ")
+                               end
+                       append("</p>")
+                       end
+                       append("</section>")
                end
-               append("</ul>")
-               append("</article>")
-               append("</div>")
        end
 
-       # display the property column
-       private fun properties do
-               # get properties
-               var mpropdefs = new HashSet[MPropDef]
-               for m in mmodule.in_nesting.greaters do
-                       for c in m.mclassdefs do mpropdefs.add_all(c.mpropdefs)
+       private fun process_generate_dot do
+               # build poset with public owners
+               var poset = new POSet[MModule]
+               for mmodule in self.mmodule.in_importation.poset do
+                       if mmodule.name == "<main>" then continue
+                       if mmodule.public_owner != null then continue
+                       if not mmodule.in_importation < self.mmodule and not self.mmodule.in_importation < mmodule and mmodule != self.mmodule then continue
+                       poset.add_node(mmodule)
+                       for omodule in mmodule.in_importation.poset do
+                               if mmodule == omodule then continue
+                               if omodule.name == "<main>" then continue
+                               if omodule.public_owner != null then continue
+                               if mmodule.in_importation < omodule then
+                                       poset.add_node(omodule)
+                                       poset.add_edge(mmodule, omodule)
+                               end
+                       end
                end
-               for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs)
-               var sorted = mpropdefs.to_a
-               var sorter = new MPropDefNameSorter
-               sorter.sort(sorted)
-               # display properties in one column
-               append("<article class='properties filterable'>")
-               append("<h2>Properties</h2>")
-               append("<ul>")
-               for mprop in sorted do
-                       if mprop isa MAttributeDef then continue
-                       if mprop.mproperty.visibility < ctx.min_visibility then continue
-                       mprop.html_list_item(self)
+               # build graph
+               var op = new Buffer
+               var name = "dep_{mmodule.name}"
+               op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
+               for mmodule in poset do
+                       if mmodule == self.mmodule then
+                               op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n")
+                       else
+                               op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
+                       end
+                       for omodule in poset[mmodule].direct_greaters do
+                               op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
+                       end
                end
-               append("</ul>")
-               append("</article>")
+               op.append("\}\n")
+               generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
        end
 end
 
@@ -862,13 +898,16 @@ class NitdocClass
 
        private fun class_doc do
                # title
-               append("<h1>{mclass.signature}</h1>")
+               append("<h1>{mclass.name}{mclass.html_short_signature}</h1>")
                append("<div class='subtitle info'>")
-               mclass.html_full_signature(self)
-               append("</div>")
+               if mclass.visibility < public_visibility then append("{mclass.visibility.to_s} ")
+               append("{mclass.kind.to_s} ")
+               mclass.html_namespace(self)
+               append("{mclass.html_short_signature}</div>")
                # comment
                var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
                append("<section class='description'>")
+               if nclass isa AStdClassdef and not nclass.full_comment.is_empty then append("<pre>{nclass.full_comment}</pre>")
                process_generate_dot
                append("</section>")
                # concerns
@@ -942,7 +981,7 @@ class NitdocClass
                        end
                        # virtual types
                        prop_sorter.sort(local_vtypes)
-                       for prop in local_vtypes do prop.html_full_desc(self)
+                       for prop in local_vtypes do prop.html_full_desc(self, self.mclass)
                        append("</section>")
                end
                # constructors
@@ -952,7 +991,7 @@ class NitdocClass
                if local_consts.length > 0 then
                        append("<section class='constructors'>")
                        append("<h2 class='section-header'>Constructors</h2>")
-                       for prop in local_consts do prop.html_full_desc(self)
+                       for prop in local_consts do prop.html_full_desc(self, self.mclass)
                        append("</section>")
                end
                # methods
@@ -976,7 +1015,7 @@ class NitdocClass
                                if concern2meths.has_key(owner) then
                                        var mmethods = concern2meths[owner]
                                        prop_sorter.sort(mmethods)
-                                       for prop in mmethods do prop.html_full_desc(self)
+                                       for prop in mmethods do prop.html_full_desc(self, self.mclass)
                                end
                                for mmodule in mmodules do
                                        append("<a id=\"{mmodule.anchor}\"></a>")
@@ -991,9 +1030,10 @@ class NitdocClass
                                        end
                                        var mmethods = concern2meths[mmodule]
                                        prop_sorter.sort(mmethods)
-                                       for prop in mmethods do prop.html_full_desc(self)
+                                       for prop in mmethods do prop.html_full_desc(self, self.mclass)
                                end
                        end
+                       append("</section>")
                end
                # inherited properties
                if inherited.length > 0 then
@@ -1006,7 +1046,8 @@ class NitdocClass
                                if not classes.has_key(mclass) then classes[mclass] = new Array[MPropDef]
                                classes[mclass].add(mmethod)
                        end
-                       append("<h3>Inherited Properties</h3>")
+                       append("<section class='inherited'>")
+                       append("<h2 class='section-header'>Inherited Properties</h2>")
                        for c, mmethods in classes do
                                prop_sorter.sort(mmethods)
                                append("<p>Defined in ")
@@ -1019,8 +1060,8 @@ class NitdocClass
                                end
                                append("</p>")
                        end
+                       append("</section>")
                end
-               append("</section>")
        end
 
        private fun process_generate_dot do
@@ -1157,30 +1198,81 @@ redef class MModule
        fun html_full_comment(page: NitdocPage) do
                if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
                        page.append("<div id='description'>")
-                       page.append("<pre class='text_label'>{page.ctx.mbuilder.mmodule2nmodule[self].full_comment}</pre>")
+                       page.append("<pre>{page.ctx.mbuilder.mmodule2nmodule[self].full_comment}</pre>")
                        page.append("</div>")
                end
        end
+
+       private fun has_mclassdef_for(mclass: MClass): Bool do
+               for mmodule in self.in_nesting.greaters do
+                       for mclassdef in mmodule.mclassdefs do
+                               if mclassdef.mclass == mclass then return true
+                       end
+               end
+               return false
+       end
+
+       private fun has_mclassdef(mclassdef: MClassDef): Bool do
+               for mmodule in self.in_nesting.greaters do
+                       for oclassdef in mmodule.mclassdefs do
+                               if mclassdef == oclassdef then return true
+                       end
+               end
+               return false
+       end
 end
 
 redef class MClass
-       # Return the module signature decorated with html
-       fun html_full_signature(page: NitdocPage) do
-               if visibility < public_visibility then page.append("{visibility.to_s} ")
-               page.append("{kind} ")
-               html_namespace(page)
+       # return the generic signature of the class
+       #       [E, F]
+       private fun html_short_signature: String do
+               if arity > 0 then
+                       return "[{intro.parameter_names.join(", ")}]"
+               else
+                       return ""
+               end
        end
 
-       # name with formal parameter
-       # Foo[A, B]
-       private fun signature: String do
+       # return the generic signature of the class with bounds
+       #       [E: <a>MType</a>, F: <a>MType</a>]
+       private fun html_signature(page: NitdocPage) do
                if arity > 0 then
-                       return "{name}[{intro.parameter_names.join(", ")}]"
-               else
-                       return name
+                       page.append("[")
+                       for i in [0..intro.parameter_names.length[ do
+                               page.append("{intro.parameter_names[i]}: ")
+                               intro.bound_mtype.arguments[i].html_link(page)
+                               if i < intro.parameter_names.length - 1 then page.append(", ")
+                       end
+                       page.append("]")
                end
        end
 
+       # Return the class namespace decorated with html
+       private fun html_namespace(page: NitdocPage) do
+               intro_mmodule.html_namespace(page)
+               page.append("::<span>")
+               html_short_link(page)
+               page.append("</span>")
+       end
+
+       # Return a link (html a tag) to the nitdoc class page
+       fun html_short_link(page: NitdocPage) do
+               if html_short_link_cache == null then
+                       var res = new Buffer
+                       res.append("<a href='{url}'")
+                       if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
+                               var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
+                               if nclass isa AStdClassdef then
+                                       res.append(" title=\"{nclass.short_comment}\"")
+                               end
+                       end
+                       res.append(">{name}</a>")
+                       html_short_link_cache = res.to_s
+               end
+               page.append(html_short_link_cache.as(not null))
+       end
+       private var html_short_link_cache: nullable String
+
        # Return a link (html a tag) to the nitdoc class page
        fun html_link(page: NitdocPage) do
                if html_link_cache == null then
@@ -1192,20 +1284,37 @@ redef class MClass
                                        res.append(" title=\"{nclass.short_comment}\"")
                                end
                        end
-                       res.append(">{signature}</a>")
+                       res.append(">{name}{html_short_signature}</a>")
                        html_link_cache = res.to_s
                end
                page.append(html_link_cache.as(not null))
        end
        private var html_link_cache: nullable String
 
-       # Return the class namespace decorated with html
-       fun html_namespace(page: NitdocPage) do
-               intro_mmodule.html_namespace(page)
-               page.append("::<span>")
-               html_link(page)
-               page.append("</span>")
+       fun html_anchor(page: NitdocPage) do
+               if html_anchor_cache == null then
+                       var res = new Buffer
+                       res.append("<a href='#{anchor}'")
+                       if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
+                               var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
+                               if nclass isa AStdClassdef then
+                                       res.append(" title=\"{nclass.short_comment}\"")
+                               end
+                       end
+                       res.append(">{name}{html_short_signature}</a>")
+                       html_anchor_cache = res.to_s
+               end
+               page.append(html_anchor_cache.as(not null))
        end
+       private var html_anchor_cache: nullable String
+
+       fun anchor: String do
+               if anchor_cache == null then
+                       anchor_cache = "CLASS_{public_owner.name}_{name}"
+               end
+               return anchor_cache.as(not null)
+       end
+       private var anchor_cache: nullable String
 
        fun url: String do
                return "class_{public_owner}_{name}.html"
@@ -1213,12 +1322,101 @@ redef class MClass
 
        # Escape name for html output
        redef fun name do return super.html_escape
+
+       # Return a list item for the mclass
+       private fun html_sidebar_item(page: NitdocModule) do
+               if page.mmodule.in_nesting.greaters.has(intro.mmodule) then
+                       page.append("<li class='intro'>")
+                       page.append("<span title='Introduced'>I</span>")
+                       html_anchor(page)
+               else if page.mmodule.has_mclassdef_for(self) then
+                       page.append("<li class='redef'>")
+                       page.append("<span title='Redefined'>R</span>")
+                       html_anchor(page)
+               else
+                       page.append("<li class='inherit'>")
+                       page.append("<span title='Inherited'>H</span>")
+                       html_link(page)
+               end
+               page.append("</li>")
+       end
+
+       private fun html_full_desc(page: NitdocModule) do
+               var classes = new Array[String]
+               classes.add(kind.to_s)
+               if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then classes.add("redef")
+               classes.add(visibility.to_s)
+               page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
+               page.append("<h3 class='signature' data-untyped-signature='{name}{html_short_signature}'>")
+               page.append("<span>")
+               html_short_link(page)
+               html_signature(page)
+               page.append("</span></h3>")
+               html_info(page)
+               html_comment(page)
+               html_redefs(page)
+               page.append("</article>")
+       end
+
+       private fun html_info(page: NitdocModule) do
+               page.append("<div class='info'>")
+               if visibility < public_visibility then page.append("{visibility.to_s} ")
+               if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then page.append("redef ")
+               page.append("{kind} ")
+               html_namespace(page)
+               page.append("{html_short_signature}</div>")
+       end
+
+       private fun html_comment(page: NitdocModule) do
+               page.mmodule.linearize_mclassdefs(mclassdefs)
+               page.append("<div class='description'>")
+               # comments for each mclassdef contained in current mmodule
+               for mclassdef in mclassdefs do
+                       if not mclassdef.is_intro and not page.mmodule.mclassdefs.has(mclassdef) then continue
+                       if page.ctx.mbuilder.mclassdef2nclassdef.has_key(mclassdef) then
+                               var nclass = page.ctx.mbuilder.mclassdef2nclassdef[mclassdef]
+                               if nclass isa AStdClassdef then
+                                       if nclass.full_comment == "" then
+                                               page.append("<p class='info inheritance'>")
+                                               page.append("<span class=\"noComment\">no comment for </span>")
+                                       else
+                                               page.append("<pre>{nclass.full_comment}</pre>")
+                                               page.append("<p class='info inheritance'>")
+                                       end
+                                       if mclassdef.is_intro then
+                                               page.append("introduction in ")
+                                       else
+                                               page.append("refinement in ")
+                                       end
+                                       mclassdef.mmodule.html_full_namespace(page)
+                                       page.append(" {page.show_source(nclass.location)}</p>")
+                               end
+                       end
+               end
+               page.append("</div>")
+       end
+       
+       private fun html_redefs(page: NitdocModule) do
+               page.mmodule.linearize_mclassdefs(mclassdefs)
+               page.append("<div class='refinements'>")
+               # comments for each mclassdef contained in current mmodule
+               for mclassdef in mclassdefs do
+                       if not page.mmodule.mclassdefs.has(mclassdef) then continue
+                       if mclassdef.is_intro then continue
+                       for mpropdef in mclassdef.mpropdefs do
+                               if mpropdef isa MAttributeDef then continue
+                               mpropdef.html_full_desc(page, self)
+                       end
+               end
+               page.append("</div>")
+       end
 end
 
 redef class MProperty
        # Return the property namespace decorated with html
        fun html_namespace(page: NitdocPage) do
                intro_mclassdef.mclass.html_namespace(page)
+               page.append(intro_mclassdef.mclass.html_short_signature)
                page.append("::<span>")
                intro.html_link(page)
                page.append("</span>")
@@ -1309,22 +1507,6 @@ redef class MPropDef
        private var html_link_cache: nullable String
 
        # Return a list item for the mpropdef
-       private fun html_list_item(page: NitdocPage) do
-               if is_intro then
-                       page.append("<li class='intro'>")
-                       page.append("<span title='introduction'>I</span>&nbsp;")
-               else
-                       page.append("<li class='redef'>")
-                       page.append("<span title='redefinition'>R</span>&nbsp;")
-               end
-               html_link(page)
-               page.append("(")
-               mclassdef.mclass.html_link(page)
-               page.append(")")
-               page.append("</li>")
-       end
-
-       # Return a list item for the mpropdef
        private fun html_sidebar_item(page: NitdocClass) do
                if is_intro and mclassdef.mclass == page.mclass then
                        page.append("<li class='intro'>")
@@ -1340,104 +1522,55 @@ redef class MPropDef
                page.append("</li>")
        end
 
-       private fun html_full_desc(page: NitdocClass) is abstract
-       private fun html_info(page: NitdocClass) is abstract
+       private fun html_full_desc(page: NitdocPage, ctx: MClass) is abstract
+       private fun html_info(page: NitdocPage, ctx: MClass) is abstract
 
        fun full_name: String do
                return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}"
        end
 
-       private fun html_inheritance(page: NitdocClass) do
-               # definitions block
-               page.append("<p class='info'>")
-               page.ctx.mainmodule.linearize_mpropdefs(mproperty.mpropdefs)
-               var previous_defs = new Array[MPropDef]
-               var next_defs = new Array[MPropDef]
-               var self_passed = false
-               for def in mproperty.mpropdefs do
-                       if def == self then
-                               self_passed = true
-                               continue
-                       end
-                       if not self_passed then
-                               if def.mclassdef.mclass.in_hierarchy(page.ctx.mainmodule) < page.mclass then continue
-                               if def.is_intro then continue
-                               previous_defs.add(def)
-                       else
-                               if page.mclass.in_hierarchy(page.ctx.mainmodule) < def.mclassdef.mclass then continue
-                               next_defs.add(def)
-                       end
-               end
-               page.append("defined by ")
-               mclassdef.mmodule.html_full_namespace(page)
-               if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
-                       page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[self].location)}")
-               end
+       private fun html_comment(page: NitdocPage) do
+               page.append("<div class='description'>")
                if not is_intro then
-                       page.append(", introduced by ")
-                       mproperty.intro.mclassdef.mclass.html_link(page)
-                       if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
-                               page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[self].location)}")
-                       end
-               end
-               if not previous_defs.is_empty then
-                       page.append(", inherited from ")
-                       for i in [0..previous_defs.length[ do
-                               var def = previous_defs[i]
-                               def.mclassdef.mclass.html_link(page)
-                               if page.ctx.mbuilder.mpropdef2npropdef.has_key(def) then
-                                       page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[def].location)}")
-                               end
-
-                               if i < previous_defs.length - 1 then page.append(", ")
-                       end
-               end
-               if not next_defs.is_empty then
-                       page.append(", redefined by ")
-                       for i in [0..next_defs.length[ do
-                               var def = next_defs[i]
-                               def.mclassdef.mclass.html_link(page)
-                               if page.ctx.mbuilder.mpropdef2npropdef.has_key(def) then
-                                       page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[def].location)}")
+                       if page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then
+                               var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro]
+                               if intro_nprop.full_comment.is_empty then
+                                       page.append("<p class='info inheritance'>")
+                                       page.append("<span class=\"noComment\">no comment for </span>")
+                               else
+                                       page.append("<pre>{intro_nprop.full_comment}</pre>")
+                                       page.append("<p class='info inheritance'>")
                                end
-                               if i < next_defs.length - 1 then page.append(", ")
+                               page.append("introduction in ")
+                               mproperty.intro.mclassdef.html_namespace(page)
+                               page.append(" {page.show_source(intro_nprop.location)}</p>")
                        end
                end
-               page.append(".</p>")
-       end
-
-       private fun html_comment(page: NitdocClass) do
-               if not page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then return
-               var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
-               page.append("<div class='description'>")
-               if not is_intro and page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then
-                       var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro]
-                       page.append("<p class='info'>from ")
-                       mproperty.html_namespace(page)
-                       page.append("</p>")
-                       if intro_nprop.full_comment == "" then
-                               page.append("<span class=\"noComment\">No comment</span>")
+               if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
+                       var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
+                       if nprop.full_comment == "" then
+                               page.append("<p class='info inheritance'>")
+                               page.append("<span class=\"noComment\">no comment for </span>")
                        else
-                               page.append("<pre>{intro_nprop.full_comment}</pre>")
+                               page.append("<pre>{nprop.full_comment}</pre>")
+                               page.append("<p class='info inheritance'>")
+                       end
+                       if is_intro then
+                               page.append("introduction in ")
+                       else 
+                               page.append("redefinition in ")
                        end
-                       page.append("<p class='info'>from ")
                        mclassdef.html_namespace(page)
-                       page.append("</p>")
-               end
-               if nprop.full_comment == "" then
-                       page.append("<span class=\"noComment\">No comment</span>")
-               else
-                       page.append("<pre>{nprop.full_comment}</pre>")
+                       page.append(" {page.show_source(nprop.location)}</p>")
                end
-               html_inheritance(page)
                page.append("</div>")
        end
 end
 
 redef class MMethodDef
-       redef fun html_full_desc(page) do
+       redef fun html_full_desc(page, ctx) do
                var classes = new Array[String]
-               var is_redef = mproperty.intro_mclassdef.mclass != page.mclass
+               var is_redef = mproperty.intro_mclassdef.mclass != ctx
                if mproperty.is_init then
                        classes.add("init")
                else
@@ -1457,15 +1590,15 @@ redef class MMethodDef
                        msignature.html_signature(page)
                        page.append("</span></h3>")
                end
-               html_info(page)
+               html_info(page, ctx)
                html_comment(page)
                page.append("</article>")
        end
-
-       redef fun html_info(page) do
+       
+       redef fun html_info(page, ctx) do
                page.append("<div class='info'>")
                if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ")
-               if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ")
+               if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ")
                if mproperty.is_init then
                        page.append("init ")
                else
@@ -1477,8 +1610,8 @@ redef class MMethodDef
 end
 
 redef class MVirtualTypeDef
-       redef fun html_full_desc(page) do
-               var is_redef = mproperty.intro_mclassdef.mclass != page.mclass
+       redef fun html_full_desc(page, ctx) do
+               var is_redef = mproperty.intro_mclassdef.mclass != ctx
                var classes = new Array[String]
                classes.add("type")
                if is_redef then classes.add("redef")
@@ -1487,14 +1620,14 @@ redef class MVirtualTypeDef
                page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}'><span>{mproperty.name}: ")
                bound.html_link(page)
                page.append("</span></h3>")
-               html_info(page)
+               html_info(page, ctx)
                html_comment(page)
                page.append("</article>")
        end
 
-       redef fun html_info(page) do
+       redef fun html_info(page, ctx) do
                page.append("<div class='info'>")
-               if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ")
+               if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ")
                page.append("type ")
                mproperty.html_namespace(page)
                page.append("</div>")