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