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