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
20 # The NitdocContext contains all the knowledge used for doc generation
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 toolcontext
= new ToolContext
42 private var mbuilder
: ModelBuilder
43 private var mainmodule
: MModule
44 private var output_dir
: String
45 private var min_visibility
: MVisibility
48 var opts
= toolcontext
.option_context
49 opts
.add_option
(opt_dir
, opt_source
, opt_sharedir
, opt_shareurl
, opt_nodot
, opt_private
)
50 opts
.add_option
(opt_custom_title
, opt_custom_footer
, opt_custom_intro
, opt_custom_brand
)
51 opts
.add_option
(opt_github_upstream
, opt_github_base_sha1
, opt_github_gitdir
)
52 opts
.add_option
(opt_piwik_tracker
, opt_piwik_site_id
)
54 var tpl
= new Template
55 tpl
.add
"Usage: nitdoc [OPTION]... <file.nit>...\n"
56 tpl
.add
"Generates HTML pages of API documentation from Nit source files."
57 toolcontext
.tooldescription
= tpl
.write_to_string
58 toolcontext
.process_options
(args
)
61 self.parse
(toolcontext
.option_context
.rest
)
64 private fun process_options
do
65 if opt_private
.value
then
66 min_visibility
= none_visibility
68 min_visibility
= protected_visibility
70 var gh_upstream
= opt_github_upstream
.value
71 var gh_base_sha
= opt_github_base_sha1
.value
72 var gh_gitdir
= opt_github_gitdir
.value
73 if not gh_upstream
== null or not gh_base_sha
== null or not gh_gitdir
== null then
74 if gh_upstream
== null or gh_base_sha
== null or gh_gitdir
== null then
75 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"
81 private fun parse
(arguments
: Array[String]) do
83 mbuilder
= new ModelBuilder(model
, toolcontext
)
84 var mmodules
= mbuilder
.parse
(arguments
)
85 if mmodules
.is_empty
then return
87 if mmodules
.length
== 1 then
88 mainmodule
= mmodules
.first
90 mainmodule
= new MModule(model
, null, "<main>", new Location(null, 0, 0, 0, 0))
91 mainmodule
.is_fictive
= true
92 mainmodule
.set_imported_mmodules
(mmodules
)
96 fun generate_nitdoc
do
105 private fun init_output_dir
do
106 # location output dir
107 var output_dir
= opt_dir
.value
108 if output_dir
== null then
111 self.output_dir
= output_dir
112 # create destination dir if it's necessary
113 if not output_dir
.file_exists
then output_dir
.mkdir
115 var sharedir
= opt_sharedir
.value
116 if sharedir
== null then
117 var dir
= toolcontext
.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 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 overviewpage
= new NitdocOverview(self)
139 overviewpage
.render
.write_to_file
("{output_dir.to_s}/index.html")
142 private fun search
do
143 var searchpage
= new NitdocSearch(self)
144 searchpage
.render
.write_to_file
("{output_dir.to_s}/search.html")
147 private fun modules
do
148 for mmodule
in mbuilder
.model
.mmodules
do
149 if mmodule
.name
== "<main>" then continue
150 var modulepage
= new NitdocModule(mmodule
, self)
151 modulepage
.render
.write_to_file
("{output_dir.to_s}/{mmodule.nitdoc_url}")
155 private fun classes
do
156 for mclass
in mbuilder
.model
.mclasses
do
157 var classpage
= new NitdocClass(mclass
, self)
158 classpage
.render
.write_to_file
("{output_dir.to_s}/{mclass.nitdoc_url}")
162 private fun quicksearch_list
do
163 var quicksearch
= new QuickSearch(self)
164 quicksearch
.render
.write_to_file
("{output_dir.to_s}/quicksearch-list.js")
168 # Nitdoc QuickSearch list generator
170 # Create a JSON object containing links to:
174 # All entities are grouped by name to make the research easier.
177 private var mmodules
= new HashSet[MModule]
178 private var mclasses
= new HashSet[MClass]
179 private var mpropdefs
= new HashMap[String, Set[MPropDef]]
181 init(ctx
: NitdocContext) do
182 for mmodule
in ctx
.mbuilder
.model
.mmodules
do
183 if mmodule
.name
== "<main>" then continue
186 for mclass
in ctx
.mbuilder
.model
.mclasses
do
187 if mclass
.visibility
< ctx
.min_visibility
then continue
190 for mproperty
in ctx
.mbuilder
.model
.mproperties
do
191 if mproperty
.visibility
< ctx
.min_visibility
then continue
192 if mproperty
isa MAttribute then continue
193 if not mpropdefs
.has_key
(mproperty
.name
) then
194 mpropdefs
[mproperty
.name
] = new HashSet[MPropDef]
196 mpropdefs
[mproperty
.name
].add_all
(mproperty
.mpropdefs
)
200 fun render
: Template do
201 var tpl
= new Template
202 tpl
.add
"var nitdocQuickSearchRawList=\{ "
203 for mmodule
in mmodules
do
204 tpl
.add
"\"{mmodule.name}\
":["
205 tpl
.add
"\{txt:\"{mmodule.full_name}\",url
:\
"{mmodule.nitdoc_url}\"\
},"
208 for mclass in mclasses do
209 var full_name = mclass.intro.mmodule.full_name
210 tpl.add "\
"{mclass.name}\":["
211 tpl.add "\
{txt:\"{full_name}\
",url:\"{mclass.nitdoc_url}\
"\},"
214 for mproperty
, mprops
in mpropdefs
do
215 tpl
.add
"\"{mproperty}\
":["
216 for mpropdef
in mprops
do
217 var full_name
= mpropdef
.mclassdef
.mclass
.full_name
218 tpl
.add
"\{txt:\"{full_name}\",url
:\
"{mpropdef.nitdoc_url}\"\
},"
228 # Define page structure and properties
229 abstract class NitdocPage
231 private var ctx: NitdocContext
232 private var model: Model
233 private var name_sorter = new MEntityNameSorter
235 init(ctx: NitdocContext) do
237 self.model = ctx.mbuilder.model
240 # Render the page as a html template
241 fun render: Template do
243 if ctx.opt_shareurl.value != null then
244 shareurl = ctx.opt_shareurl.value.as(not null)
249 tpl.title = tpl_title
250 tpl.shareurl = shareurl
251 tpl.topmenu = tpl_topmenu
253 tpl.footer = ctx.opt_custom_footer.value
254 tpl.body_attrs.add(new TagAttribute("data-bootstrap-share
", shareurl))
255 tpl.sidebar = tpl_sidebar
258 var tracker_url = ctx.opt_piwik_tracker.value
259 var site_id = ctx.opt_piwik_site_id.value
260 if tracker_url != null and site_id != null then
261 tpl.scripts.add new TplPiwikScript(tracker_url, site_id)
266 # Build page template
267 fun tpl_page: TplPage is abstract
269 # Build page sidebar if any
270 fun tpl_sidebar: nullable TplSidebar do return null
272 # Build page title string
273 fun tpl_title: String do
274 if ctx.opt_custom_title.value != null then
275 return ctx.opt_custom_title.value.to_s
280 # Build top menu template
281 fun tpl_topmenu: TplTopMenu do
282 var topmenu = new TplTopMenu
283 var brand = ctx.opt_custom_brand.value
284 if brand != null then
285 var tpl = new Template
286 tpl.add "<span
class='navbar-brand'>"
294 # Build page content template
295 fun tpl_content is abstract
297 # Clickable graphviz image using dot format
298 # return null if no graph for this page
299 fun tpl_graph(dot: FlatBuffer, name: String, title: String): nullable TplArticle do
300 if ctx.opt_nodot.value then return null
301 var output_dir = ctx.output_dir
302 var file = new OFStream.open("{output_dir}/{name}.dot
")
305 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
; \
}")
306 var fmap = new IFStream.open("{output_dir}/{name}.map
")
307 var map = fmap.read_all
310 var article = new TplArticle.with_title("graph
", title)
311 var content = new Template
312 content.add "<img src
='{name}.png' usemap
='#{name}' style
='margin:auto' alt
='{title}'/>"
314 article.content = content
318 # A (source) link template for a given location
319 fun tpl_showsource(location: nullable Location): nullable String
321 if location == null then return null
322 var source = ctx.opt_source.value
323 if source == null then return "({location.file.filename.simplify_path})"
324 # THIS IS JUST UGLY ! (but there is no replace yet)
325 var x = source.split_with("%f
")
326 source = x.join(location.file.filename.simplify_path)
327 x = source.split_with("%l
")
328 source = x.join(location.line_start.to_s)
329 x = source.split_with("%L
")
330 source = x.join(location.line_end.to_s)
331 source = source.simplify_path
332 return " (<a target
='_blank' title
='Show source' href
=\
"{source.to_s}\">source
</a
>)"
335 # MClassDef description template
336 fun tpl_mclass_article(mclass: MClass, mclassdefs: Array[MClassDef]): TplArticle do
337 var article = new TplArticle(mclass.nitdoc_id)
338 var title = new Template
339 var icon = new TplIcon.with_icon("tag
")
340 icon.css_classes.add_all(mclass.intro.tpl_css_classes)
342 title.add mclass.tpl_link
343 title.add mclass.intro.tpl_signature
344 article.title = title
345 article.title_classes.add "signature
"
346 article.subtitle = mclass.tpl_declaration
347 article.summary_title = "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
348 #article.subtitle = new Template
349 #article.subtitle.add mprop.intro.tpl_modifiers
350 #article.subtitle.add mprop.intro.tpl_namespace
351 var content = new Template
353 if not mclassdefs.has(mclass.intro) then
355 var intro = mclass.intro
356 var location = intro.location
357 var sourcelink = tpl_showsource(location)
358 var intro_def = intro.tpl_definition
359 intro_def.location = sourcelink
360 content.add intro_def
362 ctx.mainmodule.linearize_mclassdefs(mclassdefs)
363 for mclassdef in mclassdefs do
364 # add mclassdef full description
365 var location = mclassdef.location
366 var sourcelink = tpl_showsource(location)
367 var prop_def = mclassdef.tpl_definition.as(TplClassDefinition)
368 prop_def.location = sourcelink
369 for mpropdef in mclassdef.mpropdefs do
370 var intro = mpropdef.mproperty.intro
371 if mpropdef isa MAttributeDef then continue
372 if mpropdef.mproperty.visibility < ctx.min_visibility then continue
374 var lnk = new Template
375 lnk.add new TplLabel.with_classes(mpropdef.tpl_css_classes.to_a)
376 lnk.add mpropdef.tpl_link
377 if intro.mdoc != null then
379 lnk.add intro.mdoc.short_comment
381 if mpropdef.is_intro then
382 prop_def.intros.add new TplListItem.with_content(lnk)
384 prop_def.redefs.add new TplListItem.with_content(lnk)
392 # MClassDef description template
393 fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do
394 var article = mclassdef.tpl_article
395 if mclassdef.is_intro then article.content = null
396 article.source_link = tpl_showsource(mclassdef.location)
400 # MProp description template
401 fun tpl_mprop_article(mproperty: MProperty, mpropdefs: Array[MPropDef]): TplArticle do
402 var article = mproperty.tpl_article
403 if not mpropdefs.has(mproperty.intro) then
405 var intro_article = mproperty.intro.tpl_short_article
406 intro_article.source_link = tpl_showsource(mproperty.intro.location)
407 article.add_child intro_article
409 ctx.mainmodule.linearize_mpropdefs(mpropdefs)
410 for mpropdef in mpropdefs do
411 # add mpropdef description
412 var redef_article = mpropdef.tpl_article
413 redef_article.source_link = tpl_showsource(mpropdef.location)
414 article.add_child redef_article
419 # MProperty description template
420 fun tpl_mpropdef_article(mpropdef: MPropDef): TplArticle do
421 var article = mpropdef.tpl_article
422 if mpropdef.is_intro then article.content = null
423 article.source_link = tpl_showsource(mpropdef.location)
429 # Display a list of modules contained in program
433 init(ctx: NitdocContext) do super(ctx)
435 private var page = new TplPage
436 redef fun tpl_page do return page
438 private var sidebar = new TplSidebar
439 redef fun tpl_sidebar do return sidebar
441 redef fun tpl_title do
442 if ctx.opt_custom_title.value != null then
443 return ctx.opt_custom_title.value.to_s
449 redef fun tpl_topmenu do
451 topmenu.add_item(new TplLink("#", "Overview"), true)
452 topmenu
.add_item
(new TplLink("search.html", "Index"), false)
457 private fun tpl_intro
: TplSection do
458 var section
= new TplSection.with_title
("overview", tpl_title
)
459 var article
= new TplArticle("intro")
460 if ctx
.opt_custom_intro
.value
!= null then
461 article
.content
= ctx
.opt_custom_intro
.value
.to_s
463 section
.add_child article
468 private fun tpl_projects
(section
: TplSection) do
470 var mprojects
= model
.mprojects
.to_a
471 var sorter
= new MConcernRankSorter
472 sorter
.sort mprojects
473 var ssection
= new TplSection.with_title
("projects", "Projects")
474 for mproject
in mprojects
do
475 ssection
.add_child tpl_mproject_article
(mproject
)
477 section
.add_child ssection
480 redef fun tpl_content
do
483 tpl_page
.add_section top
488 # Display a list of modules, classes and properties
492 init(ctx
: NitdocContext) do super(ctx
)
494 private var page
= new TplPage
495 redef fun tpl_page
do return page
497 redef fun tpl_title
do return "Index"
499 redef fun tpl_topmenu
do
501 topmenu
.add_item
(new TplLink("index.html", "Overview"), false)
502 topmenu
.add_item
(new TplLink("#", "Index"), true)
506 redef fun tpl_content
do
507 var tpl
= new TplSearchPage("search_all")
508 var section
= new TplSection("search")
512 for mmodule
in modules_list
do
513 tpl
.modules
.add mmodule
.tpl_link
516 for mclass
in classes_list
do
517 tpl
.classes
.add mclass
.tpl_link
520 for mproperty
in mprops_list
do
522 m
.add mproperty
.intro
.tpl_link
524 m
.add mproperty
.intro
.mclassdef
.mclass
.tpl_link
528 section
.add_child tpl
529 tpl_page
.add_section section
532 # Extract mmodule list to display (sorted by name)
533 private fun modules_list
: Array[MModule] do
534 var sorted
= new Array[MModule]
535 for mmodule
in ctx
.mbuilder
.model
.mmodule_importation_hierarchy
do
536 if mmodule
.name
== "<main>" then continue
539 name_sorter
.sort
(sorted
)
543 # Extract mclass list to display (sorted by name)
544 private fun classes_list
: Array[MClass] do
545 var sorted
= new Array[MClass]
546 for mclass
in ctx
.mbuilder
.model
.mclasses
do
547 if mclass
.visibility
< ctx
.min_visibility
then continue
550 name_sorter
.sort
(sorted
)
554 # Extract mproperty list to display (sorted by name)
555 private fun mprops_list
: Array[MProperty] do
556 var sorted
= new Array[MProperty]
557 for mproperty
in ctx
.mbuilder
.model
.mproperties
do
558 if mproperty
.visibility
< ctx
.min_visibility
then continue
559 if mproperty
isa MAttribute then continue
562 name_sorter
.sort
(sorted
)
568 # Display the list of introduced and redefined classes in module
572 private var mmodule
: MModule
574 init(mmodule
: MModule, ctx
: NitdocContext) do
575 self.mmodule
= mmodule
579 private var page
= new TplPage
580 redef fun tpl_page
do return page
582 private var sidebar
= new TplSidebar
583 redef fun tpl_sidebar
do return sidebar
585 redef fun tpl_title
do return "{mmodule.nitdoc_name}"
587 redef fun tpl_topmenu
do
589 var mproject
= mmodule
.mgroup
.mproject
590 topmenu
.add_item
(new TplLink("index.html", "Overview"), false)
591 topmenu
.add_item
(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false)
592 topmenu
.add_item
(new TplLink("#", "{mmodule.nitdoc_name}"), true)
593 topmenu
.add_item
(new TplLink("search.html", "Index"), false)
597 # Class list to display in sidebar
598 fun tpl_sidebar_mclasses
do
599 var mclasses
= new HashSet[MClass]
600 mclasses
.add_all mmodule
.filter_intro_mclasses
(ctx
.min_visibility
)
601 mclasses
.add_all mmodule
.filter_redef_mclasses
(ctx
.min_visibility
)
602 if mclasses
.is_empty
then return
603 var list
= new TplList.with_classes
(["list-unstyled", "list-labeled"])
605 var sorted
= mclasses
.to_a
606 name_sorter
.sort
(sorted
)
607 for mclass
in sorted
do
608 list
.add_li tpl_sidebar_item
(mclass
)
610 tpl_sidebar
.boxes
.add
new TplSideBox.with_content
("All classes", list
)
613 private fun tpl_sidebar_item
(def
: MClass): Template do
614 var classes
= def
.intro
.tpl_css_classes
.to_a
615 if def
.intro_mmodule
== mmodule
then
620 var lnk
= new Template
621 lnk
.add
new TplLabel.with_classes
(classes
)
627 private fun tpl_intro
: TplSection do
628 var section
= new TplSection.with_title
("top", tpl_title
)
629 section
.subtitle
= mmodule
.tpl_declaration
631 var article
= new TplArticle("intro")
632 var def
= mmodule
.tpl_definition
633 var location
= mmodule
.location
634 article
.source_link
= tpl_showsource
(location
)
635 article
.content
= def
636 section
.add_child article
640 # inheritance section
641 private fun tpl_inheritance
(parent
: TplSection) do
642 # Extract relevent modules
643 var imports
= mmodule
.in_importation
.greaters
644 if imports
.length
> 10 then imports
= mmodule
.in_importation
.direct_greaters
645 var clients
= mmodule
.in_importation
.smallers
646 if clients
.length
> 10 then clients
= mmodule
.in_importation
.direct_smallers
649 var section
= new TplSection.with_title
("dependencies", "Dependencies")
652 var mmodules
= new HashSet[MModule]
653 mmodules
.add_all mmodule
.in_nesting
.direct_greaters
654 mmodules
.add_all imports
655 if clients
.length
< 10 then mmodules
.add_all clients
657 var graph
= tpl_dot
(mmodules
)
658 if graph
!= null then section
.add_child graph
661 var lst
= new Array[MModule]
662 for dep
in imports
do
663 if dep
.is_fictive
then continue
664 if dep
== mmodule
then continue
667 if not lst
.is_empty
then
669 section
.add_child tpl_list
("imports", "Imports", lst
)
673 lst
= new Array[MModule]
674 for dep
in clients
do
675 if dep
.is_fictive
then continue
676 if dep
== mmodule
then continue
679 if not lst
.is_empty
then
681 section
.add_child tpl_list
("clients", "Clients", lst
)
684 parent
.add_child section
687 private fun tpl_list
(id
: String, title
: String, mmodules
: Array[MModule]): TplArticle do
688 var article
= new TplArticle.with_title
(id
, title
)
689 var list
= new TplList.with_classes
(["list-unstyled", "list-definition"])
690 for mmodule
in mmodules
do list
.elts
.add mmodule
.tpl_list_item
691 article
.content
= list
695 private fun tpl_mclasses
(parent
: TplSection) do
696 var mclassdefs
= new HashSet[MClassDef]
697 mclassdefs
.add_all mmodule
.intro_mclassdefs
(ctx
.min_visibility
)
698 mclassdefs
.add_all mmodule
.redef_mclassdefs
(ctx
.min_visibility
)
699 var mclasses2mdefs
= sort_by_mclass
(mclassdefs
)
700 var mmodules2mclasses
= group_by_mmodule
(mclasses2mdefs
.keys
)
702 var sorted_mmodules
= mmodules2mclasses
.keys
.to_a
703 model
.mmodule_importation_hierarchy
.linearize
(sorted_mmodules
)
705 for mmodule
in sorted_mmodules
do
706 var section
= new TplSection(mmodule
.nitdoc_anchor
)
707 var title
= new Template
708 if mmodule
== sorted_mmodules
.first
then
709 title
.add
"Introductions in "
710 section
.summary_title
= "In {mmodule.nitdoc_name}"
712 title
.add
"Redefinitions from "
713 section
.summary_title
= "From {mmodule.nitdoc_name}"
715 title
.add mmodule
.tpl_link
716 section
.title
= title
718 var mclasses
= mmodules2mclasses
[mmodule
].to_a
719 name_sorter
.sort
(mclasses
)
720 for mclass
in mclasses
do
721 section
.add_child tpl_mclass_article
(mclass
, mclasses2mdefs
[mclass
].to_a
)
723 parent
.add_child section
727 private fun group_by_mmodule
(mclasses
: Collection[MClass]): Map[MModule, Set[MClass]] do
728 var res
= new HashMap[MModule, Set[MClass]]
729 for mclass
in mclasses
do
730 var mmodule
= mclass
.intro_mmodule
731 if not res
.has_key
(mmodule
) then
732 res
[mmodule
] = new HashSet[MClass]
734 res
[mmodule
].add
(mclass
)
739 redef fun tpl_content
do
744 tpl_page
.add_section top
747 # Genrate dot hierarchy for class inheritance
748 fun tpl_dot
(mmodules
: Collection[MModule]): nullable TplArticle do
749 var poset
= new POSet[MModule]
750 for mmodule
in mmodules
do
751 if mmodule
.is_fictive
then continue
752 poset
.add_node mmodule
753 for omodule
in mmodules
do
754 if mmodule
.is_fictive
then continue
755 poset
.add_node mmodule
756 if mmodule
.in_importation
< omodule
then
757 poset
.add_edge
(mmodule
, omodule
)
762 var op
= new FlatBuffer
763 var name
= "dep_{mmodule.name}"
764 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")
765 for mmodule
in poset
do
766 if mmodule
== self.mmodule
then
767 op
.append
("\"{mmodule.name}\
"[shape=box,margin=0.03];\n")
769 op
.append
("\"{mmodule.name}\
"[URL=\"{mmodule.nitdoc_url}\
"];\n")
771 for omodule
in poset
[mmodule
].direct_greaters
do
772 op
.append
("\"{mmodule.name}\
"->\"{omodule.name}\
";\n")
776 return tpl_graph
(op
, name
, "Dependency graph")
779 private fun sort_by_mclass
(mclassdefs
: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
780 var map
= new HashMap[MClass, Set[MClassDef]]
781 for mclassdef
in mclassdefs
do
782 var mclass
= mclassdef
.mclass
783 if not map
.has_key
(mclass
) then map
[mclass
] = new HashSet[MClassDef]
784 map
[mclass
].add mclassdef
791 # Display a list properties defined or redefined for this class
795 private var mclass
: MClass
796 private var mprops2mdefs
: Map[MProperty, Set[MPropDef]]
798 init(mclass
: MClass, ctx
: NitdocContext) do
801 var mpropdefs
= new HashSet[MPropDef]
802 mpropdefs
.add_all mclass
.intro_mpropdefs
(ctx
.min_visibility
)
803 mpropdefs
.add_all mclass
.redef_mpropdefs
(ctx
.min_visibility
)
804 mprops2mdefs
= sort_by_mproperty
(mpropdefs
)
807 private var page
= new TplPage
808 redef fun tpl_page
do return page
810 private var sidebar
= new TplSidebar
811 redef fun tpl_sidebar
do return sidebar
813 redef fun tpl_title
do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
815 redef fun tpl_topmenu
do
817 var mproject
= mclass
.intro_mmodule
.mgroup
.mproject
818 topmenu
.add_item
(new TplLink("index.html", "Overview"), false)
819 topmenu
.add_item
(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false)
820 topmenu
.add_item
(new TplLink("#", "{mclass.nitdoc_name}"), true)
821 topmenu
.add_item
(new TplLink("search.html", "Index"), false)
825 # Property list to display in sidebar
826 fun tpl_sidebar_properties
do
827 var kind_map
= sort_by_kind
(mclass_inherited_mprops
)
828 var summary
= new TplList.with_classes
(["list-unstyled"])
830 tpl_sidebar_list
("Virtual types", kind_map
["type"].to_a
, summary
)
831 tpl_sidebar_list
("Constructors", kind_map
["init"].to_a
, summary
)
832 tpl_sidebar_list
("Methods", kind_map
["fun"].to_a
, summary
)
833 tpl_sidebar
.boxes
.add
new TplSideBox.with_content
("All properties", summary
)
836 private fun tpl_sidebar_list
(name
: String, mprops
: Array[MProperty], summary
: TplList) do
837 if mprops
.is_empty
then return
838 name_sorter
.sort
(mprops
)
839 var entry
= new TplListItem.with_content
(name
)
840 var list
= new TplList.with_classes
(["list-unstyled", "list-labeled"])
841 for mprop
in mprops
do
842 list
.add_li tpl_sidebar_item
(mprop
)
845 summary
.elts
.add entry
848 private fun tpl_sidebar_item
(mprop
: MProperty): Template do
849 var classes
= mprop
.intro
.tpl_css_classes
.to_a
850 if not mprops2mdefs
.has_key
(mprop
) then
851 classes
.add
"inherit"
852 var lnk
= new Template
853 lnk
.add
new TplLabel.with_classes
(classes
)
854 lnk
.add mprop
.intro
.tpl_link
857 var defs
= mprops2mdefs
[mprop
]
858 if defs
.has
(mprop
.intro
) then
863 var lnk
= new Template
864 lnk
.add
new TplLabel.with_classes
(classes
)
865 lnk
.add mprop
.intro
.tpl_anchor
869 private fun tpl_intro
: TplSection do
870 var section
= new TplSection.with_title
("top", tpl_title
)
871 var subtitle
= new Template
872 subtitle
.add
"introduction in "
873 subtitle
.add mclass
.intro
.mmodule
.tpl_namespace
874 section
.subtitle
= subtitle
875 section
.add_child tpl_mclassdef_article
(mclass
.intro
)
879 private fun tpl_concerns
(section
: TplSection) do
880 var mmodules
= collect_mmodules
(mprops2mdefs
.keys
)
881 var owner_map
= sort_by_public_owner
(mmodules
)
882 var owners
= owner_map
.keys
.to_a
884 if not owners
.is_empty
then
885 var article
= new TplArticle.with_title
("concerns", "Concerns")
886 name_sorter
.sort owners
887 var list
= new TplList.with_classes
(["list-unstyled", "list-definition"])
888 for owner
in owners
do
889 var li
= new Template
890 li
.add owner
.tpl_anchor
891 if owner
.mdoc
!= null then
893 li
.add owner
.mdoc
.short_comment
895 var smmodules
= owner_map
[owner
].to_a
896 #if not smmodules.length >= 1 then
897 var slist
= new TplList.with_classes
(["list-unstyled", "list-definition"])
898 name_sorter
.sort smmodules
899 for mmodule
in smmodules
do
900 if mmodule
== owner
then continue
901 var sli
= new Template
902 sli
.add mmodule
.tpl_anchor
903 if mmodule
.mdoc
!= null then
905 sli
.add mmodule
.mdoc
.short_comment
913 article
.content
= list
914 section
.add_child article
918 private fun tpl_inheritance
(parent
: TplSection) do
920 var hparents
= new HashSet[MClass]
921 for c
in mclass
.in_hierarchy
(ctx
.mainmodule
).direct_greaters
do
922 if c
.visibility
< ctx
.min_visibility
then continue
927 var hancestors
= new HashSet[MClass]
928 for c
in mclass
.in_hierarchy
(ctx
.mainmodule
).greaters
do
929 if c
== mclass
then continue
930 if c
.visibility
< ctx
.min_visibility
then continue
931 if hparents
.has
(c
) then continue
936 var hchildren
= new HashSet[MClass]
937 for c
in mclass
.in_hierarchy
(ctx
.mainmodule
).direct_smallers
do
938 if c
.visibility
< ctx
.min_visibility
then continue
943 var hdescendants
= new HashSet[MClass]
944 for c
in mclass
.in_hierarchy
(ctx
.mainmodule
).smallers
do
945 if c
== mclass
then continue
946 if c
.visibility
< ctx
.min_visibility
then continue
947 if hchildren
.has
(c
) then continue
952 var section
= new TplSection.with_title
("inheritance", "Inheritance")
955 var mclasses
= new HashSet[MClass]
956 mclasses
.add_all hancestors
957 mclasses
.add_all hparents
958 if hchildren
.length
< 10 then mclasses
.add_all hchildren
959 if hdescendants
.length
< 10 then mclasses
.add_all hdescendants
961 var graph
= tpl_dot
(mclasses
)
962 if graph
!= null then section
.add_child graph
965 if not hparents
.is_empty
then
966 var lst
= hparents
.to_a
968 section
.add_child tpl_list
("parents", "Parents", lst
)
972 if not hancestors
.is_empty
then
973 var lst
= hancestors
.to_a
975 section
.add_child tpl_list
("ancestors", "Ancestors", lst
)
979 if not hchildren
.is_empty
and hchildren
.length
< 15 then
980 var lst
= hchildren
.to_a
982 section
.add_child tpl_list
("children", "Children", lst
)
986 if not hdescendants
.is_empty
and hchildren
.length
< 15 then
987 var lst
= hdescendants
.to_a
989 section
.add_child tpl_list
("descendants", "Descendants", lst
)
992 parent
.add_child section
995 private fun tpl_list
(id
: String, title
: String, elts
: Array[MClass]): TplArticle do
996 var article
= new TplArticle.with_title
(id
, title
)
997 var list
= new TplList.with_classes
(["list-unstyled", "list-definition"])
998 for elt
in elts
do list
.elts
.add elt
.tpl_list_item
999 article
.content
= list
1003 private fun tpl_properties
(parent
: TplSection) do
1004 var mod_map
= sort_by_mmodule
(mprops2mdefs
.keys
)
1005 var owner_map
= sort_by_public_owner
(mod_map
.keys
)
1006 var owners
= owner_map
.keys
.to_a
1008 for owner
in owners
do
1009 var section
= new TplSection(owner
.nitdoc_anchor
)
1010 var title
= new Template
1011 title
.add
"Introductions in "
1012 title
.add owner
.tpl_link
1013 section
.title
= title
1014 section
.summary_title
= "In {owner.nitdoc_name}"
1015 for mmodule
in owner_map
[owner
] do
1017 var mprops
= mod_map
[mmodule
]
1018 var kind_map
= sort_by_kind
(mprops
)
1021 var elts
= kind_map
["type"].to_a
1022 name_sorter
.sort
(elts
)
1024 var defs
= mprops2mdefs
[elt
].to_a
1025 section
.add_child tpl_mprop_article
(elt
, defs
)
1029 elts
= kind_map
["init"].to_a
1030 name_sorter
.sort
(elts
)
1032 var defs
= mprops2mdefs
[elt
].to_a
1033 section
.add_child tpl_mprop_article
(elt
, defs
)
1037 elts
= kind_map
["fun"].to_a
1038 name_sorter
.sort
(elts
)
1040 var defs
= mprops2mdefs
[elt
].to_a
1041 section
.add_child tpl_mprop_article
(elt
, defs
)
1044 parent
.add_child section
1048 redef fun tpl_content
do
1049 tpl_sidebar_properties
1052 tpl_inheritance
(top
)
1054 tpl_page
.add_section top
1057 private fun sort_by_mproperty
(mpropdefs
: Collection[MPropDef]): Map[MProperty, Set[MPropDef]] do
1058 var map
= new HashMap[MProperty, Set[MPropDef]]
1059 for mpropdef
in mpropdefs
do
1060 var mproperty
= mpropdef
.mproperty
1061 if not map
.has_key
(mproperty
) then map
[mproperty
] = new HashSet[MPropDef]
1062 map
[mproperty
].add mpropdef
1067 private fun sort_by_mmodule
(mprops
: Collection[MProperty]): Map[MModule, Set[MProperty]] do
1068 var map
= new HashMap[MModule, Set[MProperty]]
1069 for mprop
in mprops
do
1070 var mpropdefs
= mprops2mdefs
[mprop
].to_a
1071 ctx
.mainmodule
.linearize_mpropdefs
(mpropdefs
)
1072 var mmodule
= mpropdefs
.first
.mclassdef
.mmodule
1073 if not map
.has_key
(mmodule
) then map
[mmodule
] = new HashSet[MProperty]
1074 map
[mmodule
].add mprop
1079 private fun sort_by_kind
(mprops
: Collection[MProperty]): Map[String, Set[MProperty]] do
1080 var map
= new HashMap[String, Set[MProperty]]
1081 map
["type"] = new HashSet[MProperty]
1082 map
["init"] = new HashSet[MProperty]
1083 map
["fun"] = new HashSet[MProperty]
1084 for mprop
in mprops
do
1085 if mprop
isa MVirtualTypeProp then
1086 map
["type"].add mprop
1087 else if mprop
isa MMethod then
1088 if mprop
.is_init
then
1089 map
["init"].add mprop
1091 map
["fun"].add mprop
1098 private fun mclass_inherited_mprops
: Set[MProperty] do
1099 var res
= new HashSet[MProperty]
1100 var local
= mclass
.local_mproperties
(ctx
.min_visibility
)
1101 for mprop
in mclass
.inherited_mproperties
(ctx
.mainmodule
, ctx
.min_visibility
) do
1102 if local
.has
(mprop
) then continue
1103 #if mprop isa MMethod and mprop.is_init then continue
1104 if mprop
.intro
.mclassdef
.mclass
.name
== "Object" and
1105 (mprop
.visibility
== protected_visibility
or
1106 mprop
.intro
.mclassdef
.mmodule
.name
!= "kernel") then continue
1113 private fun collect_mmodules
(mprops
: Collection[MProperty]): Set[MModule] do
1114 var res
= new HashSet[MModule]
1115 for mprop
in mprops
do
1116 if mprops2mdefs
.has_key
(mprop
) then
1117 for mpropdef
in mprops2mdefs
[mprop
] do res
.add mpropdef
.mclassdef
.mmodule
1123 private fun sort_by_public_owner
(mmodules
: Collection[MModule]): Map[MModule, Set[MModule]] do
1124 var map
= new HashMap[MModule, Set[MModule]]
1125 for mmodule
in mmodules
do
1127 if mmodule
.public_owner
!= null then owner
= mmodule
.public_owner
.as(not null)
1128 if not map
.has_key
(owner
) then map
[owner
] = new HashSet[MModule]
1129 map
[owner
].add mmodule
1134 # Generate dot hierarchy for classes
1135 fun tpl_dot
(mclasses
: Collection[MClass]): nullable TplArticle do
1136 var poset
= new POSet[MClass]
1138 for mclass
in mclasses
do
1139 poset
.add_node mclass
1140 for oclass
in mclasses
do
1141 poset
.add_node oclass
1142 if mclass
.in_hierarchy
(ctx
.mainmodule
) < oclass
then
1143 poset
.add_edge
(mclass
, oclass
)
1148 var op
= new FlatBuffer
1149 var name
= "dep_{mclass.name}"
1150 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")
1153 op
.append
("\"{c.name}\
"[shape=box,margin=0.03];\n")
1155 op
.append
("\"{c.name}\
"[URL=\"{c.nitdoc_url}\
"];\n")
1157 for c2
in poset
[c
].direct_greaters
do
1158 op
.append
("\"{c.name}\
"->\"{c2.name}\
";\n")
1162 return tpl_graph
(op
, name
, "Inheritance graph")