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