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