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