nitdoc: fixed shared files with ni_nitdoc.
[nit.git] / src / ni_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 ni_nitdoc
18
19 import model_utils
20 import modelize_property
21 import markdown
22
23 # The NitdocContext contains all the knowledge used for doc generation
24 class NitdocContext
25
26 private var toolcontext = new ToolContext
27 private var model: Model
28 private var mbuilder: ModelBuilder
29 private var mainmodule: MModule
30 private var class_hierarchy: POSet[MClass]
31 private var arguments: Array[String]
32 private var output_dir: nullable String
33 private var dot_dir: nullable String
34 private var share_dir: nullable String
35 private var source: nullable String
36 private var min_visibility: MVisibility
37
38 private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir")
39 private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
40 private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir")
41 private var opt_nodot = new OptionBool("Do not generate graphes with graphiviz", "--no-dot")
42 private var opt_private: OptionBool = new OptionBool("Generate the private API", "--private")
43
44 private var opt_custom_title: OptionString = new OptionString("Title displayed in the top of the Overview page and as suffix of all page names", "--custom-title")
45 private var opt_custom_menu_items: OptionString = new OptionString("Items displayed in menu before the 'Overview' item (Each item must be enclosed in 'li' tags)", "--custom-menu-items")
46 private var opt_custom_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text")
47 private var opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text")
48
49 init do
50 toolcontext.option_context.add_option(opt_dir)
51 toolcontext.option_context.add_option(opt_source)
52 toolcontext.option_context.add_option(opt_sharedir)
53 toolcontext.option_context.add_option(opt_nodot)
54 toolcontext.option_context.add_option(opt_private)
55 toolcontext.option_context.add_option(opt_custom_title)
56 toolcontext.option_context.add_option(opt_custom_footer_text)
57 toolcontext.option_context.add_option(opt_custom_overview_text)
58 toolcontext.option_context.add_option(opt_custom_menu_items)
59 toolcontext.process_options
60 self.arguments = toolcontext.option_context.rest
61
62 if arguments.length < 1 then
63 print "usage: nitdoc [options] file..."
64 toolcontext.option_context.usage
65 exit(1)
66 end
67
68 model = new Model
69 mbuilder = new ModelBuilder(model, toolcontext)
70 # Here we load an process all modules passed on the command line
71 var mmodules = mbuilder.parse(arguments)
72 if mmodules.is_empty then return
73 mbuilder.run_phases
74
75 if mmodules.length == 1 then
76 mainmodule = mmodules.first
77 else
78 # We need a main module, so we build it by importing all modules
79 mainmodule = new MModule(model, null, "<main>", new Location(null, 0, 0, 0, 0))
80 mainmodule.set_imported_mmodules(mmodules)
81 end
82 self.class_hierarchy = mainmodule.flatten_mclass_hierarchy
83 self.process_options
84 end
85
86 private fun process_options do
87 if not opt_dir.value is null then
88 output_dir = opt_dir.value
89 else
90 output_dir = "doc"
91 end
92 if not opt_sharedir.value is null then
93 share_dir = opt_sharedir.value
94 else
95 var dir = "NIT_DIR".environ
96 if dir.is_empty then
97 dir = "{sys.program_name.dirname}/../share/ni_nitdoc"
98 else
99 dir = "{dir}/share/ni_nitdoc"
100 end
101 share_dir = dir
102 if share_dir is null then
103 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
104 abort
105 end
106 dir = "{share_dir.to_s}/scripts/js-facilities.js"
107 if share_dir is null then
108 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
109 abort
110 end
111
112 if opt_private.value then
113 min_visibility = none_visibility
114 else
115 min_visibility = protected_visibility
116 end
117 end
118 source = opt_source.value
119 end
120
121 fun generate_nitdoc do
122 # Create destination dir if it's necessary
123 if not output_dir.file_exists then output_dir.mkdir
124 sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/")
125 self.dot_dir = null
126 if not opt_nodot.value then self.dot_dir = output_dir.to_s
127 overview
128 search
129 modules
130 classes
131 quicksearch_list
132 end
133
134 private fun overview do
135 var overviewpage = new NitdocOverview(self, dot_dir)
136 overviewpage.save("{output_dir.to_s}/index.html")
137 end
138
139 private fun search do
140 var searchpage = new NitdocSearch(self)
141 searchpage.save("{output_dir.to_s}/search.html")
142 end
143
144 private fun modules do
145 for mmodule in model.mmodules do
146 if mmodule.name == "<main>" then continue
147 var modulepage = new NitdocModule(mmodule, self, dot_dir)
148 modulepage.save("{output_dir.to_s}/{mmodule.url}")
149 end
150 end
151
152 private fun classes do
153 for mclass in mbuilder.model.mclasses do
154 var classpage = new NitdocClass(mclass, self, dot_dir, source)
155 classpage.save("{output_dir.to_s}/{mclass.url}")
156 end
157 end
158
159 private fun quicksearch_list do
160 var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js")
161 file.write("var entries = \{ ")
162 for mmodule in model.mmodules do
163 file.write("\"{mmodule.name}\": [")
164 file.write("\{txt: \"{mmodule.name}\", url:\"{mmodule.url}\" \},")
165 file.write("],")
166 end
167 for mclass in model.mclasses do
168 if mclass.visibility < min_visibility then continue
169 file.write("\"{mclass.name}\": [")
170 file.write("\{txt: \"{mclass.name}\", url:\"{mclass.url}\" \},")
171 file.write("],")
172 end
173 var name2mprops = new HashMap[String, Set[MPropDef]]
174 for mproperty in model.mproperties do
175 if mproperty.visibility < min_visibility then continue
176 if mproperty isa MAttribute then continue
177 if not name2mprops.has_key(mproperty.name) then name2mprops[mproperty.name] = new HashSet[MPropDef]
178 name2mprops[mproperty.name].add_all(mproperty.mpropdefs)
179 end
180 for mproperty, mpropdefs in name2mprops do
181 file.write("\"{mproperty}\": [")
182 for mpropdef in mpropdefs do
183 file.write("\{txt: \"{mpropdef.full_name}\", url:\"{mpropdef.url}\" \},")
184 end
185 file.write("],")
186 end
187 file.write(" \};")
188 file.close
189 end
190
191 end
192
193 # Nitdoc base page
194 abstract class NitdocPage
195
196 var dot_dir: nullable String
197 var source: nullable String
198 var ctx: NitdocContext
199
200 init(ctx: NitdocContext) do
201 self.ctx = ctx
202 end
203
204 protected fun head do
205 append("<meta charset='utf-8'/>")
206 append("<script type='text/javascript' src='scripts/jquery-1.7.1.min.js'></script>")
207 append("<script type='text/javascript' src='scripts/ZeroClipboard.min.js'></script>")
208 append("<script type='text/javascript' src='quicksearch-list.js'></script>")
209 append("<script type='text/javascript' src='scripts/base64.js'></script>")
210 append("<script type='text/javascript' src='scripts/github.js'></script>")
211 append("<script type='text/javascript' src='scripts/js-facilities.js'></script>")
212 append("<link rel='stylesheet' href='styles/main.css' type='text/css' media='screen'/>")
213 append("<link rel='stylesheet' href='styles/github.css' type='text/css' media='screen'/>")
214 var title = ""
215 if ctx.opt_custom_title.value != null then
216 title = " | {ctx.opt_custom_title.value.to_s}"
217 end
218 append("<title>{self.title}{title}</title>")
219 end
220
221 protected fun menu do
222 if ctx.opt_custom_menu_items.value != null then
223 append(ctx.opt_custom_menu_items.value.to_s)
224 end
225 end
226
227 protected fun title: String is abstract
228
229 protected fun header do
230 append("<header>")
231 append("<nav class='main'>")
232 append("<ul>")
233 menu
234 append("</ul>")
235 append("</nav>")
236 append("</header>")
237 end
238
239 protected fun content is abstract
240
241 protected fun footer do
242 if ctx.opt_custom_footer_text.value != null then
243 append("<footer>{ctx.opt_custom_footer_text.value.to_s}</footer>")
244 end
245 end
246
247 # Generate a clickable graphviz image using a dot content
248 protected fun generate_dot(dot: String, name: String, alt: String) do
249 var output_dir = dot_dir
250 if output_dir == null then return
251 var file = new OFStream.open("{output_dir}/{name}.dot")
252 file.write(dot)
253 file.close
254 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 ; \}")
255 append("<article class='graph'>")
256 append("<img src='{name}.png' usemap='#{name}' style='margin:auto' alt='{alt}'/>")
257 append("</article>")
258 var fmap = new IFStream.open("{output_dir}/{name}.map")
259 append(fmap.read_all)
260 fmap.close
261 end
262
263 # Add a (source) link for a given location
264 protected fun show_source(l: Location): String
265 do
266 if source == null then
267 return "({l.file.filename.simplify_path})"
268 else
269 # THIS IS JUST UGLY ! (but there is no replace yet)
270 var x = source.split_with("%f")
271 source = x.join(l.file.filename.simplify_path)
272 x = source.split_with("%l")
273 source = x.join(l.line_start.to_s)
274 x = source.split_with("%L")
275 source = x.join(l.line_end.to_s)
276 return " (<a target='_blank' title='Show source' href=\"{source.to_s}\">source</a>)"
277 end
278 end
279
280 # Render the page as a html string
281 protected fun render do
282 append("<!DOCTYPE html>")
283 append("<head>")
284 head
285 append("</head>")
286 append("<body>")
287 header
288 append("<div class='page'>")
289 content
290 append("</div>")
291 footer
292 append("</body>")
293 end
294
295 # Append a string to the page
296 fun append(s: String) do out.write(s)
297
298 # Save html page in the specified file
299 fun save(file: String) do
300 self.out = new OFStream.open(file)
301 render
302 self.out.close
303 end
304 private var out: nullable OFStream
305 end
306
307 # The overview page
308 class NitdocOverview
309 super NitdocPage
310 private var mbuilder: ModelBuilder
311 private var mmodules = new Array[MModule]
312
313 init(ctx: NitdocContext, dot_dir: nullable String) do
314 super(ctx)
315 self.mbuilder = ctx.mbuilder
316 self.dot_dir = dot_dir
317 # get modules
318 var mmodules = new HashSet[MModule]
319 for mmodule in mbuilder.model.mmodules do
320 if mmodule.name == "<main>" then continue
321 var owner = mmodule.public_owner
322 if owner != null then
323 mmodules.add(owner)
324 else
325 mmodules.add(mmodule)
326 end
327 end
328 # sort modules
329 var sorter = new MModuleNameSorter
330 self.mmodules.add_all(mmodules)
331 sorter.sort(self.mmodules)
332 end
333
334 redef fun title do return "Overview"
335
336 redef fun menu do
337 super
338 append("<li class='current'>Overview</li>")
339 append("<li><a href='search.html'>Search</a></li>")
340 end
341
342 redef fun content do
343 var footed = ""
344 if ctx.opt_custom_footer_text.value != null then footed = "footed"
345 append("<div class='content fullpage {footed}'>")
346 var title = "Overview"
347 if ctx.opt_custom_title.value != null then
348 title = ctx.opt_custom_title.value.to_s
349 end
350 append("<h1>{title}</h1>")
351 var text = ""
352 if ctx.opt_custom_overview_text.value != null then
353 text = ctx.opt_custom_overview_text.value.to_s
354 end
355 append("<article class='overview'>{text}</article>")
356 append("<article class='overview'>")
357 # module list
358 append("<h2>Modules</h2>")
359 append("<ul>")
360 for mmodule in mmodules do
361 if mbuilder.mmodule2nmodule.has_key(mmodule) then
362 var amodule = mbuilder.mmodule2nmodule[mmodule]
363 append("<li>")
364 mmodule.html_link(self)
365 append("&nbsp;{amodule.short_comment}</li>")
366 end
367 end
368 append("</ul>")
369 # module graph
370 process_generate_dot
371 append("</article>")
372 append("</div>")
373 end
374
375 private fun process_generate_dot do
376 # build poset with public owners
377 var poset = new POSet[MModule]
378 for mmodule in mmodules do
379 poset.add_node(mmodule)
380 for omodule in mmodules do
381 if mmodule == omodule then continue
382 if mmodule.in_importation < omodule then
383 poset.add_node(omodule)
384 poset.add_edge(mmodule, omodule)
385 end
386 end
387 end
388 # build graph
389 var op = new Buffer
390 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")
391 for mmodule in poset do
392 op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
393 for omodule in poset[mmodule].direct_greaters do
394 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
395 end
396 end
397 op.append("\}\n")
398 generate_dot(op.to_s, "dep", "Modules hierarchy")
399 end
400 end
401
402 # The search page
403 class NitdocSearch
404 super NitdocPage
405
406 init(ctx: NitdocContext) do
407 super(ctx)
408 self.dot_dir = null
409 end
410
411 redef fun title do return "Search"
412
413 redef fun menu do
414 super
415 append("<li><a href='index.html'>Overview</a></li>")
416 append("<li class='current'>Search</li>")
417 end
418
419 redef fun content do
420 var footed = ""
421 if ctx.opt_custom_footer_text.value != null then footed = "footed"
422 append("<div class='content fullpage {footed}'>")
423 append("<h1>{title}</h1>")
424 module_column
425 classes_column
426 properties_column
427 append("</div>")
428 end
429
430 # Add to content modules column
431 private fun module_column do
432 var sorted = ctx.mbuilder.model.mmodule_importation_hierarchy.to_a
433 var sorter = new MModuleNameSorter
434 sorter.sort(sorted)
435 append("<article class='modules filterable'>")
436 append("<h2>Modules</h2>")
437 append("<ul>")
438 for mmodule in sorted do
439 append("<li>")
440 mmodule.html_link(self)
441 append("</li>")
442 end
443 append("</ul>")
444 append("</article>")
445 end
446
447 # Add to content classes modules
448 private fun classes_column do
449 var sorted = ctx.mbuilder.model.mclasses
450 var sorter = new MClassNameSorter
451 sorter.sort(sorted)
452 append("<article class='modules filterable'>")
453 append("<h2>Classes</h2>")
454 append("<ul>")
455 for mclass in sorted do
456 if mclass.visibility < ctx.min_visibility then continue
457 append("<li>")
458 mclass.html_link(self)
459 append("</li>")
460 end
461 append("</ul>")
462 append("</article>")
463 end
464
465 # Insert the properties column of fullindex page
466 private fun properties_column do
467 var sorted = ctx.mbuilder.model.mproperties
468 var sorter = new MPropertyNameSorter
469 sorter.sort(sorted)
470 append("<article class='modules filterable'>")
471 append("<h2>Properties</h2>")
472 append("<ul>")
473 for mproperty in sorted do
474 if mproperty.visibility < ctx.min_visibility then continue
475 if mproperty isa MAttribute then continue
476 append("<li>")
477 mproperty.intro.html_link(self)
478 append(" (")
479 mproperty.intro.mclassdef.mclass.html_link(self)
480 append(")</li>")
481 end
482 append("</ul>")
483 append("</article>")
484 end
485
486 end
487
488 # A module page
489 class NitdocModule
490 super NitdocPage
491
492 private var mmodule: MModule
493 private var mbuilder: ModelBuilder
494 private var local_mclasses = new HashSet[MClass]
495 private var intro_mclasses = new HashSet[MClass]
496 private var redef_mclasses = new HashSet[MClass]
497 private var inherited_mclasses = new HashSet[MClass]
498
499 init(mmodule: MModule, ctx: NitdocContext, dot_dir: nullable String) do
500 super(ctx)
501 self.mmodule = mmodule
502 self.mbuilder = ctx.mbuilder
503 self.dot_dir = dot_dir
504 # get local mclasses
505 for m in mmodule.in_nesting.greaters do
506 for mclassdef in m.mclassdefs do
507 if mclassdef.mclass.visibility < ctx.min_visibility then continue
508 if mclassdef.is_intro then
509 intro_mclasses.add(mclassdef.mclass)
510 else
511 redef_mclasses.add(mclassdef.mclass)
512 end
513 local_mclasses.add(mclassdef.mclass)
514 end
515 end
516 # get inherited mclasses
517 for m in mmodule.in_importation.greaters do
518 if m == mmodule then continue
519 for mclassdef in m.mclassdefs do
520 if mclassdef.mclass.visibility < ctx.min_visibility then continue
521 if local_mclasses.has(mclassdef.mclass) then continue
522 inherited_mclasses.add(mclassdef.mclass)
523 end
524 end
525 end
526
527 redef fun title do
528 if mbuilder.mmodule2nmodule.has_key(mmodule) then
529 var nmodule = mbuilder.mmodule2nmodule[mmodule]
530 return "{mmodule.name} module | {nmodule.short_comment}"
531 else
532 return "{mmodule.name} module"
533 end
534 end
535
536 redef fun menu do
537 super
538 append("<li><a href='index.html'>Overview</a></li>")
539 append("<li class='current'>{mmodule.name}</li>")
540 append("<li><a href='search.html'>Search</a></li>")
541 end
542
543 redef fun content do
544 append("<div class='menu'>")
545 classes_column
546 importation_column
547 append("</div>")
548 var footed = ""
549 if ctx.opt_custom_footer_text.value != null then footed = "footed"
550 append("<div class='content {footed}'>")
551 module_doc
552 append("</div>")
553 end
554
555 private fun classes_column do
556 var sorter = new MClassNameSorter
557 var sorted = new Array[MClass]
558 sorted.add_all(local_mclasses)
559 sorter.sort(sorted)
560 if not sorted.is_empty then
561 append("<nav class='properties filterable'>")
562 append("<h3>Classes</h3>")
563 append("<h4>Classes</h4>")
564 append("<ul>")
565 for mclass in sorted do mclass.html_sidebar_item(self)
566 append("</ul>")
567 append("</nav>")
568 end
569 end
570
571 private fun importation_column do
572 append("<nav>")
573 append("<h3>Module Hierarchy</h3>")
574 var dependencies = new Array[MModule]
575 for dep in mmodule.in_importation.greaters do
576 if dep == mmodule or dep.public_owner != null then continue
577 dependencies.add(dep)
578 end
579 if mmodule.in_nesting.direct_greaters.length > 0 then
580 append("<h4>Nested Modules</h4>")
581 display_module_list(mmodule.in_nesting.direct_greaters.to_a)
582 end
583 if dependencies.length > 0 then
584 append("<h4>All dependencies</h4>")
585 display_module_list(dependencies)
586 end
587 var clients = new Array[MModule]
588 for dep in mmodule.in_importation.smallers do
589 if dep.name == "<main>" then continue
590 if dep == mmodule or dep.public_owner != null then continue
591 clients.add(dep)
592 end
593 if clients.length > 0 then
594 append("<h4>All clients</h4>")
595 display_module_list(clients)
596 end
597 append("</nav>")
598 end
599
600 private fun display_module_list(list: Array[MModule]) do
601 append("<ul>")
602 var sorter = new MModuleNameSorter
603 sorter.sort(list)
604 for m in list do
605 append("<li>")
606 m.html_link(self)
607 append("</li>")
608 end
609 append("</ul>")
610 end
611
612 private fun module_doc do
613 # title
614 append("<h1>{mmodule.name}</h1>")
615 append("<div class='subtitle info'>")
616 mmodule.html_signature(self)
617 append("</div>")
618 # comment
619 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
620 append("<section class='description'>")
621 if not nmodule.full_comment.is_empty then append("<div>{nmodule.full_comment}</div>")
622 process_generate_dot
623 append("</section>")
624 # classes
625 var class_sorter = new MClassNameSorter
626 # intro
627 if not intro_mclasses.is_empty then
628 var sorted = new Array[MClass]
629 sorted.add_all(intro_mclasses)
630 class_sorter.sort(sorted)
631 append("<section class='classes'>")
632 append("<h2 class='section-header'>Introduced classes</h2>")
633 for mclass in sorted do mclass.html_full_desc(self)
634 append("</section>")
635 end
636 # redefs
637 var redefs = new Array[MClass]
638 for mclass in redef_mclasses do if not intro_mclasses.has(mclass) then redefs.add(mclass)
639 class_sorter.sort(redefs)
640 if not redefs.is_empty then
641 append("<section class='classes'>")
642 append("<h2 class='section-header'>Refined classes</h2>")
643 for mclass in redefs do mclass.html_full_desc(self)
644 append("</section>")
645 end
646 # inherited properties
647 var inherited = new Array[MClass]
648 inherited.add_all(inherited_mclasses)
649 if inherited_mclasses.length > 0 then
650 var modules2classes = new ArrayMap[MModule, Array[MClass]]
651 for mclass in inherited_mclasses do
652 if not modules2classes.has_key(mclass.intro_mmodule) then modules2classes[mclass.intro_mmodule] = new Array[MClass]
653 modules2classes[mclass.intro_mmodule].add(mclass)
654 end
655 append("<section class='classes'>")
656 append("<h2 class='section-header'>Inherited Classes</h2>")
657 var mmodules = new Array[MModule]
658 mmodules.add_all(modules2classes.keys)
659 var msorter = new MModuleNameSorter
660 msorter.sort(mmodules)
661 for m in mmodules do
662 var mclasses = modules2classes[m]
663 class_sorter.sort(mclasses)
664 append("<p>Defined in ")
665 m.html_link(self)
666 append(": ")
667 for i in [0..mclasses.length[ do
668 var mclass = mclasses[i]
669 mclass.html_link(self)
670 if i <= mclasses.length - 1 then append(", ")
671 end
672 append("</p>")
673 end
674 append("</section>")
675 end
676 end
677
678 private fun process_generate_dot do
679 # build poset with public owners
680 var poset = new POSet[MModule]
681 for mmodule in self.mmodule.in_importation.poset do
682 if mmodule.name == "<main>" then continue
683 if mmodule.public_owner != null then continue
684 if not mmodule.in_importation < self.mmodule and not self.mmodule.in_importation < mmodule and mmodule != self.mmodule then continue
685 poset.add_node(mmodule)
686 for omodule in mmodule.in_importation.poset do
687 if mmodule == omodule then continue
688 if omodule.name == "<main>" then continue
689 if omodule.public_owner != null then continue
690 if mmodule.in_importation < omodule then
691 poset.add_node(omodule)
692 poset.add_edge(mmodule, omodule)
693 end
694 end
695 end
696 # build graph
697 var op = new Buffer
698 var name = "dep_{mmodule.name}"
699 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")
700 for mmodule in poset do
701 if mmodule == self.mmodule then
702 op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n")
703 else
704 op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
705 end
706 for omodule in poset[mmodule].direct_greaters do
707 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
708 end
709 end
710 op.append("\}\n")
711 generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
712 end
713 end
714
715 # A class page
716 class NitdocClass
717 super NitdocPage
718
719 private var mclass: MClass
720 private var vtypes = new HashSet[MVirtualTypeDef]
721 private var consts = new HashSet[MMethodDef]
722 private var meths = new HashSet[MMethodDef]
723 private var inherited = new HashSet[MPropDef]
724
725 init(mclass: MClass, ctx: NitdocContext, dot_dir: nullable String, source: nullable String) do
726 super(ctx)
727 self.mclass = mclass
728 self.dot_dir = dot_dir
729 self.source = source
730 # load properties
731 var locals = new HashSet[MProperty]
732 for mclassdef in mclass.mclassdefs do
733 for mpropdef in mclassdef.mpropdefs do
734 if mpropdef.mproperty.visibility < ctx.min_visibility then continue
735 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
736 if mpropdef isa MMethodDef then
737 if mpropdef.mproperty.is_init then
738 consts.add(mpropdef)
739 else
740 meths.add(mpropdef)
741 end
742 end
743 locals.add(mpropdef.mproperty)
744 end
745 end
746 # get inherited properties
747 for pclass in mclass.in_hierarchy(ctx.mainmodule).greaters do
748 if pclass == mclass then continue
749 for pclassdef in pclass.mclassdefs do
750 for mprop in pclassdef.intro_mproperties do
751 var mpropdef = mprop.intro
752 if mprop.visibility < ctx.min_visibility then continue
753 if locals.has(mprop) then continue
754 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
755 if mpropdef isa MMethodDef then
756 if mpropdef.mproperty.is_init then
757 consts.add(mpropdef)
758 else
759 meths.add(mpropdef)
760 end
761 end
762 inherited.add(mpropdef)
763 end
764 end
765 end
766 end
767
768 redef fun title do
769 var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
770 if nclass isa AStdClassdef then
771 return "{mclass.name} class | {nclass.short_comment}"
772 else
773 return "{mclass.name} class"
774 end
775 end
776
777 redef fun menu do
778 super
779 append("<li><a href='index.html'>Overview</a></li>")
780 var public_owner = mclass.public_owner
781 if public_owner is null then
782 append("<li>")
783 mclass.intro_mmodule.html_link(self)
784 append("</li>")
785 else
786 append("<li>")
787 public_owner.html_link(self)
788 append("</li>")
789 end
790 append("<li class='current'>{mclass.name}</li>")
791 append("<li><a href='search.html'>Search</a></li>")
792 end
793
794 redef fun content do
795 append("<div class='menu'>")
796 properties_column
797 inheritance_column
798 append("</div>")
799 var footed = ""
800 if ctx.opt_custom_footer_text.value != null then footed = "footed"
801 append("<div class='content {footed}'>")
802 class_doc
803 append("</div>")
804 end
805
806 private fun properties_column do
807 var sorter = new MPropDefNameSorter
808 append("<nav class='properties filterable'>")
809 append("<h3>Properties</h3>")
810 # virtual types
811 if vtypes.length > 0 then
812 var vts = new Array[MVirtualTypeDef]
813 vts.add_all(vtypes)
814 sorter.sort(vts)
815 append("<h4>Virtual Types</h4>")
816 append("<ul>")
817 for mprop in vts do
818 mprop.html_sidebar_item(self)
819 end
820 append("</ul>")
821 end
822 # constructors
823 if consts.length > 0 then
824 var cts = new Array[MMethodDef]
825 cts.add_all(consts)
826 sorter.sort(cts)
827 append("<h4>Constructors</h4>")
828 append("<ul>")
829 for mprop in cts do
830 if mprop.mproperty.name == "init" and mprop.mclassdef.mclass != mclass then continue
831 mprop.html_sidebar_item(self)
832 end
833 append("</ul>")
834 end
835 # methods
836 if meths.length > 0 then
837 var mts = new Array[MMethodDef]
838 mts.add_all(meths)
839 sorter.sort(mts)
840 append("<h4>Methods</h4>")
841 append("<ul>")
842 for mprop in mts do
843 if mclass.name != "Object" and mprop.mproperty.intro_mclassdef.mclass.name == "Object" and mprop.mproperty.visibility <= protected_visibility then continue
844 mprop.html_sidebar_item(self)
845 end
846 append("</ul>")
847 end
848 append("</nav>")
849 end
850
851 private fun inheritance_column do
852 var sorted = new Array[MClass]
853 var sorterp = new MClassNameSorter
854 append("<nav>")
855 append("<h3>Inheritance</h3>")
856 var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a
857 if greaters.length > 1 then
858 ctx.mainmodule.linearize_mclasses(greaters)
859 append("<h4>Superclasses</h4>")
860 append("<ul>")
861 for sup in greaters do
862 if sup == mclass then continue
863 append("<li>")
864 sup.html_link(self)
865 append("</li>")
866 end
867 append("</ul>")
868 end
869 var smallers = mclass.in_hierarchy(ctx.mainmodule).smallers.to_a
870 var direct_smallers = mclass.in_hierarchy(ctx.mainmodule).direct_smallers.to_a
871 if smallers.length <= 1 then
872 append("<h4>No Known Subclasses</h4>")
873 else if smallers.length <= 100 then
874 ctx.mainmodule.linearize_mclasses(smallers)
875 append("<h4>Subclasses</h4>")
876 append("<ul>")
877 for sub in smallers do
878 if sub == mclass then continue
879 append("<li>")
880 sub.html_link(self)
881 append("</li>")
882 end
883 append("</ul>")
884 else if direct_smallers.length <= 100 then
885 ctx.mainmodule.linearize_mclasses(direct_smallers)
886 append("<h4>Direct Subclasses Only</h4>")
887 append("<ul>")
888 for sub in direct_smallers do
889 if sub == mclass then continue
890 append("<li>")
891 sub.html_link(self)
892 append("</li>")
893 end
894 append("</ul>")
895 else
896 append("<h4>Too much Subclasses to list</h4>")
897 end
898 append("</nav>")
899 end
900
901 private fun class_doc do
902 # title
903 append("<h1>{mclass.name}{mclass.html_short_signature}</h1>")
904 append("<div class='subtitle info'>")
905 if mclass.visibility < public_visibility then append("{mclass.visibility.to_s} ")
906 append("{mclass.kind.to_s} ")
907 mclass.html_namespace(self)
908 append("{mclass.html_short_signature}</div>")
909 # comment
910 var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
911 append("<section class='description'>")
912 if nclass isa AStdClassdef and not nclass.full_comment.is_empty then append("<div>{nclass.full_comment}</div>")
913 process_generate_dot
914 append("</section>")
915 # concerns
916 var concern2meths = new ArrayMap[MModule, Array[MMethodDef]]
917 var sorted_meths = new Array[MMethodDef]
918 var sorted = new Array[MModule]
919 sorted_meths.add_all(meths)
920 ctx.mainmodule.linearize_mpropdefs(sorted_meths)
921 for meth in meths do
922 if inherited.has(meth) then continue
923 var mmodule = meth.mclassdef.mmodule
924 if not concern2meths.has_key(mmodule) then
925 sorted.add(mmodule)
926 concern2meths[mmodule] = new Array[MMethodDef]
927 end
928 concern2meths[mmodule].add(meth)
929 end
930 var sections = new ArrayMap[MModule, Array[MModule]]
931 for mmodule in concern2meths.keys do
932 var owner = mmodule.public_owner
933 if owner == null then owner = mmodule
934 if not sections.has_key(owner) then sections[owner] = new Array[MModule]
935 if owner != mmodule then sections[owner].add(mmodule)
936 end
937 append("<section class='concerns'>")
938 append("<h2 class='section-header'>Concerns</h2>")
939 append("<ul>")
940 for owner, mmodules in sections do
941 var nowner = ctx.mbuilder.mmodule2nmodule[owner]
942 append("<li>")
943 if nowner.short_comment.is_empty then
944 append("<a href=\"#{owner.anchor}\">{owner.name}</a>")
945 else
946 append("<a href=\"#{owner.anchor}\">{owner.name}</a>: {nowner.short_comment}")
947 end
948 if not mmodules.is_empty then
949 append("<ul>")
950 for mmodule in mmodules do
951 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
952 if nmodule.short_comment.is_empty then
953 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a></li>")
954 else
955 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a>: {nmodule.short_comment}</li>")
956 end
957 end
958 append("</ul>")
959 end
960 append("</li>")
961 end
962 append("</ul>")
963 append("</section>")
964 # properties
965 var prop_sorter = new MPropDefNameSorter
966 var lmmodule = new List[MModule]
967 # virtual and formal types
968 var local_vtypes = new Array[MVirtualTypeDef]
969 for vt in vtypes do if not inherited.has(vt) then local_vtypes.add(vt)
970 if local_vtypes.length > 0 or mclass.arity > 0 then
971 append("<section class='types'>")
972 append("<h2>Formal and Virtual Types</h2>")
973 # formal types
974 if mclass.arity > 0 and nclass isa AStdClassdef then
975 for ft, bound in mclass.parameter_types do
976 append("<article id='FT_{ft}'>")
977 append("<h3 class='signature' data-untyped-signature='{ft.to_s}'><span>{ft}: ")
978 bound.html_link(self)
979 append("</span></h3>")
980 append("<div class=\"info\">formal generic type</div>")
981 append("</article>")
982 end
983 end
984 # virtual types
985 prop_sorter.sort(local_vtypes)
986 for prop in local_vtypes do prop.html_full_desc(self, self.mclass)
987 append("</section>")
988 end
989 # constructors
990 var local_consts = new Array[MMethodDef]
991 for const in consts do if not inherited.has(const) then local_consts.add(const)
992 prop_sorter.sort(local_consts)
993 if local_consts.length > 0 then
994 append("<section class='constructors'>")
995 append("<h2 class='section-header'>Constructors</h2>")
996 for prop in local_consts do prop.html_full_desc(self, self.mclass)
997 append("</section>")
998 end
999 # methods
1000 if not concern2meths.is_empty then
1001 append("<section class='methods'>")
1002 append("<h2 class='section-header'>Methods</h2>")
1003 for owner, mmodules in sections do
1004 append("<a id=\"{owner.anchor}\"></a>")
1005 if owner != mclass.intro_mmodule and owner != mclass.public_owner then
1006 var nowner = ctx.mbuilder.mmodule2nmodule[owner]
1007 append("<h3 class=\"concern-toplevel\">Methods refined in ")
1008 owner.html_link(self)
1009 append("</h3>")
1010 append("<p class=\"concern-doc\">")
1011 owner.html_link(self)
1012 if not nowner.short_comment.is_empty then
1013 append(": {nowner.short_comment}")
1014 end
1015 append("</p>")
1016 end
1017 if concern2meths.has_key(owner) then
1018 var mmethods = concern2meths[owner]
1019 prop_sorter.sort(mmethods)
1020 for prop in mmethods do prop.html_full_desc(self, self.mclass)
1021 end
1022 for mmodule in mmodules do
1023 append("<a id=\"{mmodule.anchor}\"></a>")
1024 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
1025 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
1026 append("<p class=\"concern-doc\">")
1027 mmodule.html_link(self)
1028 if not nmodule.short_comment.is_empty then
1029 append(": {nmodule.short_comment}")
1030 end
1031 append("</p>")
1032 end
1033 var mmethods = concern2meths[mmodule]
1034 prop_sorter.sort(mmethods)
1035 for prop in mmethods do prop.html_full_desc(self, self.mclass)
1036 end
1037 end
1038 append("</section>")
1039 end
1040 # inherited properties
1041 if inherited.length > 0 then
1042 var sorted_inherited = new Array[MPropDef]
1043 sorted_inherited.add_all(inherited)
1044 ctx.mainmodule.linearize_mpropdefs(sorted_inherited)
1045 var classes = new ArrayMap[MClass, Array[MPropDef]]
1046 for mmethod in sorted_inherited.reversed do
1047 var mclass = mmethod.mclassdef.mclass
1048 if not classes.has_key(mclass) then classes[mclass] = new Array[MPropDef]
1049 classes[mclass].add(mmethod)
1050 end
1051 append("<section class='inherited'>")
1052 append("<h2 class='section-header'>Inherited Properties</h2>")
1053 for c, mmethods in classes do
1054 prop_sorter.sort(mmethods)
1055 append("<p>Defined in ")
1056 c.html_link(self)
1057 append(": ")
1058 for i in [0..mmethods.length[ do
1059 var mmethod = mmethods[i]
1060 mmethod.html_link(self)
1061 if i <= mmethods.length - 1 then append(", ")
1062 end
1063 append("</p>")
1064 end
1065 append("</section>")
1066 end
1067 end
1068
1069 private fun process_generate_dot do
1070 var pe = ctx.class_hierarchy[mclass]
1071 var cla = new HashSet[MClass]
1072 var sm = new HashSet[MClass]
1073 var sm2 = new HashSet[MClass]
1074 sm.add(mclass)
1075 while cla.length + sm.length < 10 and sm.length > 0 do
1076 cla.add_all(sm)
1077 sm2.clear
1078 for x in sm do
1079 sm2.add_all(pe.poset[x].direct_smallers)
1080 end
1081 var t = sm
1082 sm = sm2
1083 sm2 = t
1084 end
1085 cla.add_all(pe.greaters)
1086
1087 var op = new Buffer
1088 var name = "dep_{mclass.name}"
1089 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")
1090 for c in cla do
1091 if c == mclass then
1092 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1093 else
1094 op.append("\"{c.name}\"[URL=\"{c.url}\"];\n")
1095 end
1096 for c2 in pe.poset[c].direct_greaters do
1097 if not cla.has(c2) then continue
1098 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1099 end
1100 if not pe.poset[c].direct_smallers.is_empty then
1101 var others = true
1102 for c2 in pe.poset[c].direct_smallers do
1103 if cla.has(c2) then others = false
1104 end
1105 if others then
1106 op.append("\"{c.name}...\"[label=\"\"];\n")
1107 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1108 end
1109 end
1110 end
1111 op.append("\}\n")
1112 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1113 end
1114 end
1115
1116 #
1117 # Model redefs
1118 #
1119
1120 redef class MModule
1121 # URL to nitdoc page
1122 fun url: String do
1123 if url_cache == null then
1124 var res = new Buffer
1125 res.append("module_")
1126 var mowner = public_owner
1127 if mowner != null then
1128 res.append("{public_owner.name}_")
1129 end
1130 res.append("{self.name}.html")
1131 url_cache = res.to_s
1132 end
1133 return url_cache.as(not null)
1134 end
1135 private var url_cache: nullable String
1136
1137 # html anchor id to the module in a nitdoc page
1138 fun anchor: String do
1139 if anchor_cache == null then
1140 var res = new Buffer
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 anchor_cache = res.to_s
1148 end
1149 return anchor_cache.as(not null)
1150 end
1151 private var anchor_cache: nullable String
1152
1153 # Return a link (html a tag) to the nitdoc module page
1154 fun html_link(page: NitdocPage) do
1155 if html_link_cache == null then
1156 var res = new Buffer
1157 if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
1158 res.append("<a href='{url}' title='{page.ctx.mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>")
1159 else
1160 res.append("<a href='{url}'>{name}</a>")
1161 end
1162 html_link_cache = res.to_s
1163 end
1164 page.append(html_link_cache.as(not null))
1165 end
1166 private var html_link_cache: nullable String
1167
1168 # Return the module signature decorated with html
1169 fun html_signature(page: NitdocPage) do
1170 page.append("<span>module ")
1171 html_full_namespace(page)
1172 page.append("</span>")
1173 end
1174
1175 # Return the module full namespace decorated with html
1176 fun html_full_namespace(page: NitdocPage) do
1177 page.append("<span>")
1178 var mowner = public_owner
1179 if mowner != null then
1180 public_owner.html_namespace(page)
1181 page.append("::")
1182 end
1183 html_link(page)
1184 page.append("</span>")
1185 end
1186
1187 # Return the module full namespace decorated with html
1188 fun html_namespace(page: NitdocPage) do
1189 page.append("<span>")
1190 var mowner = public_owner
1191 if mowner != null then
1192 public_owner.html_namespace(page)
1193 else
1194 html_link(page)
1195 end
1196 page.append("</span>")
1197 end
1198
1199 # Return the full comment of the module decorated with html
1200 fun html_full_comment(page: NitdocPage) do
1201 if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
1202 page.append("<div id='description'>")
1203 page.append("<div>{page.ctx.mbuilder.mmodule2nmodule[self].full_comment}</div>")
1204 page.append("</div>")
1205 end
1206 end
1207
1208 private fun has_mclassdef_for(mclass: MClass): Bool do
1209 for mmodule in self.in_nesting.greaters do
1210 for mclassdef in mmodule.mclassdefs do
1211 if mclassdef.mclass == mclass then return true
1212 end
1213 end
1214 return false
1215 end
1216
1217 private fun has_mclassdef(mclassdef: MClassDef): Bool do
1218 for mmodule in self.in_nesting.greaters do
1219 for oclassdef in mmodule.mclassdefs do
1220 if mclassdef == oclassdef then return true
1221 end
1222 end
1223 return false
1224 end
1225 end
1226
1227 redef class MClass
1228 # return the generic signature of the class
1229 # [E, F]
1230 private fun html_short_signature: String do
1231 if arity > 0 then
1232 return "[{intro.parameter_names.join(", ")}]"
1233 else
1234 return ""
1235 end
1236 end
1237
1238 # return the generic signature of the class with bounds
1239 # [E: <a>MType</a>, F: <a>MType</a>]
1240 private fun html_signature(page: NitdocPage) do
1241 if arity > 0 then
1242 page.append("[")
1243 for i in [0..intro.parameter_names.length[ do
1244 page.append("{intro.parameter_names[i]}: ")
1245 intro.bound_mtype.arguments[i].html_link(page)
1246 if i < intro.parameter_names.length - 1 then page.append(", ")
1247 end
1248 page.append("]")
1249 end
1250 end
1251
1252 # Return the class namespace decorated with html
1253 private fun html_namespace(page: NitdocPage) do
1254 intro_mmodule.html_namespace(page)
1255 page.append("::<span>")
1256 html_short_link(page)
1257 page.append("</span>")
1258 end
1259
1260 # Return a link (html a tag) to the nitdoc class page
1261 fun html_short_link(page: NitdocPage) do
1262 if html_short_link_cache == null then
1263 var res = new Buffer
1264 res.append("<a href='{url}'")
1265 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1266 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1267 if nclass isa AStdClassdef then
1268 res.append(" title=\"{nclass.short_comment}\"")
1269 end
1270 end
1271 res.append(">{name}</a>")
1272 html_short_link_cache = res.to_s
1273 end
1274 page.append(html_short_link_cache.as(not null))
1275 end
1276 private var html_short_link_cache: nullable String
1277
1278 # Return a link (html a tag) to the nitdoc class page
1279 fun html_link(page: NitdocPage) do
1280 if html_link_cache == null then
1281 var res = new Buffer
1282 res.append("<a href='{url}'")
1283 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1284 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1285 if nclass isa AStdClassdef then
1286 res.append(" title=\"{nclass.short_comment}\"")
1287 end
1288 end
1289 res.append(">{name}{html_short_signature}</a>")
1290 html_link_cache = res.to_s
1291 end
1292 page.append(html_link_cache.as(not null))
1293 end
1294 private var html_link_cache: nullable String
1295
1296 fun html_anchor(page: NitdocPage) do
1297 if html_anchor_cache == null then
1298 var res = new Buffer
1299 res.append("<a href='#{anchor}'")
1300 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1301 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1302 if nclass isa AStdClassdef then
1303 res.append(" title=\"{nclass.short_comment}\"")
1304 end
1305 end
1306 res.append(">{name}{html_short_signature}</a>")
1307 html_anchor_cache = res.to_s
1308 end
1309 page.append(html_anchor_cache.as(not null))
1310 end
1311 private var html_anchor_cache: nullable String
1312
1313 fun anchor: String do
1314 if anchor_cache == null then
1315 anchor_cache = "CLASS_{public_owner.name}_{name}"
1316 end
1317 return anchor_cache.as(not null)
1318 end
1319 private var anchor_cache: nullable String
1320
1321 fun url: String do
1322 return "class_{public_owner}_{name}.html"
1323 end
1324
1325 # Escape name for html output
1326 redef fun name do return super.html_escape
1327
1328 # Return a list item for the mclass
1329 private fun html_sidebar_item(page: NitdocModule) do
1330 if page.mmodule.in_nesting.greaters.has(intro.mmodule) then
1331 page.append("<li class='intro'>")
1332 page.append("<span title='Introduced'>I</span>")
1333 html_anchor(page)
1334 else if page.mmodule.has_mclassdef_for(self) then
1335 page.append("<li class='redef'>")
1336 page.append("<span title='Redefined'>R</span>")
1337 html_anchor(page)
1338 else
1339 page.append("<li class='inherit'>")
1340 page.append("<span title='Inherited'>H</span>")
1341 html_link(page)
1342 end
1343 page.append("</li>")
1344 end
1345
1346 private fun html_full_desc(page: NitdocModule) do
1347 var classes = new Array[String]
1348 classes.add(kind.to_s)
1349 if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then classes.add("redef")
1350 classes.add(visibility.to_s)
1351 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1352 page.append("<h3 class='signature' data-untyped-signature='{name}{html_short_signature}'>")
1353 page.append("<span>")
1354 html_short_link(page)
1355 html_signature(page)
1356 page.append("</span></h3>")
1357 html_info(page)
1358 html_comment(page)
1359 html_redefs(page)
1360 page.append("</article>")
1361 end
1362
1363 private fun html_info(page: NitdocModule) do
1364 page.append("<div class='info'>")
1365 if visibility < public_visibility then page.append("{visibility.to_s} ")
1366 if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then page.append("redef ")
1367 page.append("{kind} ")
1368 html_namespace(page)
1369 page.append("{html_short_signature}</div>")
1370 end
1371
1372 private fun html_comment(page: NitdocModule) do
1373 page.mmodule.linearize_mclassdefs(mclassdefs)
1374 page.append("<div class='description'>")
1375 # comments for each mclassdef contained in current mmodule
1376 for mclassdef in mclassdefs do
1377 if not mclassdef.is_intro and not page.mmodule.mclassdefs.has(mclassdef) then continue
1378 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(mclassdef) then
1379 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[mclassdef]
1380 if nclass isa AStdClassdef then
1381 if nclass.full_comment == "" then
1382 page.append("<p class='info inheritance'>")
1383 page.append("<span class=\"noComment\">no comment for </span>")
1384 else
1385 page.append("<div>{nclass.full_comment}</div>")
1386 page.append("<p class='info inheritance'>")
1387 end
1388 if mclassdef.is_intro then
1389 page.append("introduction in ")
1390 else
1391 page.append("refinement in ")
1392 end
1393 mclassdef.mmodule.html_full_namespace(page)
1394 page.append(" {page.show_source(nclass.location)}</p>")
1395 end
1396 end
1397 end
1398 page.append("</div>")
1399 end
1400
1401 private fun html_redefs(page: NitdocModule) do
1402 page.mmodule.linearize_mclassdefs(mclassdefs)
1403 page.append("<div class='refinements'>")
1404 # comments for each mclassdef contained in current mmodule
1405 for mclassdef in mclassdefs do
1406 if not page.mmodule.mclassdefs.has(mclassdef) then continue
1407 if mclassdef.is_intro then continue
1408 for mpropdef in mclassdef.mpropdefs do
1409 if mpropdef isa MAttributeDef then continue
1410 mpropdef.html_full_desc(page, self)
1411 end
1412 end
1413 page.append("</div>")
1414 end
1415 end
1416
1417 redef class MProperty
1418 # Return the property namespace decorated with html
1419 fun html_namespace(page: NitdocPage) do
1420 intro_mclassdef.mclass.html_namespace(page)
1421 page.append(intro_mclassdef.mclass.html_short_signature)
1422 page.append("::<span>")
1423 intro.html_link(page)
1424 page.append("</span>")
1425 end
1426
1427 # Escape name for html output
1428 redef fun name do return super.html_escape
1429 end
1430
1431 redef class MType
1432 fun html_link(page: NitdocPage) is abstract
1433 end
1434
1435 redef class MClassType
1436 redef fun html_link(page) do mclass.html_link(page)
1437 end
1438
1439 redef class MNullableType
1440 redef fun html_link(page) do
1441 page.append("nullable ")
1442 mtype.html_link(page)
1443 end
1444 end
1445
1446 redef class MGenericType
1447 redef fun html_link(page) do
1448 page.append("<a href='{mclass.url}'>{mclass.name}</a>[")
1449 for i in [0..arguments.length[ do
1450 arguments[i].html_link(page)
1451 if i < arguments.length - 1 then page.append(", ")
1452 end
1453 page.append("]")
1454 end
1455 end
1456
1457 redef class MParameterType
1458 redef fun html_link(page) do
1459 var name = mclass.intro.parameter_names[rank]
1460 page.append("<a href='{mclass.url}#FT_{name}' title='formal type'>{name}</a>")
1461 end
1462 end
1463
1464 redef class MVirtualType
1465 redef fun html_link(page) do mproperty.intro.html_link(page)
1466 end
1467
1468 redef class MClassDef
1469 # Return the classdef namespace decorated with html
1470 fun html_namespace(page: NitdocPage) do
1471 mmodule.html_full_namespace(page)
1472 page.append("::<span>")
1473 mclass.html_link(page)
1474 page.append("</span>")
1475 end
1476 end
1477
1478 redef class MPropDef
1479 fun url: String do
1480 if url_cache == null then
1481 url_cache = "{mclassdef.mclass.url}#{anchor}"
1482 end
1483 return url_cache.as(not null)
1484 end
1485 private var url_cache: nullable String
1486
1487 fun anchor: String do
1488 if anchor_cache == null then
1489 anchor_cache = "PROP_{mclassdef.mclass.public_owner.name}_{mproperty.name}"
1490 end
1491 return anchor_cache.as(not null)
1492 end
1493 private var anchor_cache: nullable String
1494
1495 # Return a link (html a tag) to the nitdoc class page
1496 fun html_link(page: NitdocPage) do
1497 if html_link_cache == null then
1498 var res = new Buffer
1499 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1500 var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
1501 res.append("<a href=\"{url}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>")
1502 else
1503 res.append("<a href=\"{url}\">{mproperty.name}</a>")
1504 end
1505 html_link_cache = res.to_s
1506 end
1507 page.append(html_link_cache.as(not null))
1508 end
1509 private var html_link_cache: nullable String
1510
1511 # Return a list item for the mpropdef
1512 private fun html_sidebar_item(page: NitdocClass) do
1513 if is_intro and mclassdef.mclass == page.mclass then
1514 page.append("<li class='intro'>")
1515 page.append("<span title='Introduced'>I</span>")
1516 else if is_intro and mclassdef.mclass != page.mclass then
1517 page.append("<li class='inherit'>")
1518 page.append("<span title='Inherited'>H</span>")
1519 else
1520 page.append("<li class='redef'>")
1521 page.append("<span title='Redefined'>R</span>")
1522 end
1523 html_link(page)
1524 page.append("</li>")
1525 end
1526
1527 private fun html_full_desc(page: NitdocPage, ctx: MClass) is abstract
1528 private fun html_info(page: NitdocPage, ctx: MClass) is abstract
1529
1530 fun full_name: String do
1531 return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}"
1532 end
1533
1534 private fun html_comment(page: NitdocPage) do
1535 page.append("<div class='description'>")
1536 if not is_intro then
1537 if page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then
1538 var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro]
1539 if intro_nprop.full_comment.is_empty then
1540 page.append("<p class='info inheritance'>")
1541 page.append("<span class=\"noComment\">no comment for </span>")
1542 else
1543 page.append("<div>{intro_nprop.full_comment}</div>")
1544 page.append("<p class='info inheritance'>")
1545 end
1546 page.append("introduction in ")
1547 mproperty.intro.mclassdef.html_namespace(page)
1548 page.append(" {page.show_source(intro_nprop.location)}</p>")
1549 end
1550 end
1551 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1552 var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
1553 if nprop.full_comment == "" then
1554 page.append("<p class='info inheritance'>")
1555 page.append("<span class=\"noComment\">no comment for </span>")
1556 else
1557 page.append("<div>{nprop.full_comment}</div>")
1558 page.append("<p class='info inheritance'>")
1559 end
1560 if is_intro then
1561 page.append("introduction in ")
1562 else
1563 page.append("redefinition in ")
1564 end
1565 mclassdef.html_namespace(page)
1566 page.append(" {page.show_source(nprop.location)}</p>")
1567 end
1568 page.append("</div>")
1569 end
1570 end
1571
1572 redef class MMethodDef
1573 redef fun html_full_desc(page, ctx) do
1574 var classes = new Array[String]
1575 var is_redef = mproperty.intro_mclassdef.mclass != ctx
1576 if mproperty.is_init then
1577 classes.add("init")
1578 else
1579 classes.add("fun")
1580 end
1581 if is_redef then classes.add("redef")
1582 classes.add(mproperty.visibility.to_s)
1583 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1584 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1585 page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}{msignature.untyped_signature(page)}'>")
1586 page.append("<span>{mproperty.name}")
1587 msignature.html_signature(page)
1588 page.append("</span></h3>")
1589 else
1590 page.append("<h3 class='signature' data-untyped-signature='init{msignature.untyped_signature(page)}'>")
1591 page.append("<span>init")
1592 msignature.html_signature(page)
1593 page.append("</span></h3>")
1594 end
1595 html_info(page, ctx)
1596 html_comment(page)
1597 page.append("</article>")
1598 end
1599
1600 redef fun html_info(page, ctx) do
1601 page.append("<div class='info'>")
1602 if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ")
1603 if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ")
1604 if mproperty.is_init then
1605 page.append("init ")
1606 else
1607 page.append("fun ")
1608 end
1609 mproperty.html_namespace(page)
1610 page.append("</div>")
1611 end
1612 end
1613
1614 redef class MVirtualTypeDef
1615 redef fun html_full_desc(page, ctx) do
1616 var is_redef = mproperty.intro_mclassdef.mclass != ctx
1617 var classes = new Array[String]
1618 classes.add("type")
1619 if is_redef then classes.add("redef")
1620 classes.add(mproperty.visibility.to_s)
1621 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1622 page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}'><span>{mproperty.name}: ")
1623 bound.html_link(page)
1624 page.append("</span></h3>")
1625 html_info(page, ctx)
1626 html_comment(page)
1627 page.append("</article>")
1628 end
1629
1630 redef fun html_info(page, ctx) do
1631 page.append("<div class='info'>")
1632 if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ")
1633 page.append("type ")
1634 mproperty.html_namespace(page)
1635 page.append("</div>")
1636 end
1637 end
1638
1639 redef class MSignature
1640 private fun html_signature(page: NitdocPage) do
1641 if not mparameters.is_empty then
1642 page.append("(")
1643 for i in [0..mparameters.length[ do
1644 mparameters[i].html_link(page)
1645 if i < mparameters.length - 1 then page.append(", ")
1646 end
1647 page.append(")")
1648 end
1649 if return_mtype != null then
1650 page.append(": ")
1651 return_mtype.html_link(page)
1652 end
1653 end
1654
1655 private fun untyped_signature(page: NitdocPage): String do
1656 var res = new Buffer
1657 if not mparameters.is_empty then
1658 res.append("(")
1659 for i in [0..mparameters.length[ do
1660 res.append(mparameters[i].name)
1661 if i < mparameters.length - 1 then res.append(", ")
1662 end
1663 res.append(")")
1664 end
1665 return res.to_s
1666 end
1667 end
1668
1669 redef class MParameter
1670 private fun html_link(page: NitdocPage) do
1671 page.append("{name}: ")
1672 mtype.html_link(page)
1673 if is_vararg then page.append("...")
1674 end
1675 end
1676
1677 #
1678 # Nodes redefs
1679 #
1680
1681 redef class AModule
1682 private fun short_comment: String do
1683 if n_moduledecl != null and n_moduledecl.n_doc != null then
1684 return n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1685 end
1686 return ""
1687 end
1688
1689 private fun full_comment: String do
1690 if n_moduledecl != null and n_moduledecl.n_doc != null then
1691 return n_moduledecl.n_doc.full_markdown.html
1692 end
1693 return ""
1694 end
1695 end
1696
1697 redef class AStdClassdef
1698 private fun short_comment: String do
1699 if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1700 return ""
1701 end
1702
1703 private fun full_comment: String do
1704 if n_doc != null then return n_doc.full_markdown.html
1705 return ""
1706 end
1707 end
1708
1709 redef class APropdef
1710 private fun short_comment: String do
1711 if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1712 return ""
1713 end
1714
1715 private fun full_comment: String do
1716 if n_doc != null then return n_doc.full_markdown.html
1717 return ""
1718 end
1719 end
1720
1721 var nitdoc = new NitdocContext
1722 nitdoc.generate_nitdoc