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