nitdoc: fix concerns list display
[nit.git] / src / nitdoc.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 # Documentation generator for the nit language.
16 # Generate API documentation in HTML format from nit source code.
17 module nitdoc
18
19 import model_utils
20 import modelize_property
21 import markdown
22 import doc_template
23
24 # The NitdocContext contains all the knowledge used for doc generation
25 class NitdocContext
26
27 private var toolcontext = new ToolContext
28 private var mbuilder: ModelBuilder
29 private var mainmodule: MModule
30 private var output_dir: String
31 private var min_visibility: MVisibility
32
33 private var opt_dir = new OptionString("output directory", "-d", "--dir")
34 private var opt_source = new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source")
35 private var opt_sharedir = new OptionString("directory containing nitdoc assets", "--sharedir")
36 private var opt_shareurl = new OptionString("use shareurl instead of copy shared files", "--shareurl")
37 private var opt_nodot = new OptionBool("do not generate graphes with graphviz", "--no-dot")
38 private var opt_private = new OptionBool("also generate private API", "--private")
39
40 private var opt_custom_title = new OptionString("custom title for homepage", "--custom-title")
41 private var opt_custom_menu = new OptionString("custom items added in top menu (each item must be enclosed in 'li' tags)", "--custom-menu-items")
42 private var opt_custom_intro = new OptionString("custom intro text for homepage", "--custom-overview-text")
43 private var opt_custom_footer = new OptionString("custom footer text", "--custom-footer-text")
44
45 private var opt_github_upstream = new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream")
46 private var opt_github_base_sha1 = new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1")
47 private var opt_github_gitdir = new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir")
48
49 private var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker")
50 private var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id")
51
52 init do
53 var opts = toolcontext.option_context
54 opts.add_option(opt_dir, opt_source, opt_sharedir, opt_shareurl, opt_nodot, opt_private)
55 opts.add_option(opt_custom_title, opt_custom_footer, opt_custom_intro, opt_custom_menu)
56 opts.add_option(opt_github_upstream, opt_github_base_sha1, opt_github_gitdir)
57 opts.add_option(opt_piwik_tracker, opt_piwik_site_id)
58
59 var tpl = new Template
60 tpl.add "Usage: nitdoc [OPTION]... <file.nit>...\n"
61 tpl.add "Generates HTML pages of API documentation from Nit source files."
62 toolcontext.tooldescription = tpl.write_to_string
63 toolcontext.process_options(args)
64
65 self.process_options
66 self.parse(toolcontext.option_context.rest)
67 end
68
69 private fun process_options do
70 if opt_private.value then
71 min_visibility = none_visibility
72 else
73 min_visibility = protected_visibility
74 end
75 var gh_upstream = opt_github_upstream.value
76 var gh_base_sha = opt_github_base_sha1.value
77 var gh_gitdir = opt_github_gitdir.value
78 if not gh_upstream == null or not gh_base_sha == null or not gh_gitdir == null then
79 if gh_upstream == null or gh_base_sha == null or gh_gitdir == null then
80 print "Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin"
81 abort
82 end
83 end
84 end
85
86 private fun parse(arguments: Array[String]) do
87 var model = new Model
88 mbuilder = new ModelBuilder(model, toolcontext)
89 var mmodules = mbuilder.parse(arguments)
90 if mmodules.is_empty then return
91 mbuilder.run_phases
92 if mmodules.length == 1 then
93 mainmodule = mmodules.first
94 else
95 mainmodule = new MModule(model, null, "<main>", new Location(null, 0, 0, 0, 0))
96 mainmodule.set_imported_mmodules(mmodules)
97 end
98 end
99
100 private fun generate_nitdoc do
101 init_output_dir
102 overview
103 search
104 modules
105 classes
106 quicksearch_list
107 end
108
109 private fun init_output_dir do
110 # location output dir
111 var output_dir = opt_dir.value
112 if output_dir == null then
113 output_dir = "doc"
114 end
115 self.output_dir = output_dir
116 # create destination dir if it's necessary
117 if not output_dir.file_exists then output_dir.mkdir
118 # locate share dir
119 var sharedir = opt_sharedir.value
120 if sharedir == null then
121 var dir = toolcontext.nit_dir
122 if dir == null then
123 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
124 abort
125 end
126 sharedir = "{dir}/share/nitdoc"
127 if not sharedir.file_exists then
128 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
129 abort
130 end
131 end
132 # copy shared files
133 if opt_shareurl.value == null then
134 sys.system("cp -r {sharedir.to_s}/* {output_dir.to_s}/")
135 else
136 sys.system("cp -r {sharedir.to_s}/resources/ {output_dir.to_s}/resources/")
137 end
138
139 end
140
141 private fun overview do
142 var overviewpage = new NitdocOverview(self)
143 overviewpage.render.write_to_file("{output_dir.to_s}/index.html")
144 end
145
146 private fun search do
147 var searchpage = new NitdocSearch(self)
148 searchpage.render.write_to_file("{output_dir.to_s}/search.html")
149 end
150
151 private fun modules do
152 for mmodule in mbuilder.model.mmodules do
153 if mmodule.name == "<main>" then continue
154 var modulepage = new NitdocModule(mmodule, self)
155 modulepage.render.write_to_file("{output_dir.to_s}/{mmodule.nitdoc_url}")
156 end
157 end
158
159 private fun classes do
160 for mclass in mbuilder.model.mclasses do
161 var classpage = new NitdocClass(mclass, self)
162 classpage.render.write_to_file("{output_dir.to_s}/{mclass.nitdoc_url}")
163 end
164 end
165
166 private fun quicksearch_list do
167 var quicksearch = new QuickSearch(self)
168 quicksearch.render.write_to_file("{output_dir.to_s}/quicksearch-list.js")
169 end
170 end
171
172 # Nitdoc QuickSearch list generator
173 #
174 # Create a JSON object containing links to:
175 # * modules
176 # * mclasses
177 # * mpropdefs
178 # All entities are grouped by name to make the research easier.
179 class QuickSearch
180
181 private var mmodules = new HashSet[MModule]
182 private var mclasses = new HashSet[MClass]
183 private var mpropdefs = new HashMap[String, Set[MPropDef]]
184
185 init(ctx: NitdocContext) do
186 for mmodule in ctx.mbuilder.model.mmodules do
187 if mmodule.name == "<main>" then continue
188 mmodules.add mmodule
189 end
190 for mclass in ctx.mbuilder.model.mclasses do
191 if mclass.visibility < ctx.min_visibility then continue
192 mclasses.add mclass
193 end
194 for mproperty in ctx.mbuilder.model.mproperties do
195 if mproperty.visibility < ctx.min_visibility then continue
196 if mproperty isa MAttribute then continue
197 if not mpropdefs.has_key(mproperty.name) then
198 mpropdefs[mproperty.name] = new HashSet[MPropDef]
199 end
200 mpropdefs[mproperty.name].add_all(mproperty.mpropdefs)
201 end
202 end
203
204 fun render: Template do
205 var tpl = new Template
206 tpl.add "var nitdocQuickSearchRawList=\{ "
207 for mmodule in mmodules do
208 tpl.add "\"{mmodule.name}\":["
209 tpl.add "\{txt:\"{mmodule.full_name}\",url:\"{mmodule.nitdoc_url}\"\},"
210 tpl.add "],"
211 end
212 for mclass in mclasses do
213 var full_name = mclass.intro.mmodule.full_name
214 tpl.add "\"{mclass.name}\":["
215 tpl.add "\{txt:\"{full_name}\",url:\"{mclass.nitdoc_url}\"\},"
216 tpl.add "],"
217 end
218 for mproperty, mprops in mpropdefs do
219 tpl.add "\"{mproperty}\":["
220 for mpropdef in mprops do
221 var full_name = mpropdef.mclassdef.mclass.full_name
222 tpl.add "\{txt:\"{full_name}\",url:\"{mpropdef.nitdoc_url}\"\},"
223 end
224 tpl.add "],"
225 end
226 tpl.add " \};"
227 return tpl
228 end
229 end
230
231 # Nitdoc base page
232 # Define page structure and properties
233 abstract class NitdocPage
234
235 private var ctx: NitdocContext
236 private var shareurl = "."
237
238 init(ctx: NitdocContext) do
239 self.ctx = ctx
240 if ctx.opt_shareurl.value != null then shareurl = ctx.opt_shareurl.value.as(not null)
241 end
242
243 # Render the page as a html template
244 fun render: Template do
245 var tpl = new TplNitdocPage
246 tpl.head = tpl_head
247 tpl.topmenu = tpl_topmenu
248 tpl.sidebar = tpl_sidebar
249 tpl.content = tpl_content
250 tpl.footer = tpl_footer
251
252 tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl))
253 if ctx.opt_github_upstream.value != null and ctx.opt_github_base_sha1.value != null then
254 tpl.body_attrs.add(new TagAttribute("data-github-upstream", ctx.opt_github_upstream.value))
255 tpl.body_attrs.add(new TagAttribute("data-github-base-sha1", ctx.opt_github_base_sha1.value))
256 end
257 var requirejs = new TplScript
258 requirejs.attrs.add(new TagAttribute("data-main", "{shareurl}/js/nitdoc"))
259 requirejs.attrs.add(new TagAttribute("src", "{shareurl}/js/lib/require.js"))
260 tpl.scripts.add requirejs
261
262 # piwik tracking
263 var tracker_url = ctx.opt_piwik_tracker.value
264 var site_id = ctx.opt_piwik_site_id.value
265 if tracker_url != null and site_id != null then
266 tpl.scripts.add new TplPiwikScript(tracker_url, site_id)
267 end
268 return tpl
269 end
270
271 # Page title string
272 fun tpl_title: String do
273 if ctx.opt_custom_title.value != null then
274 return ctx.opt_custom_title.value.to_s
275 else
276 return "Nitdoc"
277 end
278 end
279
280 # Page <head> template
281 fun tpl_head: TplHead do return new TplHead(tpl_title, shareurl)
282
283 # Top menu template
284 fun tpl_topmenu: TplTopMenu do
285 var topmenu = new TplTopMenu
286 var custom_elt = ctx.opt_custom_menu.value
287 if custom_elt != null then topmenu.add_raw(custom_elt)
288 return topmenu
289 end
290
291 # Page sidebar template
292 # return null if no sidebar for this page
293 fun tpl_sidebar: nullable TplSidebar do return null
294
295 # Page content template
296 fun tpl_content: Template is abstract
297
298 # Page footer template
299 # return null if no footer for this page
300 fun tpl_footer: nullable TplFooter do
301 if ctx.opt_custom_footer.value != null then
302 return new TplFooter(ctx.opt_custom_footer.value.to_s)
303 end
304 return null
305 end
306
307 # Clickable graphviz image using dot format
308 # return null if no graph for this page
309 fun tpl_graph(dot: FlatBuffer, name: String, alt: String): nullable TplGraph do
310 if ctx.opt_nodot.value then return null
311 var output_dir = ctx.output_dir
312 var file = new OFStream.open("{output_dir}/{name}.dot")
313 file.write(dot)
314 file.close
315 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 ; \}")
316 var fmap = new IFStream.open("{output_dir}/{name}.map")
317 var map = fmap.read_all
318 fmap.close
319 return new TplGraph(name, alt, map)
320 end
321
322 # A (source) link template for a given location
323 fun tpl_showsource(location: nullable Location): nullable String
324 do
325 if location == null then return null
326 var source = ctx.opt_source.value
327 if source == null then return "({location.file.filename.simplify_path})"
328 # THIS IS JUST UGLY ! (but there is no replace yet)
329 var x = source.split_with("%f")
330 source = x.join(location.file.filename.simplify_path)
331 x = source.split_with("%l")
332 source = x.join(location.line_start.to_s)
333 x = source.split_with("%L")
334 source = x.join(location.line_end.to_s)
335 source = source.simplify_path
336 return " (<a target='_blank' title='Show source' href=\"{source.to_s}\">source</a>)"
337 end
338
339 # MClassDef description template
340 fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do
341 var article = mclassdef.tpl_article
342 article.content = new Template
343 if not mclassdef.is_intro then
344 # add intro synopsys
345 var intro = mclassdef.mclass.intro
346 var location = intro.location
347 var sourcelink = tpl_showsource(location)
348 var intro_def = intro.tpl_short_definition
349 intro_def.location = sourcelink
350 intro_def.github_area = tpl_github(intro.full_namespace, intro.mdoc, location)
351 article.content.add intro_def
352 end
353 # add mclassdef full description
354 var location = mclassdef.location
355 var sourcelink = tpl_showsource(location)
356 var prop_def = mclassdef.tpl_definition
357 prop_def.location = sourcelink
358 prop_def.github_area = tpl_github(mclassdef.full_namespace, mclassdef.mdoc, location)
359 article.content.add prop_def
360 return article
361 end
362
363 # MPropDef description template
364 fun tpl_mpropdef_article(mpropdef: MPropDef): TplArticle do
365 var article = mpropdef.tpl_article
366 article.content = new Template
367 if not mpropdef.is_intro then
368 # add intro synopsys
369 var intro = mpropdef.mproperty.intro
370 var location = intro.location
371 var sourcelink = tpl_showsource(location)
372 var intro_def = intro.tpl_short_definition
373 intro_def.location = sourcelink
374 intro_def.github_area = tpl_github(intro.full_namespace, intro.mdoc, location)
375 article.content.add intro_def
376 end
377 # add mpropdef description
378 var location = mpropdef.location
379 var sourcelink = tpl_showsource(location)
380 var prop_def = mpropdef.tpl_definition
381 prop_def.location = sourcelink
382 prop_def.github_area = tpl_github(mpropdef.full_namespace, mpropdef.mdoc, location)
383 article.content.add prop_def
384 return article
385 end
386
387 # Github area (for Github comment edition plugin)
388 # return null if no github plugin for this page
389 fun tpl_github(namespace: String, mdoc: nullable MDoc, loc: nullable Location): nullable TplGithubArea do
390 if loc == null then return null
391 if ctx.opt_github_gitdir.value == null then return null
392 var gitdir = ctx.opt_github_gitdir.value.as(not null)
393 var location = loc.github(gitdir)
394 var comment: String
395 if mdoc != null then
396 comment = mdoc.full_comment
397 else
398 comment = ""
399 end
400 return new TplGithubArea(comment, namespace, location)
401 end
402 end
403
404 # The overview page
405 # Display a list of modules contained in program
406 class NitdocOverview
407 super NitdocPage
408
409 private var mmodules = new Array[MModule]
410
411 init(ctx: NitdocContext) do
412 super(ctx)
413 # get modules
414 var mmodules = new HashSet[MModule]
415 for mmodule in ctx.mbuilder.model.mmodule_importation_hierarchy do
416 if mmodule.name == "<main>" then continue
417 var owner = mmodule.public_owner
418 if owner != null then
419 mmodules.add(owner)
420 else
421 mmodules.add(mmodule)
422 end
423 end
424 # sort modules
425 var sorter = new MModuleNameSorter
426 self.mmodules.add_all(mmodules)
427 sorter.sort(self.mmodules)
428 end
429
430 redef fun tpl_title do return "Overview | {super}"
431
432 redef fun tpl_topmenu do
433 var topmenu = super
434 topmenu.add_elt("#", "Overview", true)
435 topmenu.add_elt("search.html", "Search", false)
436 return topmenu
437 end
438
439 redef fun tpl_content do
440 var tpl = new TplOverviewPage
441 # title
442 if ctx.opt_custom_title.value != null then
443 tpl.title = ctx.opt_custom_title.value.to_s
444 else
445 tpl.title = "Overview"
446 end
447 # intro text
448 if ctx.opt_custom_intro.value != null then
449 tpl.text = ctx.opt_custom_intro.value.to_s
450 end
451 # module list
452 for mmodule in mmodules do
453 if mmodule.mdoc != null then
454 var mtpl = new Template
455 mtpl.add mmodule.tpl_link
456 mtpl.add "&nbsp;"
457 mtpl.add mmodule.mdoc.short_comment
458 tpl.modules.add mtpl
459 end
460 end
461 # module graph
462 tpl.graph = tpl_dot
463 return tpl
464 end
465
466 # Genrate dot and template for module hierarchy
467 fun tpl_dot: nullable TplGraph do
468 # build poset with public owners
469 var poset = new POSet[MModule]
470 for mmodule in mmodules do
471 poset.add_node(mmodule)
472 for omodule in mmodules do
473 if mmodule == omodule then continue
474 if mmodule.in_importation < omodule then
475 poset.add_node(omodule)
476 poset.add_edge(mmodule, omodule)
477 end
478 end
479 end
480 # build graph
481 var op = new FlatBuffer
482 op.append("digraph dep \{ 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")
483 for mmodule in poset do
484 op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n")
485 for omodule in poset[mmodule].direct_greaters do
486 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
487 end
488 end
489 op.append("\}\n")
490 return tpl_graph(op, "dep", "Modules hierarchy")
491 end
492 end
493
494 # The search page
495 # Display a list of modules, classes and properties
496 class NitdocSearch
497 super NitdocPage
498
499 init(ctx: NitdocContext) do super(ctx)
500
501 redef fun tpl_title do return "Search | {super}"
502
503 redef fun tpl_topmenu do
504 var topmenu = super
505 topmenu.add_elt("index.html", "Overview", false)
506 topmenu.add_elt("#", "Search", true)
507 return topmenu
508 end
509
510 redef fun tpl_content do
511 var tpl = new TplSearchPage
512 # title
513 tpl.title = "Search"
514 # modules list
515 for mmodule in modules_list do
516 tpl.modules.add mmodule.tpl_link
517 end
518 # classes list
519 for mclass in classes_list do
520 tpl.classes.add mclass.tpl_link
521 end
522 # properties list
523 for mproperty in mprops_list do
524 var m = new Template
525 m.add mproperty.intro.tpl_link
526 m.add " ("
527 m.add mproperty.intro.mclassdef.mclass.tpl_link
528 m.add ")"
529 tpl.props.add m
530 end
531 return tpl
532 end
533
534 # Extract mmodule list to display (sorted by name)
535 private fun modules_list: Array[MModule] do
536 var sorted = new Array[MModule]
537 for mmodule in ctx.mbuilder.model.mmodule_importation_hierarchy do
538 if mmodule.name == "<main>" then continue
539 sorted.add mmodule
540 end
541 var sorter = new MModuleNameSorter
542 sorter.sort(sorted)
543 return sorted
544 end
545
546 # Extract mclass list to display (sorted by name)
547 private fun classes_list: Array[MClass] do
548 var sorted = new Array[MClass]
549 for mclass in ctx.mbuilder.model.mclasses do
550 if mclass.visibility < ctx.min_visibility then continue
551 sorted.add mclass
552 end
553 var sorter = new MClassNameSorter
554 sorter.sort(sorted)
555 return sorted
556 end
557
558 # Extract mproperty list to display (sorted by name)
559 private fun mprops_list: Array[MProperty] do
560 var sorted = new Array[MProperty]
561 for mproperty in ctx.mbuilder.model.mproperties do
562 if mproperty.visibility < ctx.min_visibility then continue
563 if mproperty isa MAttribute then continue
564 sorted.add mproperty
565 end
566 var sorter = new MPropertyNameSorter
567 sorter.sort(sorted)
568 return sorted
569 end
570 end
571
572 # A module page
573 # Display the list of introduced and redefined classes in module
574 class NitdocModule
575 super NitdocPage
576
577 private var mmodule: MModule
578 private var intro_mclassdefs: Set[MClassDef]
579 private var redef_mclassdefs: Set[MClassDef]
580 private var sorted_intro_mclassdefs: Array[MClassDef]
581 private var sorted_redef_mclassdefs: Array[MClassDef]
582
583 init(mmodule: MModule, ctx: NitdocContext) do
584 self.mmodule = mmodule
585 var sorter = new MClassDefNameSorter
586 intro_mclassdefs = in_nesting_intro_mclassdefs(ctx.min_visibility)
587 sorted_intro_mclassdefs = intro_mclassdefs.to_a
588 sorter.sort sorted_intro_mclassdefs
589 redef_mclassdefs = in_nesting_redef_mclassdefs(ctx.min_visibility)
590 sorted_redef_mclassdefs = redef_mclassdefs.to_a
591 sorter.sort sorted_redef_mclassdefs
592 super(ctx)
593 end
594
595 redef fun tpl_title do
596 if mmodule.mdoc != null then
597 return "{mmodule.nitdoc_name} module | {mmodule.mdoc.short_comment} | {super}"
598 else
599 return "{mmodule.nitdoc_name} module | {super}"
600 end
601 end
602
603 redef fun tpl_topmenu do
604 var topmenu = super
605 topmenu.add_elt("index.html", "Overview", false)
606 topmenu.add_elt("#", "{mmodule.nitdoc_name}", true)
607 topmenu.add_elt("search.html", "Search", false)
608 return topmenu
609 end
610
611 redef fun tpl_sidebar do
612 var sidebar = new TplSidebar
613 tpl_sidebar_classes(sidebar)
614 tpl_sidebar_inheritance(sidebar)
615 return sidebar
616 end
617
618 # Classes to display on sidebar
619 fun tpl_sidebar_classes(sidebar: TplSidebar) do
620 var box = new TplSidebarBox("Class Definitions")
621 var group = new TplSidebarGroup("Introductions")
622 for mclassdef in sorted_intro_mclassdefs do
623 tpl_sidebar_item(mclassdef, group)
624 end
625 box.elts.add group
626 group = new TplSidebarGroup("Refinements")
627 for mclassdef in sorted_redef_mclassdefs do
628 if intro_mclassdefs.has(mclassdef.mclass.intro) then continue
629 tpl_sidebar_item(mclassdef, group)
630 end
631 box.elts.add group
632 sidebar.boxes.add box
633 end
634
635 # Module inheritance to display on sidebar
636 fun tpl_sidebar_inheritance(sidebar: TplSidebar) do
637 var box = new TplSidebarBox("Module Hierarchy")
638 box.elts.add tpl_sidebar_group("Nested Modules", mmodule.in_nesting.direct_greaters.to_a)
639 var dependencies = new Array[MModule]
640 for dep in mmodule.in_importation.greaters do
641 if dep == mmodule or
642 dep.direct_owner == mmodule or
643 dep.public_owner == mmodule then continue
644 dependencies.add(dep)
645 end
646 if dependencies.length > 0 then
647 box.elts.add tpl_sidebar_group("All dependencies", dependencies)
648 end
649 var clients = new Array[MModule]
650 for dep in mmodule.in_importation.smallers do
651 if dep.name == "<main>" then continue
652 if dep == mmodule then continue
653 clients.add(dep)
654 end
655 if clients.length > 0 then
656 box.elts.add tpl_sidebar_group("All clients", clients)
657 end
658 sidebar.boxes.add box
659 end
660
661 private fun tpl_sidebar_item(mclassdef: MClassDef, group: TplSidebarGroup) do
662 if mclassdef.is_intro then
663 group.add_bullet("I", "Introduced", mclassdef.tpl_link_anchor, ["intro"])
664 else
665 group.add_bullet("R", "Redefined", mclassdef.tpl_link_anchor, ["redef"])
666 end
667 end
668
669 private fun tpl_sidebar_group(name: String, elts: Array[MModule]): TplSidebarGroup do
670 var group = new TplSidebarGroup(name)
671 for elt in elts do
672 group.add_elt(elt.tpl_link, new Array[String])
673 end
674 return group
675 end
676
677 redef fun tpl_content do
678 var class_sorter = new MClassNameSorter
679 var tpl = new TplModulePage
680 tpl.title = mmodule.nitdoc_name
681 tpl.subtitle = mmodule.tpl_signature
682 tpl.definition = mmodule.tpl_definition
683 var location = mmodule.location
684 tpl.definition.location = tpl_showsource(location)
685 tpl.definition.github_area = tpl_github(mmodule.full_namespace, mmodule.mdoc, location)
686 tpl.graph = tpl_dot
687 for mclassdef in sorted_intro_mclassdefs do tpl.intros.add tpl_mclassdef_article(mclassdef)
688 for mclassdef in sorted_redef_mclassdefs do
689 if intro_mclassdefs.has(mclassdef.mclass.intro) then continue
690 tpl.redefs.add tpl_mclassdef_article(mclassdef)
691 end
692 return tpl
693 end
694
695 # Genrate dot hierarchy for class inheritance
696 fun tpl_dot: nullable TplGraph do
697 # build poset with public owners
698 var poset = new POSet[MModule]
699 for mmodule in self.mmodule.in_importation.poset do
700 if mmodule.name == "<main>" then continue
701 #if mmodule.public_owner != null then continue
702 if not mmodule.in_importation < self.mmodule and not self.mmodule.in_importation < mmodule and mmodule != self.mmodule then continue
703 poset.add_node(mmodule)
704 for omodule in mmodule.in_importation.poset do
705 if mmodule == omodule then continue
706 if omodule.name == "<main>" then continue
707 if not omodule.in_importation < self.mmodule and not self.mmodule.in_importation < omodule then continue
708 if omodule.in_importation < mmodule then
709 poset.add_node(omodule)
710 poset.add_edge(omodule, mmodule)
711 end
712 if mmodule.in_importation < omodule then
713 poset.add_node(omodule)
714 poset.add_edge(mmodule, omodule)
715 end
716 #if omodule.public_owner != null then continue
717 #if mmodule.in_importation < omodule then
718 #poset.add_node(omodule)
719 #poset.add_edge(mmodule, omodule)
720 #end
721 end
722 end
723 # build graph
724 var op = new FlatBuffer
725 var name = "dep_{mmodule.name}"
726 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")
727 for mmodule in poset do
728 if mmodule == self.mmodule then
729 op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n")
730 else
731 op.append("\"{mmodule.name}\"[URL=\"{mmodule.nitdoc_url}\"];\n")
732 end
733 for omodule in poset[mmodule].direct_greaters do
734 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
735 end
736 end
737 op.append("\}\n")
738 return tpl_graph(op, name, "Dependency graph for module {mmodule.name}")
739 end
740
741 private fun in_nesting_intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
742 var res = new HashSet[MClassDef]
743 for mmodule in self.mmodule.in_nesting.greaters do
744 res.add_all mmodule.intro_mclassdefs(min_visibility)
745 end
746 return res
747 end
748
749 private fun in_nesting_redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
750 var res = new HashSet[MClassDef]
751 for mmodule in self.mmodule.in_nesting.greaters do
752 res.add_all mmodule.redef_mclassdefs(min_visibility)
753 end
754 return res
755 end
756 end
757
758 # A class page
759 # Display a list properties defined or redefined for this class
760 class NitdocClass
761 super NitdocPage
762
763 private var mclass: MClass
764 private var inherited_mpropdefs: Set[MPropDef]
765 private var intro_mpropdefs: Set[MPropDef]
766 private var redef_mpropdefs: Set[MPropDef]
767 private var all_mpropdefs: Set[MPropDef]
768
769 init(mclass: MClass, ctx: NitdocContext) do
770 self.mclass = mclass
771 super(ctx)
772 intro_mpropdefs = mclass_intro_mpropdefs
773 redef_mpropdefs = mclass_redef_mpropdefs
774 inherited_mpropdefs = in_nesting_inherited_mpropedefs
775 all_mpropdefs = new HashSet[MPropDef]
776 all_mpropdefs.add_all intro_mpropdefs
777 all_mpropdefs.add_all redef_mpropdefs
778 all_mpropdefs.add_all inherited_mpropdefs
779 end
780
781 private fun in_nesting_inherited_mpropedefs: Set[MPropDef] do
782 var res = new HashSet[MPropDef]
783 var local = mclass.local_mproperties(ctx.min_visibility)
784 for mprop in mclass.inherited_mproperties(ctx.mainmodule, ctx.min_visibility) do
785 if local.has(mprop) then continue
786 if mprop isa MMethod and mprop.is_init then continue
787 res.add mprop.intro
788 end
789 return res
790 end
791
792 private fun mclass_intro_mpropdefs: Set[MPropDef] do
793 var res = new HashSet[MPropDef]
794 for mclassdef in mclass.mclassdefs do
795 for mpropdef in mclassdef.mpropdefs do
796 if not mpropdef.is_intro then continue
797 if mclassdef.mmodule.public_owner != mclass.public_owner then continue
798 var mprop = mpropdef.mproperty
799 if mprop isa MMethod and mprop.is_init and mclass.is_abstract then continue
800 if mprop.visibility < ctx.min_visibility then continue
801 res.add mpropdef
802 end
803 end
804 return res
805 end
806
807 private fun mclass_redef_mpropdefs: Set[MPropDef] do
808 var res = new HashSet[MPropDef]
809 for mclassdef in mclass.mclassdefs do
810 if mclassdef == mclass.intro then continue
811 for mpropdef in mclassdef.mpropdefs do
812 if mclassdef.mmodule.public_owner == mclass.public_owner then continue
813 if mpropdef.mproperty.visibility < ctx.min_visibility then continue
814 res.add mpropdef
815 end
816 end
817 return res
818 end
819
820 redef fun tpl_title do
821 if mclass.mdoc != null then
822 return "{mclass.nitdoc_name} class | {mclass.mdoc.short_comment} | {super}"
823 else
824 return "{mclass.nitdoc_name} class | {super}"
825 end
826 end
827
828 redef fun tpl_topmenu do
829 var topmenu = super
830 var mmodule: MModule
831 if mclass.public_owner == null then
832 mmodule = mclass.intro_mmodule
833 else
834 mmodule = mclass.public_owner.as(not null)
835 end
836 topmenu.add_elt("index.html", "Overview", false)
837 topmenu.add_elt("{mmodule.nitdoc_url}", "{mmodule.nitdoc_name}", false)
838 topmenu.add_elt("#", "{mclass.nitdoc_name}", true)
839 topmenu.add_elt("search.html", "Search", false)
840 return topmenu
841 end
842
843 redef fun tpl_sidebar do
844 var sidebar = new TplSidebar
845 tpl_sidebar_properties(sidebar)
846 tpl_sidebar_inheritance(sidebar)
847 return sidebar
848 end
849
850 # Property list to display in sidebar
851 fun tpl_sidebar_properties(sidebar: TplSidebar) do
852 var kind_map = sort_by_kind(all_mpropdefs)
853 var sorter = new MPropDefNameSorter
854 var box = new TplSidebarBox("Properties")
855 # virtual types
856 var elts = kind_map["type"].to_a
857 sorter.sort(elts)
858 var group = new TplSidebarGroup("Virtual Types")
859 for mprop in elts do
860 tpl_sidebar_item(mprop, group)
861 end
862 box.elts.add group
863 # constructors
864 elts = kind_map["init"].to_a
865 sorter.sort(elts)
866 group = new TplSidebarGroup("Constructors")
867 for mprop in elts do
868 tpl_sidebar_item(mprop, group)
869 end
870 box.elts.add group
871 # methods
872 elts = kind_map["fun"].to_a
873 sorter.sort(elts)
874 group = new TplSidebarGroup("Methods")
875 for mprop in elts do
876 tpl_sidebar_item(mprop, group)
877 end
878 box.elts.add group
879 sidebar.boxes.add box
880 end
881
882 # Class inheritance to display in sidebar
883 fun tpl_sidebar_inheritance(sidebar: TplSidebar) do
884 var sorted = new Array[MClass]
885 var sorterp = new MClassNameSorter
886 var box = new TplSidebarBox("Inheritance")
887 var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a
888 if greaters.length > 1 then
889 ctx.mainmodule.linearize_mclasses(greaters)
890 box.elts.add tpl_sidebar_group("Superclasses", greaters)
891 end
892 var smallers = mclass.in_hierarchy(ctx.mainmodule).smallers.to_a
893 var direct_smallers = mclass.in_hierarchy(ctx.mainmodule).direct_smallers.to_a
894 if smallers.length <= 1 then
895 box.elts.add(new TplSidebarGroup("No Known Subclasses"))
896 else if smallers.length <= 100 then
897 ctx.mainmodule.linearize_mclasses(smallers)
898 box.elts.add tpl_sidebar_group("Subclasses", smallers)
899 else if direct_smallers.length <= 100 then
900 ctx.mainmodule.linearize_mclasses(direct_smallers)
901 box.elts.add tpl_sidebar_group("Direct Subclasses Only", direct_smallers)
902 else
903 box.elts.add(new TplSidebarGroup("Too much Subclasses to list"))
904 end
905 sidebar.boxes.add box
906 end
907
908 private fun tpl_sidebar_item(mprop: MPropDef, group: TplSidebarGroup) do
909 if mprop.is_intro and mprop.mclassdef.mclass == mclass then
910 group.add_bullet("I", "Introduced", mprop.tpl_link, ["intro"])
911 else if mprop.is_intro and mprop.mclassdef.mclass != mclass then
912 group.add_bullet("H", "Inherited", mprop.tpl_link, ["inherit"])
913 else
914 group.add_bullet("R", "Redefined", mprop.tpl_link, ["redef"])
915 end
916 end
917
918 private fun tpl_sidebar_group(name: String, elts: Array[MClass]): TplSidebarGroup do
919 var group = new TplSidebarGroup(name)
920 for elt in elts do
921 if elt == mclass then continue
922 group.add_elt(elt.tpl_link, new Array[String])
923 end
924 return group
925 end
926
927 redef fun tpl_content do
928 var intro = mclass.intro
929 var tpl = new TplClassPage
930 tpl.title = "{mclass.nitdoc_name}{mclass.tpl_short_signature}"
931 tpl.subtitle = mclass.tpl_namespace_with_signature
932 tpl.definition = intro.tpl_definition
933 var location = intro.location
934 tpl.definition.location = tpl_showsource(location)
935 tpl.definition.github_area = tpl_github(intro.full_namespace, intro.mdoc, location)
936 tpl.graph = tpl_dot
937
938 # properties
939 var prop_sorter = new MPropDefNameSorter
940 var kind_map = sort_by_kind(intro_mpropdefs)
941
942 # virtual types
943 var elts = kind_map["type"].to_a
944 prop_sorter.sort(elts)
945 for elt in elts do tpl.types.add tpl_mpropdef_article(elt)
946
947 # constructors
948 elts = kind_map["init"].to_a
949 prop_sorter.sort(elts)
950 for elt in elts do tpl.inits.add tpl_mpropdef_article(elt)
951
952 # intro methods
953 elts = kind_map["fun"].to_a
954 prop_sorter.sort(elts)
955 for elt in elts do tpl.methods.add tpl_mpropdef_article(elt)
956
957 # redef methods
958 kind_map = sort_by_kind(redef_mpropdefs)
959 var module_sorter = new MModuleNameSorter
960 var module_map = sort_by_mmodule(kind_map["fun"])
961 var owner_map = sort_by_public_owner(module_map.keys)
962 var owners = owner_map.keys.to_a
963 module_sorter.sort owners
964
965 var ctpl = new TplConcernList
966 var mtpl = new Template
967 for owner in owners do
968 # concerns list
969 var octpl = new TplConcernListElt
970 octpl.anchor = "#{owner.nitdoc_anchor}"
971 octpl.name = owner.nitdoc_name
972 if owner.mdoc != null then
973 octpl.comment = owner.mdoc.short_comment
974 end
975 ctpl.elts.add octpl
976 # concern section
977 var otpl = new TplTopConcern
978 otpl.anchor = owner.nitdoc_anchor
979 otpl.concern = owner.tpl_link
980 mtpl.add otpl
981
982 var mmodules = owner_map[owner].to_a
983 module_sorter.sort mmodules
984 var stpl = new TplConcernList
985 for mmodule in mmodules do
986 # concerns list
987 if mmodule != owner then
988 var mctpl = new TplConcernListElt
989 mctpl.anchor = "#{mmodule.nitdoc_anchor}"
990 mctpl.name = mmodule.nitdoc_name
991 if mmodule.mdoc != null then
992 mctpl.comment = mmodule.mdoc.short_comment
993 end
994 stpl.elts.add mctpl
995 # concern section
996 var cctpl = new TplConcern
997 cctpl.anchor = mmodule.nitdoc_anchor
998 cctpl.concern = mmodule.tpl_link
999 if mmodule.mdoc != null then
1000 cctpl.comment = mmodule.mdoc.short_comment
1001 end
1002 mtpl.add cctpl
1003 end
1004 var mprops = module_map[mmodule].to_a
1005 prop_sorter.sort mprops
1006 for mprop in mprops do mtpl.add tpl_mpropdef_article(mprop)
1007 end
1008 ctpl.elts.add stpl
1009 end
1010 if not owners.is_empty then
1011 tpl.concerns = ctpl
1012 end
1013 tpl.methods.add mtpl
1014 return tpl
1015 end
1016
1017 private fun sort_by_kind(mpropdefs: Set[MPropDef]): Map[String, Set[MPropDef]] do
1018 var map = new HashMap[String, Set[MPropDef]]
1019 map["type"] = new HashSet[MPropDef]
1020 map["init"] = new HashSet[MPropDef]
1021 map["fun"] = new HashSet[MPropDef]
1022 for mpropdef in mpropdefs do
1023 if mpropdef isa MVirtualTypeDef then
1024 map["type"].add mpropdef
1025 else if mpropdef isa MMethodDef then
1026 if mpropdef.mproperty.is_init then
1027 map["init"].add mpropdef
1028 else
1029 map["fun"].add mpropdef
1030 end
1031 end
1032 end
1033 return map
1034 end
1035
1036 private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do
1037 var map = new HashMap[MModule, Set[MPropDef]]
1038 for mpropdef in mpropdefs do
1039 var mmodule = mpropdef.mclassdef.mmodule
1040 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef]
1041 map[mmodule].add mpropdef
1042 end
1043 return map
1044 end
1045
1046 private fun sort_by_public_owner(mmodules: Collection[MModule]): Map[MModule, Set[MModule]] do
1047 var map = new HashMap[MModule, Set[MModule]]
1048 for mmodule in mmodules do
1049 var owner = mmodule
1050 if mmodule.public_owner != null then owner = mmodule.public_owner.as(not null)
1051 if not map.has_key(owner) then map[owner] = new HashSet[MModule]
1052 map[owner].add mmodule
1053 end
1054 return map
1055 end
1056
1057 # Generate dot hierarchy for classes
1058 fun tpl_dot: nullable TplGraph do
1059 var pe = mclass.in_hierarchy(ctx.mainmodule)
1060 var cla = new HashSet[MClass]
1061 var sm = new HashSet[MClass]
1062 var sm2 = new HashSet[MClass]
1063 sm.add(mclass)
1064 while cla.length + sm.length < 10 and sm.length > 0 do
1065 cla.add_all(sm)
1066 sm2.clear
1067 for x in sm do
1068 sm2.add_all(pe.poset[x].direct_smallers)
1069 end
1070 var t = sm
1071 sm = sm2
1072 sm2 = t
1073 end
1074 cla.add_all(pe.greaters)
1075
1076 var op = new FlatBuffer
1077 var name = "dep_{mclass.name}"
1078 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")
1079 for c in cla do
1080 if c == mclass then
1081 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1082 else
1083 op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n")
1084 end
1085 for c2 in pe.poset[c].direct_greaters do
1086 if not cla.has(c2) then continue
1087 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1088 end
1089 if not pe.poset[c].direct_smallers.is_empty then
1090 var others = true
1091 for c2 in pe.poset[c].direct_smallers do
1092 if cla.has(c2) then others = false
1093 end
1094 if others then
1095 op.append("\"{c.name}...\"[label=\"\"];\n")
1096 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1097 end
1098 end
1099 end
1100 op.append("\}\n")
1101 return tpl_graph(op, name, "Dependency graph for class {mclass.name}")
1102 end
1103 end
1104
1105 #
1106 # Model redefs
1107 #
1108
1109 redef class MModule
1110 # Return the HTML escaped name of the module
1111 private fun nitdoc_name: String do return name.html_escape
1112
1113 private fun full_namespace: String do
1114 if public_owner != null then
1115 return "{public_owner.nitdoc_name}::{nitdoc_name}"
1116 end
1117 return nitdoc_name
1118 end
1119
1120 # URL to nitdoc page
1121 # module_owner_name.html
1122 private fun nitdoc_url: String do
1123 var res = new FlatBuffer
1124 res.append("module_")
1125 var mowner = public_owner
1126 if mowner != null then
1127 res.append("{public_owner.name}_")
1128 end
1129 res.append("{self.name}.html")
1130 return res.to_s
1131 end
1132
1133 # html nitdoc_anchor id for the module in a nitdoc page
1134 # MOD_owner_name
1135 private fun nitdoc_anchor: String do
1136 var res = new FlatBuffer
1137 res.append("MOD_")
1138 var mowner = public_owner
1139 if mowner != null then
1140 res.append("{public_owner.name}_")
1141 end
1142 res.append(self.name)
1143 return res.to_s
1144 end
1145
1146 # Return a link (html a tag) to the nitdoc module page
1147 private fun tpl_link: TplLink do
1148 var tpl = new TplLink
1149 tpl.href = nitdoc_url
1150 tpl.text = nitdoc_name
1151 if mdoc != null then
1152 tpl.title = mdoc.short_comment
1153 end
1154 return tpl
1155 end
1156
1157 # Return the module signature decorated with html
1158 private fun tpl_signature: Template do
1159 var tpl = new Template
1160 tpl.add "<span>module "
1161 tpl.add tpl_full_namespace
1162 tpl.add "</span>"
1163 return tpl
1164 end
1165
1166 # Return the module full namespace decorated with html
1167 private fun tpl_full_namespace: Template do
1168 var tpl = new Template
1169 tpl.add ("<span>")
1170 var mowner = public_owner
1171 if mowner != null then
1172 tpl.add public_owner.tpl_namespace
1173 tpl.add "::"
1174 end
1175 tpl.add tpl_link
1176 tpl.add "</span>"
1177 return tpl
1178 end
1179
1180 # Return the module full namespace decorated with html
1181 private fun tpl_namespace: Template do
1182 var tpl = new Template
1183 tpl.add "<span>"
1184 var mowner = public_owner
1185 if mowner != null then
1186 tpl.add public_owner.tpl_namespace
1187 else
1188 tpl.add tpl_link
1189 end
1190 tpl.add "</span>"
1191 return tpl
1192 end
1193
1194 # Description with short comment
1195 private fun tpl_short_definition: TplDefinition do
1196 var tpl = new TplDefinition
1197 tpl.namespace = tpl_namespace
1198 if mdoc != null then
1199 tpl.comment = mdoc.tpl_short_comment
1200 end
1201 return tpl
1202 end
1203
1204 # Description with full comment
1205 private fun tpl_definition: TplDefinition do
1206 var tpl = new TplDefinition
1207 tpl.namespace = tpl_namespace
1208 if mdoc != null then
1209 tpl.comment = mdoc.tpl_comment
1210 end
1211 return tpl
1212 end
1213 end
1214
1215 redef class MClass
1216 # Return the HTML escaped name of the module
1217 private fun nitdoc_name: String do return name.html_escape
1218
1219 # URL to nitdoc page
1220 # class_owner_name.html
1221 private fun nitdoc_url: String do
1222 return "class_{public_owner}_{name}.html"
1223 end
1224
1225 # html nitdoc_anchor id for the class in a nitdoc page
1226 # MOD_owner_name
1227 private fun nitdoc_anchor: String do
1228 return "CLASS_{public_owner.name}_{name}"
1229 end
1230
1231 # Return a link (with signature) to the nitdoc class page
1232 private fun tpl_link: TplLink do
1233 var tpl = new TplLink
1234 tpl.href = nitdoc_url
1235 tpl.text = "{nitdoc_name}{tpl_short_signature.write_to_string}"
1236 if intro.mdoc != null then
1237 tpl.title = intro.mdoc.short_comment
1238 end
1239 return tpl
1240 end
1241
1242 # Return a short link (without signature) to the nitdoc class page
1243 private fun tpl_short_link: TplLink do
1244 var tpl = new TplLink
1245 tpl.href = nitdoc_url
1246 tpl.text = nitdoc_name
1247 if intro.mdoc != null then
1248 tpl.title = intro.mdoc.short_comment
1249 end
1250 return tpl
1251 end
1252
1253 # Return a link (with signature) to the class nitdoc_anchor
1254 private fun tpl_link_anchor: TplLink do
1255 var tpl = new TplLink
1256 tpl.href = "#{nitdoc_anchor}"
1257 tpl.text = "{nitdoc_name}{tpl_short_signature.write_to_string}"
1258 if intro.mdoc != null then
1259 tpl.title = intro.mdoc.short_comment
1260 end
1261 return tpl
1262 end
1263
1264 # Return the generic signature of the class with bounds
1265 private fun tpl_signature: Template do
1266 var tpl = new Template
1267 if arity > 0 then
1268 tpl.add "["
1269 for i in [0..intro.parameter_names.length[ do
1270 tpl.add "{intro.parameter_names[i]}: "
1271 tpl.add intro.bound_mtype.arguments[i].tpl_link
1272 if i < intro.parameter_names.length - 1 then tpl.add ", "
1273 end
1274 tpl.add "]"
1275 end
1276 return tpl
1277 end
1278
1279 # Return the generic signature of the class without bounds
1280 private fun tpl_short_signature: String do
1281 if arity > 0 then
1282 return "[{intro.parameter_names.join(", ")}]"
1283 else
1284 return ""
1285 end
1286 end
1287
1288 # Return the class namespace decorated with html
1289 private fun tpl_namespace: Template do
1290 var tpl = new Template
1291 tpl.add intro_mmodule.tpl_namespace
1292 tpl.add "::<span>"
1293 tpl.add tpl_short_link
1294 tpl.add "</span>"
1295 return tpl
1296 end
1297
1298 private fun tpl_namespace_with_signature: Template do
1299 var tpl = new Template
1300 tpl.add intro.tpl_modifiers
1301 tpl.add intro.mmodule.tpl_namespace
1302 tpl.add "::"
1303 tpl.add nitdoc_name
1304 tpl.add tpl_signature
1305 return tpl
1306 end
1307 end
1308
1309 redef class MProperty
1310 # Escape name for html output
1311 private fun nitdoc_name: String do return name.html_escape
1312
1313 # Return the property namespace decorated with html
1314 private fun tpl_namespace: Template do
1315 var tpl = new Template
1316 tpl.add intro_mclassdef.mclass.tpl_namespace
1317 tpl.add intro_mclassdef.mclass.tpl_short_signature
1318 tpl.add "::<span>"
1319 tpl.add intro.tpl_link
1320 tpl.add "</span>"
1321 return tpl
1322 end
1323
1324 private fun tpl_signature: Template is abstract
1325 end
1326
1327 redef class MMethod
1328 redef fun tpl_signature do return intro.msignature.tpl_signature
1329 end
1330
1331 redef class MVirtualTypeProp
1332 redef fun tpl_signature do
1333 var tpl = new Template
1334 tpl.add ": "
1335 tpl.add intro.bound.tpl_link
1336 return tpl
1337 end
1338 end
1339
1340 redef class MType
1341 # Link to the type definition in the nitdoc page
1342 private fun tpl_link: Template is abstract
1343 end
1344
1345 redef class MClassType
1346 redef fun tpl_link do return mclass.tpl_link
1347 end
1348
1349 redef class MNullableType
1350 redef fun tpl_link do
1351 var tpl = new Template
1352 tpl.add "nullable "
1353 tpl.add mtype.tpl_link
1354 return tpl
1355 end
1356 end
1357
1358 redef class MGenericType
1359 redef fun tpl_link: Template do
1360 var tpl = new Template
1361 tpl.add mclass.tpl_short_link
1362 tpl.add "["
1363 for i in [0..arguments.length[ do
1364 tpl.add arguments[i].tpl_link
1365 if i < arguments.length - 1 then tpl.add ", "
1366 end
1367 tpl.add "]"
1368 return tpl
1369 end
1370 end
1371
1372 redef class MParameterType
1373 redef fun tpl_link do
1374 var name = mclass.intro.parameter_names[rank]
1375 var tpl = new TplLink
1376 tpl.href = "{mclass.nitdoc_url}#FT_{name}"
1377 tpl.text = name
1378 tpl.title = "formal type"
1379 return tpl
1380 end
1381 end
1382
1383 redef class MVirtualType
1384 redef fun tpl_link do return mproperty.intro.tpl_link
1385 end
1386
1387 redef class MClassDef
1388 # Return the classdef namespace decorated with html
1389 private fun tpl_namespace: Template do
1390 var tpl = new Template
1391 tpl.add mmodule.tpl_namespace
1392 tpl.add "::<span>"
1393 tpl.add mclass.tpl_link
1394 tpl.add "</span>"
1395 return tpl
1396 end
1397
1398 private fun full_namespace: String do
1399 return "{mmodule.full_namespace}::{mclass.nitdoc_name}"
1400 end
1401
1402 private fun tpl_link_anchor: TplLink do return mclass.tpl_link_anchor
1403
1404 private fun tpl_article: TplArticle do
1405 var tpl = new TplArticle
1406 tpl.id = mclass.nitdoc_anchor
1407 tpl.classes.add_all(tpl_css_classes)
1408 tpl.title = new Template
1409 tpl.title.add mclass.tpl_short_link
1410 tpl.title.add mclass.tpl_signature
1411 tpl.subtitle = new Template
1412 tpl.subtitle.add tpl_modifiers
1413 tpl.subtitle.add tpl_namespace
1414 tpl.content = new Template
1415 tpl.content.add tpl_definition
1416 return tpl
1417 end
1418
1419 private fun tpl_css_classes: Set[String] do
1420 var set = new HashSet[String]
1421 set.add_all mclass.intro.modifiers
1422 set.add_all modifiers
1423 return set
1424 end
1425
1426 private fun tpl_modifiers: Template do
1427 var tpl = new Template
1428 for modifier in modifiers do
1429 if modifier == "public" then continue
1430 tpl.add "{modifier} "
1431 end
1432 return tpl
1433 end
1434
1435 private fun tpl_short_definition: TplDefinition do
1436 var tpl = new TplDefinition
1437 tpl.namespace = mmodule.tpl_full_namespace
1438 if mdoc != null then
1439 tpl.comment = mdoc.tpl_short_comment
1440 end
1441 return tpl
1442 end
1443
1444 private fun tpl_definition: TplDefinition do
1445 var tpl = new TplDefinition
1446 tpl.namespace = mmodule.tpl_full_namespace
1447 if mdoc != null then
1448 tpl.comment = mdoc.tpl_comment
1449 end
1450 return tpl
1451 end
1452 end
1453
1454 redef class MPropDef
1455 # Return the full qualified name of the mpropdef
1456 # module::classdef::name
1457 private fun tpl_namespace: Template do
1458 var tpl = new Template
1459 tpl.add mclassdef.tpl_namespace
1460 tpl.add "::"
1461 tpl.add mproperty.name
1462 return tpl
1463 end
1464
1465 private fun full_namespace: String do
1466 return "{mclassdef.full_namespace}::{mproperty.nitdoc_name}"
1467 end
1468
1469 # URL into the nitdoc page
1470 # class_owner_name.html#nitdoc_anchor
1471 private fun nitdoc_url: String do
1472 return "{mclassdef.mclass.nitdoc_url}#{nitdoc_anchor}"
1473 end
1474
1475 # html nitdoc_anchor id for the property in a nitdoc class page
1476 # PROP_mclass_propertyname
1477 private fun nitdoc_anchor: String do
1478 return "PROP_{mclassdef.mclass.public_owner.nitdoc_name}_{mproperty.name.replace(" ", "_")}"
1479 end
1480
1481 # Return a link to property into the nitdoc class page
1482 # <a href="nitdoc_url" title="short_comment">nitdoc_name</a>
1483 private fun tpl_link: TplLink do
1484 var tpl = new TplLink
1485 tpl.href = nitdoc_url
1486 tpl.text = mproperty.nitdoc_name
1487 if mproperty.intro.mdoc != null then
1488 tpl.title = mproperty.intro.mdoc.short_comment
1489 end
1490 return tpl
1491 end
1492
1493 private fun tpl_article: TplArticle do
1494 var tpl = new TplArticle
1495 tpl.id = nitdoc_anchor
1496 tpl.classes.add_all(tpl_css_classes)
1497 tpl.title = new Template
1498 tpl.title.add mproperty.nitdoc_name
1499 tpl.title.add mproperty.tpl_signature
1500 tpl.subtitle = new Template
1501 tpl.subtitle.add tpl_modifiers
1502 tpl.subtitle.add tpl_namespace
1503 tpl.content = new Template
1504 tpl.content.add tpl_definition
1505 return tpl
1506 end
1507
1508 private fun tpl_css_classes: Set[String] do
1509 var set = new HashSet[String]
1510 set.add_all mproperty.intro.modifiers
1511 set.add_all modifiers
1512 return set
1513 end
1514
1515 private fun tpl_modifiers: Template do
1516 var tpl = new Template
1517 for modifier in modifiers do
1518 if modifier == "public" then continue
1519 tpl.add "{modifier} "
1520 end
1521 return tpl
1522 end
1523
1524 private fun tpl_short_definition: TplDefinition do
1525 var tpl = new TplDefinition
1526 tpl.namespace = mclassdef.tpl_namespace
1527 if mdoc != null then
1528 tpl.comment = mdoc.tpl_short_comment
1529 end
1530 return tpl
1531 end
1532
1533 private fun tpl_definition: TplDefinition do
1534 var tpl = new TplDefinition
1535 tpl.namespace = mclassdef.tpl_namespace
1536 if mdoc != null then
1537 tpl.comment = mdoc.tpl_comment
1538 end
1539 return tpl
1540 end
1541 end
1542
1543 redef class MSignature
1544 private fun tpl_signature: Template do
1545 var tpl = new Template
1546 if not mparameters.is_empty then
1547 tpl.add "("
1548 for i in [0..mparameters.length[ do
1549 tpl.add mparameters[i].tpl_link
1550 if i < mparameters.length - 1 then tpl.add ", "
1551 end
1552 tpl.add ")"
1553 end
1554 if return_mtype != null then
1555 tpl.add ": "
1556 tpl.add return_mtype.tpl_link
1557 end
1558 return tpl
1559 end
1560 end
1561
1562 redef class MParameter
1563 private fun tpl_link: Template do
1564 var tpl = new Template
1565 tpl.add "{name}: "
1566 tpl.add mtype.tpl_link
1567 if is_vararg then tpl.add "..."
1568 return tpl
1569 end
1570 end
1571
1572 redef class Location
1573 fun github(gitdir: String): String do
1574 var base_dir = getcwd.join_path(gitdir).simplify_path
1575 var file_loc = getcwd.join_path(file.filename).simplify_path
1576 var gith_loc = file_loc.substring(base_dir.length + 1, file_loc.length)
1577 return "{gith_loc}:{line_start},{column_start}--{line_end},{column_end}"
1578 end
1579 end
1580
1581 redef class MDoc
1582 private fun short_comment: String do
1583 return content.first.html_escape
1584 end
1585
1586 private fun full_comment: String do
1587 return content.join("\n").html_escape
1588 end
1589
1590 private fun tpl_short_comment: TplShortComment do
1591 return new TplShortComment(short_markdown)
1592 end
1593
1594 private fun tpl_comment: TplComment do
1595 return new TplComment(full_markdown)
1596 end
1597 end
1598
1599 var nitdoc = new NitdocContext
1600 nitdoc.generate_nitdoc
1601