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