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