1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Nitdoc page generation
21 redef class ToolContext
22 private var opt_dir
= new OptionString("output directory", "-d", "--dir")
23 private var opt_source
= new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source")
24 private var opt_sharedir
= new OptionString("directory containing nitdoc assets", "--sharedir")
25 private var opt_shareurl
= new OptionString("use shareurl instead of copy shared files", "--shareurl")
26 private var opt_nodot
= new OptionBool("do not generate graphes with graphviz", "--no-dot")
27 private var opt_private
= new OptionBool("also generate private API", "--private")
29 private var opt_custom_title
= new OptionString("custom title for homepage", "--custom-title")
30 private var opt_custom_brand
= new OptionString("custom link to external site", "--custom-brand")
31 private var opt_custom_intro
= new OptionString("custom intro text for homepage", "--custom-overview-text")
32 private var opt_custom_footer
= new OptionString("custom footer text", "--custom-footer-text")
34 private var opt_github_upstream
= new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream")
35 private var opt_github_base_sha1
= new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1")
36 private var opt_github_gitdir
= new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir")
38 private var opt_piwik_tracker
= new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker")
39 private var opt_piwik_site_id
= new OptionString("Piwik site ID", "--piwik-site-id")
41 private var output_dir
: String
42 private var min_visibility
: MVisibility
47 var opts
= option_context
48 opts
.add_option
(opt_dir
, opt_source
, opt_sharedir
, opt_shareurl
, opt_nodot
, opt_private
)
49 opts
.add_option
(opt_custom_title
, opt_custom_footer
, opt_custom_intro
, opt_custom_brand
)
50 opts
.add_option
(opt_github_upstream
, opt_github_base_sha1
, opt_github_gitdir
)
51 opts
.add_option
(opt_piwik_tracker
, opt_piwik_site_id
)
53 var tpl
= new Template
54 tpl
.add
"Usage: nitdoc [OPTION]... <file.nit>...\n"
55 tpl
.add
"Generates HTML pages of API documentation from Nit source files."
56 tooldescription
= tpl
.write_to_string
59 redef fun process_options
(args
) do
63 var output_dir
= opt_dir
.value
64 if output_dir
== null then
67 self.output_dir
= output_dir
69 if opt_private
.value
then
70 min_visibility
= none_visibility
72 min_visibility
= protected_visibility
75 var gh_upstream
= opt_github_upstream
.value
76 var gh_base_sha
= opt_github_base_sha1
.value
77 var gh_gitdir
= opt_github_gitdir
.value
78 if not gh_upstream
== null or not gh_base_sha
== null or not gh_gitdir
== null then
79 if gh_upstream
== null or gh_base_sha
== null or gh_gitdir
== null then
80 print
"Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin"
87 # The Nitdoc class explores the model and generate pages for each mentities found
90 var mainmodule
: MModule
93 init(ctx
: ToolContext, model
: Model, mainmodule
: MModule) do
96 self.mainmodule
= mainmodule
110 private fun init_output_dir
do
111 # create destination dir if it's necessary
112 var output_dir
= ctx
.output_dir
113 if not output_dir
.file_exists
then output_dir
.mkdir
115 var sharedir
= ctx
.opt_sharedir
.value
116 if sharedir
== null then
117 var dir
= ctx
.nit_dir
119 print
"Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
122 sharedir
= "{dir}/share/nitdoc"
123 if not sharedir
.file_exists
then
124 print
"Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
129 if ctx
.opt_shareurl
.value
== null then
130 sys
.system
("cp -r {sharedir.to_s}/* {output_dir.to_s}/")
132 sys
.system
("cp -r {sharedir.to_s}/resources/ {output_dir.to_s}/resources/")
137 private fun overview
do
138 var page
= new NitdocOverview(ctx
, model
, mainmodule
)
139 page
.render
.write_to_file
("{ctx.output_dir.to_s}/{page.page_url}")
142 private fun search
do
143 var page
= new NitdocSearch(ctx
, model
, mainmodule
)
144 page
.render
.write_to_file
("{ctx.output_dir.to_s}/{page.page_url}")
147 private fun groups
do
148 for mproject
in model
.mprojects
do
149 for mgroup
in mproject
.mgroups
.to_a
do
150 var page
= new NitdocGroup(ctx
, model
, mainmodule
, mgroup
)
151 page
.render
.write_to_file
("{ctx.output_dir.to_s}/{page.page_url}")
156 private fun modules
do
157 for mmodule
in model
.mmodules
do
158 if mmodule
.is_fictive
then continue
159 var page
= new NitdocModule(ctx
, model
, mainmodule
, mmodule
)
160 page
.render
.write_to_file
("{ctx.output_dir.to_s}/{page.page_url}")
164 private fun classes
do
165 for mclass
in model
.mclasses
do
166 var page
= new NitdocClass(ctx
, model
, mainmodule
, mclass
)
167 page
.render
.write_to_file
("{ctx.output_dir.to_s}/{page.page_url}")
171 private fun properties
do
172 for mproperty
in model
.mproperties
do
173 var page
= new NitdocProperty(ctx
, model
, mainmodule
, mproperty
)
174 page
.render
.write_to_file
("{ctx.output_dir.to_s}/{page.page_url}")
178 private fun quicksearch_list
do
179 var quicksearch
= new QuickSearch(ctx
, model
)
180 quicksearch
.render
.write_to_file
("{ctx.output_dir.to_s}/quicksearch-list.js")
184 # Nitdoc QuickSearch list generator
186 # Create a JSON object containing links to:
190 # All entities are grouped by name to make the research easier.
193 private var mmodules
= new HashSet[MModule]
194 private var mclasses
= new HashSet[MClass]
195 private var mpropdefs
= new HashMap[String, Set[MPropDef]]
197 init(ctx
: ToolContext, model
: Model) do
198 for mmodule
in model
.mmodules
do
199 if mmodule
.is_fictive
then continue
202 for mclass
in model
.mclasses
do
203 if mclass
.visibility
< ctx
.min_visibility
then continue
206 for mproperty
in model
.mproperties
do
207 if mproperty
.visibility
< ctx
.min_visibility
then continue
208 if mproperty
isa MAttribute then continue
209 if not mpropdefs
.has_key
(mproperty
.name
) then
210 mpropdefs
[mproperty
.name
] = new HashSet[MPropDef]
212 mpropdefs
[mproperty
.name
].add_all
(mproperty
.mpropdefs
)
216 fun render
: Template do
217 var tpl
= new Template
218 tpl
.add
"var nitdocQuickSearchRawList=\{ "
219 for mmodule
in mmodules
do
220 tpl
.add
"\"{mmodule.name}\
":["
221 tpl
.add
"\{txt:\"{mmodule.full_name}\",url
:\
"{mmodule.nitdoc_url}\"\
},"
224 for mclass in mclasses do
225 var full_name = mclass.intro.mmodule.full_name
226 tpl.add "\
"{mclass.name}\":["
227 tpl.add "\
{txt:\"{full_name}\
",url:\"{mclass.nitdoc_url}\
"\},"
230 for mproperty
, mprops
in mpropdefs
do
231 tpl
.add
"\"{mproperty}\
":["
232 for mpropdef
in mprops
do
233 var full_name
= mpropdef
.mclassdef
.mclass
.full_name
234 tpl
.add
"\{txt:\"{full_name}\",url
:\
"{mpropdef.nitdoc_url}\"\
},"
244 # Define page structure and properties
245 abstract class NitdocPage
247 private var ctx: ToolContext
248 private var model: Model
249 private var mainmodule: MModule
250 private var name_sorter = new MEntityNameSorter
252 init(ctx: ToolContext, model: Model, mainmodule: MModule) do
255 self.mainmodule = mainmodule
258 # Render the page as a html template
259 fun render: Template do
261 if ctx.opt_shareurl.value != null then
262 shareurl = ctx.opt_shareurl.value.as(not null)
267 tpl.title = tpl_title
269 tpl.shareurl = shareurl
270 tpl.topmenu = tpl_topmenu
272 tpl.footer = ctx.opt_custom_footer.value
273 tpl.body_attrs.add(new TagAttribute("data-bootstrap-share
", shareurl))
274 tpl.sidebar = tpl_sidebar
277 var tracker_url = ctx.opt_piwik_tracker.value
278 var site_id = ctx.opt_piwik_site_id.value
279 if tracker_url != null and site_id != null then
280 tpl.scripts.add new TplPiwikScript(tracker_url, site_id)
286 fun page_url: String is abstract
288 # Build page template
289 fun tpl_page: TplPage is abstract
291 # Build page sidebar if any
292 fun tpl_sidebar: nullable TplSidebar do return null
294 # Build page title string
295 fun tpl_title: String do
296 if ctx.opt_custom_title.value != null then
297 return ctx.opt_custom_title.value.to_s
302 # Build top menu template
303 fun tpl_topmenu: TplTopMenu do
304 var topmenu = new TplTopMenu(page_url)
305 var brand = ctx.opt_custom_brand.value
306 if brand != null then
307 var tpl = new Template
308 tpl.add "<span
class='navbar-brand'>"
313 topmenu.add_link new TplLink("index
.html
", "Overview")
314 topmenu.add_link new TplLink("search
.html
", "Index")
318 # Build page content template
319 fun tpl_content is abstract
321 # Clickable graphviz image using dot format
322 # return null if no graph for this page
323 fun tpl_graph(dot: FlatBuffer, name: String, title: nullable String): nullable TplArticle do
324 if ctx.opt_nodot.value then return null
325 var output_dir = ctx.output_dir
326 var file = new OFStream.open("{output_dir}/{name}.dot
")
329 sys.system("\
{ test -f {output_dir}/{name}.png
&& test
-f
{output_dir}/{name}.s
.dot
&& diff
{output_dir}/{name}.dot
{output_dir}/{name}.s
.dot
>/dev
/null 2>&1 ; \
} || \
{ cp {output_dir}/{name}.dot
{output_dir}/{name}.s
.dot
&& dot
-Tpng -o
{output_dir}/{name}.png
-Tcmapx -o
{output_dir}/{name}.map
{output_dir}/{name}.s
.dot
; \
}")
330 var fmap = new IFStream.open("{output_dir}/{name}.map
")
331 var map = fmap.read_all
334 var article = new TplArticle("graph
")
336 if title != null then
337 article.title = title
338 alt = "alt
='{title}'"
340 article.css_classes.add "text-center
"
341 var content = new Template
342 content.add "<img src
='{name}.png' usemap
='#{name}' style
='margin:auto' {alt}/>"
344 article.content = content
348 # A (source) link template for a given location
349 fun tpl_showsource(location: nullable Location): nullable String
351 if location == null then return null
352 var source = ctx.opt_source.value
353 if source == null then return location.file.filename.simplify_path
354 # THIS IS JUST UGLY ! (but there is no replace yet)
355 var x = source.split_with("%f
")
356 source = x.join(location.file.filename.simplify_path)
357 x = source.split_with("%l
")
358 source = x.join(location.line_start.to_s)
359 x = source.split_with("%L
")
360 source = x.join(location.line_end.to_s)
361 source = source.simplify_path
362 return "<a target
='_blank' title
='Show source' href
=\
"{source.to_s}\">View Source</a
>"
365 # MProject description template
366 fun tpl_mproject_article(mproject: MProject): TplArticle do
367 var article = mproject.tpl_article
368 article.subtitle = mproject.tpl_declaration
369 article.content = mproject.tpl_definition
370 if mproject.mdoc != null then
371 article.content = mproject.mdoc.tpl_short_comment
376 # MGroup description template
377 fun tpl_mgroup_article(mgroup: MGroup): TplArticle do
378 var article = mgroup.tpl_article
379 article.subtitle = mgroup.tpl_declaration
380 article.content = mgroup.tpl_definition
384 # MModule description template
385 fun tpl_mmodule_article(mmodule: MModule): TplArticle do
386 var article = mmodule.tpl_article
387 article.subtitle = mmodule.tpl_declaration
388 article.content = mmodule.tpl_definition
390 var intros = mmodule.intro_mclassdefs(ctx.min_visibility).to_a
391 if not intros.is_empty then
392 mainmodule.linearize_mclassdefs(intros)
393 var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}_intros
", "Introduces")
394 var intros_lst = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
395 for mclassdef in intros do
396 intros_lst.add_li mclassdef.tpl_list_item
398 if not intros_lst.is_empty then
399 intros_art.content = intros_lst
400 article.add_child intros_art
403 var redefs = mmodule.redef_mclassdefs(ctx.min_visibility).to_a
404 if not redefs.is_empty then
405 mainmodule.linearize_mclassdefs(redefs)
406 var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}_redefs
", "Redefines")
407 var redefs_lst = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
408 for mclassdef in redefs do
409 redefs_lst.add_li mclassdef.tpl_list_item
411 if not redefs_lst.is_empty then
412 redefs_art.content = redefs_lst
413 article.add_child redefs_art
419 # MClassDef description template
420 fun tpl_mclass_article(mclass: MClass, mclassdefs: Array[MClassDef]): TplArticle do
421 var article = mclass.tpl_article
422 if not mclassdefs.has(mclass.intro) then
424 var intro_article = mclass.intro.tpl_short_article
425 intro_article.source_link = tpl_showsource(mclass.intro.location)
426 article.add_child intro_article
428 mainmodule.linearize_mclassdefs(mclassdefs)
429 for mclassdef in mclassdefs do
430 # add mclassdef full description
431 var redef_article = mclassdef.tpl_article
432 redef_article.source_link = tpl_showsource(mclassdef.location)
433 article.add_child redef_article
435 var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}_intros
", "Introduces")
436 var intros_lst = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
437 for mpropdef in mclassdef.collect_intro_mpropdefs(ctx.min_visibility) do
438 intros_lst.add_li mpropdef.tpl_list_item
440 if not intros_lst.is_empty then
441 intros.content = intros_lst
442 redef_article.add_child intros
444 var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}_redefs
", "Redefines")
445 var redefs_lst = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
446 for mpropdef in mclassdef.collect_redef_mpropdefs(ctx.min_visibility) do
447 redefs_lst.add_li mpropdef.tpl_list_item
449 if not redefs_lst.is_empty then
450 redefs.content = redefs_lst
451 redef_article.add_child redefs
457 # MClassDef description template
458 fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do
459 var article = mclassdef.tpl_article
460 if mclassdef.is_intro then article.content = null
461 article.source_link = tpl_showsource(mclassdef.location)
465 # MProp description template
466 fun tpl_mprop_article(mproperty: MProperty, mpropdefs: Array[MPropDef]): TplArticle do
467 var article = mproperty.tpl_article
468 if not mpropdefs.has(mproperty.intro) then
470 var intro_article = mproperty.intro.tpl_short_article
471 intro_article.source_link = tpl_showsource(mproperty.intro.location)
472 article.add_child intro_article
474 mainmodule.linearize_mpropdefs(mpropdefs)
475 for mpropdef in mpropdefs do
476 # add mpropdef description
477 var redef_article = mpropdef.tpl_article
478 redef_article.source_link = tpl_showsource(mpropdef.location)
479 article.add_child redef_article
484 # MProperty description template
485 fun tpl_mpropdef_article(mpropdef: MPropDef): TplArticle do
486 var article = mpropdef.tpl_article
487 if mpropdef.is_intro then article.content = null
488 article.source_link = tpl_showsource(mpropdef.location)
494 # Display a list of modules contained in program
498 private var page = new TplPage
499 redef fun tpl_page do return page
501 private var sidebar = new TplSidebar
502 redef fun tpl_sidebar do return sidebar
504 redef fun tpl_title do
505 if ctx.opt_custom_title.value != null then
506 return ctx.opt_custom_title.value.to_s
512 redef fun page_url do return "index
.html
"
515 private fun tpl_intro: TplSection do
516 var section = new TplSection.with_title("overview
", tpl_title)
517 var article = new TplArticle("intro
")
518 if ctx.opt_custom_intro.value != null then
519 article.content = ctx.opt_custom_intro.value.to_s
521 section.add_child article
526 private fun tpl_projects(section: TplSection) do
528 var mprojects = model.mprojects.to_a
529 var sorter = new MConcernRankSorter
530 sorter.sort mprojects
531 var ssection = new TplSection.with_title("projects
", "Projects")
532 for mproject in mprojects do
533 ssection.add_child tpl_mproject_article(mproject)
535 section.add_child ssection
538 redef fun tpl_content do
541 tpl_page.add_section top
546 # Display a list of modules, classes and properties
550 private var page = new TplPage
551 redef fun tpl_page do return page
553 redef fun tpl_title do return "Index"
555 redef fun page_url do return "search
.html
"
557 redef fun tpl_content do
558 var tpl = new TplSearchPage("search_all
")
559 var section = new TplSection("search
")
563 for mmodule in modules_list do
564 tpl.modules.add mmodule.tpl_link
567 for mclass in classes_list do
568 tpl.classes.add mclass.tpl_link
571 for mproperty in mprops_list do
573 m.add mproperty.intro.tpl_link
575 m.add mproperty.intro.mclassdef.mclass.tpl_link
579 section.add_child tpl
580 tpl_page.add_section section
583 # Extract mmodule list to display (sorted by name)
584 private fun modules_list: Array[MModule] do
585 var sorted = new Array[MModule]
586 for mmodule in model.mmodule_importation_hierarchy do
587 if mmodule.is_fictive then continue
590 name_sorter.sort(sorted)
594 # Extract mclass list to display (sorted by name)
595 private fun classes_list: Array[MClass] do
596 var sorted = new Array[MClass]
597 for mclass in model.mclasses do
598 if mclass.visibility < ctx.min_visibility then continue
601 name_sorter.sort(sorted)
605 # Extract mproperty list to display (sorted by name)
606 private fun mprops_list: Array[MProperty] do
607 var sorted = new Array[MProperty]
608 for mproperty in model.mproperties do
609 if mproperty.visibility < ctx.min_visibility then continue
610 if mproperty isa MAttribute then continue
613 name_sorter.sort(sorted)
619 # Display a flattened view of the group
623 private var mgroup: MGroup
625 private var concerns: ConcernsTree
626 private var intros: Set[MClass]
627 private var redefs: Set[MClass]
629 init(ctx: ToolContext, model: Model, mainmodule: MModule, mgroup: MGroup) do
632 self.concerns = model.concerns_tree(mgroup.collect_mmodules)
633 self.concerns.sort_with(new MConcernRankSorter)
634 self.intros = mgroup.in_nesting_intro_mclasses(ctx.min_visibility)
635 var redefs = new HashSet[MClass]
636 for rdef in mgroup.in_nesting_redef_mclasses(ctx.min_visibility) do
637 if intros.has(rdef) then continue
643 private var page = new TplPage
644 redef fun tpl_page do return page
646 private var sidebar = new TplSidebar
647 redef fun tpl_sidebar do return sidebar
649 redef fun tpl_title do return mgroup.nitdoc_name
651 redef fun page_url do return mgroup.nitdoc_url
653 redef fun tpl_topmenu do
655 var mproject = mgroup.mproject
656 if not mgroup.is_root then
657 topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
659 topmenu.add_link new TplLink(page_url, mproject.nitdoc_name)
663 # Class list to display in sidebar
664 fun tpl_sidebar_mclasses do
665 var mclasses = new HashSet[MClass]
666 mclasses.add_all intros
667 mclasses.add_all redefs
668 if mclasses.is_empty then return
669 var list = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
671 var sorted = mclasses.to_a
672 name_sorter.sort(sorted)
673 for mclass in sorted do
674 list.add_li tpl_sidebar_item(mclass)
676 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes
", list)
679 private fun tpl_sidebar_item(def: MClass): TplListItem do
680 var classes = def.intro.tpl_css_classes.to_a
681 if intros.has(def) then
686 var lnk = new Template
687 lnk.add new TplLabel.with_classes(classes)
689 return new TplListItem.with_content(lnk)
693 private fun tpl_intro: TplSection do
694 var section = new TplSection.with_title("top
", tpl_title)
695 var article = new TplArticle("intro
")
697 if mgroup.is_root then
698 section.subtitle = mgroup.mproject.tpl_declaration
699 article.content = mgroup.mproject.tpl_definition
701 section.subtitle = mgroup.tpl_declaration
702 article.content = mgroup.tpl_definition
704 section.add_child article
708 private fun tpl_concerns(section: TplSection) do
709 if concerns.is_empty then return
710 section.add_child new TplArticle.with_content("concerns
", "Concerns", concerns.to_tpl)
713 private fun tpl_groups(parent: TplSection) do
714 var lst = concerns.to_a
716 for mentity in lst do
717 if mentity isa MProject then
718 section.add_child new TplSection(mentity.nitdoc_id)
719 else if mentity isa MGroup then
720 section.add_child new TplSection(mentity.nitdoc_id)
721 else if mentity isa MModule then
722 section.add_child tpl_mmodule_article(mentity)
727 redef fun tpl_content do
732 tpl_page.add_section top
735 private fun sort_by_mclass(mclassdefs: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
736 var map = new HashMap[MClass, Set[MClassDef]]
737 for mclassdef in mclassdefs do
738 var mclass = mclassdef.mclass
739 if not map.has_key(mclass) then map[mclass] = new HashSet[MClassDef]
740 map[mclass].add mclassdef
747 # Display the list of introduced and redefined classes in module
751 private var mmodule: MModule
752 private var concerns: ConcernsTree
753 private var mclasses2mdefs: Map[MClass, Set[MClassDef]]
754 private var mmodules2mclasses: Map[MModule, Set[MClass]]
757 init(ctx: ToolContext, model: Model, mainmodule: MModule, mmodule: MModule) do
759 self.mmodule = mmodule
760 var mclassdefs = new HashSet[MClassDef]
761 mclassdefs.add_all mmodule.intro_mclassdefs(ctx.min_visibility)
762 mclassdefs.add_all mmodule.redef_mclassdefs(ctx.min_visibility)
763 self.mclasses2mdefs = sort_by_mclass(mclassdefs)
764 self.mmodules2mclasses = group_by_mmodule(mclasses2mdefs.keys)
765 self.concerns = model.concerns_tree(mmodules2mclasses.keys)
767 mmodule.mgroup.mproject.booster_rank = -1000
768 mmodule.mgroup.booster_rank = -1000
769 mmodule.booster_rank = -1000
770 self.concerns.sort_with(new MConcernRankSorter)
771 mmodule.mgroup.mproject.booster_rank = 0
772 mmodule.mgroup.booster_rank = 0
773 mmodule.booster_rank = 0
776 private var page = new TplPage
777 redef fun tpl_page do return page
779 private var sidebar = new TplSidebar
780 redef fun tpl_sidebar do return sidebar
782 redef fun tpl_title do return mmodule.nitdoc_name
783 redef fun page_url do return mmodule.nitdoc_url
785 redef fun tpl_topmenu do
787 var mproject = mmodule.mgroup.mproject
788 topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
789 topmenu.add_link new TplLink(page_url, mmodule.nitdoc_name)
793 # Class list to display in sidebar
794 fun tpl_sidebar_mclasses do
795 var mclasses = new HashSet[MClass]
796 mclasses.add_all mmodule.filter_intro_mclasses(ctx.min_visibility)
797 mclasses.add_all mmodule.filter_redef_mclasses(ctx.min_visibility)
798 if mclasses.is_empty then return
799 var list = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
801 var sorted = mclasses.to_a
802 name_sorter.sort(sorted)
803 for mclass in sorted do
804 list.add_li tpl_sidebar_item(mclass)
806 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes
", list)
809 private fun tpl_sidebar_item(def: MClass): TplListItem do
810 var classes = def.intro.tpl_css_classes.to_a
811 if def.intro_mmodule == mmodule then
816 var lnk = new Template
817 lnk.add new TplLabel.with_classes(classes)
819 return new TplListItem.with_content(lnk)
823 private fun tpl_intro: TplSection do
824 var section = new TplSection.with_title("top
", tpl_title)
825 section.subtitle = mmodule.tpl_declaration
827 var article = new TplArticle("intro
")
828 var def = mmodule.tpl_definition
829 var location = mmodule.location
830 article.source_link = tpl_showsource(location)
831 article.content = def
832 section.add_child article
836 # inheritance section
837 private fun tpl_inheritance(parent: TplSection) do
838 # Extract relevent modules
839 var imports = mmodule.in_importation.greaters
840 if imports.length > 10 then imports = mmodule.in_importation.direct_greaters
841 var clients = mmodule.in_importation.smallers
842 if clients.length > 10 then clients = mmodule.in_importation.direct_smallers
845 var section = new TplSection.with_title("dependencies
", "Dependencies")
848 var mmodules = new HashSet[MModule]
849 mmodules.add_all mmodule.in_nesting.direct_greaters
850 mmodules.add_all imports
851 if clients.length < 10 then mmodules.add_all clients
853 var graph = tpl_dot(mmodules)
854 if graph != null then section.add_child graph
857 var lst = new Array[MModule]
858 for dep in imports do
859 if dep.is_fictive then continue
860 if dep == mmodule then continue
863 if not lst.is_empty then
865 section.add_child tpl_list("imports
", "Imports", lst)
869 lst = new Array[MModule]
870 for dep in clients do
871 if dep.is_fictive then continue
872 if dep == mmodule then continue
875 if not lst.is_empty then
877 section.add_child tpl_list("clients
", "Clients", lst)
880 parent.add_child section
883 private fun tpl_list(id: String, title: String, mmodules: Array[MModule]): TplArticle do
884 var article = new TplArticle.with_title(id, title)
885 var list = new TplList.with_classes(["list-unstyled
", "list-definition
"])
886 for mmodule in mmodules do list.elts.add mmodule.tpl_list_item
887 article.content = list
891 private fun tpl_concerns(parent: TplSection) do
892 if concerns.is_empty then return
893 parent.add_child new TplArticle.with_content("concerns
", "Concerns", concerns.to_tpl)
896 private fun tpl_mclasses(parent: TplSection) do
897 for mentity in concerns do
898 if mentity isa MProject then
899 parent.add_child new TplSection(mentity.nitdoc_id)
900 else if mentity isa MGroup then
901 parent.add_child new TplSection(mentity.nitdoc_id)
902 else if mentity isa MModule then
903 var section = new TplSection(mentity.nitdoc_id)
904 var title = new Template
905 if mentity == mmodule then
907 section.summary_title = "in {mentity.nitdoc_name}"
910 section.summary_title = "from
{mentity.nitdoc_name}"
912 title.add mentity.tpl_namespace
913 section.title = title
915 var mclasses = mmodules2mclasses[mentity].to_a
916 name_sorter.sort(mclasses)
917 for mclass in mclasses do
918 section.add_child tpl_mclass_article(mclass, mclasses2mdefs[mclass].to_a)
920 parent.add_child section
925 private fun group_by_mmodule(mclasses: Collection[MClass]): Map[MModule, Set[MClass]] do
926 var res = new HashMap[MModule, Set[MClass]]
927 for mclass in mclasses do
928 var mmodule = mclass.intro_mmodule
929 if not res.has_key(mmodule) then
930 res[mmodule] = new HashSet[MClass]
932 res[mmodule].add(mclass)
937 redef fun tpl_content do
943 tpl_page.add_section top
946 # Genrate dot hierarchy for class inheritance
947 fun tpl_dot(mmodules: Collection[MModule]): nullable TplArticle do
948 var poset = new POSet[MModule]
949 for mmodule in mmodules do
950 if mmodule.is_fictive then continue
951 poset.add_node mmodule
952 for omodule in mmodules do
953 if mmodule.is_fictive then continue
954 poset.add_node mmodule
955 if mmodule.in_importation < omodule then
956 poset.add_edge(mmodule, omodule)
961 var op = new FlatBuffer
962 var name = "dep_
{mmodule.name}"
963 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
")
964 for mmodule in poset do
965 if mmodule == self.mmodule then
966 op.append("\
"{mmodule.name}\"[shape
=box
,margin
=0.03];\n
")
968 op.append("\
"{mmodule.name}\"[URL=\
"{mmodule.nitdoc_url}\"];\n
")
970 for omodule in poset[mmodule].direct_greaters do
971 op.append("\
"{mmodule.name}\"->\
"{omodule.name}\";\n
")
975 return tpl_graph(op, name, null)
978 private fun sort_by_mclass(mclassdefs: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
979 var map = new HashMap[MClass, Set[MClassDef]]
980 for mclassdef in mclassdefs do
981 var mclass = mclassdef.mclass
982 if not map.has_key(mclass) then map[mclass] = new HashSet[MClassDef]
983 map[mclass].add mclassdef
990 # Display a list properties defined or redefined for this class
994 private var mclass: MClass
995 private var concerns: ConcernsTree
996 private var mprops2mdefs: Map[MProperty, Set[MPropDef]]
997 private var mmodules2mprops: Map[MModule, Set[MProperty]]
999 init(ctx: ToolContext, model: Model, mainmodule: MModule, mclass: MClass) do
1001 self.mclass = mclass
1002 var mpropdefs = new HashSet[MPropDef]
1003 mpropdefs.add_all mclass.intro_mpropdefs(ctx.min_visibility)
1004 mpropdefs.add_all mclass.redef_mpropdefs(ctx.min_visibility)
1005 self.mprops2mdefs = sort_by_mproperty(mpropdefs)
1006 self.mmodules2mprops = sort_by_mmodule(mprops2mdefs.keys)
1007 self.concerns = model.concerns_tree(mmodules2mprops.keys)
1008 self.concerns.sort_with(new MConcernRankSorter)
1011 private var page = new TplPage
1012 redef fun tpl_page do return page
1014 private var sidebar = new TplSidebar
1015 redef fun tpl_sidebar do return sidebar
1017 redef fun tpl_title do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
1018 redef fun page_url do return mclass.nitdoc_url
1020 redef fun tpl_topmenu do
1022 var mproject = mclass.intro_mmodule.mgroup.mproject
1023 topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
1024 topmenu.add_link new TplLink(page_url, mclass.nitdoc_name)
1028 # Property list to display in sidebar
1029 fun tpl_sidebar_properties do
1030 var kind_map = sort_by_kind(mclass_inherited_mprops)
1031 var summary = new TplList.with_classes(["list-unstyled
"])
1033 tpl_sidebar_list("Virtual types
", kind_map["type"].to_a, summary)
1034 tpl_sidebar_list("Constructors", kind_map["init"].to_a, summary)
1035 tpl_sidebar_list("Methods", kind_map["fun"].to_a, summary)
1036 tpl_sidebar.boxes.add new TplSideBox.with_content("All properties
", summary)
1039 private fun tpl_sidebar_list(name: String, mprops: Array[MProperty], summary: TplList) do
1040 if mprops.is_empty then return
1041 name_sorter.sort(mprops)
1042 var entry = new TplListItem.with_content(name)
1043 var list = new TplList.with_classes(["list-unstyled
", "list-labeled
"])
1044 for mprop in mprops do
1045 list.add_li tpl_sidebar_item(mprop)
1048 summary.elts.add entry
1051 private fun tpl_sidebar_item(mprop: MProperty): TplListItem do
1052 var classes = mprop.intro.tpl_css_classes.to_a
1053 if not mprops2mdefs.has_key(mprop) then
1054 classes.add "inherit
"
1055 var lnk = new Template
1056 lnk.add new TplLabel.with_classes(classes)
1057 lnk.add mprop.intro.tpl_link
1058 return new TplListItem.with_content(lnk)
1060 var defs = mprops2mdefs[mprop]
1061 if defs.has(mprop.intro) then
1066 var lnk = new Template
1067 lnk.add new TplLabel.with_classes(classes)
1068 lnk.add mprop.intro.tpl_anchor
1069 return new TplListItem.with_content(lnk)
1072 private fun tpl_intro: TplSection do
1073 var section = new TplSection.with_title("top
", tpl_title)
1074 section.subtitle = mclass.intro.tpl_declaration
1075 var article = new TplArticle("comment
")
1076 if mclass.mdoc != null then
1077 article.content = mclass.mdoc.tpl_comment
1079 section.add_child article
1083 private fun tpl_concerns(parent: TplSection) do
1085 var section = new TplSection.with_title("intro
", "Introduction")
1086 section.summary_title = "Introduction"
1087 section.add_child tpl_mclassdef_article(mclass.intro)
1088 parent.add_child section
1090 if concerns.is_empty then return
1091 parent.add_child new TplArticle.with_content("concerns
", "Concerns", concerns.to_tpl)
1094 private fun tpl_inheritance(parent: TplSection) do
1096 var hparents = new HashSet[MClass]
1097 for c in mclass.in_hierarchy(mainmodule).direct_greaters do
1098 if c.visibility < ctx.min_visibility then continue
1103 var hancestors = new HashSet[MClass]
1104 for c in mclass.in_hierarchy(mainmodule).greaters do
1105 if c == mclass then continue
1106 if c.visibility < ctx.min_visibility then continue
1107 if hparents.has(c) then continue
1112 var hchildren = new HashSet[MClass]
1113 for c in mclass.in_hierarchy(mainmodule).direct_smallers do
1114 if c.visibility < ctx.min_visibility then continue
1119 var hdescendants = new HashSet[MClass]
1120 for c in mclass.in_hierarchy(mainmodule).smallers do
1121 if c == mclass then continue
1122 if c.visibility < ctx.min_visibility then continue
1123 if hchildren.has(c) then continue
1128 var section = new TplSection.with_title("inheritance
", "Inheritance")
1131 var mclasses = new HashSet[MClass]
1132 mclasses.add_all hancestors
1133 mclasses.add_all hparents
1134 if hchildren.length < 10 then mclasses.add_all hchildren
1135 if hdescendants.length < 10 then mclasses.add_all hdescendants
1137 var graph = tpl_dot(mclasses)
1138 if graph != null then section.add_child graph
1141 if not hparents.is_empty then
1142 var lst = hparents.to_a
1143 name_sorter.sort lst
1144 section.add_child tpl_list("parents
", "Parents", lst)
1148 if not hancestors.is_empty then
1149 var lst = hancestors.to_a
1150 name_sorter.sort lst
1151 section.add_child tpl_list("ancestors
", "Ancestors", lst)
1155 if not hchildren.is_empty and hchildren.length < 15 then
1156 var lst = hchildren.to_a
1157 name_sorter.sort lst
1158 section.add_child tpl_list("children
", "Children", lst)
1162 if not hdescendants.is_empty and hchildren.length < 15 then
1163 var lst = hdescendants.to_a
1164 name_sorter.sort lst
1165 section.add_child tpl_list("descendants
", "Descendants", lst)
1168 parent.add_child section
1171 private fun tpl_list(id: String, title: String, elts: Array[MClass]): TplArticle do
1172 var article = new TplArticle.with_title(id, title)
1173 var list = new TplList.with_classes(["list-unstyled
", "list-definition
"])
1174 for elt in elts do list.elts.add elt.tpl_list_item
1175 article.content = list
1179 private fun tpl_properties(parent: TplSection) do
1180 var lst = concerns.to_a
1181 for mentity in lst do
1182 if mentity isa MProject then
1183 parent.add_child new TplSection(mentity.nitdoc_id)
1184 else if mentity isa MGroup then
1185 parent.add_child new TplSection(mentity.nitdoc_id)
1186 else if mentity isa MModule then
1187 var section = new TplSection(mentity.nitdoc_id)
1188 var title = new Template
1190 title.add mentity.tpl_namespace
1191 section.title = title
1192 section.summary_title = "in {mentity.nitdoc_name}"
1195 var mprops = mmodules2mprops[mentity]
1196 var kind_map = sort_by_kind(mprops)
1199 var elts = kind_map["type"].to_a
1200 name_sorter.sort(elts)
1202 var defs = mprops2mdefs[elt].to_a
1203 section.add_child tpl_mprop_article(elt, defs)
1207 elts = kind_map["init"].to_a
1208 name_sorter.sort(elts)
1210 var defs = mprops2mdefs[elt].to_a
1211 section.add_child tpl_mprop_article(elt, defs)
1215 elts = kind_map["fun"].to_a
1216 name_sorter.sort(elts)
1218 var defs = mprops2mdefs[elt].to_a
1219 section.add_child tpl_mprop_article(elt, defs)
1221 parent.add_child section
1226 redef fun tpl_content do
1227 tpl_sidebar_properties
1229 tpl_inheritance(top)
1232 tpl_page.add_section top
1235 private fun sort_by_mproperty(mpropdefs: Collection[MPropDef]): Map[MProperty, Set[MPropDef]] do
1236 var map = new HashMap[MProperty, Set[MPropDef]]
1237 for mpropdef in mpropdefs do
1238 var mproperty = mpropdef.mproperty
1239 if not map.has_key(mproperty) then map[mproperty] = new HashSet[MPropDef]
1240 map[mproperty].add mpropdef
1245 private fun sort_by_mmodule(mprops: Collection[MProperty]): Map[MModule, Set[MProperty]] do
1246 var map = new HashMap[MModule, Set[MProperty]]
1247 for mprop in mprops do
1248 var mpropdefs = mprops2mdefs[mprop].to_a
1249 mainmodule.linearize_mpropdefs(mpropdefs)
1250 var mmodule = mpropdefs.first.mclassdef.mmodule
1251 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MProperty]
1252 map[mmodule].add mprop
1257 private fun sort_by_kind(mprops: Collection[MProperty]): Map[String, Set[MProperty]] do
1258 var map = new HashMap[String, Set[MProperty]]
1259 map["type"] = new HashSet[MProperty]
1260 map["init"] = new HashSet[MProperty]
1261 map["fun"] = new HashSet[MProperty]
1262 for mprop in mprops do
1263 if mprop isa MVirtualTypeProp then
1264 map["type"].add mprop
1265 else if mprop isa MMethod then
1266 if mprop.is_init then
1267 map["init"].add mprop
1269 map["fun"].add mprop
1276 private fun mclass_inherited_mprops: Set[MProperty] do
1277 var res = new HashSet[MProperty]
1278 var local = mclass.local_mproperties(ctx.min_visibility)
1279 for mprop in mclass.inherited_mproperties(mainmodule, ctx.min_visibility) do
1280 if local.has(mprop) then continue
1281 #if mprop isa MMethod and mprop.is_init then continue
1282 if mprop.intro.mclassdef.mclass.name == "Object" and
1283 (mprop.visibility == protected_visibility or
1284 mprop.intro.mclassdef.mmodule.name != "kernel
") then continue
1291 private fun collect_mmodules(mprops: Collection[MProperty]): Set[MModule] do
1292 var res = new HashSet[MModule]
1293 for mprop in mprops do
1294 if mprops2mdefs.has_key(mprop) then
1295 for mpropdef in mprops2mdefs[mprop] do res.add mpropdef.mclassdef.mmodule
1301 # Generate dot hierarchy for classes
1302 fun tpl_dot(mclasses: Collection[MClass]): nullable TplArticle do
1303 var poset = new POSet[MClass]
1305 for mclass in mclasses do
1306 poset.add_node mclass
1307 for oclass in mclasses do
1308 poset.add_node oclass
1309 if mclass.in_hierarchy(mainmodule) < oclass then
1310 poset.add_edge(mclass, oclass)
1315 var op = new FlatBuffer
1316 var name = "dep_
{mclass.name}"
1317 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
")
1320 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
1322 op.append("\
"{c.name}\"[URL=\
"{c.nitdoc_url}\"];\n
")
1324 for c2 in poset[c].direct_greaters do
1325 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
1329 return tpl_graph(op, name, null)
1334 class NitdocProperty
1337 private var mproperty: MProperty
1338 private var concerns: ConcernsTree
1339 private var mmodules2mdefs: Map[MModule, Set[MPropDef]]
1341 init(ctx: ToolContext, model: Model, mainmodule: MModule, mproperty: MProperty) do
1343 self.mproperty = mproperty
1344 self.mmodules2mdefs = sort_by_mmodule(collect_mpropdefs)
1345 self.concerns = model.concerns_tree(mmodules2mdefs.keys)
1346 self.concerns.sort_with(new MConcernRankSorter)
1349 private fun collect_mpropdefs: Set[MPropDef] do
1350 var res = new HashSet[MPropDef]
1351 for mpropdef in mproperty.mpropdefs do
1352 if not mpropdef.is_intro then res.add mpropdef
1357 private var page = new TplPage
1358 redef fun tpl_page do return page
1360 private var sidebar = new TplSidebar
1361 redef fun tpl_sidebar do return sidebar
1363 redef fun tpl_title do
1364 return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}"
1367 redef fun page_url do return mproperty.nitdoc_url
1369 redef fun tpl_topmenu do
1371 var mmodule = mproperty.intro_mclassdef.mmodule
1372 var mproject = mmodule.mgroup.mproject
1373 var mclass = mproperty.intro_mclassdef.mclass
1374 topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
1375 topmenu.add_link new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}")
1376 topmenu.add_link new TplLink(page_url, mproperty.nitdoc_name)
1380 private fun tpl_intro: TplSection do
1381 var section = new TplSection.with_title("top
", tpl_title)
1382 section.subtitle = mproperty.tpl_declaration
1383 var article = new TplArticle("comment
")
1384 if mproperty.intro.mdoc != null then
1385 article.content = mproperty.intro.mdoc.tpl_comment
1387 section.add_child article
1391 private fun tpl_properties(parent: TplSection) do
1393 var section = new TplSection.with_title("intro
", "Introduction")
1394 section.summary_title = "Introduction"
1395 section.add_child tpl_mpropdef_article(mproperty.intro)
1396 parent.add_child section
1399 if concerns.is_empty then return
1400 parent.add_child new TplArticle.with_content("Concerns", "Concerns", concerns.to_tpl)
1403 var lst = concerns.to_a
1404 for mentity in lst do
1405 if mentity isa MProject then
1406 parent.add_child new TplSection(mentity.nitdoc_id)
1407 else if mentity isa MGroup then
1408 parent.add_child new TplSection(mentity.nitdoc_id)
1409 else if mentity isa MModule then
1410 var ssection = new TplSection(mentity.nitdoc_id)
1411 var title = new Template
1413 title.add mentity.tpl_namespace
1414 ssection.title = title
1415 ssection.summary_title = "in {mentity.nitdoc_name}"
1418 var mpropdefs = mmodules2mdefs[mentity].to_a
1419 name_sorter.sort(mpropdefs)
1420 for mpropdef in mpropdefs do
1421 ssection.add_child tpl_mpropdef_article(mpropdef)
1423 parent.add_child ssection
1428 redef fun tpl_content do
1431 tpl_page.add_section top
1434 private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do
1435 var map = new HashMap[MModule, Set[MPropDef]]
1436 for mpropdef in mpropdefs do
1437 var mmodule = mpropdef.mclassdef.mmodule
1438 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef]
1439 map[mmodule].add mpropdef