nitdoc: display redefined methods when mmodule.public_owner is null
[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 var owner = mclassdef.mmodule.public_owner
796 if owner == null then owner = mclassdef.mmodule
797 for mpropdef in mclassdef.mpropdefs do
798 if not mpropdef.is_intro then continue
799 if owner != mclass.public_owner then continue
800 var mprop = mpropdef.mproperty
801 if mprop isa MMethod and mprop.is_init and mclass.is_abstract then continue
802 if mprop.visibility < ctx.min_visibility then continue
803 res.add mpropdef
804 end
805 end
806 return res
807 end
808
809 private fun mclass_redef_mpropdefs: Set[MPropDef] do
810 var res = new HashSet[MPropDef]
811 for mclassdef in mclass.mclassdefs do
812 if mclassdef == mclass.intro then continue
813 var owner = mclassdef.mmodule.public_owner
814 if owner == null then owner = mclassdef.mmodule
815 for mpropdef in mclassdef.mpropdefs do
816 if owner == mclass.public_owner then continue
817 if mpropdef.mproperty.visibility < ctx.min_visibility then continue
818 res.add mpropdef
819 end
820 end
821 return res
822 end
823
824 redef fun tpl_title do
825 if mclass.mdoc != null then
826 return "{mclass.nitdoc_name} class | {mclass.mdoc.short_comment} | {super}"
827 else
828 return "{mclass.nitdoc_name} class | {super}"
829 end
830 end
831
832 redef fun tpl_topmenu do
833 var topmenu = super
834 var mmodule: MModule
835 if mclass.public_owner == null then
836 mmodule = mclass.intro_mmodule
837 else
838 mmodule = mclass.public_owner.as(not null)
839 end
840 topmenu.add_elt("index.html", "Overview", false)
841 topmenu.add_elt("{mmodule.nitdoc_url}", "{mmodule.nitdoc_name}", false)
842 topmenu.add_elt("#", "{mclass.nitdoc_name}", true)
843 topmenu.add_elt("search.html", "Search", false)
844 return topmenu
845 end
846
847 redef fun tpl_sidebar do
848 var sidebar = new TplSidebar
849 tpl_sidebar_properties(sidebar)
850 tpl_sidebar_inheritance(sidebar)
851 return sidebar
852 end
853
854 # Property list to display in sidebar
855 fun tpl_sidebar_properties(sidebar: TplSidebar) do
856 var kind_map = sort_by_kind(all_mpropdefs)
857 var sorter = new MPropDefNameSorter
858 var box = new TplSidebarBox("Properties")
859 # virtual types
860 var elts = kind_map["type"].to_a
861 sorter.sort(elts)
862 var group = new TplSidebarGroup("Virtual Types")
863 for mprop in elts do
864 tpl_sidebar_item(mprop, group)
865 end
866 box.elts.add group
867 # constructors
868 elts = kind_map["init"].to_a
869 sorter.sort(elts)
870 group = new TplSidebarGroup("Constructors")
871 for mprop in elts do
872 tpl_sidebar_item(mprop, group)
873 end
874 box.elts.add group
875 # methods
876 elts = kind_map["fun"].to_a
877 sorter.sort(elts)
878 group = new TplSidebarGroup("Methods")
879 for mprop in elts do
880 tpl_sidebar_item(mprop, group)
881 end
882 box.elts.add group
883 sidebar.boxes.add box
884 end
885
886 # Class inheritance to display in sidebar
887 fun tpl_sidebar_inheritance(sidebar: TplSidebar) do
888 var sorted = new Array[MClass]
889 var sorterp = new MClassNameSorter
890 var box = new TplSidebarBox("Inheritance")
891 var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a
892 if greaters.length > 1 then
893 ctx.mainmodule.linearize_mclasses(greaters)
894 box.elts.add tpl_sidebar_group("Superclasses", greaters)
895 end
896 var smallers = mclass.in_hierarchy(ctx.mainmodule).smallers.to_a
897 var direct_smallers = mclass.in_hierarchy(ctx.mainmodule).direct_smallers.to_a
898 if smallers.length <= 1 then
899 box.elts.add(new TplSidebarGroup("No Known Subclasses"))
900 else if smallers.length <= 100 then
901 ctx.mainmodule.linearize_mclasses(smallers)
902 box.elts.add tpl_sidebar_group("Subclasses", smallers)
903 else if direct_smallers.length <= 100 then
904 ctx.mainmodule.linearize_mclasses(direct_smallers)
905 box.elts.add tpl_sidebar_group("Direct Subclasses Only", direct_smallers)
906 else
907 box.elts.add(new TplSidebarGroup("Too much Subclasses to list"))
908 end
909 sidebar.boxes.add box
910 end
911
912 private fun tpl_sidebar_item(mprop: MPropDef, group: TplSidebarGroup) do
913 if mprop.is_intro and mprop.mclassdef.mclass == mclass then
914 group.add_bullet("I", "Introduced", mprop.tpl_link, ["intro"])
915 else if mprop.is_intro and mprop.mclassdef.mclass != mclass then
916 group.add_bullet("H", "Inherited", mprop.tpl_link, ["inherit"])
917 else
918 group.add_bullet("R", "Redefined", mprop.tpl_link, ["redef"])
919 end
920 end
921
922 private fun tpl_sidebar_group(name: String, elts: Array[MClass]): TplSidebarGroup do
923 var group = new TplSidebarGroup(name)
924 for elt in elts do
925 if elt == mclass then continue
926 group.add_elt(elt.tpl_link, new Array[String])
927 end
928 return group
929 end
930
931 redef fun tpl_content do
932 var intro = mclass.intro
933 var tpl = new TplClassPage
934 tpl.title = "{mclass.nitdoc_name}{mclass.tpl_short_signature}"
935 tpl.subtitle = mclass.tpl_namespace_with_signature
936 tpl.definition = intro.tpl_definition
937 var location = intro.location
938 tpl.definition.location = tpl_showsource(location)
939 tpl.definition.github_area = tpl_github(intro.full_namespace, intro.mdoc, location)
940 tpl.graph = tpl_dot
941
942 # properties
943 var prop_sorter = new MPropDefNameSorter
944 var kind_map = sort_by_kind(intro_mpropdefs)
945
946 # virtual types
947 var elts = kind_map["type"].to_a
948 prop_sorter.sort(elts)
949 for elt in elts do tpl.types.add tpl_mpropdef_article(elt)
950
951 # constructors
952 elts = kind_map["init"].to_a
953 prop_sorter.sort(elts)
954 for elt in elts do tpl.inits.add tpl_mpropdef_article(elt)
955
956 # intro methods
957 elts = kind_map["fun"].to_a
958 prop_sorter.sort(elts)
959 for elt in elts do tpl.methods.add tpl_mpropdef_article(elt)
960
961 # redef methods
962 kind_map = sort_by_kind(redef_mpropdefs)
963 var module_sorter = new MModuleNameSorter
964 var module_map = sort_by_mmodule(kind_map["fun"])
965 var owner_map = sort_by_public_owner(module_map.keys)
966 var owners = owner_map.keys.to_a
967 module_sorter.sort owners
968
969 var ctpl = new TplConcernList
970 var mtpl = new Template
971 for owner in owners do
972 # concerns list
973 var octpl = new TplConcernListElt
974 octpl.anchor = "#{owner.nitdoc_anchor}"
975 octpl.name = owner.nitdoc_name
976 if owner.mdoc != null then
977 octpl.comment = owner.mdoc.short_comment
978 end
979 ctpl.elts.add octpl
980 # concern section
981 var otpl = new TplTopConcern
982 otpl.anchor = owner.nitdoc_anchor
983 otpl.concern = owner.tpl_link
984 mtpl.add otpl
985
986 var mmodules = owner_map[owner].to_a
987 module_sorter.sort mmodules
988 var stpl = new TplConcernList
989 for mmodule in mmodules do
990 # concerns list
991 if mmodule != owner then
992 var mctpl = new TplConcernListElt
993 mctpl.anchor = "#{mmodule.nitdoc_anchor}"
994 mctpl.name = mmodule.nitdoc_name
995 if mmodule.mdoc != null then
996 mctpl.comment = mmodule.mdoc.short_comment
997 end
998 stpl.elts.add mctpl
999 # concern section
1000 var cctpl = new TplConcern
1001 cctpl.anchor = mmodule.nitdoc_anchor
1002 cctpl.concern = mmodule.tpl_link
1003 if mmodule.mdoc != null then
1004 cctpl.comment = mmodule.mdoc.short_comment
1005 end
1006 mtpl.add cctpl
1007 end
1008 var mprops = module_map[mmodule].to_a
1009 prop_sorter.sort mprops
1010 for mprop in mprops do mtpl.add tpl_mpropdef_article(mprop)
1011 end
1012 ctpl.elts.add stpl
1013 end
1014 if not owners.is_empty then
1015 tpl.concerns = ctpl
1016 end
1017 tpl.methods.add mtpl
1018 return tpl
1019 end
1020
1021 private fun sort_by_kind(mpropdefs: Set[MPropDef]): Map[String, Set[MPropDef]] do
1022 var map = new HashMap[String, Set[MPropDef]]
1023 map["type"] = new HashSet[MPropDef]
1024 map["init"] = new HashSet[MPropDef]
1025 map["fun"] = new HashSet[MPropDef]
1026 for mpropdef in mpropdefs do
1027 if mpropdef isa MVirtualTypeDef then
1028 map["type"].add mpropdef
1029 else if mpropdef isa MMethodDef then
1030 if mpropdef.mproperty.is_init then
1031 map["init"].add mpropdef
1032 else
1033 map["fun"].add mpropdef
1034 end
1035 end
1036 end
1037 return map
1038 end
1039
1040 private fun sort_by_mmodule(mpropdefs: Collection[MPropDef]): Map[MModule, Set[MPropDef]] do
1041 var map = new HashMap[MModule, Set[MPropDef]]
1042 for mpropdef in mpropdefs do
1043 var mmodule = mpropdef.mclassdef.mmodule
1044 if not map.has_key(mmodule) then map[mmodule] = new HashSet[MPropDef]
1045 map[mmodule].add mpropdef
1046 end
1047 return map
1048 end
1049
1050 private fun sort_by_public_owner(mmodules: Collection[MModule]): Map[MModule, Set[MModule]] do
1051 var map = new HashMap[MModule, Set[MModule]]
1052 for mmodule in mmodules do
1053 var owner = mmodule
1054 if mmodule.public_owner != null then owner = mmodule.public_owner.as(not null)
1055 if not map.has_key(owner) then map[owner] = new HashSet[MModule]
1056 map[owner].add mmodule
1057 end
1058 return map
1059 end
1060
1061 # Generate dot hierarchy for classes
1062 fun tpl_dot: nullable TplGraph do
1063 var pe = mclass.in_hierarchy(ctx.mainmodule)
1064 var cla = new HashSet[MClass]
1065 var sm = new HashSet[MClass]
1066 var sm2 = new HashSet[MClass]
1067 sm.add(mclass)
1068 while cla.length + sm.length < 10 and sm.length > 0 do
1069 cla.add_all(sm)
1070 sm2.clear
1071 for x in sm do
1072 sm2.add_all(pe.poset[x].direct_smallers)
1073 end
1074 var t = sm
1075 sm = sm2
1076 sm2 = t
1077 end
1078 cla.add_all(pe.greaters)
1079
1080 var op = new FlatBuffer
1081 var name = "dep_{mclass.name}"
1082 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")
1083 for c in cla do
1084 if c == mclass then
1085 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1086 else
1087 op.append("\"{c.name}\"[URL=\"{c.nitdoc_url}\"];\n")
1088 end
1089 for c2 in pe.poset[c].direct_greaters do
1090 if not cla.has(c2) then continue
1091 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1092 end
1093 if not pe.poset[c].direct_smallers.is_empty then
1094 var others = true
1095 for c2 in pe.poset[c].direct_smallers do
1096 if cla.has(c2) then others = false
1097 end
1098 if others then
1099 op.append("\"{c.name}...\"[label=\"\"];\n")
1100 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1101 end
1102 end
1103 end
1104 op.append("\}\n")
1105 return tpl_graph(op, name, "Dependency graph for class {mclass.name}")
1106 end
1107 end
1108
1109 #
1110 # Model redefs
1111 #
1112
1113 redef class MModule
1114 # Return the HTML escaped name of the module
1115 private fun nitdoc_name: String do return name.html_escape
1116
1117 private fun full_namespace: String do
1118 if public_owner != null then
1119 return "{public_owner.nitdoc_name}::{nitdoc_name}"
1120 end
1121 return nitdoc_name
1122 end
1123
1124 # URL to nitdoc page
1125 # module_owner_name.html
1126 private fun nitdoc_url: String do
1127 var res = new FlatBuffer
1128 res.append("module_")
1129 var mowner = public_owner
1130 if mowner != null then
1131 res.append("{public_owner.name}_")
1132 end
1133 res.append("{self.name}.html")
1134 return res.to_s
1135 end
1136
1137 # html nitdoc_anchor id for the module in a nitdoc page
1138 # MOD_owner_name
1139 private fun nitdoc_anchor: String do
1140 var res = new FlatBuffer
1141 res.append("MOD_")
1142 var mowner = public_owner
1143 if mowner != null then
1144 res.append("{public_owner.name}_")
1145 end
1146 res.append(self.name)
1147 return res.to_s
1148 end
1149
1150 # Return a link (html a tag) to the nitdoc module page
1151 private fun tpl_link: TplLink do
1152 var tpl = new TplLink
1153 tpl.href = nitdoc_url
1154 tpl.text = nitdoc_name
1155 if mdoc != null then
1156 tpl.title = mdoc.short_comment
1157 end
1158 return tpl
1159 end
1160
1161 # Return the module signature decorated with html
1162 private fun tpl_signature: Template do
1163 var tpl = new Template
1164 tpl.add "<span>module "
1165 tpl.add tpl_full_namespace
1166 tpl.add "</span>"
1167 return tpl
1168 end
1169
1170 # Return the module full namespace decorated with html
1171 private fun tpl_full_namespace: Template do
1172 var tpl = new Template
1173 tpl.add ("<span>")
1174 var mowner = public_owner
1175 if mowner != null then
1176 tpl.add public_owner.tpl_namespace
1177 tpl.add "::"
1178 end
1179 tpl.add tpl_link
1180 tpl.add "</span>"
1181 return tpl
1182 end
1183
1184 # Return the module full namespace decorated with html
1185 private fun tpl_namespace: Template do
1186 var tpl = new Template
1187 tpl.add "<span>"
1188 var mowner = public_owner
1189 if mowner != null then
1190 tpl.add public_owner.tpl_namespace
1191 else
1192 tpl.add tpl_link
1193 end
1194 tpl.add "</span>"
1195 return tpl
1196 end
1197
1198 # Description with short comment
1199 private fun tpl_short_definition: TplDefinition do
1200 var tpl = new TplDefinition
1201 tpl.namespace = tpl_namespace
1202 if mdoc != null then
1203 tpl.comment = mdoc.tpl_short_comment
1204 end
1205 return tpl
1206 end
1207
1208 # Description with full comment
1209 private fun tpl_definition: TplDefinition do
1210 var tpl = new TplDefinition
1211 tpl.namespace = tpl_namespace
1212 if mdoc != null then
1213 tpl.comment = mdoc.tpl_comment
1214 end
1215 return tpl
1216 end
1217 end
1218
1219 redef class MClass
1220 # Return the HTML escaped name of the module
1221 private fun nitdoc_name: String do return name.html_escape
1222
1223 # URL to nitdoc page
1224 # class_owner_name.html
1225 private fun nitdoc_url: String do
1226 return "class_{public_owner}_{name}.html"
1227 end
1228
1229 # html nitdoc_anchor id for the class in a nitdoc page
1230 # MOD_owner_name
1231 private fun nitdoc_anchor: String do
1232 return "CLASS_{public_owner.name}_{name}"
1233 end
1234
1235 # Return a link (with signature) to the nitdoc class page
1236 private fun tpl_link: TplLink do
1237 var tpl = new TplLink
1238 tpl.href = nitdoc_url
1239 tpl.text = "{nitdoc_name}{tpl_short_signature.write_to_string}"
1240 if intro.mdoc != null then
1241 tpl.title = intro.mdoc.short_comment
1242 end
1243 return tpl
1244 end
1245
1246 # Return a short link (without signature) to the nitdoc class page
1247 private fun tpl_short_link: TplLink do
1248 var tpl = new TplLink
1249 tpl.href = nitdoc_url
1250 tpl.text = nitdoc_name
1251 if intro.mdoc != null then
1252 tpl.title = intro.mdoc.short_comment
1253 end
1254 return tpl
1255 end
1256
1257 # Return a link (with signature) to the class nitdoc_anchor
1258 private fun tpl_link_anchor: TplLink do
1259 var tpl = new TplLink
1260 tpl.href = "#{nitdoc_anchor}"
1261 tpl.text = "{nitdoc_name}{tpl_short_signature.write_to_string}"
1262 if intro.mdoc != null then
1263 tpl.title = intro.mdoc.short_comment
1264 end
1265 return tpl
1266 end
1267
1268 # Return the generic signature of the class with bounds
1269 private fun tpl_signature: Template do
1270 var tpl = new Template
1271 if arity > 0 then
1272 tpl.add "["
1273 for i in [0..intro.parameter_names.length[ do
1274 tpl.add "{intro.parameter_names[i]}: "
1275 tpl.add intro.bound_mtype.arguments[i].tpl_link
1276 if i < intro.parameter_names.length - 1 then tpl.add ", "
1277 end
1278 tpl.add "]"
1279 end
1280 return tpl
1281 end
1282
1283 # Return the generic signature of the class without bounds
1284 private fun tpl_short_signature: String do
1285 if arity > 0 then
1286 return "[{intro.parameter_names.join(", ")}]"
1287 else
1288 return ""
1289 end
1290 end
1291
1292 # Return the class namespace decorated with html
1293 private fun tpl_namespace: Template do
1294 var tpl = new Template
1295 tpl.add intro_mmodule.tpl_namespace
1296 tpl.add "::<span>"
1297 tpl.add tpl_short_link
1298 tpl.add "</span>"
1299 return tpl
1300 end
1301
1302 private fun tpl_namespace_with_signature: Template do
1303 var tpl = new Template
1304 tpl.add intro.tpl_modifiers
1305 tpl.add intro.mmodule.tpl_namespace
1306 tpl.add "::"
1307 tpl.add nitdoc_name
1308 tpl.add tpl_signature
1309 return tpl
1310 end
1311 end
1312
1313 redef class MProperty
1314 # Escape name for html output
1315 private fun nitdoc_name: String do return name.html_escape
1316
1317 # Return the property namespace decorated with html
1318 private fun tpl_namespace: Template do
1319 var tpl = new Template
1320 tpl.add intro_mclassdef.mclass.tpl_namespace
1321 tpl.add intro_mclassdef.mclass.tpl_short_signature
1322 tpl.add "::<span>"
1323 tpl.add intro.tpl_link
1324 tpl.add "</span>"
1325 return tpl
1326 end
1327
1328 private fun tpl_signature: Template is abstract
1329 end
1330
1331 redef class MMethod
1332 redef fun tpl_signature do return intro.msignature.tpl_signature
1333 end
1334
1335 redef class MVirtualTypeProp
1336 redef fun tpl_signature do
1337 var tpl = new Template
1338 tpl.add ": "
1339 tpl.add intro.bound.tpl_link
1340 return tpl
1341 end
1342 end
1343
1344 redef class MType
1345 # Link to the type definition in the nitdoc page
1346 private fun tpl_link: Template is abstract
1347 end
1348
1349 redef class MClassType
1350 redef fun tpl_link do return mclass.tpl_link
1351 end
1352
1353 redef class MNullableType
1354 redef fun tpl_link do
1355 var tpl = new Template
1356 tpl.add "nullable "
1357 tpl.add mtype.tpl_link
1358 return tpl
1359 end
1360 end
1361
1362 redef class MGenericType
1363 redef fun tpl_link: Template do
1364 var tpl = new Template
1365 tpl.add mclass.tpl_short_link
1366 tpl.add "["
1367 for i in [0..arguments.length[ do
1368 tpl.add arguments[i].tpl_link
1369 if i < arguments.length - 1 then tpl.add ", "
1370 end
1371 tpl.add "]"
1372 return tpl
1373 end
1374 end
1375
1376 redef class MParameterType
1377 redef fun tpl_link do
1378 var name = mclass.intro.parameter_names[rank]
1379 var tpl = new TplLink
1380 tpl.href = "{mclass.nitdoc_url}#FT_{name}"
1381 tpl.text = name
1382 tpl.title = "formal type"
1383 return tpl
1384 end
1385 end
1386
1387 redef class MVirtualType
1388 redef fun tpl_link do return mproperty.intro.tpl_link
1389 end
1390
1391 redef class MClassDef
1392 # Return the classdef namespace decorated with html
1393 private fun tpl_namespace: Template do
1394 var tpl = new Template
1395 tpl.add mmodule.tpl_namespace
1396 tpl.add "::<span>"
1397 tpl.add mclass.tpl_link
1398 tpl.add "</span>"
1399 return tpl
1400 end
1401
1402 private fun full_namespace: String do
1403 return "{mmodule.full_namespace}::{mclass.nitdoc_name}"
1404 end
1405
1406 private fun tpl_link_anchor: TplLink do return mclass.tpl_link_anchor
1407
1408 private fun tpl_article: TplArticle do
1409 var tpl = new TplArticle
1410 tpl.id = mclass.nitdoc_anchor
1411 tpl.classes.add_all(tpl_css_classes)
1412 tpl.title = new Template
1413 tpl.title.add mclass.tpl_short_link
1414 tpl.title.add mclass.tpl_signature
1415 tpl.subtitle = new Template
1416 tpl.subtitle.add tpl_modifiers
1417 tpl.subtitle.add tpl_namespace
1418 tpl.content = new Template
1419 tpl.content.add tpl_definition
1420 return tpl
1421 end
1422
1423 private fun tpl_css_classes: Set[String] do
1424 var set = new HashSet[String]
1425 set.add_all mclass.intro.modifiers
1426 set.add_all modifiers
1427 return set
1428 end
1429
1430 private fun tpl_modifiers: Template do
1431 var tpl = new Template
1432 for modifier in modifiers do
1433 if modifier == "public" then continue
1434 tpl.add "{modifier} "
1435 end
1436 return tpl
1437 end
1438
1439 private fun tpl_short_definition: TplDefinition do
1440 var tpl = new TplDefinition
1441 tpl.namespace = mmodule.tpl_full_namespace
1442 if mdoc != null then
1443 tpl.comment = mdoc.tpl_short_comment
1444 end
1445 return tpl
1446 end
1447
1448 private fun tpl_definition: TplDefinition do
1449 var tpl = new TplDefinition
1450 tpl.namespace = mmodule.tpl_full_namespace
1451 if mdoc != null then
1452 tpl.comment = mdoc.tpl_comment
1453 end
1454 return tpl
1455 end
1456 end
1457
1458 redef class MPropDef
1459 # Return the full qualified name of the mpropdef
1460 # module::classdef::name
1461 private fun tpl_namespace: Template do
1462 var tpl = new Template
1463 tpl.add mclassdef.tpl_namespace
1464 tpl.add "::"
1465 tpl.add mproperty.name
1466 return tpl
1467 end
1468
1469 private fun full_namespace: String do
1470 return "{mclassdef.full_namespace}::{mproperty.nitdoc_name}"
1471 end
1472
1473 # URL into the nitdoc page
1474 # class_owner_name.html#nitdoc_anchor
1475 private fun nitdoc_url: String do
1476 return "{mclassdef.mclass.nitdoc_url}#{nitdoc_anchor}"
1477 end
1478
1479 # html nitdoc_anchor id for the property in a nitdoc class page
1480 # PROP_mclass_propertyname
1481 private fun nitdoc_anchor: String do
1482 return "PROP_{mclassdef.mclass.public_owner.nitdoc_name}_{mproperty.name.replace(" ", "_")}"
1483 end
1484
1485 # Return a link to property into the nitdoc class page
1486 # <a href="nitdoc_url" title="short_comment">nitdoc_name</a>
1487 private fun tpl_link: TplLink do
1488 var tpl = new TplLink
1489 tpl.href = nitdoc_url
1490 tpl.text = mproperty.nitdoc_name
1491 if mproperty.intro.mdoc != null then
1492 tpl.title = mproperty.intro.mdoc.short_comment
1493 end
1494 return tpl
1495 end
1496
1497 private fun tpl_article: TplArticle do
1498 var tpl = new TplArticle
1499 tpl.id = nitdoc_anchor
1500 tpl.classes.add_all(tpl_css_classes)
1501 tpl.title = new Template
1502 tpl.title.add mproperty.nitdoc_name
1503 tpl.title.add mproperty.tpl_signature
1504 tpl.subtitle = new Template
1505 tpl.subtitle.add tpl_modifiers
1506 tpl.subtitle.add tpl_namespace
1507 tpl.content = new Template
1508 tpl.content.add tpl_definition
1509 return tpl
1510 end
1511
1512 private fun tpl_css_classes: Set[String] do
1513 var set = new HashSet[String]
1514 set.add_all mproperty.intro.modifiers
1515 set.add_all modifiers
1516 return set
1517 end
1518
1519 private fun tpl_modifiers: Template do
1520 var tpl = new Template
1521 for modifier in modifiers do
1522 if modifier == "public" then continue
1523 tpl.add "{modifier} "
1524 end
1525 return tpl
1526 end
1527
1528 private fun tpl_short_definition: TplDefinition do
1529 var tpl = new TplDefinition
1530 tpl.namespace = mclassdef.tpl_namespace
1531 if mdoc != null then
1532 tpl.comment = mdoc.tpl_short_comment
1533 end
1534 return tpl
1535 end
1536
1537 private fun tpl_definition: TplDefinition do
1538 var tpl = new TplDefinition
1539 tpl.namespace = mclassdef.tpl_namespace
1540 if mdoc != null then
1541 tpl.comment = mdoc.tpl_comment
1542 end
1543 return tpl
1544 end
1545 end
1546
1547 redef class MSignature
1548 private fun tpl_signature: Template do
1549 var tpl = new Template
1550 if not mparameters.is_empty then
1551 tpl.add "("
1552 for i in [0..mparameters.length[ do
1553 tpl.add mparameters[i].tpl_link
1554 if i < mparameters.length - 1 then tpl.add ", "
1555 end
1556 tpl.add ")"
1557 end
1558 if return_mtype != null then
1559 tpl.add ": "
1560 tpl.add return_mtype.tpl_link
1561 end
1562 return tpl
1563 end
1564 end
1565
1566 redef class MParameter
1567 private fun tpl_link: Template do
1568 var tpl = new Template
1569 tpl.add "{name}: "
1570 tpl.add mtype.tpl_link
1571 if is_vararg then tpl.add "..."
1572 return tpl
1573 end
1574 end
1575
1576 redef class Location
1577 fun github(gitdir: String): String do
1578 var base_dir = getcwd.join_path(gitdir).simplify_path
1579 var file_loc = getcwd.join_path(file.filename).simplify_path
1580 var gith_loc = file_loc.substring(base_dir.length + 1, file_loc.length)
1581 return "{gith_loc}:{line_start},{column_start}--{line_end},{column_end}"
1582 end
1583 end
1584
1585 redef class MDoc
1586 private fun short_comment: String do
1587 return content.first.html_escape
1588 end
1589
1590 private fun full_comment: String do
1591 return content.join("\n").html_escape
1592 end
1593
1594 private fun tpl_short_comment: TplShortComment do
1595 return new TplShortComment(short_markdown)
1596 end
1597
1598 private fun tpl_comment: TplComment do
1599 return new TplComment(full_markdown)
1600 end
1601 end
1602
1603 var nitdoc = new NitdocContext
1604 nitdoc.generate_nitdoc
1605