From: Alexandre Terrasa Date: Mon, 30 Jun 2014 18:10:19 +0000 (-0400) Subject: nitdoc: better display of concerns X-Git-Tag: v0.6.6~3^2~6 X-Git-Url: http://nitlanguage.org nitdoc: better display of concerns Signed-off-by: Alexandre Terrasa --- diff --git a/src/doc/doc_model.nit b/src/doc/doc_model.nit index 023290d..d460f07 100644 --- a/src/doc/doc_model.nit +++ b/src/doc/doc_model.nit @@ -19,6 +19,7 @@ import model_utils import modelize_property import markdown import doc_templates +import ordered_tree redef class MDoc # Comment synopsys HTML escaped @@ -144,7 +145,6 @@ redef class MConcern end redef class MProject - redef fun nitdoc_name do return name.html_escape redef fun nitdoc_id do return nitdoc_name redef fun nitdoc_url do return "project_{name}.html" @@ -690,3 +690,39 @@ redef class MParameter end end +redef class ConcernsTree + + private var seen = new HashSet[MConcern] + + redef fun add(p, e) do + if seen.has(e) then return + seen.add e + super(p, e) + end + + fun to_tpl: TplList do + var lst = new TplList.with_classes(["list-unstyled", "list-definition"]) + for r in roots do + var li = r.tpl_concern_item + lst.add_li li + build_list(r, li) + end + return lst + end + + private fun build_list(e: MConcern, li: TplListItem) do + if not sub.has_key(e) then return + var subs = sub[e] + var lst = new TplList.with_classes(["list-unstyled", "list-definition"]) + for e2 in subs do + if e2 isa MGroup and e2.is_root then + build_list(e2, li) + else + var sli = e2.tpl_concern_item + lst.add_li sli + build_list(e2, sli) + end + end + li.append lst + end +end diff --git a/src/doc/doc_pages.nit b/src/doc/doc_pages.nit index 45e844d..cba936f 100644 --- a/src/doc/doc_pages.nit +++ b/src/doc/doc_pages.nit @@ -332,59 +332,90 @@ abstract class NitdocPage return " (source)" 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 + if mproject.mdoc != null then + article.content = mproject.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 + # mclassdefs list + var intros = mmodule.intro_mclassdefs(ctx.min_visibility).to_a + ctx.mainmodule.linearize_mclassdefs(intros) + var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}_intros", "Introduces") + var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) + for mclassdef in intros do + intros_lst.add_li new TplListItem.with_content(mclassdef.tpl_list_item) + end + if not intros_lst.is_empty then + intros_art.content = intros_lst + article.add_child intros_art + end + var redefs = mmodule.redef_mclassdefs(ctx.min_visibility).to_a + ctx.mainmodule.linearize_mclassdefs(redefs) + var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}_redefs", "Redefines") + var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) + for mclassdef in redefs do + redefs_lst.add_li new TplListItem.with_content(mclassdef.tpl_list_item) + end + if not redefs_lst.is_empty then + redefs_art.content = redefs_lst + article.add_child redefs_art + end + return article + end + # MClassDef description template fun tpl_mclass_article(mclass: MClass, mclassdefs: Array[MClassDef]): TplArticle do - var article = new TplArticle(mclass.nitdoc_id) - var title = new Template - var icon = new TplIcon.with_icon("tag") - icon.css_classes.add_all(mclass.intro.tpl_css_classes) - title.add icon - title.add mclass.tpl_link - title.add mclass.intro.tpl_signature - article.title = title - article.title_classes.add "signature" - article.subtitle = mclass.tpl_declaration - article.summary_title = "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}" - #article.subtitle = new Template - #article.subtitle.add mprop.intro.tpl_modifiers - #article.subtitle.add mprop.intro.tpl_namespace - var content = new Template - + var article = mclass.tpl_article if not mclassdefs.has(mclass.intro) then # add intro synopsys - var intro = mclass.intro - var location = intro.location - var sourcelink = tpl_showsource(location) - var intro_def = intro.tpl_definition - intro_def.location = sourcelink - content.add intro_def + var intro_article = mclass.intro.tpl_short_article + intro_article.source_link = tpl_showsource(mclass.intro.location) + article.add_child intro_article end ctx.mainmodule.linearize_mclassdefs(mclassdefs) for mclassdef in mclassdefs do # add mclassdef full description - var location = mclassdef.location - var sourcelink = tpl_showsource(location) - var prop_def = mclassdef.tpl_definition.as(TplClassDefinition) - prop_def.location = sourcelink - for mpropdef in mclassdef.mpropdefs do - var intro = mpropdef.mproperty.intro - if mpropdef isa MAttributeDef then continue - if mpropdef.mproperty.visibility < ctx.min_visibility then continue - - var lnk = new Template - lnk.add new TplLabel.with_classes(mpropdef.tpl_css_classes.to_a) - lnk.add mpropdef.tpl_link - if intro.mdoc != null then - lnk.add ": " - lnk.add intro.mdoc.short_comment - end - if mpropdef.is_intro then - prop_def.intros.add new TplListItem.with_content(lnk) - else - prop_def.redefs.add new TplListItem.with_content(lnk) - end + var redef_article = mclassdef.tpl_article + redef_article.source_link = tpl_showsource(mclassdef.location) + article.add_child redef_article + # mpropdefs list + var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}_intros", "Introduces") + var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) + for mpropdef in mclassdef.collect_intro_mpropdefs(ctx.min_visibility) do + intros_lst.add_li new TplListItem.with_content(mpropdef.tpl_list_item) + end + if not intros_lst.is_empty then + intros.content = intros_lst + redef_article.add_child intros + end + var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}_redefs", "Redefines") + var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) + for mpropdef in mclassdef.collect_redef_mpropdefs(ctx.min_visibility) do + redefs_lst.add_li new TplListItem.with_content(mpropdef.tpl_list_item) + end + if not redefs_lst.is_empty then + redefs.content = redefs_lst + redef_article.add_child redefs end - content.add prop_def end return article end @@ -570,10 +601,28 @@ class NitdocModule super NitdocPage private var mmodule: MModule + private var concerns: ConcernsTree + private var mclasses2mdefs: Map[MClass, Set[MClassDef]] + private var mmodules2mclasses: Map[MModule, Set[MClass]] init(mmodule: MModule, ctx: NitdocContext) do self.mmodule = mmodule super(ctx) + + var mclassdefs = new HashSet[MClassDef] + mclassdefs.add_all mmodule.intro_mclassdefs(ctx.min_visibility) + mclassdefs.add_all mmodule.redef_mclassdefs(ctx.min_visibility) + self.mclasses2mdefs = sort_by_mclass(mclassdefs) + self.mmodules2mclasses = group_by_mmodule(mclasses2mdefs.keys) + self.concerns = model.concerns_tree(mmodules2mclasses.keys) + # rank concerns + mmodule.mgroup.mproject.booster_rank = -1000 + mmodule.mgroup.booster_rank = -1000 + mmodule.booster_rank = -1000 + self.concerns.sort_with(new MConcernRankSorter) + mmodule.mgroup.mproject.booster_rank = 0 + mmodule.mgroup.booster_rank = 0 + mmodule.booster_rank = 0 end private var page = new TplPage @@ -692,35 +741,37 @@ class NitdocModule return article end + private fun tpl_concerns(parent: TplSection) do + if concerns.is_empty then return + parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl) + end + private fun tpl_mclasses(parent: TplSection) do - var mclassdefs = new HashSet[MClassDef] - mclassdefs.add_all mmodule.intro_mclassdefs(ctx.min_visibility) - mclassdefs.add_all mmodule.redef_mclassdefs(ctx.min_visibility) - var mclasses2mdefs = sort_by_mclass(mclassdefs) - var mmodules2mclasses = group_by_mmodule(mclasses2mdefs.keys) - - var sorted_mmodules = mmodules2mclasses.keys.to_a - model.mmodule_importation_hierarchy.linearize(sorted_mmodules) - - for mmodule in sorted_mmodules do - var section = new TplSection(mmodule.nitdoc_anchor) - var title = new Template - if mmodule == sorted_mmodules.first then - title.add "Introductions in " - section.summary_title = "In {mmodule.nitdoc_name}" - else - title.add "Redefinitions from " - section.summary_title = "From {mmodule.nitdoc_name}" - end - title.add mmodule.tpl_link - section.title = title + for mentity in concerns do + if mentity isa MProject then + parent.add_child new TplSection(mentity.nitdoc_id) + else if mentity isa MGroup then + parent.add_child new TplSection(mentity.nitdoc_id) + else if mentity isa MModule then + var section = new TplSection(mentity.nitdoc_id) + var title = new Template + if mentity == mmodule then + title.add "in " + section.summary_title = "in {mentity.nitdoc_name}" + else + title.add "from " + section.summary_title = "from {mentity.nitdoc_name}" + end + title.add mentity.tpl_namespace + section.title = title - var mclasses = mmodules2mclasses[mmodule].to_a - name_sorter.sort(mclasses) - for mclass in mclasses do - section.add_child tpl_mclass_article(mclass, mclasses2mdefs[mclass].to_a) + var mclasses = mmodules2mclasses[mentity].to_a + name_sorter.sort(mclasses) + for mclass in mclasses do + section.add_child tpl_mclass_article(mclass, mclasses2mdefs[mclass].to_a) + end + parent.add_child section end - parent.add_child section end end @@ -739,6 +790,7 @@ class NitdocModule redef fun tpl_content do tpl_sidebar_mclasses var top = tpl_intro + tpl_concerns(top) tpl_inheritance(top) tpl_mclasses(top) tpl_page.add_section top @@ -793,7 +845,9 @@ class NitdocClass super NitdocPage private var mclass: MClass + private var concerns: ConcernsTree private var mprops2mdefs: Map[MProperty, Set[MPropDef]] + private var mmodules2mprops: Map[MModule, Set[MProperty]] init(mclass: MClass, ctx: NitdocContext) do self.mclass = mclass @@ -801,7 +855,10 @@ class NitdocClass var mpropdefs = new HashSet[MPropDef] mpropdefs.add_all mclass.intro_mpropdefs(ctx.min_visibility) mpropdefs.add_all mclass.redef_mpropdefs(ctx.min_visibility) - mprops2mdefs = sort_by_mproperty(mpropdefs) + self.mprops2mdefs = sort_by_mproperty(mpropdefs) + self.mmodules2mprops = sort_by_mmodule(mprops2mdefs.keys) + self.concerns = model.concerns_tree(mmodules2mprops.keys) + self.concerns.sort_with(new MConcernRankSorter) end private var page = new TplPage @@ -868,51 +925,18 @@ class NitdocClass private fun tpl_intro: TplSection do var section = new TplSection.with_title("top", tpl_title) - var subtitle = new Template - subtitle.add "introduction in " - subtitle.add mclass.intro.mmodule.tpl_namespace - section.subtitle = subtitle - section.add_child tpl_mclassdef_article(mclass.intro) + section.subtitle = mclass.intro.tpl_declaration + var article = new TplArticle("comment") + if mclass.mdoc != null then + article.content = mclass.mdoc.tpl_comment + end + section.add_child article return section end - private fun tpl_concerns(section: TplSection) do - var mmodules = collect_mmodules(mprops2mdefs.keys) - var owner_map = sort_by_public_owner(mmodules) - var owners = owner_map.keys.to_a - - if not owners.is_empty then - var article = new TplArticle.with_title("concerns", "Concerns") - name_sorter.sort owners - var list = new TplList.with_classes(["list-unstyled", "list-definition"]) - for owner in owners do - var li = new Template - li.add owner.tpl_anchor - if owner.mdoc != null then - li.add ": " - li.add owner.mdoc.short_comment - end - var smmodules = owner_map[owner].to_a - #if not smmodules.length >= 1 then - var slist = new TplList.with_classes(["list-unstyled", "list-definition"]) - name_sorter.sort smmodules - for mmodule in smmodules do - if mmodule == owner then continue - var sli = new Template - sli.add mmodule.tpl_anchor - if mmodule.mdoc != null then - sli.add ": " - sli.add mmodule.mdoc.short_comment - end - slist.add_li(sli) - end - li.add slist - list.add_li li - #end - end - article.content = list - section.add_child article - end + private fun tpl_concerns(parent: TplSection) do + if concerns.is_empty then return + parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl) end private fun tpl_inheritance(parent: TplSection) do @@ -1001,20 +1025,22 @@ class NitdocClass end private fun tpl_properties(parent: TplSection) do - var mod_map = sort_by_mmodule(mprops2mdefs.keys) - var owner_map = sort_by_public_owner(mod_map.keys) - var owners = owner_map.keys.to_a - - for owner in owners do - var section = new TplSection(owner.nitdoc_anchor) - var title = new Template - title.add "Introductions in " - title.add owner.tpl_link - section.title = title - section.summary_title = "In {owner.nitdoc_name}" - for mmodule in owner_map[owner] do + var lst = concerns.to_a + for mentity in lst do + if mentity isa MProject then + parent.add_child new TplSection(mentity.nitdoc_id) + else if mentity isa MGroup then + parent.add_child new TplSection(mentity.nitdoc_id) + else if mentity isa MModule then + var section = new TplSection(mentity.nitdoc_id) + var title = new Template + title.add "in " + title.add mentity.tpl_namespace + section.title = title + section.summary_title = "In {mentity.nitdoc_name}" + # properties - var mprops = mod_map[mmodule] + var mprops = mmodules2mprops[mentity] var kind_map = sort_by_kind(mprops) # virtual types @@ -1040,8 +1066,8 @@ class NitdocClass var defs = mprops2mdefs[elt].to_a section.add_child tpl_mprop_article(elt, defs) end + parent.add_child section end - parent.add_child section end end diff --git a/src/doc/doc_templates.nit b/src/doc/doc_templates.nit index cc86549..ecc2d3d 100644 --- a/src/doc/doc_templates.nit +++ b/src/doc/doc_templates.nit @@ -376,8 +376,14 @@ class TplSectionElt # if null use `title` instead var summary_title: nullable String writable - # Parent section of this section if any - var parent: nullable TplSection + # CSS classes to apply on the section element + var css_classes = new Array[String] + + # CSS classes to apply on the title heading element + var title_classes = new Array[String] + + # Parent article/section if any + var parent: nullable TplSectionElt init(id: String) do self.id = id @@ -392,17 +398,6 @@ class TplSectionElt return parent.hlvl + 1 end - # Render this section in the summary - protected fun render_summary(parent: TplSummaryElt) is abstract - - # Is the section empty (no content at all) - fun is_empty: Bool is abstract -end - -# A HTML
element -class TplSection - super TplSectionElt - # Elements contained by this section var children = new Array[TplSectionElt] @@ -412,9 +407,11 @@ class TplSection children.add child end - redef fun is_empty: Bool do return children.is_empty + # Is the section empty (no content at all) + fun is_empty: Bool do return children.is_empty - redef fun render_summary(parent) do + # Render this section in the summary + fun render_summary(parent: TplSummaryElt) do if is_empty then return var title = summary_title if title == null and self.title != null then title = self.title.write_to_string @@ -426,6 +423,11 @@ class TplSection end parent.add_child entry end +end + +# A HTML
element +class TplSection + super TplSectionElt redef fun rendering do add "
" @@ -489,6 +491,12 @@ class TplArticle if content != null then add content.as(not null) end + if source_link != null then + add source_link.as(not null) + end + for child in children do + add child + end add """""" end @@ -679,6 +687,8 @@ class TplList init with_classes(classes: Array[String]) do self.css_classes = classes + fun is_empty: Bool do return elts.is_empty + redef fun rendering do if elts.is_empty then return add "
    " diff --git a/src/model_utils.nit b/src/model_utils.nit index 6decb0f..3fba8dc 100644 --- a/src/model_utils.nit +++ b/src/model_utils.nit @@ -19,6 +19,31 @@ module model_utils import modelbuilder +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 writable = 0 + + # Concern ranking used for ordering + # see: `MConcernRankSorter` + # Rank can be positive or negative + fun concern_rank: Int is abstract +end + +redef class MProject + redef fun concern_rank 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 fun in_nesting_intro_mclasses(min_visibility: MVisibility): Set[MClass] do var res = new HashSet[MClass] @@ -61,6 +86,15 @@ redef class MGroup end return res end + + redef fun concern_rank do + var max = 0 + for mmodule in collect_mmodules do + var mmax = mmodule.concern_rank + if mmax > max then max = mmax + end + return max + 1 + end end redef class MModule @@ -166,6 +200,15 @@ redef class MModule end return res end + + redef fun concern_rank 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 redef class MClass @@ -429,6 +472,35 @@ redef class MClassDef res.add mclass.kind.to_s return res end + + fun collect_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do + var res = new HashSet[MPropDef] + for mpropdef in mpropdefs do + if mpropdef.mproperty.visibility < min_visibility then continue + res.add mpropdef + end + return res + end + + fun collect_intro_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do + var res = new HashSet[MPropDef] + for mpropdef in mpropdefs do + if not mpropdef.is_intro then continue + if mpropdef.mproperty.visibility < min_visibility then continue + res.add mpropdef + end + return res + end + + fun collect_redef_mpropdefs(min_visibility: MVisibility): Set[MPropDef] do + var res = new HashSet[MPropDef] + for mpropdef in mpropdefs do + if mpropdef.is_intro then continue + if mpropdef.mproperty.visibility < min_visibility then continue + res.add mpropdef + end + return res + end end redef class MPropDef @@ -459,7 +531,6 @@ redef class MPropDef end end - # Sorters # Sort mentities by their name @@ -469,3 +540,24 @@ class MEntityNameSorter init do end end +# Sort MConcerns based on the module importation hierarchy ranking +# see also: `MConcern::concern_rank` and `MConcern::booster_rank` +# +# Comparison is made with the formula: +# +# 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 AbstractSorter[MConcern] + + init do end + + 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