Merge branch 'master' into polymorphic_extern_classes
[nit.git] / src / doc / doc_pages.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Nitdoc page generation
16 module doc_pages
17
18 import toolcontext
19 import doc_model
20
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")
28
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")
33
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")
37
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")
40
41 private var output_dir: String
42 private var min_visibility: MVisibility
43
44 redef init do
45 super
46
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)
52
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
57 end
58
59 redef fun process_options(args) do
60 super
61
62 # output dir
63 var output_dir = opt_dir.value
64 if output_dir == null then
65 output_dir = "doc"
66 end
67 self.output_dir = output_dir
68 # min visibility
69 if opt_private.value then
70 min_visibility = none_visibility
71 else
72 min_visibility = protected_visibility
73 end
74 # github urls
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"
81 abort
82 end
83 end
84 end
85 end
86
87 # The Nitdoc class explores the model and generate pages for each mentities found
88 class Nitdoc
89 var model: Model
90 var mainmodule: MModule
91 var ctx: ToolContext
92
93 init(ctx: ToolContext, model: Model, mainmodule: MModule) do
94 self.ctx = ctx
95 self.model = model
96 self.mainmodule = mainmodule
97 end
98
99 fun generate do
100 init_output_dir
101 overview
102 search
103 groups
104 modules
105 classes
106 properties
107 quicksearch_list
108 end
109
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
114 # locate share dir
115 var sharedir = ctx.opt_sharedir.value
116 if sharedir == null then
117 var dir = ctx.nit_dir
118 if dir == null then
119 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
120 abort
121 end
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"
125 abort
126 end
127 end
128 # copy shared files
129 if ctx.opt_shareurl.value == null then
130 sys.system("cp -r {sharedir.to_s}/* {output_dir.to_s}/")
131 else
132 sys.system("cp -r {sharedir.to_s}/resources/ {output_dir.to_s}/resources/")
133 end
134
135 end
136
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}")
140 end
141
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}")
145 end
146
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}")
152 end
153 end
154 end
155
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}")
161 end
162 end
163
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}")
168 end
169 end
170
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}")
175 end
176 end
177
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")
181 end
182 end
183
184 # Nitdoc QuickSearch list generator
185 #
186 # Create a JSON object containing links to:
187 # * modules
188 # * mclasses
189 # * mpropdefs
190 # All entities are grouped by name to make the research easier.
191 class QuickSearch
192
193 private var mmodules = new HashSet[MModule]
194 private var mclasses = new HashSet[MClass]
195 private var mpropdefs = new HashMap[String, Set[MPropDef]]
196
197 init(ctx: ToolContext, model: Model) do
198 for mmodule in model.mmodules do
199 if mmodule.is_fictive then continue
200 mmodules.add mmodule
201 end
202 for mclass in model.mclasses do
203 if mclass.visibility < ctx.min_visibility then continue
204 mclasses.add mclass
205 end
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]
211 end
212 mpropdefs[mproperty.name].add_all(mproperty.mpropdefs)
213 end
214 end
215
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}\"\},"
222 tpl.add "],"
223 end
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}\"\},"
228 tpl.add "],"
229 end
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}\"\},"
235 end
236 tpl.add "],"
237 end
238 tpl.add " \};"
239 return tpl
240 end
241 end
242
243 # Nitdoc base page
244 # Define page structure and properties
245 abstract class NitdocPage
246
247 private var ctx: ToolContext
248 private var model: Model
249 private var mainmodule: MModule
250 private var name_sorter = new MEntityNameSorter
251
252 init(ctx: ToolContext, model: Model, mainmodule: MModule) do
253 self.ctx = ctx
254 self.model = model
255 self.mainmodule = mainmodule
256 end
257
258 # Render the page as a html template
259 fun render: Template do
260 var shareurl = "."
261 if ctx.opt_shareurl.value != null then
262 shareurl = ctx.opt_shareurl.value.as(not null)
263 end
264
265 # build page
266 var tpl = tpl_page
267 tpl.title = tpl_title
268 tpl.url = page_url
269 tpl.shareurl = shareurl
270 tpl.topmenu = tpl_topmenu
271 tpl_content
272 tpl.footer = ctx.opt_custom_footer.value
273 tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl))
274 tpl.sidebar = tpl_sidebar
275
276 # piwik tracking
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)
281 end
282 return tpl
283 end
284
285 # URL to this page.
286 fun page_url: String is abstract
287
288 # Build page template
289 fun tpl_page: TplPage is abstract
290
291 # Build page sidebar if any
292 fun tpl_sidebar: nullable TplSidebar do return null
293
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
298 end
299 return "Nitdoc"
300 end
301
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'>"
309 tpl.add brand
310 tpl.add "</span>"
311 topmenu.brand = tpl
312 end
313 topmenu.add_link new TplLink("index.html", "Overview")
314 topmenu.add_link new TplLink("search.html", "Index")
315 return topmenu
316 end
317
318 # Build page content template
319 fun tpl_content is abstract
320
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")
327 file.write(dot)
328 file.close
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
332 fmap.close
333
334 var article = new TplArticle("graph")
335 var alt = ""
336 if title != null then
337 article.title = title
338 alt = "alt='{title}'"
339 end
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}/>"
343 content.add map
344 article.content = content
345 return article
346 end
347
348 # A (source) link template for a given location
349 fun tpl_showsource(location: nullable Location): nullable String
350 do
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>"
363 end
364
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
372 end
373 return article
374 end
375
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
381 return article
382 end
383
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
389 # mclassdefs list
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
397 end
398 if not intros_lst.is_empty then
399 intros_art.content = intros_lst
400 article.add_child intros_art
401 end
402 end
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
410 end
411 if not redefs_lst.is_empty then
412 redefs_art.content = redefs_lst
413 article.add_child redefs_art
414 end
415 end
416 return article
417 end
418
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
423 # add intro synopsys
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
427 end
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
434 # mpropdefs list
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
439 end
440 if not intros_lst.is_empty then
441 intros.content = intros_lst
442 redef_article.add_child intros
443 end
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
448 end
449 if not redefs_lst.is_empty then
450 redefs.content = redefs_lst
451 redef_article.add_child redefs
452 end
453 end
454 return article
455 end
456
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)
462 return article
463 end
464
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
469 # add intro synopsys
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
473 end
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
480 end
481 return article
482 end
483
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)
489 return article
490 end
491 end
492
493 # The overview page
494 # Display a list of modules contained in program
495 class NitdocOverview
496 super NitdocPage
497
498 private var page = new TplPage
499 redef fun tpl_page do return page
500
501 private var sidebar = new TplSidebar
502 redef fun tpl_sidebar do return sidebar
503
504 redef fun tpl_title do
505 if ctx.opt_custom_title.value != null then
506 return ctx.opt_custom_title.value.to_s
507 else
508 return "Overview"
509 end
510 end
511
512 redef fun page_url do return "index.html"
513
514 # intro text
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
520 end
521 section.add_child article
522 return section
523 end
524
525 # projects list
526 private fun tpl_projects(section: TplSection) do
527 # Projects list
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)
534 end
535 section.add_child ssection
536 end
537
538 redef fun tpl_content do
539 var top = tpl_intro
540 tpl_projects(top)
541 tpl_page.add_section top
542 end
543 end
544
545 # The search page
546 # Display a list of modules, classes and properties
547 class NitdocSearch
548 super NitdocPage
549
550 private var page = new TplPage
551 redef fun tpl_page do return page
552
553 redef fun tpl_title do return "Index"
554
555 redef fun page_url do return "search.html"
556
557 redef fun tpl_content do
558 var tpl = new TplSearchPage("search_all")
559 var section = new TplSection("search")
560 # title
561 tpl.title = "Index"
562 # modules list
563 for mmodule in modules_list do
564 tpl.modules.add mmodule.tpl_link
565 end
566 # classes list
567 for mclass in classes_list do
568 tpl.classes.add mclass.tpl_link
569 end
570 # properties list
571 for mproperty in mprops_list do
572 var m = new Template
573 m.add mproperty.intro.tpl_link
574 m.add " ("
575 m.add mproperty.intro.mclassdef.mclass.tpl_link
576 m.add ")"
577 tpl.props.add m
578 end
579 section.add_child tpl
580 tpl_page.add_section section
581 end
582
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
588 sorted.add mmodule
589 end
590 name_sorter.sort(sorted)
591 return sorted
592 end
593
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
599 sorted.add mclass
600 end
601 name_sorter.sort(sorted)
602 return sorted
603 end
604
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
611 sorted.add mproperty
612 end
613 name_sorter.sort(sorted)
614 return sorted
615 end
616 end
617
618 # A group page
619 # Display a flattened view of the group
620 class NitdocGroup
621 super NitdocPage
622
623 private var mgroup: MGroup
624
625 private var concerns: ConcernsTree
626 private var intros: Set[MClass]
627 private var redefs: Set[MClass]
628
629 init(ctx: ToolContext, model: Model, mainmodule: MModule, mgroup: MGroup) do
630 super
631 self.mgroup = mgroup
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
638 redefs.add rdef
639 end
640 self.redefs = redefs
641 end
642
643 private var page = new TplPage
644 redef fun tpl_page do return page
645
646 private var sidebar = new TplSidebar
647 redef fun tpl_sidebar do return sidebar
648
649 redef fun tpl_title do return mgroup.nitdoc_name
650
651 redef fun page_url do return mgroup.nitdoc_url
652
653 redef fun tpl_topmenu do
654 var topmenu = super
655 var mproject = mgroup.mproject
656 if not mgroup.is_root then
657 topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
658 end
659 topmenu.add_link new TplLink(page_url, mproject.nitdoc_name)
660 return topmenu
661 end
662
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"])
670
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)
675 end
676 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
677 end
678
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
682 classes.add "intro"
683 else
684 classes.add "redef"
685 end
686 var lnk = new Template
687 lnk.add new TplLabel.with_classes(classes)
688 lnk.add def.tpl_link
689 return new TplListItem.with_content(lnk)
690 end
691
692 # intro text
693 private fun tpl_intro: TplSection do
694 var section = new TplSection.with_title("top", tpl_title)
695 var article = new TplArticle("intro")
696
697 if mgroup.is_root then
698 section.subtitle = mgroup.mproject.tpl_declaration
699 article.content = mgroup.mproject.tpl_definition
700 else
701 section.subtitle = mgroup.tpl_declaration
702 article.content = mgroup.tpl_definition
703 end
704 section.add_child article
705 return section
706 end
707
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)
711 end
712
713 private fun tpl_groups(parent: TplSection) do
714 var lst = concerns.to_a
715 var section = parent
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)
723 end
724 end
725 end
726
727 redef fun tpl_content do
728 tpl_sidebar_mclasses
729 var top = tpl_intro
730 tpl_concerns(top)
731 tpl_groups(top)
732 tpl_page.add_section top
733 end
734
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
741 end
742 return map
743 end
744 end
745
746 # A module page
747 # Display the list of introduced and redefined classes in module
748 class NitdocModule
749 super NitdocPage
750
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]]
755
756
757 init(ctx: ToolContext, model: Model, mainmodule: MModule, mmodule: MModule) do
758 super
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)
766 # rank concerns
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
774 end
775
776 private var page = new TplPage
777 redef fun tpl_page do return page
778
779 private var sidebar = new TplSidebar
780 redef fun tpl_sidebar do return sidebar
781
782 redef fun tpl_title do return mmodule.nitdoc_name
783 redef fun page_url do return mmodule.nitdoc_url
784
785 redef fun tpl_topmenu do
786 var topmenu = super
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)
790 return topmenu
791 end
792
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"])
800
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)
805 end
806 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
807 end
808
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
812 classes.add "intro"
813 else
814 classes.add "redef"
815 end
816 var lnk = new Template
817 lnk.add new TplLabel.with_classes(classes)
818 lnk.add def.tpl_link
819 return new TplListItem.with_content(lnk)
820 end
821
822 # intro text
823 private fun tpl_intro: TplSection do
824 var section = new TplSection.with_title("top", tpl_title)
825 section.subtitle = mmodule.tpl_declaration
826
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
833 return section
834 end
835
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
843
844 # Display lists
845 var section = new TplSection.with_title("dependencies", "Dependencies")
846
847 # Graph
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
852 mmodules.add mmodule
853 var graph = tpl_dot(mmodules)
854 if graph != null then section.add_child graph
855
856 # Imports
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
861 lst.add(dep)
862 end
863 if not lst.is_empty then
864 name_sorter.sort lst
865 section.add_child tpl_list("imports", "Imports", lst)
866 end
867
868 # Clients
869 lst = new Array[MModule]
870 for dep in clients do
871 if dep.is_fictive then continue
872 if dep == mmodule then continue
873 lst.add(dep)
874 end
875 if not lst.is_empty then
876 name_sorter.sort lst
877 section.add_child tpl_list("clients", "Clients", lst)
878 end
879
880 parent.add_child section
881 end
882
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
888 return article
889 end
890
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)
894 end
895
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
906 title.add "in "
907 section.summary_title = "in {mentity.nitdoc_name}"
908 else
909 title.add "from "
910 section.summary_title = "from {mentity.nitdoc_name}"
911 end
912 title.add mentity.tpl_namespace
913 section.title = title
914
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)
919 end
920 parent.add_child section
921 end
922 end
923 end
924
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]
931 end
932 res[mmodule].add(mclass)
933 end
934 return res
935 end
936
937 redef fun tpl_content do
938 tpl_sidebar_mclasses
939 var top = tpl_intro
940 tpl_inheritance(top)
941 tpl_concerns(top)
942 tpl_mclasses(top)
943 tpl_page.add_section top
944 end
945
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)
957 end
958 end
959 end
960 # build graph
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")
967 else
968 op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n")
969 end
970 for omodule in poset[mmodule].direct_greaters do
971 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
972 end
973 end
974 op.append("\}\n")
975 return tpl_graph(op, name, null)
976 end
977
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
984 end
985 return map
986 end
987 end
988
989 # A class page
990 # Display a list properties defined or redefined for this class
991 class NitdocClass
992 super NitdocPage
993
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]]
998
999 init(ctx: ToolContext, model: Model, mainmodule: MModule, mclass: MClass) do
1000 super
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)
1009 end
1010
1011 private var page = new TplPage
1012 redef fun tpl_page do return page
1013
1014 private var sidebar = new TplSidebar
1015 redef fun tpl_sidebar do return sidebar
1016
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
1019
1020 redef fun tpl_topmenu do
1021 var topmenu = super
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)
1025 return topmenu
1026 end
1027
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"])
1032
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)
1037 end
1038
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)
1046 end
1047 entry.append list
1048 summary.elts.add entry
1049 end
1050
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)
1059 end
1060 var defs = mprops2mdefs[mprop]
1061 if defs.has(mprop.intro) then
1062 classes.add "intro"
1063 else
1064 classes.add "redef"
1065 end
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)
1070 end
1071
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
1078 end
1079 section.add_child article
1080 return section
1081 end
1082
1083 private fun tpl_concerns(parent: TplSection) do
1084 # intro title
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
1089 # concerns
1090 if concerns.is_empty then return
1091 parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
1092 end
1093
1094 private fun tpl_inheritance(parent: TplSection) do
1095 # parents
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
1099 hparents.add c
1100 end
1101
1102 # ancestors
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
1108 hancestors.add c
1109 end
1110
1111 # children
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
1115 hchildren.add c
1116 end
1117
1118 # descendants
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
1124 hdescendants.add c
1125 end
1126
1127 # Display lists
1128 var section = new TplSection.with_title("inheritance", "Inheritance")
1129
1130 # Graph
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
1136 mclasses.add mclass
1137 var graph = tpl_dot(mclasses)
1138 if graph != null then section.add_child graph
1139
1140 # parents
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)
1145 end
1146
1147 # ancestors
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)
1152 end
1153
1154 # children
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)
1159 end
1160
1161 # descendants
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)
1166 end
1167
1168 parent.add_child section
1169 end
1170
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
1176 return article
1177 end
1178
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
1189 title.add "in "
1190 title.add mentity.tpl_namespace
1191 section.title = title
1192 section.summary_title = "in {mentity.nitdoc_name}"
1193
1194 # properties
1195 var mprops = mmodules2mprops[mentity]
1196 var kind_map = sort_by_kind(mprops)
1197
1198 # virtual types
1199 var elts = kind_map["type"].to_a
1200 name_sorter.sort(elts)
1201 for elt in elts do
1202 var defs = mprops2mdefs[elt].to_a
1203 section.add_child tpl_mprop_article(elt, defs)
1204 end
1205
1206 # constructors
1207 elts = kind_map["init"].to_a
1208 name_sorter.sort(elts)
1209 for elt in elts do
1210 var defs = mprops2mdefs[elt].to_a
1211 section.add_child tpl_mprop_article(elt, defs)
1212 end
1213
1214 # methods
1215 elts = kind_map["fun"].to_a
1216 name_sorter.sort(elts)
1217 for elt in elts do
1218 var defs = mprops2mdefs[elt].to_a
1219 section.add_child tpl_mprop_article(elt, defs)
1220 end
1221 parent.add_child section
1222 end
1223 end
1224 end
1225
1226 redef fun tpl_content do
1227 tpl_sidebar_properties
1228 var top = tpl_intro
1229 tpl_inheritance(top)
1230 tpl_concerns(top)
1231 tpl_properties(top)
1232 tpl_page.add_section top
1233 end
1234
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
1241 end
1242 return map
1243 end
1244
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
1253 end
1254 return map
1255 end
1256
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
1268 else
1269 map["fun"].add mprop
1270 end
1271 end
1272 end
1273 return map
1274 end
1275
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
1285 res.add mprop
1286 end
1287 res.add_all local
1288 return res
1289 end
1290
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
1296 end
1297 end
1298 return res
1299 end
1300
1301 # Generate dot hierarchy for classes
1302 fun tpl_dot(mclasses: Collection[MClass]): nullable TplArticle do
1303 var poset = new POSet[MClass]
1304
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)
1311 end
1312 end
1313 end
1314
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")
1318 for c in poset do
1319 if c == mclass then
1320 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1321 else
1322 op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n")
1323 end
1324 for c2 in poset[c].direct_greaters do
1325 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1326 end
1327 end
1328 op.append("\}\n")
1329 return tpl_graph(op, name, null)
1330 end
1331 end
1332
1333 # A MProperty page
1334 class NitdocProperty
1335 super NitdocPage
1336
1337 private var mproperty: MProperty
1338 private var concerns: ConcernsTree
1339 private var mmodules2mdefs: Map[MModule, Set[MPropDef]]
1340
1341 init(ctx: ToolContext, model: Model, mainmodule: MModule, mproperty: MProperty) do
1342 super
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)
1347 end
1348
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
1353 end
1354 return res
1355 end
1356
1357 private var page = new TplPage
1358 redef fun tpl_page do return page
1359
1360 private var sidebar = new TplSidebar
1361 redef fun tpl_sidebar do return sidebar
1362
1363 redef fun tpl_title do
1364 return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}"
1365 end
1366
1367 redef fun page_url do return mproperty.nitdoc_url
1368
1369 redef fun tpl_topmenu do
1370 var topmenu = super
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)
1377 return topmenu
1378 end
1379
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
1386 end
1387 section.add_child article
1388 return section
1389 end
1390
1391 private fun tpl_properties(parent: TplSection) do
1392 # intro title
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
1397
1398 # concerns
1399 if concerns.is_empty then return
1400 parent.add_child new TplArticle.with_content("Concerns", "Concerns", concerns.to_tpl)
1401
1402 # redef list
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
1412 title.add "in "
1413 title.add mentity.tpl_namespace
1414 ssection.title = title
1415 ssection.summary_title = "in {mentity.nitdoc_name}"
1416
1417 # properties
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)
1422 end
1423 parent.add_child ssection
1424 end
1425 end
1426 end
1427
1428 redef fun tpl_content do
1429 var top = tpl_intro
1430 tpl_properties(top)
1431 tpl_page.add_section top
1432 end
1433
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
1440 end
1441 return map
1442 end
1443 end
1444