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