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