nitdoc: extract NitdocContext from Nitdoc generation
[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 output_dir: String
42 private var min_visibility: MVisibility
43 var toolcontext: ToolContext
44
45 init(toolcontext: ToolContext) do
46 self.toolcontext = toolcontext
47
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 end
59
60 fun process_options(args: Sequence[String]) do
61 toolcontext.process_options(args)
62 # output dir
63 var output_dir = opt_dir.value
64 if output_dir == null then
65 output_dir = "doc"
66 end
67 self.output_dir = output_dir
68 # min visibility
69 if opt_private.value then
70 min_visibility = none_visibility
71 else
72 min_visibility = protected_visibility
73 end
74 # github urls
75 var gh_upstream = opt_github_upstream.value
76 var gh_base_sha = opt_github_base_sha1.value
77 var gh_gitdir = opt_github_gitdir.value
78 if not gh_upstream == null or not gh_base_sha == null or not gh_gitdir == null then
79 if gh_upstream == null or gh_base_sha == null or gh_gitdir == null then
80 print "Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin"
81 abort
82 end
83 end
84 end
85 end
86
87 # The Nitdoc class explores the model and generate pages for each mentities found
88 class Nitdoc
89 var model: Model
90 var mainmodule: MModule
91 var ctx: NitdocContext
92
93 init(ctx: NitdocContext, model: Model, mainmodule: MModule) do
94 self.ctx = ctx
95 self.model = model
96 self.mainmodule = mainmodule
97 end
98
99 fun generate do
100 init_output_dir
101 overview
102 search
103 groups
104 modules
105 classes
106 properties
107 quicksearch_list
108 end
109
110 private fun init_output_dir do
111 # create destination dir if it's necessary
112 var output_dir = ctx.output_dir
113 if not output_dir.file_exists then output_dir.mkdir
114 # locate share dir
115 var sharedir = ctx.opt_sharedir.value
116 if sharedir == null then
117 var dir = ctx.toolcontext.nit_dir
118 if dir == null then
119 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
120 abort
121 end
122 sharedir = "{dir}/share/nitdoc"
123 if not sharedir.file_exists then
124 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
125 abort
126 end
127 end
128 # copy shared files
129 if ctx.opt_shareurl.value == null then
130 sys.system("cp -r {sharedir.to_s}/* {output_dir.to_s}/")
131 else
132 sys.system("cp -r {sharedir.to_s}/resources/ {output_dir.to_s}/resources/")
133 end
134
135 end
136
137 private fun overview do
138 var overviewpage = new NitdocOverview(ctx, model, mainmodule)
139 overviewpage.render.write_to_file("{ctx.output_dir.to_s}/index.html")
140 end
141
142 private fun search do
143 var searchpage = new NitdocSearch(ctx, model, mainmodule)
144 searchpage.render.write_to_file("{ctx.output_dir.to_s}/search.html")
145 end
146
147 private fun groups do
148 for mproject in model.mprojects do
149 for mgroup in mproject.mgroups.to_a do
150 var page = new NitdocGroup(ctx, model, mainmodule, mgroup)
151 page.render.write_to_file("{ctx.output_dir.to_s}/{mgroup.nitdoc_url}")
152 end
153 end
154 end
155
156 private fun modules do
157 for mmodule in model.mmodules do
158 if mmodule.name == "<main>" then continue
159 var modulepage = new NitdocModule(ctx, model, mainmodule, mmodule)
160 modulepage.render.write_to_file("{ctx.output_dir.to_s}/{mmodule.nitdoc_url}")
161 end
162 end
163
164 private fun classes do
165 for mclass in model.mclasses do
166 var classpage = new NitdocClass(ctx, model, mainmodule, mclass)
167 classpage.render.write_to_file("{ctx.output_dir.to_s}/{mclass.nitdoc_url}")
168 end
169 end
170
171 private fun properties do
172 for mproperty in model.mproperties do
173 var page = new NitdocProperty(ctx, model, mainmodule, mproperty)
174 page.render.write_to_file("{ctx.output_dir.to_s}/{mproperty.nitdoc_url}")
175 end
176 end
177
178 private fun quicksearch_list do
179 var quicksearch = new QuickSearch(ctx, model)
180 quicksearch.render.write_to_file("{ctx.output_dir.to_s}/quicksearch-list.js")
181 end
182 end
183
184 # Nitdoc QuickSearch list generator
185 #
186 # Create a JSON object containing links to:
187 # * modules
188 # * mclasses
189 # * mpropdefs
190 # All entities are grouped by name to make the research easier.
191 class QuickSearch
192
193 private var mmodules = new HashSet[MModule]
194 private var mclasses = new HashSet[MClass]
195 private var mpropdefs = new HashMap[String, Set[MPropDef]]
196
197 init(ctx: NitdocContext, model: Model) do
198 for mmodule in model.mmodules do
199 if mmodule.name == "<main>" then continue
200 mmodules.add mmodule
201 end
202 for mclass in model.mclasses do
203 if mclass.visibility < ctx.min_visibility then continue
204 mclasses.add mclass
205 end
206 for mproperty in model.mproperties do
207 if mproperty.visibility < ctx.min_visibility then continue
208 if mproperty isa MAttribute then continue
209 if not mpropdefs.has_key(mproperty.name) then
210 mpropdefs[mproperty.name] = new HashSet[MPropDef]
211 end
212 mpropdefs[mproperty.name].add_all(mproperty.mpropdefs)
213 end
214 end
215
216 fun render: Template do
217 var tpl = new Template
218 tpl.add "var nitdocQuickSearchRawList=\{ "
219 for mmodule in mmodules do
220 tpl.add "\"{mmodule.name}\":["
221 tpl.add "\{txt:\"{mmodule.full_name}\",url:\"{mmodule.nitdoc_url}\"\},"
222 tpl.add "],"
223 end
224 for mclass in mclasses do
225 var full_name = mclass.intro.mmodule.full_name
226 tpl.add "\"{mclass.name}\":["
227 tpl.add "\{txt:\"{full_name}\",url:\"{mclass.nitdoc_url}\"\},"
228 tpl.add "],"
229 end
230 for mproperty, mprops in mpropdefs do
231 tpl.add "\"{mproperty}\":["
232 for mpropdef in mprops do
233 var full_name = mpropdef.mclassdef.mclass.full_name
234 tpl.add "\{txt:\"{full_name}\",url:\"{mpropdef.nitdoc_url}\"\},"
235 end
236 tpl.add "],"
237 end
238 tpl.add " \};"
239 return tpl
240 end
241 end
242
243 # Nitdoc base page
244 # Define page structure and properties
245 abstract class NitdocPage
246
247 private var ctx: NitdocContext
248 private var model: Model
249 private var mainmodule: MModule
250 private var name_sorter = new MEntityNameSorter
251
252 init(ctx: NitdocContext, model: Model, mainmodule: MModule) do
253 self.ctx = ctx
254 self.model = model
255 self.mainmodule = mainmodule
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 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 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 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 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 private var page = new TplPage
489 redef fun tpl_page do return page
490
491 private var sidebar = new TplSidebar
492 redef fun tpl_sidebar do return sidebar
493
494 redef fun tpl_title do
495 if ctx.opt_custom_title.value != null then
496 return ctx.opt_custom_title.value.to_s
497 else
498 return "Overview"
499 end
500 end
501
502 redef fun tpl_topmenu do
503 var topmenu = super
504 topmenu.add_item(new TplLink("#", "Overview"), true)
505 topmenu.add_item(new TplLink("search.html", "Index"), false)
506 return topmenu
507 end
508
509 # intro text
510 private fun tpl_intro: TplSection do
511 var section = new TplSection.with_title("overview", tpl_title)
512 var article = new TplArticle("intro")
513 if ctx.opt_custom_intro.value != null then
514 article.content = ctx.opt_custom_intro.value.to_s
515 end
516 section.add_child article
517 return section
518 end
519
520 # projects list
521 private fun tpl_projects(section: TplSection) do
522 # Projects list
523 var mprojects = model.mprojects.to_a
524 var sorter = new MConcernRankSorter
525 sorter.sort mprojects
526 var ssection = new TplSection.with_title("projects", "Projects")
527 for mproject in mprojects do
528 ssection.add_child tpl_mproject_article(mproject)
529 end
530 section.add_child ssection
531 end
532
533 redef fun tpl_content do
534 var top = tpl_intro
535 tpl_projects(top)
536 tpl_page.add_section top
537 end
538 end
539
540 # The search page
541 # Display a list of modules, classes and properties
542 class NitdocSearch
543 super NitdocPage
544
545 private var page = new TplPage
546 redef fun tpl_page do return page
547
548 redef fun tpl_title do return "Index"
549
550 redef fun tpl_topmenu do
551 var topmenu = super
552 topmenu.add_item(new TplLink("index.html", "Overview"), false)
553 topmenu.add_item(new TplLink("#", "Index"), true)
554 return topmenu
555 end
556
557 redef fun tpl_content do
558 var tpl = new TplSearchPage("search_all")
559 var section = new TplSection("search")
560 # title
561 tpl.title = "Index"
562 # modules list
563 for mmodule in modules_list do
564 tpl.modules.add mmodule.tpl_link
565 end
566 # classes list
567 for mclass in classes_list do
568 tpl.classes.add mclass.tpl_link
569 end
570 # properties list
571 for mproperty in mprops_list do
572 var m = new Template
573 m.add mproperty.intro.tpl_link
574 m.add " ("
575 m.add mproperty.intro.mclassdef.mclass.tpl_link
576 m.add ")"
577 tpl.props.add m
578 end
579 section.add_child tpl
580 tpl_page.add_section section
581 end
582
583 # Extract mmodule list to display (sorted by name)
584 private fun modules_list: Array[MModule] do
585 var sorted = new Array[MModule]
586 for mmodule in model.mmodule_importation_hierarchy do
587 if mmodule.name == "<main>" then continue
588 sorted.add mmodule
589 end
590 name_sorter.sort(sorted)
591 return sorted
592 end
593
594 # Extract mclass list to display (sorted by name)
595 private fun classes_list: Array[MClass] do
596 var sorted = new Array[MClass]
597 for mclass in model.mclasses do
598 if mclass.visibility < ctx.min_visibility then continue
599 sorted.add mclass
600 end
601 name_sorter.sort(sorted)
602 return sorted
603 end
604
605 # Extract mproperty list to display (sorted by name)
606 private fun mprops_list: Array[MProperty] do
607 var sorted = new Array[MProperty]
608 for mproperty in model.mproperties do
609 if mproperty.visibility < ctx.min_visibility then continue
610 if mproperty isa MAttribute then continue
611 sorted.add mproperty
612 end
613 name_sorter.sort(sorted)
614 return sorted
615 end
616 end
617
618 # A group page
619 # Display a flattened view of the group
620 class NitdocGroup
621 super NitdocPage
622
623 private var mgroup: MGroup
624
625 private var concerns: ConcernsTree
626 private var intros: Set[MClass]
627 private var redefs: Set[MClass]
628
629 init(ctx: NitdocContext, model: Model, mainmodule: MModule, mgroup: MGroup) do
630 super
631 self.mgroup = mgroup
632 self.concerns = model.concerns_tree(mgroup.collect_mmodules)
633 self.concerns.sort_with(new MConcernRankSorter)
634 self.intros = mgroup.in_nesting_intro_mclasses(ctx.min_visibility)
635 var redefs = new HashSet[MClass]
636 for rdef in mgroup.in_nesting_redef_mclasses(ctx.min_visibility) do
637 if intros.has(rdef) then continue
638 redefs.add rdef
639 end
640 self.redefs = redefs
641 end
642
643 private var page = new TplPage
644 redef fun tpl_page do return page
645
646 private var sidebar = new TplSidebar
647 redef fun tpl_sidebar do return sidebar
648
649 redef fun tpl_title do return "{mgroup.nitdoc_name}"
650
651 redef fun tpl_topmenu do
652 var topmenu = super
653 var mproject = mgroup.mproject
654 topmenu.add_item(new TplLink("index.html", "Overview"), false)
655 if mgroup.is_root then
656 topmenu.add_item(new TplLink("#", "{mproject.nitdoc_name}"), true)
657 else
658 topmenu.add_item(new TplLink(mproject.nitdoc_url, "{mproject.nitdoc_name}"), false)
659 topmenu.add_item(new TplLink("#", "{mgroup.nitdoc_name}"), true)
660 end
661 topmenu.add_item(new TplLink("search.html", "Index"), false)
662 return topmenu
663 end
664
665 # Class list to display in sidebar
666 fun tpl_sidebar_mclasses do
667 var mclasses = new HashSet[MClass]
668 mclasses.add_all intros
669 mclasses.add_all redefs
670 if mclasses.is_empty then return
671 var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
672
673 var sorted = mclasses.to_a
674 name_sorter.sort(sorted)
675 for mclass in sorted do
676 list.add_li tpl_sidebar_item(mclass)
677 end
678 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
679 end
680
681 private fun tpl_sidebar_item(def: MClass): Template do
682 var classes = def.intro.tpl_css_classes.to_a
683 if intros.has(def) then
684 classes.add "intro"
685 else
686 classes.add "redef"
687 end
688 var lnk = new Template
689 lnk.add new TplLabel.with_classes(classes)
690 lnk.add def.tpl_link
691 return lnk
692 end
693
694 # intro text
695 private fun tpl_intro: TplSection do
696 var section = new TplSection.with_title("top", tpl_title)
697 var article = new TplArticle("intro")
698
699 if mgroup.is_root then
700 section.subtitle = mgroup.mproject.tpl_declaration
701 article.content = mgroup.mproject.tpl_definition
702 else
703 section.subtitle = mgroup.tpl_declaration
704 article.content = mgroup.tpl_definition
705 end
706 section.add_child article
707 return section
708 end
709
710 private fun tpl_concerns(section: TplSection) do
711 if concerns.is_empty then return
712 section.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
713 end
714
715 private fun tpl_groups(parent: TplSection) do
716 var lst = concerns.to_a
717 var section = parent
718 for mentity in lst do
719 if mentity isa MProject then
720 section.add_child new TplSection(mentity.nitdoc_id)
721 else if mentity isa MGroup then
722 section.add_child new TplSection(mentity.nitdoc_id)
723 else if mentity isa MModule then
724 section.add_child tpl_mmodule_article(mentity)
725 end
726 end
727 end
728
729 redef fun tpl_content do
730 tpl_sidebar_mclasses
731 var top = tpl_intro
732 tpl_concerns(top)
733 tpl_groups(top)
734 tpl_page.add_section top
735 end
736
737 private fun sort_by_mclass(mclassdefs: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
738 var map = new HashMap[MClass, Set[MClassDef]]
739 for mclassdef in mclassdefs do
740 var mclass = mclassdef.mclass
741 if not map.has_key(mclass) then map[mclass] = new HashSet[MClassDef]
742 map[mclass].add mclassdef
743 end
744 return map
745 end
746 end
747
748 # A module page
749 # Display the list of introduced and redefined classes in module
750 class NitdocModule
751 super NitdocPage
752
753 private var mmodule: MModule
754 private var concerns: ConcernsTree
755 private var mclasses2mdefs: Map[MClass, Set[MClassDef]]
756 private var mmodules2mclasses: Map[MModule, Set[MClass]]
757
758
759 init(ctx: NitdocContext, model: Model, mainmodule: MModule, mmodule: MModule) do
760 super
761 self.mmodule = mmodule
762 var mclassdefs = new HashSet[MClassDef]
763 mclassdefs.add_all mmodule.intro_mclassdefs(ctx.min_visibility)
764 mclassdefs.add_all mmodule.redef_mclassdefs(ctx.min_visibility)
765 self.mclasses2mdefs = sort_by_mclass(mclassdefs)
766 self.mmodules2mclasses = group_by_mmodule(mclasses2mdefs.keys)
767 self.concerns = model.concerns_tree(mmodules2mclasses.keys)
768 # rank concerns
769 mmodule.mgroup.mproject.booster_rank = -1000
770 mmodule.mgroup.booster_rank = -1000
771 mmodule.booster_rank = -1000
772 self.concerns.sort_with(new MConcernRankSorter)
773 mmodule.mgroup.mproject.booster_rank = 0
774 mmodule.mgroup.booster_rank = 0
775 mmodule.booster_rank = 0
776 end
777
778 private var page = new TplPage
779 redef fun tpl_page do return page
780
781 private var sidebar = new TplSidebar
782 redef fun tpl_sidebar do return sidebar
783
784 redef fun tpl_title do return "{mmodule.nitdoc_name}"
785
786 redef fun tpl_topmenu do
787 var topmenu = super
788 var mproject = mmodule.mgroup.mproject
789 topmenu.add_item(new TplLink("index.html", "Overview"), false)
790 topmenu.add_item(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false)
791 topmenu.add_item(new TplLink("#", "{mmodule.nitdoc_name}"), true)
792 topmenu.add_item(new TplLink("search.html", "Index"), false)
793 return topmenu
794 end
795
796 # Class list to display in sidebar
797 fun tpl_sidebar_mclasses do
798 var mclasses = new HashSet[MClass]
799 mclasses.add_all mmodule.filter_intro_mclasses(ctx.min_visibility)
800 mclasses.add_all mmodule.filter_redef_mclasses(ctx.min_visibility)
801 if mclasses.is_empty then return
802 var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
803
804 var sorted = mclasses.to_a
805 name_sorter.sort(sorted)
806 for mclass in sorted do
807 list.add_li tpl_sidebar_item(mclass)
808 end
809 tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list)
810 end
811
812 private fun tpl_sidebar_item(def: MClass): Template do
813 var classes = def.intro.tpl_css_classes.to_a
814 if def.intro_mmodule == mmodule then
815 classes.add "intro"
816 else
817 classes.add "redef"
818 end
819 var lnk = new Template
820 lnk.add new TplLabel.with_classes(classes)
821 lnk.add def.tpl_link
822 return lnk
823 end
824
825 # intro text
826 private fun tpl_intro: TplSection do
827 var section = new TplSection.with_title("top", tpl_title)
828 section.subtitle = mmodule.tpl_declaration
829
830 var article = new TplArticle("intro")
831 var def = mmodule.tpl_definition
832 var location = mmodule.location
833 article.source_link = tpl_showsource(location)
834 article.content = def
835 section.add_child article
836 return section
837 end
838
839 # inheritance section
840 private fun tpl_inheritance(parent: TplSection) do
841 # Extract relevent modules
842 var imports = mmodule.in_importation.greaters
843 if imports.length > 10 then imports = mmodule.in_importation.direct_greaters
844 var clients = mmodule.in_importation.smallers
845 if clients.length > 10 then clients = mmodule.in_importation.direct_smallers
846
847 # Display lists
848 var section = new TplSection.with_title("dependencies", "Dependencies")
849
850 # Graph
851 var mmodules = new HashSet[MModule]
852 mmodules.add_all mmodule.in_nesting.direct_greaters
853 mmodules.add_all imports
854 if clients.length < 10 then mmodules.add_all clients
855 mmodules.add mmodule
856 var graph = tpl_dot(mmodules)
857 if graph != null then section.add_child graph
858
859 # Imports
860 var lst = new Array[MModule]
861 for dep in imports do
862 if dep.is_fictive then continue
863 if dep == mmodule then continue
864 lst.add(dep)
865 end
866 if not lst.is_empty then
867 name_sorter.sort lst
868 section.add_child tpl_list("imports", "Imports", lst)
869 end
870
871 # Clients
872 lst = new Array[MModule]
873 for dep in clients do
874 if dep.is_fictive then continue
875 if dep == mmodule then continue
876 lst.add(dep)
877 end
878 if not lst.is_empty then
879 name_sorter.sort lst
880 section.add_child tpl_list("clients", "Clients", lst)
881 end
882
883 parent.add_child section
884 end
885
886 private fun tpl_list(id: String, title: String, mmodules: Array[MModule]): TplArticle do
887 var article = new TplArticle.with_title(id, title)
888 var list = new TplList.with_classes(["list-unstyled", "list-definition"])
889 for mmodule in mmodules do list.elts.add mmodule.tpl_list_item
890 article.content = list
891 return article
892 end
893
894 private fun tpl_concerns(parent: TplSection) do
895 if concerns.is_empty then return
896 parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
897 end
898
899 private fun tpl_mclasses(parent: TplSection) do
900 for mentity in concerns do
901 if mentity isa MProject then
902 parent.add_child new TplSection(mentity.nitdoc_id)
903 else if mentity isa MGroup then
904 parent.add_child new TplSection(mentity.nitdoc_id)
905 else if mentity isa MModule then
906 var section = new TplSection(mentity.nitdoc_id)
907 var title = new Template
908 if mentity == mmodule then
909 title.add "in "
910 section.summary_title = "in {mentity.nitdoc_name}"
911 else
912 title.add "from "
913 section.summary_title = "from {mentity.nitdoc_name}"
914 end
915 title.add mentity.tpl_namespace
916 section.title = title
917
918 var mclasses = mmodules2mclasses[mentity].to_a
919 name_sorter.sort(mclasses)
920 for mclass in mclasses do
921 section.add_child tpl_mclass_article(mclass, mclasses2mdefs[mclass].to_a)
922 end
923 parent.add_child section
924 end
925 end
926 end
927
928 private fun group_by_mmodule(mclasses: Collection[MClass]): Map[MModule, Set[MClass]] do
929 var res = new HashMap[MModule, Set[MClass]]
930 for mclass in mclasses do
931 var mmodule = mclass.intro_mmodule
932 if not res.has_key(mmodule) then
933 res[mmodule] = new HashSet[MClass]
934 end
935 res[mmodule].add(mclass)
936 end
937 return res
938 end
939
940 redef fun tpl_content do
941 tpl_sidebar_mclasses
942 var top = tpl_intro
943 tpl_inheritance(top)
944 tpl_concerns(top)
945 tpl_mclasses(top)
946 tpl_page.add_section top
947 end
948
949 # Genrate dot hierarchy for class inheritance
950 fun tpl_dot(mmodules: Collection[MModule]): nullable TplArticle do
951 var poset = new POSet[MModule]
952 for mmodule in mmodules do
953 if mmodule.is_fictive then continue
954 poset.add_node mmodule
955 for omodule in mmodules do
956 if mmodule.is_fictive then continue
957 poset.add_node mmodule
958 if mmodule.in_importation < omodule then
959 poset.add_edge(mmodule, omodule)
960 end
961 end
962 end
963 # build graph
964 var op = new FlatBuffer
965 var name = "dep_{mmodule.name}"
966 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")
967 for mmodule in poset do
968 if mmodule == self.mmodule then
969 op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n")
970 else
971 op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n")
972 end
973 for omodule in poset[mmodule].direct_greaters do
974 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
975 end
976 end
977 op.append("\}\n")
978 return tpl_graph(op, name, null)
979 end
980
981 private fun sort_by_mclass(mclassdefs: Collection[MClassDef]): Map[MClass, Set[MClassDef]] do
982 var map = new HashMap[MClass, Set[MClassDef]]
983 for mclassdef in mclassdefs do
984 var mclass = mclassdef.mclass
985 if not map.has_key(mclass) then map[mclass] = new HashSet[MClassDef]
986 map[mclass].add mclassdef
987 end
988 return map
989 end
990 end
991
992 # A class page
993 # Display a list properties defined or redefined for this class
994 class NitdocClass
995 super NitdocPage
996
997 private var mclass: MClass
998 private var concerns: ConcernsTree
999 private var mprops2mdefs: Map[MProperty, Set[MPropDef]]
1000 private var mmodules2mprops: Map[MModule, Set[MProperty]]
1001
1002 init(ctx: NitdocContext, model: Model, mainmodule: MModule, mclass: MClass) do
1003 super
1004 self.mclass = mclass
1005 var mpropdefs = new HashSet[MPropDef]
1006 mpropdefs.add_all mclass.intro_mpropdefs(ctx.min_visibility)
1007 mpropdefs.add_all mclass.redef_mpropdefs(ctx.min_visibility)
1008 self.mprops2mdefs = sort_by_mproperty(mpropdefs)
1009 self.mmodules2mprops = sort_by_mmodule(mprops2mdefs.keys)
1010 self.concerns = model.concerns_tree(mmodules2mprops.keys)
1011 self.concerns.sort_with(new MConcernRankSorter)
1012 end
1013
1014 private var page = new TplPage
1015 redef fun tpl_page do return page
1016
1017 private var sidebar = new TplSidebar
1018 redef fun tpl_sidebar do return sidebar
1019
1020 redef fun tpl_title do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
1021
1022 redef fun tpl_topmenu do
1023 var topmenu = super
1024 var mproject = mclass.intro_mmodule.mgroup.mproject
1025 topmenu.add_item(new TplLink("index.html", "Overview"), false)
1026 topmenu.add_item(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false)
1027 topmenu.add_item(new TplLink("#", "{mclass.nitdoc_name}"), true)
1028 topmenu.add_item(new TplLink("search.html", "Index"), false)
1029 return topmenu
1030 end
1031
1032 # Property list to display in sidebar
1033 fun tpl_sidebar_properties do
1034 var kind_map = sort_by_kind(mclass_inherited_mprops)
1035 var summary = new TplList.with_classes(["list-unstyled"])
1036
1037 tpl_sidebar_list("Virtual types", kind_map["type"].to_a, summary)
1038 tpl_sidebar_list("Constructors", kind_map["init"].to_a, summary)
1039 tpl_sidebar_list("Methods", kind_map["fun"].to_a, summary)
1040 tpl_sidebar.boxes.add new TplSideBox.with_content("All properties", summary)
1041 end
1042
1043 private fun tpl_sidebar_list(name: String, mprops: Array[MProperty], summary: TplList) do
1044 if mprops.is_empty then return
1045 name_sorter.sort(mprops)
1046 var entry = new TplListItem.with_content(name)
1047 var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
1048 for mprop in mprops do
1049 list.add_li tpl_sidebar_item(mprop)
1050 end
1051 entry.append list
1052 summary.elts.add entry
1053 end
1054
1055 private fun tpl_sidebar_item(mprop: MProperty): Template do
1056 var classes = mprop.intro.tpl_css_classes.to_a
1057 if not mprops2mdefs.has_key(mprop) then
1058 classes.add "inherit"
1059 var lnk = new Template
1060 lnk.add new TplLabel.with_classes(classes)
1061 lnk.add mprop.intro.tpl_link
1062 return lnk
1063 end
1064 var defs = mprops2mdefs[mprop]
1065 if defs.has(mprop.intro) then
1066 classes.add "intro"
1067 else
1068 classes.add "redef"
1069 end
1070 var lnk = new Template
1071 lnk.add new TplLabel.with_classes(classes)
1072 lnk.add mprop.intro.tpl_anchor
1073 return lnk
1074 end
1075
1076 private fun tpl_intro: TplSection do
1077 var section = new TplSection.with_title("top", tpl_title)
1078 section.subtitle = mclass.intro.tpl_declaration
1079 var article = new TplArticle("comment")
1080 if mclass.mdoc != null then
1081 article.content = mclass.mdoc.tpl_comment
1082 end
1083 section.add_child article
1084 return section
1085 end
1086
1087 private fun tpl_concerns(parent: TplSection) do
1088 # intro title
1089 var section = new TplSection.with_title("intro", "Introduction")
1090 section.summary_title = "Introduction"
1091 section.add_child tpl_mclassdef_article(mclass.intro)
1092 parent.add_child section
1093 # concerns
1094 if concerns.is_empty then return
1095 parent.add_child new TplArticle.with_content("concerns", "Concerns", concerns.to_tpl)
1096 end
1097
1098 private fun tpl_inheritance(parent: TplSection) do
1099 # parents
1100 var hparents = new HashSet[MClass]
1101 for c in mclass.in_hierarchy(mainmodule).direct_greaters do
1102 if c.visibility < ctx.min_visibility then continue
1103 hparents.add c
1104 end
1105
1106 # ancestors
1107 var hancestors = new HashSet[MClass]
1108 for c in mclass.in_hierarchy(mainmodule).greaters do
1109 if c == mclass then continue
1110 if c.visibility < ctx.min_visibility then continue
1111 if hparents.has(c) then continue
1112 hancestors.add c
1113 end
1114
1115 # children
1116 var hchildren = new HashSet[MClass]
1117 for c in mclass.in_hierarchy(mainmodule).direct_smallers do
1118 if c.visibility < ctx.min_visibility then continue
1119 hchildren.add c
1120 end
1121
1122 # descendants
1123 var hdescendants = new HashSet[MClass]
1124 for c in mclass.in_hierarchy(mainmodule).smallers do
1125 if c == mclass then continue
1126 if c.visibility < ctx.min_visibility then continue
1127 if hchildren.has(c) then continue
1128 hdescendants.add c
1129 end
1130
1131 # Display lists
1132 var section = new TplSection.with_title("inheritance", "Inheritance")
1133
1134 # Graph
1135 var mclasses = new HashSet[MClass]
1136 mclasses.add_all hancestors
1137 mclasses.add_all hparents
1138 if hchildren.length < 10 then mclasses.add_all hchildren
1139 if hdescendants.length < 10 then mclasses.add_all hdescendants
1140 mclasses.add mclass
1141 var graph = tpl_dot(mclasses)
1142 if graph != null then section.add_child graph
1143
1144 # parents
1145 if not hparents.is_empty then
1146 var lst = hparents.to_a
1147 name_sorter.sort lst
1148 section.add_child tpl_list("parents", "Parents", lst)
1149 end
1150
1151 # ancestors
1152 if not hancestors.is_empty then
1153 var lst = hancestors.to_a
1154 name_sorter.sort lst
1155 section.add_child tpl_list("ancestors", "Ancestors", lst)
1156 end
1157
1158 # children
1159 if not hchildren.is_empty and hchildren.length < 15 then
1160 var lst = hchildren.to_a
1161 name_sorter.sort lst
1162 section.add_child tpl_list("children", "Children", lst)
1163 end
1164
1165 # descendants
1166 if not hdescendants.is_empty and hchildren.length < 15 then
1167 var lst = hdescendants.to_a
1168 name_sorter.sort lst
1169 section.add_child tpl_list("descendants", "Descendants", lst)
1170 end
1171
1172 parent.add_child section
1173 end
1174
1175 private fun tpl_list(id: String, title: String, elts: Array[MClass]): TplArticle do
1176 var article = new TplArticle.with_title(id, title)
1177 var list = new TplList.with_classes(["list-unstyled", "list-definition"])
1178 for elt in elts do list.elts.add elt.tpl_list_item
1179 article.content = list
1180 return article
1181 end
1182
1183 private fun tpl_properties(parent: TplSection) do
1184 var lst = concerns.to_a
1185 for mentity in lst do
1186 if mentity isa MProject then
1187 parent.add_child new TplSection(mentity.nitdoc_id)
1188 else if mentity isa MGroup then
1189 parent.add_child new TplSection(mentity.nitdoc_id)
1190 else if mentity isa MModule then
1191 var section = new TplSection(mentity.nitdoc_id)
1192 var title = new Template
1193 title.add "in "
1194 title.add mentity.tpl_namespace
1195 section.title = title
1196 section.summary_title = "in {mentity.nitdoc_name}"
1197
1198 # properties
1199 var mprops = mmodules2mprops[mentity]
1200 var kind_map = sort_by_kind(mprops)
1201
1202 # virtual types
1203 var elts = kind_map["type"].to_a
1204 name_sorter.sort(elts)
1205 for elt in elts do
1206 var defs = mprops2mdefs[elt].to_a
1207 section.add_child tpl_mprop_article(elt, defs)
1208 end
1209
1210 # constructors
1211 elts = kind_map["init"].to_a
1212 name_sorter.sort(elts)
1213 for elt in elts do
1214 var defs = mprops2mdefs[elt].to_a
1215 section.add_child tpl_mprop_article(elt, defs)
1216 end
1217
1218 # methods
1219 elts = kind_map["fun"].to_a
1220 name_sorter.sort(elts)
1221 for elt in elts do
1222 var defs = mprops2mdefs[elt].to_a
1223 section.add_child tpl_mprop_article(elt, defs)
1224 end
1225 parent.add_child section
1226 end
1227 end
1228 end
1229
1230 redef fun tpl_content do
1231 tpl_sidebar_properties
1232 var top = tpl_intro
1233 tpl_inheritance(top)
1234 tpl_concerns(top)
1235 tpl_properties(top)
1236 tpl_page.add_section top
1237 end
1238
1239 private fun sort_by_mproperty(mpropdefs: Collection[MPropDef]): Map[MProperty, Set[MPropDef]] do
1240 var map = new HashMap[MProperty, Set[MPropDef]]
1241 for mpropdef in mpropdefs do
1242 var mproperty = mpropdef.mproperty
1243 if not map.has_key(mproperty) then map[mproperty] = new HashSet[MPropDef]
1244 map[mproperty].add mpropdef
1245 end
1246 return map
1247 end
1248
1249 private fun sort_by_mmodule(mprops: Collection[MProperty]): Map[MModule, Set[MProperty]] do
1250 var map = new HashMap[MModule, Set[MProperty]]
1251 for mprop in mprops do
1252 var mpropdefs = mprops2mdefs[mprop].to_a
1253 mainmodule.linearize_mpropdefs(mpropdefs)
1254 var mmodule = mpropdefs.first.mclassdef.mmodule
1255 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MProperty]
1256 map[mmodule].add mprop
1257 end
1258 return map
1259 end
1260
1261 private fun sort_by_kind(mprops: Collection[MProperty]): Map[String, Set[MProperty]] do
1262 var map = new HashMap[String, Set[MProperty]]
1263 map["type"] = new HashSet[MProperty]
1264 map["init"] = new HashSet[MProperty]
1265 map["fun"] = new HashSet[MProperty]
1266 for mprop in mprops do
1267 if mprop isa MVirtualTypeProp then
1268 map["type"].add mprop
1269 else if mprop isa MMethod then
1270 if mprop.is_init then
1271 map["init"].add mprop
1272 else
1273 map["fun"].add mprop
1274 end
1275 end
1276 end
1277 return map
1278 end
1279
1280 private fun mclass_inherited_mprops: Set[MProperty] do
1281 var res = new HashSet[MProperty]
1282 var local = mclass.local_mproperties(ctx.min_visibility)
1283 for mprop in mclass.inherited_mproperties(mainmodule, ctx.min_visibility) do
1284 if local.has(mprop) then continue
1285 #if mprop isa MMethod and mprop.is_init then continue
1286 if mprop.intro.mclassdef.mclass.name == "Object" and
1287 (mprop.visibility == protected_visibility or
1288 mprop.intro.mclassdef.mmodule.name != "kernel") then continue
1289 res.add mprop
1290 end
1291 res.add_all local
1292 return res
1293 end
1294
1295 private fun collect_mmodules(mprops: Collection[MProperty]): Set[MModule] do
1296 var res = new HashSet[MModule]
1297 for mprop in mprops do
1298 if mprops2mdefs.has_key(mprop) then
1299 for mpropdef in mprops2mdefs[mprop] do res.add mpropdef.mclassdef.mmodule
1300 end
1301 end
1302 return res
1303 end
1304
1305 private fun sort_by_public_owner(mmodules: Collection[MModule]): Map[MModule, Set[MModule]] do
1306 var map = new HashMap[MModule, Set[MModule]]
1307 for mmodule in mmodules do
1308 var owner = mmodule
1309 if mmodule.public_owner != null then owner = mmodule.public_owner.as(not null)
1310 if not map.has_key(owner) then map[owner] = new HashSet[MModule]
1311 map[owner].add mmodule
1312 end
1313 return map
1314 end
1315
1316 # Generate dot hierarchy for classes
1317 fun tpl_dot(mclasses: Collection[MClass]): nullable TplArticle do
1318 var poset = new POSet[MClass]
1319
1320 for mclass in mclasses do
1321 poset.add_node mclass
1322 for oclass in mclasses do
1323 poset.add_node oclass
1324 if mclass.in_hierarchy(mainmodule) < oclass then
1325 poset.add_edge(mclass, oclass)
1326 end
1327 end
1328 end
1329
1330 var op = new FlatBuffer
1331 var name = "dep_{mclass.name}"
1332 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")
1333 for c in poset do
1334 if c == mclass then
1335 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1336 else
1337 op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n")
1338 end
1339 for c2 in poset[c].direct_greaters do
1340 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1341 end
1342 end
1343 op.append("\}\n")
1344 return tpl_graph(op, name, null)
1345 end
1346 end
1347
1348 # A MProperty page
1349 class NitdocProperty
1350 super NitdocPage
1351
1352 private var mproperty: MProperty
1353 private var concerns: ConcernsTree
1354 private var mmodules2mdefs: Map[MModule, Set[MPropDef]]
1355
1356 init(ctx: NitdocContext, model: Model, mainmodule: MModule, mproperty: MProperty) do
1357 super
1358 self.mproperty = mproperty
1359 self.mmodules2mdefs = sort_by_mmodule(collect_mpropdefs)
1360 self.concerns = model.concerns_tree(mmodules2mdefs.keys)
1361 self.concerns.sort_with(new MConcernRankSorter)
1362 end
1363
1364 private fun collect_mpropdefs: Set[MPropDef] do
1365 var res = new HashSet[MPropDef]
1366 for mpropdef in mproperty.mpropdefs do
1367 if not mpropdef.is_intro then res.add mpropdef
1368 end
1369 return res
1370 end
1371
1372 private var page = new TplPage
1373 redef fun tpl_page do return page
1374
1375 private var sidebar = new TplSidebar
1376 redef fun tpl_sidebar do return sidebar
1377
1378 redef fun tpl_title do
1379 return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}"
1380 end
1381
1382 redef fun tpl_topmenu do
1383 var topmenu = super
1384 var mmodule = mproperty.intro_mclassdef.mmodule
1385 var mproject = mmodule.mgroup.mproject
1386 var mclass = mproperty.intro_mclassdef.mclass
1387 topmenu.add_item(new TplLink("index.html", "Overview"), false)
1388 topmenu.add_item(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false)
1389 topmenu.add_item(new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}"), false)
1390 topmenu.add_item(new TplLink("#", "{mproperty.nitdoc_name}"), true)
1391 topmenu.add_item(new TplLink("search.html", "Index"), false)
1392 return topmenu
1393 end
1394
1395 private fun tpl_intro: TplSection do
1396 var section = new TplSection.with_title("top", tpl_title)
1397 section.subtitle = mproperty.tpl_declaration
1398 var article = new TplArticle("comment")
1399 if mproperty.intro.mdoc != null then
1400 article.content = mproperty.intro.mdoc.tpl_comment
1401 end
1402 section.add_child article
1403 return section
1404 end
1405
1406 private fun tpl_properties(parent: TplSection) do
1407 # intro title
1408 var section = new TplSection.with_title("intro", "Introduction")
1409 section.summary_title = "Introduction"
1410 section.add_child tpl_mpropdef_article(mproperty.intro)
1411 parent.add_child section
1412
1413 # concerns
1414 if concerns.is_empty then return
1415 parent.add_child new TplArticle.with_content("Concerns", "Concerns", concerns.to_tpl)
1416
1417 # redef list
1418 var lst = concerns.to_a
1419 for mentity in lst do
1420 if mentity isa MProject then
1421 parent.add_child new TplSection(mentity.nitdoc_id)
1422 else if mentity isa MGroup then
1423 parent.add_child new TplSection(mentity.nitdoc_id)
1424 else if mentity isa MModule then
1425 var ssection = new TplSection(mentity.nitdoc_id)
1426 var title = new Template
1427 title.add "in "
1428 title.add mentity.tpl_namespace
1429 ssection.title = title
1430 ssection.summary_title = "in {mentity.nitdoc_name}"
1431
1432 # properties
1433 var mpropdefs = mmodules2mdefs[mentity].to_a
1434 name_sorter.sort(mpropdefs)
1435 for mpropdef in mpropdefs do
1436 ssection.add_child tpl_mpropdef_article(mpropdef)
1437 end
1438 parent.add_child ssection
1439 end
1440 end
1441 end
1442
1443 redef fun tpl_content do
1444 var top = tpl_intro
1445 tpl_properties(top)
1446 tpl_page.add_section top
1447 end
1448
1449 private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do
1450 var map = new HashMap[MModule, Set[MPropDef]]
1451 for mpropdef in mpropdefs do
1452 var mmodule = mpropdef.mclassdef.mmodule
1453 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef]
1454 map[mmodule].add mpropdef
1455 end
1456 return map
1457 end
1458 end
1459