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