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