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