nitdoc: Factorize filtering logic.
[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 private import json::static
21
22 redef class ToolContext
23 private var opt_dir = new OptionString("output directory", "-d", "--dir")
24 private var opt_source = new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source")
25 private var opt_sharedir = new OptionString("directory containing nitdoc assets", "--sharedir")
26 private var opt_shareurl = new OptionString("use shareurl instead of copy shared files", "--shareurl")
27 private var opt_nodot = new OptionBool("do not generate graphes with graphviz", "--no-dot")
28 private var opt_private = new OptionBool("also generate private API", "--private")
29
30 private var opt_custom_title = new OptionString("custom title for homepage", "--custom-title")
31 private var opt_custom_brand = new OptionString("custom link to external site", "--custom-brand")
32 private var opt_custom_intro = new OptionString("custom intro text for homepage", "--custom-overview-text")
33 private var opt_custom_footer = new OptionString("custom footer text", "--custom-footer-text")
34
35 private var opt_github_upstream = new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream")
36 private var opt_github_base_sha1 = new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1")
37 private var opt_github_gitdir = new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir")
38
39 private var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker")
40 private var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id")
41
42 private var output_dir: String
43 private var min_visibility: MVisibility
44
45 redef init do
46 super
47
48 var opts = 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)
53
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 tooldescription = tpl.write_to_string
58 end
59
60 redef fun process_options(args) do
61 super
62
63 # output dir
64 var output_dir = opt_dir.value
65 if output_dir == null then
66 output_dir = "doc"
67 end
68 self.output_dir = output_dir
69 # min visibility
70 if opt_private.value then
71 min_visibility = none_visibility
72 else
73 min_visibility = protected_visibility
74 end
75 # github urls
76 var gh_upstream = opt_github_upstream.value
77 var gh_base_sha = opt_github_base_sha1.value
78 var gh_gitdir = opt_github_gitdir.value
79 if not gh_upstream == null or not gh_base_sha == null or not gh_gitdir == null then
80 if gh_upstream == null or gh_base_sha == null or gh_gitdir == null then
81 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"
82 abort
83 end
84 end
85 end
86
87 # Filter the entity based on the options specified by the user.
88 #
89 # Return `true` if the specified entity has to be included in the generated
90 # documentation
91 private fun filter_mclass(mclass: MClass): Bool do
92 return mclass.visibility >= min_visibility
93 end
94
95 # Filter the entity based on the options specified by the user.
96 #
97 # Return `true` if the specified entity has to be included in the generated
98 # documentation
99 private fun filter_mproperty(mproperty: MProperty): Bool do
100 return mproperty.visibility >= min_visibility and
101 not mproperty isa MAttribute
102 end
103 end
104
105 # The Nitdoc class explores the model and generate pages for each mentities found
106 class Nitdoc
107 var ctx: ToolContext
108 var model: Model
109 var mainmodule: MModule
110
111 fun generate do
112 init_output_dir
113 overview
114 search
115 groups
116 modules
117 classes
118 properties
119 quicksearch_list
120 end
121
122 private fun init_output_dir do
123 # create destination dir if it's necessary
124 var output_dir = ctx.output_dir
125 if not output_dir.file_exists then output_dir.mkdir
126 # locate share dir
127 var sharedir = ctx.opt_sharedir.value
128 if sharedir == null then
129 var dir = ctx.nit_dir
130 sharedir = dir/"share/nitdoc"
131 if not sharedir.file_exists then
132 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
133 abort
134 end
135 end
136 # copy shared files
137 if ctx.opt_shareurl.value == null then
138 sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/")
139 else
140 sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/resources/ {output_dir.to_s.escape_to_sh}/resources/")
141 end
142
143 end
144
145 private fun overview do
146 var page = new NitdocOverview(ctx, model, mainmodule)
147 page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
148 end
149
150 private fun search do
151 var page = new NitdocSearch(ctx, model, mainmodule)
152 page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
153 end
154
155 private fun groups do
156 for mproject in model.mprojects do
157 for mgroup in mproject.mgroups.to_a do
158 var page = new NitdocGroup(ctx, model, mainmodule, mgroup)
159 page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
160 end
161 end
162 end
163
164 private fun modules do
165 for mmodule in model.mmodules do
166 if mmodule.is_fictive then continue
167 var page = new NitdocModule(ctx, model, mainmodule, mmodule)
168 page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
169 end
170 end
171
172 private fun classes do
173 for mclass in model.mclasses do
174 if not ctx.filter_mclass(mclass) then continue
175 var page = new NitdocClass(ctx, model, mainmodule, mclass)
176 page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
177 end
178 end
179
180 private fun properties do
181 for mproperty in model.mproperties do
182 if not ctx.filter_mproperty(mproperty) then continue
183 if mproperty isa MInnerClass then continue
184 var page = new NitdocProperty(ctx, model, mainmodule, mproperty)
185 page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
186 end
187 end
188
189 private fun quicksearch_list do
190 var quicksearch = new QuickSearch(ctx, model)
191 quicksearch.render.write_to_file("{ctx.output_dir.to_s}/quicksearch-list.js")
192 end
193 end
194
195 # Nitdoc QuickSearch list generator
196 #
197 # Create a JSON object containing links to:
198 # * modules
199 # * mclasses
200 # * mpropdefs
201 # All entities are grouped by name to make the research easier.
202 class QuickSearch
203
204 private var table = new QuickSearchTable
205
206 var ctx: ToolContext
207 var model: Model
208
209 init do
210 for mmodule in model.mmodules do
211 if mmodule.is_fictive then continue
212 add_result_for(mmodule.name, mmodule.full_name, mmodule.nitdoc_url)
213 end
214 for mclass in model.mclasses do
215 if not ctx.filter_mclass(mclass) then continue
216 add_result_for(mclass.name, mclass.full_name, mclass.nitdoc_url)
217 end
218 for mproperty in model.mproperties do
219 if not ctx.filter_mproperty(mproperty) then continue
220 for mpropdef in mproperty.mpropdefs do
221 var full_name = mpropdef.mclassdef.mclass.full_name
222 var cls_url = mpropdef.mclassdef.mclass.nitdoc_url
223 var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}"
224 add_result_for(mproperty.name, full_name, def_url)
225 end
226 end
227 end
228
229 private fun add_result_for(query: String, txt: String, url: String) do
230 table[query].add new QuickSearchResult(txt, url)
231 end
232
233 fun render: Template do
234 var tpl = new Template
235 var buffer = new RopeBuffer
236 tpl.add buffer
237 buffer.append "var nitdocQuickSearchRawList="
238 table.append_json buffer
239 buffer.append ";"
240 return tpl
241 end
242 end
243
244 # The result map for QuickSearch.
245 private class QuickSearchTable
246 super JsonMapRead[String, QuickSearchResultList]
247 super HashMap[String, QuickSearchResultList]
248
249 redef fun provide_default_value(key) do
250 var v = new QuickSearchResultList
251 self[key] = v
252 return v
253 end
254 end
255
256 # A QuickSearch result list.
257 private class QuickSearchResultList
258 super JsonSequenceRead[QuickSearchResult]
259 super Array[QuickSearchResult]
260 end
261
262 # A QuickSearch result.
263 private class QuickSearchResult
264 super Jsonable
265
266 # The text of the link.
267 var txt: String
268
269 # The destination of the link.
270 var url: String
271
272 redef fun to_json do
273 return "\{\"txt\":{txt.to_json},\"url\":{url.to_json}\}"
274 end
275 end
276
277 # Nitdoc base page
278 # Define page structure and properties
279 abstract class NitdocPage
280
281 private var ctx: ToolContext
282 private var model: Model
283 private var mainmodule: MModule
284 private var name_sorter = new MEntityNameSorter
285
286 # Render the page as a html template
287 fun render: Template do
288 var shareurl = "."
289 if ctx.opt_shareurl.value != null then
290 shareurl = ctx.opt_shareurl.value.as(not null)
291 end
292
293 # build page
294 var tpl = tpl_page
295 tpl.title = tpl_title
296 tpl.url = page_url
297 tpl.shareurl = shareurl
298 tpl.topmenu = tpl_topmenu
299 tpl_content
300 tpl.footer = ctx.opt_custom_footer.value
301 tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl))
302 tpl.sidebar = tpl_sidebar
303
304 # piwik tracking
305 var tracker_url = ctx.opt_piwik_tracker.value
306 var site_id = ctx.opt_piwik_site_id.value
307 if tracker_url != null and site_id != null then
308 tpl.scripts.add new TplPiwikScript(tracker_url, site_id)
309 end
310 return tpl
311 end
312
313 # URL to this page.
314 fun page_url: String is abstract
315
316 # Build page template
317 fun tpl_page: TplPage is abstract
318
319 # Build page sidebar if any
320 fun tpl_sidebar: nullable TplSidebar do return null
321
322 # Build page title string
323 fun tpl_title: String do
324 if ctx.opt_custom_title.value != null then
325 return ctx.opt_custom_title.value.to_s
326 end
327 return "Nitdoc"
328 end
329
330 # Build top menu template
331 fun tpl_topmenu: TplTopMenu do
332 var topmenu = new TplTopMenu(page_url)
333 var brand = ctx.opt_custom_brand.value
334 if brand != null then
335 var tpl = new Template
336 tpl.add "<span class='navbar-brand'>"
337 tpl.add brand
338 tpl.add "</span>"
339 topmenu.brand = tpl
340 end
341 topmenu.add_link new TplLink("index.html", "Overview")
342 topmenu.add_link new TplLink("search.html", "Index")
343 return topmenu
344 end
345
346 # Build page content template
347 fun tpl_content is abstract
348
349 # Clickable graphviz image using dot format
350 # return null if no graph for this page
351 fun tpl_graph(dot: Buffer, name: String, title: nullable String): nullable TplArticle do
352 if ctx.opt_nodot.value then return null
353 var output_dir = ctx.output_dir
354 var path = output_dir / name
355 var path_sh = path.escape_to_sh
356 var file = new OFStream.open("{path}.dot")
357 file.write(dot)
358 file.close
359 sys.system("\{ test -f {path_sh}.png && test -f {path_sh}.s.dot && diff -- {path_sh}.dot {path_sh}.s.dot >/dev/null 2>&1 ; \} || \{ cp -- {path_sh}.dot {path_sh}.s.dot && dot -Tpng -o{path_sh}.png -Tcmapx -o{path_sh}.map {path_sh}.s.dot ; \}")
360 var fmap = new IFStream.open("{path}.map")
361 var map = fmap.read_all
362 fmap.close
363
364 var article = new TplArticle("graph")
365 var alt = ""
366 if title != null then
367 article.title = title
368 alt = "alt='{title.html_escape}'"
369 end
370 article.css_classes.add "text-center"
371 var content = new Template
372 var name_html = name.html_escape
373 content.add "<img src='{name_html}.png' usemap='#{name_html}' style='margin:auto' {alt}/>"
374 content.add map
375 article.content = content
376 return article
377 end
378
379 # A (source) link template for a given location
380 fun tpl_showsource(location: nullable Location): nullable String
381 do
382 if location == null then return null
383 var source = ctx.opt_source.value
384 if source == null then
385 var url = location.file.filename.simplify_path
386 return "<a target='_blank' title='Show source' href=\"{url}\">View Source</a>"
387 end
388 # THIS IS JUST UGLY ! (but there is no replace yet)
389 var x = source.split_with("%f")
390 source = x.join(location.file.filename.simplify_path)
391 x = source.split_with("%l")
392 source = x.join(location.line_start.to_s)
393 x = source.split_with("%L")
394 source = x.join(location.line_end.to_s)
395 source = source.simplify_path
396 return "<a target='_blank' title='Show source' href=\"{source.to_s}\">View Source</a>"
397 end
398
399 # MProject description template
400 fun tpl_mproject_article(mproject: MProject): TplArticle do
401 var article = mproject.tpl_article
402 article.subtitle = mproject.tpl_declaration
403 article.content = mproject.tpl_definition
404 if mproject.mdoc != null then
405 article.content = mproject.mdoc.tpl_short_comment
406 end
407 return article
408 end
409
410 # MGroup description template
411 fun tpl_mgroup_article(mgroup: MGroup): TplArticle do
412 var article = mgroup.tpl_article
413 article.subtitle = mgroup.tpl_declaration
414 article.content = mgroup.tpl_definition
415 return article
416 end
417
418 # MModule description template
419 fun tpl_mmodule_article(mmodule: MModule): TplArticle do
420 var article = mmodule.tpl_article
421 article.subtitle = mmodule.tpl_declaration
422 article.content = mmodule.tpl_definition
423 # mclassdefs list
424 var intros = mmodule.intro_mclassdefs(ctx.min_visibility).to_a
425 if not intros.is_empty then
426 mainmodule.linearize_mclassdefs(intros)
427 var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}_intros", "Introduces")
428 var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
429 for mclassdef in intros do
430 intros_lst.add_li mclassdef.tpl_list_item
431 end
432 if not intros_lst.is_empty then
433 intros_art.content = intros_lst
434 article.add_child intros_art
435 end
436 end
437 var redefs = mmodule.redef_mclassdefs(ctx.min_visibility).to_a
438 if not redefs.is_empty then
439 mainmodule.linearize_mclassdefs(redefs)
440 var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}_redefs", "Redefines")
441 var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
442 for mclassdef in redefs do
443 redefs_lst.add_li mclassdef.tpl_list_item
444 end
445 if not redefs_lst.is_empty then
446 redefs_art.content = redefs_lst
447 article.add_child redefs_art
448 end
449 end
450 return article
451 end
452
453 # MClassDef description template
454 fun tpl_mclass_article(mclass: MClass, mclassdefs: Array[MClassDef]): TplArticle do
455 var article = mclass.tpl_article
456 if not mclassdefs.has(mclass.intro) then
457 # add intro synopsys
458 var intro_article = mclass.intro.tpl_short_article
459 intro_article.source_link = tpl_showsource(mclass.intro.location)
460 article.add_child intro_article
461 end
462 mainmodule.linearize_mclassdefs(mclassdefs)
463 for mclassdef in mclassdefs do
464 # add mclassdef full description
465 var redef_article = mclassdef.tpl_article
466 redef_article.source_link = tpl_showsource(mclassdef.location)
467 article.add_child redef_article
468 # mpropdefs list
469 var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}_intros", "Introduces")
470 var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
471 for mpropdef in mclassdef.collect_intro_mpropdefs(ctx.min_visibility) do
472 intros_lst.add_li mpropdef.tpl_list_item
473 end
474 if not intros_lst.is_empty then
475 intros.content = intros_lst
476 redef_article.add_child intros
477 end
478 var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}_redefs", "Redefines")
479 var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
480 for mpropdef in mclassdef.collect_redef_mpropdefs(ctx.min_visibility) do
481 redefs_lst.add_li mpropdef.tpl_list_item
482 end
483 if not redefs_lst.is_empty then
484 redefs.content = redefs_lst
485 redef_article.add_child redefs
486 end
487 end
488 return article
489 end
490
491 # MClassDef description template
492 fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do
493 var article = mclassdef.tpl_article
494 if mclassdef.is_intro then article.content = null
495 article.source_link = tpl_showsource(mclassdef.location)
496 return article
497 end
498
499 # MProp description template
500 #
501 # `main_mpropdef`: The most important mpropdef to display
502 # `local_mpropdefs`: List of other locally defined mpropdefs to display
503 # `lin`: full linearization from local_mpropdefs to intro (displayed in redef tree)
504 fun tpl_mprop_article(main_mpropdef: MPropDef, local_mpropdefs: Array[MPropDef],
505 lin: Array[MPropDef]): TplArticle do
506 var mprop = main_mpropdef.mproperty
507 var article = new TplArticle(mprop.nitdoc_id)
508 var title = new Template
509 title.add mprop.tpl_icon
510 title.add "<span id='{main_mpropdef.nitdoc_id}'></span>"
511 if main_mpropdef.is_intro then
512 title.add mprop.tpl_link
513 title.add mprop.intro.tpl_signature
514 else
515 var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url
516 var def_url = "{cls_url}#{mprop.nitdoc_id}"
517 var lnk = new TplLink.with_title(def_url, mprop.name, "Go to introduction")
518 title.add "redef "
519 title.add lnk
520 end
521 article.title = title
522 article.title_classes.add "signature"
523 article.summary_title = "{mprop.nitdoc_name}"
524 article.subtitle = main_mpropdef.tpl_namespace
525 if main_mpropdef.mdoc != null then
526 article.content = main_mpropdef.mdoc.tpl_comment
527 end
528 var subarticle = new TplArticle("{main_mpropdef.nitdoc_id}_redefs")
529 # Add redef in same `MClass`
530 if local_mpropdefs.length > 1 then
531 for mpropdef in local_mpropdefs do
532 if mpropdef == main_mpropdef then continue
533 var redef_article = new TplArticle("{mpropdef.nitdoc_id}")
534 var redef_title = new Template
535 redef_title.add "also redef in "
536 redef_title.add mpropdef.tpl_namespace
537 redef_article.title = redef_title
538 redef_article.title_classes.add "signature info"
539 redef_article.css_classes.add "nospace"
540 var redef_content = new Template
541 if mpropdef.mdoc != null then
542 redef_content.add mpropdef.mdoc.tpl_comment
543 end
544 redef_article.content = redef_content
545 subarticle.add_child redef_article
546 end
547 end
548 # Add linearization
549 if lin.length > 1 then
550 var lin_article = new TplArticle("{main_mpropdef.nitdoc_id}_lin")
551 lin_article.title = "Inheritance"
552 var lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
553 for mpropdef in lin do
554 lst.add_li mpropdef.tpl_inheritance_item
555 end
556 lin_article.content = lst
557 subarticle.add_child lin_article
558 end
559 article.add_child subarticle
560 return article
561 end
562
563 # MProperty description template
564 fun tpl_mpropdef_article(mpropdef: MPropDef): TplArticle do
565 var article = mpropdef.tpl_article
566 article.source_link = tpl_showsource(mpropdef.location)
567 return article
568 end
569 end
570
571 # The overview page
572 # Display a list of modules contained in program
573 class NitdocOverview
574 super NitdocPage
575
576 private var page = new TplPage
577 redef fun tpl_page do return page
578
579 private var sidebar = new TplSidebar
580 redef fun tpl_sidebar do return sidebar
581
582 redef fun tpl_title do
583 if ctx.opt_custom_title.value != null then
584 return ctx.opt_custom_title.value.to_s
585 else
586 return "Overview"
587 end
588 end
589
590 redef fun page_url do return "index.html"
591
592 # intro text
593 private fun tpl_intro: TplSection do
594 var section = new TplSection.with_title("overview", tpl_title)
595 var article = new TplArticle("intro")
596 if ctx.opt_custom_intro.value != null then
597 article.content = ctx.opt_custom_intro.value.to_s
598 end
599 section.add_child article
600 return section
601 end
602
603 # projects list
604 private fun tpl_projects(section: TplSection) do
605 # Projects list
606 var mprojects = model.mprojects.to_a
607 var sorter = new MConcernRankSorter
608 sorter.sort mprojects
609 var ssection = new TplSection.with_title("projects", "Projects")
610 for mproject in mprojects do
611 ssection.add_child tpl_mproject_article(mproject)
612 end
613 section.add_child ssection
614 end
615
616 redef fun tpl_content do
617 var top = tpl_intro
618 tpl_projects(top)
619 tpl_page.add_section top
620 end
621 end
622
623 # The search page
624 # Display a list of modules, classes and properties
625 class NitdocSearch
626 super NitdocPage
627
628 private var page = new TplPage
629 redef fun tpl_page do return page
630
631 redef fun tpl_title do return "Index"
632
633 redef fun page_url do return "search.html"
634
635 redef fun tpl_content do
636 var tpl = new TplSearchPage("search_all")
637 var section = new TplSection("search")
638 # title
639 tpl.title = "Index"
640 # modules list
641 for mmodule in modules_list do
642 tpl.modules.add mmodule.tpl_link
643 end
644 # classes list
645 for mclass in classes_list do
646 tpl.classes.add mclass.tpl_link
647 end
648 # properties list
649 for mproperty in mprops_list do
650 var m = new Template
651 m.add mproperty.intro.tpl_link
652 m.add " ("
653 m.add mproperty.intro.mclassdef.mclass.tpl_link
654 m.add ")"
655 tpl.props.add m
656 end
657 section.add_child tpl
658 tpl_page.add_section section
659 end
660
661 # Extract mmodule list to display (sorted by name)
662 private fun modules_list: Array[MModule] do
663 var sorted = new Array[MModule]
664 for mmodule in model.mmodule_importation_hierarchy do
665 if mmodule.is_fictive then continue
666 sorted.add mmodule
667 end
668 name_sorter.sort(sorted)
669 return sorted
670 end
671
672 # Extract mclass list to display (sorted by name)
673 private fun classes_list: Array[MClass] do
674 var sorted = new Array[MClass]
675 for mclass in model.mclasses do
676 if not ctx.filter_mclass(mclass) then continue
677 sorted.add mclass
678 end
679 name_sorter.sort(sorted)
680 return sorted
681 end
682
683 # Extract mproperty list to display (sorted by name)
684 private fun mprops_list: Array[MProperty] do
685 var sorted = new Array[MProperty]
686 for mproperty in model.mproperties do
687 if ctx.filter_mproperty(mproperty) then sorted.add mproperty
688 end
689 name_sorter.sort(sorted)
690 return sorted
691 end
692 end
693
694 # A group page
695 # Display a flattened view of the group
696 class NitdocGroup
697 super NitdocPage
698
699 private var mgroup: MGroup
700
701 private var concerns: ConcernsTree is noinit
702 private var intros: Set[MClass] is noinit
703 private var redefs: Set[MClass] is noinit
704
705 init do
706 self.concerns = model.concerns_tree(mgroup.collect_mmodules)
707 self.concerns.sort_with(new MConcernRankSorter)
708 self.intros = mgroup.in_nesting_intro_mclasses(ctx.min_visibility)
709 var redefs = new HashSet[MClass]
710 for rdef in mgroup.in_nesting_redef_mclasses(ctx.min_visibility) do
711 if intros.has(rdef) then continue
712 redefs.add rdef
713 end
714 self.redefs = redefs
715 end
716
717 private var page = new TplPage
718 redef fun tpl_page do return page
719
720 private var sidebar = new TplSidebar
721 redef fun tpl_sidebar do return sidebar
722
723 redef fun tpl_title do return mgroup.nitdoc_name
724
725 redef fun page_url do return mgroup.nitdoc_url
726
727 redef fun tpl_topmenu do
728 var topmenu = super
729 var mproject = mgroup.mproject
730 if not mgroup.is_root then
731 topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
732 end
733 topmenu.add_link new TplLink(page_url, mproject.nitdoc_name)
734 return topmenu
735 end
736
737 # Class list to display in sidebar
738 fun tpl_sidebar_mclasses do
739 var mclasses = new HashSet[MClass]
740 mclasses.add_all intros
741 mclasses.add_all redefs
742 if mclasses.is_empty then return
743 var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
744
745 var sorted = mclasses.to_a
746 name_sorter.sort(sorted)
747 for mclass in sorted do
748 list.add_li tpl_sidebar_item(mclass)
749 end
750 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
751 end
752
753 private fun tpl_sidebar_item(def: MClass): TplListItem do
754 var classes = def.intro.tpl_css_classes.to_a
755 if intros.has(def) then
756 classes.add "intro"
757 else
758 classes.add "redef"
759 end
760 var lnk = new Template
761 lnk.add new TplLabel.with_classes(classes)
762 lnk.add def.tpl_link
763 return new TplListItem.with_content(lnk)
764 end
765
766 # intro text
767 private fun tpl_intro: TplSection do
768 var section = new TplSection.with_title("top", tpl_title)
769 var article = new TplArticle("intro")
770
771 if mgroup.is_root then
772 section.subtitle = mgroup.mproject.tpl_declaration
773 article.content = mgroup.mproject.tpl_definition
774 else
775 section.subtitle = mgroup.tpl_declaration
776 article.content = mgroup.tpl_definition
777 end
778 section.add_child article
779 return section
780 end
781
782 private fun tpl_concerns(section: TplSection) do
783 if concerns.is_empty then return
784 section.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
785 end
786
787 private fun tpl_groups(parent: TplSection) do
788 var lst = concerns.to_a
789 var section = parent
790 for mentity in lst do
791 if mentity isa MProject then
792 section.add_child new TplSection(mentity.nitdoc_id)
793 else if mentity isa MGroup then
794 section.add_child new TplSection(mentity.nitdoc_id)
795 else if mentity isa MModule then
796 section.add_child tpl_mmodule_article(mentity)
797 end
798 end
799 end
800
801 redef fun tpl_content do
802 tpl_sidebar_mclasses
803 var top = tpl_intro
804 tpl_concerns(top)
805 tpl_groups(top)
806 tpl_page.add_section top
807 end
808
809 private fun sort_by_mclass(mclassdefs: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
810 var map = new HashMap[MClass, Set[MClassDef]]
811 for mclassdef in mclassdefs do
812 var mclass = mclassdef.mclass
813 if not map.has_key(mclass) then map[mclass] = new HashSet[MClassDef]
814 map[mclass].add mclassdef
815 end
816 return map
817 end
818 end
819
820 # A module page
821 # Display the list of introduced and redefined classes in module
822 class NitdocModule
823 super NitdocPage
824
825 private var mmodule: MModule
826 private var concerns: ConcernsTree is noinit
827 private var mclasses2mdefs: Map[MClass, Set[MClassDef]] is noinit
828 private var mmodules2mclasses: Map[MModule, Set[MClass]] is noinit
829
830
831 init do
832 var mclassdefs = new HashSet[MClassDef]
833 mclassdefs.add_all mmodule.intro_mclassdefs(ctx.min_visibility)
834 mclassdefs.add_all mmodule.redef_mclassdefs(ctx.min_visibility)
835 self.mclasses2mdefs = sort_by_mclass(mclassdefs)
836 self.mmodules2mclasses = group_by_mmodule(mclasses2mdefs.keys)
837 self.concerns = model.concerns_tree(mmodules2mclasses.keys)
838 # rank concerns
839 mmodule.mgroup.mproject.booster_rank = -1000
840 mmodule.mgroup.booster_rank = -1000
841 mmodule.booster_rank = -1000
842 self.concerns.sort_with(new MConcernRankSorter)
843 mmodule.mgroup.mproject.booster_rank = 0
844 mmodule.mgroup.booster_rank = 0
845 mmodule.booster_rank = 0
846 end
847
848 private var page = new TplPage
849 redef fun tpl_page do return page
850
851 private var sidebar = new TplSidebar
852 redef fun tpl_sidebar do return sidebar
853
854 redef fun tpl_title do return mmodule.nitdoc_name
855 redef fun page_url do return mmodule.nitdoc_url
856
857 redef fun tpl_topmenu do
858 var topmenu = super
859 var mproject = mmodule.mgroup.mproject
860 topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name)
861 topmenu.add_link new TplLink(page_url, mmodule.nitdoc_name)
862 return topmenu
863 end
864
865 # Class list to display in sidebar
866 fun tpl_sidebar_mclasses do
867 var mclasses = new HashSet[MClass]
868 mclasses.add_all mmodule.filter_intro_mclasses(ctx.min_visibility)
869 mclasses.add_all mmodule.filter_redef_mclasses(ctx.min_visibility)
870 if mclasses.is_empty then return
871 var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
872
873 var sorted = mclasses.to_a
874 name_sorter.sort(sorted)
875 for mclass in sorted do
876 list.add_li tpl_sidebar_item(mclass)
877 end
878 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
879 end
880
881 private fun tpl_sidebar_item(def: MClass): TplListItem do
882 var classes = def.intro.tpl_css_classes.to_a
883 if def.intro_mmodule == mmodule then
884 classes.add "intro"
885 else
886 classes.add "redef"
887 end
888 var lnk = new Template
889 lnk.add new TplLabel.with_classes(classes)
890 lnk.add def.tpl_link
891 return new TplListItem.with_content(lnk)
892 end
893
894 # intro text
895 private fun tpl_intro: TplSection do
896 var section = new TplSection.with_title("top", tpl_title)
897 section.subtitle = mmodule.tpl_declaration
898
899 var article = new TplArticle("intro")
900 var def = mmodule.tpl_definition
901 var location = mmodule.location
902 article.source_link = tpl_showsource(location)
903 article.content = def
904 section.add_child article
905 return section
906 end
907
908 # inheritance section
909 private fun tpl_inheritance(parent: TplSection) do
910 # Extract relevent modules
911 var imports = mmodule.in_importation.greaters
912 if imports.length > 10 then imports = mmodule.in_importation.direct_greaters
913 var clients = mmodule.in_importation.smallers
914 if clients.length > 10 then clients = mmodule.in_importation.direct_smallers
915
916 # Display lists
917 var section = new TplSection.with_title("dependencies", "Dependencies")
918
919 # Graph
920 var mmodules = new HashSet[MModule]
921 mmodules.add_all mmodule.nested_mmodules
922 mmodules.add_all imports
923 if clients.length < 10 then mmodules.add_all clients
924 mmodules.add mmodule
925 var graph = tpl_dot(mmodules)
926 if graph != null then section.add_child graph
927
928 # Imports
929 var lst = new Array[MModule]
930 for dep in imports do
931 if dep.is_fictive then continue
932 if dep == mmodule then continue
933 lst.add(dep)
934 end
935 if not lst.is_empty then
936 name_sorter.sort lst
937 section.add_child tpl_list("imports", "Imports", lst)
938 end
939
940 # Clients
941 lst = new Array[MModule]
942 for dep in clients do
943 if dep.is_fictive then continue
944 if dep == mmodule then continue
945 lst.add(dep)
946 end
947 if not lst.is_empty then
948 name_sorter.sort lst
949 section.add_child tpl_list("clients", "Clients", lst)
950 end
951
952 parent.add_child section
953 end
954
955 private fun tpl_list(id: String, title: String, mmodules: Array[MModule]): TplArticle do
956 var article = new TplArticle.with_title(id, title)
957 var list = new TplList.with_classes(["list-unstyled", "list-definition"])
958 for mmodule in mmodules do list.elts.add mmodule.tpl_list_item
959 article.content = list
960 return article
961 end
962
963 private fun tpl_concerns(parent: TplSection) do
964 if concerns.is_empty then return
965 parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
966 end
967
968 private fun tpl_mclasses(parent: TplSection) do
969 for mentity in concerns do
970 if mentity isa MProject then
971 parent.add_child new TplSection(mentity.nitdoc_id)
972 else if mentity isa MGroup then
973 parent.add_child new TplSection(mentity.nitdoc_id)
974 else if mentity isa MModule then
975 var section = new TplSection(mentity.nitdoc_id)
976 var title = new Template
977 if mentity == mmodule then
978 title.add "in "
979 section.summary_title = "in {mentity.nitdoc_name}"
980 else
981 title.add "from "
982 section.summary_title = "from {mentity.nitdoc_name}"
983 end
984 title.add mentity.tpl_namespace
985 section.title = title
986
987 var mclasses = mmodules2mclasses[mentity].to_a
988 name_sorter.sort(mclasses)
989 for mclass in mclasses do
990 section.add_child tpl_mclass_article(mclass, mclasses2mdefs[mclass].to_a)
991 end
992 parent.add_child section
993 end
994 end
995 end
996
997 private fun group_by_mmodule(mclasses: Collection[MClass]): Map[MModule, Set[MClass]] do
998 var res = new HashMap[MModule, Set[MClass]]
999 for mclass in mclasses do
1000 var mmodule = mclass.intro_mmodule
1001 if not res.has_key(mmodule) then
1002 res[mmodule] = new HashSet[MClass]
1003 end
1004 res[mmodule].add(mclass)
1005 end
1006 return res
1007 end
1008
1009 redef fun tpl_content do
1010 tpl_sidebar_mclasses
1011 var top = tpl_intro
1012 tpl_inheritance(top)
1013 tpl_concerns(top)
1014 tpl_mclasses(top)
1015 tpl_page.add_section top
1016 end
1017
1018 # Genrate dot hierarchy for class inheritance
1019 fun tpl_dot(mmodules: Collection[MModule]): nullable TplArticle do
1020 var poset = new POSet[MModule]
1021 for mmodule in mmodules do
1022 if mmodule.is_fictive then continue
1023 poset.add_node mmodule
1024 for omodule in mmodules do
1025 if mmodule.is_fictive then continue
1026 poset.add_node mmodule
1027 if mmodule.in_importation < omodule then
1028 poset.add_edge(mmodule, omodule)
1029 end
1030 end
1031 end
1032 # build graph
1033 var op = new RopeBuffer
1034 var name = "dep_module_{mmodule.nitdoc_id}"
1035 op.append("digraph \"{name.escape_to_dot}\" \{ 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")
1036 for mmodule in poset do
1037 if mmodule == self.mmodule then
1038 op.append("\"{mmodule.name.escape_to_dot}\"[shape=box,margin=0.03];\n")
1039 else
1040 op.append("\"{mmodule.name.escape_to_dot}\"[URL=\"{mmodule.nitdoc_url.escape_to_dot}\"];\n")
1041 end
1042 for omodule in poset[mmodule].direct_greaters do
1043 op.append("\"{mmodule.name.escape_to_dot}\"->\"{omodule.name.escape_to_dot}\";\n")
1044 end
1045 end
1046 op.append("\}\n")
1047 return tpl_graph(op, name, null)
1048 end
1049
1050 private fun sort_by_mclass(mclassdefs: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
1051 var map = new HashMap[MClass, Set[MClassDef]]
1052 for mclassdef in mclassdefs do
1053 var mclass = mclassdef.mclass
1054 if not map.has_key(mclass) then map[mclass] = new HashSet[MClassDef]
1055 map[mclass].add mclassdef
1056 end
1057 return map
1058 end
1059 end
1060
1061 # A class page
1062 # Display a list properties defined or redefined for this class
1063 class NitdocClass
1064 super NitdocPage
1065
1066 private var mclass: MClass
1067 private var concerns: ConcernsTree is noinit
1068 private var mprops2mdefs: Map[MProperty, Set[MPropDef]] is noinit
1069 private var mmodules2mprops: Map[MModule, Set[MProperty]] is noinit
1070
1071 init do
1072 var mpropdefs = new HashSet[MPropDef]
1073 mpropdefs.add_all mclass.intro_mpropdefs(ctx.min_visibility)
1074 mpropdefs.add_all mclass.redef_mpropdefs(ctx.min_visibility)
1075 self.mprops2mdefs = sort_by_mproperty(mpropdefs)
1076 self.mmodules2mprops = sort_by_mmodule(mprops2mdefs.keys)
1077 self.concerns = model.concerns_tree(mmodules2mprops.keys)
1078 self.concerns.sort_with(new MConcernRankSorter)
1079 end
1080
1081 private var page = new TplPage
1082 redef fun tpl_page do return page
1083
1084 private var sidebar = new TplSidebar
1085 redef fun tpl_sidebar do return sidebar
1086
1087 redef fun tpl_title do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
1088 redef fun page_url do return mclass.nitdoc_url
1089
1090 redef fun tpl_topmenu do
1091 var topmenu = super
1092 var mproject = mclass.intro_mmodule.mgroup.mproject
1093 topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
1094 topmenu.add_link new TplLink(page_url, mclass.nitdoc_name)
1095 return topmenu
1096 end
1097
1098 # Property list to display in sidebar
1099 fun tpl_sidebar_properties do
1100 var by_kind = new PropertiesByKind.with_elements(mclass_inherited_mprops)
1101 var summary = new TplList.with_classes(["list-unstyled"])
1102
1103 by_kind.sort_groups(name_sorter)
1104 for g in by_kind.groups do tpl_sidebar_list(g, summary)
1105 tpl_sidebar.boxes.add new TplSideBox.with_content("All properties", summary)
1106 end
1107
1108 private fun tpl_sidebar_list(mprops: PropertyGroup[MProperty], summary: TplList) do
1109 if mprops.is_empty then return
1110 var entry = new TplListItem.with_content(mprops.title)
1111 var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
1112 for mprop in mprops do
1113 list.add_li tpl_sidebar_item(mprop)
1114 end
1115 entry.append list
1116 summary.elts.add entry
1117 end
1118
1119 private fun tpl_sidebar_item(mprop: MProperty): TplListItem do
1120 var classes = mprop.intro.tpl_css_classes.to_a
1121 if not mprops2mdefs.has_key(mprop) then
1122 classes.add "inherit"
1123 var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url
1124 var def_url = "{cls_url}#{mprop.nitdoc_id}"
1125 var lnk = new TplLink(def_url, mprop.name)
1126 if mprop.intro.mdoc != null then lnk.title = mprop.intro.mdoc.short_comment
1127 var item = new Template
1128 item.add new TplLabel.with_classes(classes)
1129 item.add lnk
1130 return new TplListItem.with_content(item)
1131 end
1132 var defs = mprops2mdefs[mprop]
1133 if defs.has(mprop.intro) then
1134 classes.add "intro"
1135 else
1136 classes.add "redef"
1137 end
1138 var lnk = new Template
1139 lnk.add new TplLabel.with_classes(classes)
1140 lnk.add mprop.tpl_anchor
1141 return new TplListItem.with_content(lnk)
1142 end
1143
1144 private fun tpl_intro: TplSection do
1145 var section = new TplSection.with_title("top", tpl_title)
1146 section.subtitle = mclass.intro.tpl_declaration
1147 var article = new TplArticle("comment")
1148 if mclass.mdoc != null then
1149 article.content = mclass.mdoc.tpl_comment
1150 end
1151 section.add_child article
1152 return section
1153 end
1154
1155 private fun tpl_concerns(parent: TplSection) do
1156 # intro title
1157 var section = new TplSection.with_title("intro", "Introduction")
1158 section.summary_title = "Introduction"
1159 section.add_child tpl_mclassdef_article(mclass.intro)
1160 parent.add_child section
1161 # concerns
1162 if concerns.is_empty then return
1163 parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
1164 end
1165
1166 private fun tpl_inheritance(parent: TplSection) do
1167 # parents
1168 var hparents = new HashSet[MClass]
1169 for c in mclass.in_hierarchy(mainmodule).direct_greaters do
1170 if ctx.filter_mclass(c) then hparents.add c
1171 end
1172
1173 # ancestors
1174 var hancestors = new HashSet[MClass]
1175 for c in mclass.in_hierarchy(mainmodule).greaters do
1176 if c == mclass then continue
1177 if not ctx.filter_mclass(c) then continue
1178 if hparents.has(c) then continue
1179 hancestors.add c
1180 end
1181
1182 # children
1183 var hchildren = new HashSet[MClass]
1184 for c in mclass.in_hierarchy(mainmodule).direct_smallers do
1185 if ctx.filter_mclass(c) then hchildren.add c
1186 end
1187
1188 # descendants
1189 var hdescendants = new HashSet[MClass]
1190 for c in mclass.in_hierarchy(mainmodule).smallers do
1191 if c == mclass then continue
1192 if not ctx.filter_mclass(c) then continue
1193 if hchildren.has(c) then continue
1194 hdescendants.add c
1195 end
1196
1197 # Display lists
1198 var section = new TplSection.with_title("inheritance", "Inheritance")
1199
1200 # Graph
1201 var mclasses = new HashSet[MClass]
1202 mclasses.add_all hancestors
1203 mclasses.add_all hparents
1204 mclasses.add_all hchildren
1205 mclasses.add_all hdescendants
1206 mclasses.add mclass
1207 var graph = tpl_dot(mclasses)
1208 if graph != null then section.add_child graph
1209
1210 # parents
1211 if not hparents.is_empty then
1212 var lst = hparents.to_a
1213 name_sorter.sort lst
1214 section.add_child tpl_list("parents", "Parents", lst)
1215 end
1216
1217 # ancestors
1218 if not hancestors.is_empty then
1219 var lst = hancestors.to_a
1220 name_sorter.sort lst
1221 section.add_child tpl_list("ancestors", "Ancestors", lst)
1222 end
1223
1224 # children
1225 if not hchildren.is_empty then
1226 var lst = hchildren.to_a
1227 name_sorter.sort lst
1228 section.add_child tpl_list("children", "Children", lst)
1229 end
1230
1231 # descendants
1232 if not hdescendants.is_empty then
1233 var lst = hdescendants.to_a
1234 name_sorter.sort lst
1235 section.add_child tpl_list("descendants", "Descendants", lst)
1236 end
1237
1238 parent.add_child section
1239 end
1240
1241 private fun tpl_list(id: String, title: String, elts: Array[MClass]): TplArticle do
1242 var article = new TplArticle.with_title(id, title)
1243 if elts.length > 20 then
1244 var tpl = new Template
1245 for e in elts do
1246 tpl.add e.tpl_link
1247 if e != elts.last then tpl.add ", "
1248 end
1249 article.content = tpl
1250 else
1251 var list = new TplList.with_classes(["list-unstyled", "list-definition"])
1252 for elt in elts do list.elts.add elt.tpl_list_item
1253 article.content = list
1254 end
1255 return article
1256 end
1257
1258 private fun tpl_properties(parent: TplSection) do
1259 var lst = concerns.to_a
1260 for mentity in lst do
1261 if mentity isa MProject then
1262 parent.add_child new TplSection(mentity.nitdoc_id)
1263 else if mentity isa MGroup then
1264 parent.add_child new TplSection(mentity.nitdoc_id)
1265 else if mentity isa MModule then
1266 var section = new TplSection(mentity.nitdoc_id)
1267 var title = new Template
1268 title.add "in "
1269 title.add mentity.tpl_namespace
1270 section.title = title
1271 section.summary_title = "in {mentity.nitdoc_name}"
1272
1273 # properties
1274 var mprops = mmodules2mprops[mentity]
1275 var by_kind = new PropertiesByKind.with_elements(mprops)
1276
1277 for g in by_kind.groups do
1278 for article in tpl_mproperty_articles(g) do
1279 section.add_child article
1280 end
1281 end
1282 parent.add_child section
1283 end
1284 end
1285 end
1286
1287 private fun tpl_mproperty_articles(elts: Collection[MProperty]):
1288 Sequence[TplArticle] do
1289 var articles = new List[TplArticle]
1290 for elt in elts do
1291 var local_defs = mprops2mdefs[elt]
1292 # var all_defs = elt.mpropdefs
1293 var all_defs = new HashSet[MPropDef]
1294 for local_def in local_defs do
1295 all_defs.add local_def
1296 var mpropdef = local_def
1297 while not mpropdef.is_intro do
1298 mpropdef = mpropdef.lookup_next_definition(mainmodule, mpropdef.mclassdef.bound_mtype)
1299 all_defs.add mpropdef
1300 end
1301 end
1302 var loc_lin = local_defs.to_a
1303 mainmodule.linearize_mpropdefs(loc_lin)
1304 var all_lin = all_defs.to_a
1305 mainmodule.linearize_mpropdefs(all_lin)
1306 articles.add tpl_mprop_article(loc_lin.first, loc_lin, all_lin)
1307 end
1308 return articles
1309 end
1310
1311 redef fun tpl_content do
1312 tpl_sidebar_properties
1313 var top = tpl_intro
1314 tpl_inheritance(top)
1315 tpl_concerns(top)
1316 tpl_properties(top)
1317 tpl_page.add_section top
1318 end
1319
1320 private fun sort_by_mproperty(mpropdefs: Collection[MPropDef]): Map[MProperty, Set[MPropDef]] do
1321 var map = new HashMap[MProperty, Set[MPropDef]]
1322 for mpropdef in mpropdefs do
1323 var mproperty = mpropdef.mproperty
1324 if not map.has_key(mproperty) then map[mproperty] = new HashSet[MPropDef]
1325 map[mproperty].add mpropdef
1326 end
1327 return map
1328 end
1329
1330 private fun sort_by_mmodule(mprops: Collection[MProperty]): Map[MModule, Set[MProperty]] do
1331 var map = new HashMap[MModule, Set[MProperty]]
1332 for mprop in mprops do
1333 var mpropdefs = mprops2mdefs[mprop].to_a
1334 mainmodule.linearize_mpropdefs(mpropdefs)
1335 var mmodule = mpropdefs.first.mclassdef.mmodule
1336 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MProperty]
1337 map[mmodule].add mprop
1338 end
1339 return map
1340 end
1341
1342 private fun mclass_inherited_mprops: Set[MProperty] do
1343 var res = new HashSet[MProperty]
1344 var local = mclass.local_mproperties(ctx.min_visibility)
1345 for mprop in mclass.inherited_mproperties(mainmodule, ctx.min_visibility) do
1346 if local.has(mprop) then continue
1347 #if mprop isa MMethod and mprop.is_init then continue
1348 if mprop.intro.mclassdef.mclass.name == "Object" and
1349 (mprop.visibility == protected_visibility or
1350 mprop.intro.mclassdef.mmodule.name != "kernel") then continue
1351 res.add mprop
1352 end
1353 res.add_all local
1354 return res
1355 end
1356
1357 private fun collect_mmodules(mprops: Collection[MProperty]): Set[MModule] do
1358 var res = new HashSet[MModule]
1359 for mprop in mprops do
1360 if mprops2mdefs.has_key(mprop) then
1361 for mpropdef in mprops2mdefs[mprop] do res.add mpropdef.mclassdef.mmodule
1362 end
1363 end
1364 return res
1365 end
1366
1367 # Generate dot hierarchy for classes
1368 fun tpl_dot(mclasses: Collection[MClass]): nullable TplArticle do
1369 var poset = new POSet[MClass]
1370
1371 for mclass in mclasses do
1372 poset.add_node mclass
1373 for oclass in mclasses do
1374 if mclass == oclass then continue
1375 poset.add_node oclass
1376 if mclass.in_hierarchy(mainmodule) < oclass then
1377 poset.add_edge(mclass, oclass)
1378 end
1379 end
1380 end
1381
1382 var op = new RopeBuffer
1383 var name = "dep_class_{mclass.nitdoc_id}"
1384 op.append("digraph \"{name.escape_to_dot}\" \{ 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")
1385 var classes = poset.to_a
1386 var todo = new Array[MClass]
1387 var done = new HashSet[MClass]
1388 mainmodule.linearize_mclasses(classes)
1389 if not classes.is_empty then todo.add classes.first
1390 while not todo.is_empty do
1391 var c = todo.shift
1392 if done.has(c) then continue
1393 done.add c
1394 if c == mclass then
1395 op.append("\"{c.name.escape_to_dot}\"[shape=box,margin=0.03];\n")
1396 else
1397 op.append("\"{c.name.escape_to_dot}\"[URL=\"{c.nitdoc_url.escape_to_dot}\"];\n")
1398 end
1399 var smallers = poset[c].direct_smallers
1400 if smallers.length < 10 then
1401 for c2 in smallers do
1402 op.append("\"{c2.name.escape_to_dot}\"->\"{c.name.escape_to_dot}\";\n")
1403 end
1404 todo.add_all smallers
1405 else
1406 op.append("\"...\"->\"{c.name.escape_to_dot}\";\n")
1407 end
1408 end
1409 op.append("\}\n")
1410 return tpl_graph(op, name, null)
1411 end
1412 end
1413
1414 # Groups properties by kind.
1415 private class PropertiesByKind
1416 # The virtual types.
1417 var virtual_types = new PropertyGroup[MVirtualTypeProp]("Virtual types")
1418
1419 # The constructors.
1420 var constructors = new PropertyGroup[MMethod]("Contructors")
1421
1422 # The attributes.
1423 var attributes = new PropertyGroup[MAttribute]("Attributes")
1424
1425 # The methods.
1426 var methods = new PropertyGroup[MMethod]("Methods")
1427
1428 # The inner classes.
1429 var inner_classes = new PropertyGroup[MInnerClass]("Inner classes")
1430
1431 # All the groups.
1432 #
1433 # Sorted in the order they are displayed to the user.
1434 var groups: SequenceRead[PropertyGroup[MProperty]] = [
1435 virtual_types,
1436 constructors,
1437 attributes,
1438 methods,
1439 inner_classes: PropertyGroup[MProperty]]
1440
1441 # Add each the specified property to the appropriate list.
1442 init with_elements(properties: Collection[MProperty]) do add_all(properties)
1443
1444 # Add the specified property to the appropriate list.
1445 fun add(property: MProperty) do
1446 if property isa MMethod then
1447 if property.is_init then
1448 constructors.add property
1449 else
1450 methods.add property
1451 end
1452 else if property isa MVirtualTypeProp then
1453 virtual_types.add property
1454 else if property isa MAttribute then
1455 attributes.add property
1456 else if property isa MInnerClass then
1457 inner_classes.add property
1458 else
1459 abort
1460 end
1461 end
1462
1463 # Add each the specified property to the appropriate list.
1464 fun add_all(properties: Collection[MProperty]) do
1465 for p in properties do add(p)
1466 end
1467
1468 # Sort each group with the specified comparator.
1469 fun sort_groups(comparator: Comparator) do
1470 for g in groups do comparator.sort(g)
1471 end
1472 end
1473
1474 # A Group of properties of the same kind.
1475 private class PropertyGroup[E: MProperty]
1476 super Array[E]
1477
1478 # The title of the group, as displayed to the user.
1479 var title: String
1480 end
1481
1482 # A MProperty page
1483 class NitdocProperty
1484 super NitdocPage
1485
1486 private var mproperty: MProperty
1487 private var concerns: ConcernsTree is noinit
1488 private var mmodules2mdefs: Map[MModule, Set[MPropDef]] is noinit
1489
1490 init do
1491 self.mproperty = mproperty
1492 self.mmodules2mdefs = sort_by_mmodule(collect_mpropdefs)
1493 self.concerns = model.concerns_tree(mmodules2mdefs.keys)
1494 self.concerns.sort_with(new MConcernRankSorter)
1495 end
1496
1497 private fun collect_mpropdefs: Set[MPropDef] do
1498 var res = new HashSet[MPropDef]
1499 for mpropdef in mproperty.mpropdefs do
1500 if not mpropdef.is_intro then res.add mpropdef
1501 end
1502 return res
1503 end
1504
1505 private var page = new TplPage
1506 redef fun tpl_page do return page
1507
1508 private var sidebar = new TplSidebar
1509 redef fun tpl_sidebar do return sidebar
1510
1511 redef fun tpl_title do
1512 return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}"
1513 end
1514
1515 redef fun page_url do return mproperty.nitdoc_url
1516
1517 redef fun tpl_topmenu do
1518 var topmenu = super
1519 var mmodule = mproperty.intro_mclassdef.mmodule
1520 var mproject = mmodule.mgroup.mproject
1521 var mclass = mproperty.intro_mclassdef.mclass
1522 topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
1523 topmenu.add_link new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}")
1524 topmenu.add_link new TplLink(page_url, mproperty.nitdoc_name)
1525 return topmenu
1526 end
1527
1528 private fun tpl_intro: TplSection do
1529 var title = new Template
1530 title.add mproperty.nitdoc_name
1531 title.add mproperty.intro.tpl_signature
1532 var section = new TplSection.with_title("top", title)
1533 section.subtitle = mproperty.tpl_namespace
1534 section.summary_title = mproperty.nitdoc_name
1535 return section
1536 end
1537
1538 private fun tpl_properties(parent: TplSection) do
1539 # intro title
1540 var ns = mproperty.intro.mclassdef.mmodule.tpl_namespace
1541 var section = new TplSection("intro")
1542 var title = new Template
1543 title.add "Introduction in "
1544 title.add ns
1545 section.title = title
1546 section.summary_title = "Introduction"
1547 section.add_child tpl_mpropdef_article(mproperty.intro)
1548 parent.add_child section
1549
1550 # concerns
1551 if concerns.is_empty then return
1552 parent.add_child new TplArticle.with_content("Concerns", "Concerns", concerns.to_tpl)
1553
1554 # redef list
1555 var lst = concerns.to_a
1556 for mentity in lst do
1557 if mentity isa MProject then
1558 parent.add_child new TplSection(mentity.nitdoc_id)
1559 else if mentity isa MGroup then
1560 parent.add_child new TplSection(mentity.nitdoc_id)
1561 else if mentity isa MModule then
1562 var ssection = new TplSection(mentity.nitdoc_id)
1563 title = new Template
1564 title.add "in "
1565 title.add mentity.tpl_namespace
1566 ssection.title = title
1567 ssection.summary_title = "in {mentity.nitdoc_name}"
1568
1569 # properties
1570 var mpropdefs = mmodules2mdefs[mentity].to_a
1571 name_sorter.sort(mpropdefs)
1572 for mpropdef in mpropdefs do
1573 ssection.add_child tpl_mpropdef_article(mpropdef)
1574 end
1575 parent.add_child ssection
1576 end
1577 end
1578 end
1579
1580 redef fun tpl_content do
1581 var top = tpl_intro
1582 tpl_properties(top)
1583 tpl_page.add_section top
1584 end
1585
1586 private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do
1587 var map = new HashMap[MModule, Set[MPropDef]]
1588 for mpropdef in mpropdefs do
1589 var mmodule = mpropdef.mclassdef.mmodule
1590 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef]
1591 map[mmodule].add mpropdef
1592 end
1593 return map
1594 end
1595 end
1596