6eeccd97872eb7c82e9f3d6acb6e2867a33860f2
[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 search
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 search do
139 var searchpage = new NitdocSearch(self)
140 searchpage.save("{output_dir.to_s}/search.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='search.html'>Search</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 search page
401 class NitdocSearch
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 "Search"
410
411 redef fun menu do
412 super
413 append("<li><a href='index.html'>Overview</a></li>")
414 append("<li class='current'>Search</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>{title}</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='search.html'>Search</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 var locals = new HashSet[MProperty]
694 for mclassdef in mclass.mclassdefs do
695 for mpropdef in mclassdef.mpropdefs do
696 if mpropdef.mproperty.visibility < ctx.min_visibility then continue
697 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
698 if mpropdef isa MMethodDef then
699 if mpropdef.mproperty.is_init then
700 consts.add(mpropdef)
701 else
702 meths.add(mpropdef)
703 end
704 end
705 locals.add(mpropdef.mproperty)
706 end
707 end
708 # get inherited properties
709 for pclass in mclass.in_hierarchy(ctx.mainmodule).greaters do
710 if pclass == mclass then continue
711 for pclassdef in pclass.mclassdefs do
712 for mprop in pclassdef.intro_mproperties do
713 var mpropdef = mprop.intro
714 if mprop.visibility < ctx.min_visibility then continue
715 if locals.has(mprop) then continue
716 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
717 if mpropdef isa MMethodDef then
718 if mpropdef.mproperty.is_init then
719 consts.add(mpropdef)
720 else
721 meths.add(mpropdef)
722 end
723 end
724 inherited.add(mpropdef)
725 end
726 end
727 end
728 end
729
730 redef fun title do
731 var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
732 if nclass isa AStdClassdef then
733 return "{mclass.name} class | {nclass.short_comment}"
734 else
735 return "{mclass.name} class"
736 end
737 end
738
739 redef fun menu do
740 super
741 append("<li><a href='index.html'>Overview</a></li>")
742 var public_owner = mclass.public_owner
743 if public_owner is null then
744 append("<li>")
745 mclass.intro_mmodule.html_link(self)
746 append("</li>")
747 else
748 append("<li>")
749 public_owner.html_link(self)
750 append("</li>")
751 end
752 append("<li class='current'>{mclass.name}</li>")
753 append("<li><a href='search.html'>Search</a></li>")
754 end
755
756 redef fun content do
757 append("<div class='menu'>")
758 properties_column
759 inheritance_column
760 append("</div>")
761 var footed = ""
762 if ctx.opt_custom_footer_text.value != null then footed = "footed"
763 append("<div class='content {footed}'>")
764 class_doc
765 append("</div>")
766 end
767
768 private fun properties_column do
769 var sorter = new MPropDefNameSorter
770 append("<nav class='properties filterable'>")
771 append("<h3>Properties</h3>")
772 # virtual types
773 if vtypes.length > 0 then
774 var vts = new Array[MVirtualTypeDef]
775 vts.add_all(vtypes)
776 sorter.sort(vts)
777 append("<h4>Virtual Types</h4>")
778 append("<ul>")
779 for mprop in vts do
780 mprop.html_sidebar_item(self)
781 end
782 append("</ul>")
783 end
784 # constructors
785 if consts.length > 0 then
786 var cts = new Array[MMethodDef]
787 cts.add_all(consts)
788 sorter.sort(cts)
789 append("<h4>Constructors</h4>")
790 append("<ul>")
791 for mprop in cts do
792 if mprop.mproperty.name == "init" and mprop.mclassdef.mclass != mclass then continue
793 mprop.html_sidebar_item(self)
794 end
795 append("</ul>")
796 end
797 # methods
798 if meths.length > 0 then
799 var mts = new Array[MMethodDef]
800 mts.add_all(meths)
801 sorter.sort(mts)
802 append("<h4>Methods</h4>")
803 append("<ul>")
804 for mprop in mts do
805 if mclass.name != "Object" and mprop.mproperty.intro_mclassdef.mclass.name == "Object" and mprop.mproperty.visibility <= protected_visibility then continue
806 mprop.html_sidebar_item(self)
807 end
808 append("</ul>")
809 end
810 append("</nav>")
811 end
812
813 private fun inheritance_column do
814 var sorted = new Array[MClass]
815 var sorterp = new MClassNameSorter
816 append("<nav>")
817 append("<h3>Inheritance</h3>")
818 var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a
819 if greaters.length > 1 then
820 ctx.mainmodule.linearize_mclasses(greaters)
821 append("<h4>Superclasses</h4>")
822 append("<ul>")
823 for sup in greaters do
824 if sup == mclass then continue
825 append("<li>")
826 sup.html_link(self)
827 append("</li>")
828 end
829 append("</ul>")
830 end
831 var smallers = mclass.in_hierarchy(ctx.mainmodule).smallers.to_a
832 var direct_smallers = mclass.in_hierarchy(ctx.mainmodule).direct_smallers.to_a
833 if smallers.length <= 1 then
834 append("<h4>No Known Subclasses</h4>")
835 else if smallers.length <= 100 then
836 ctx.mainmodule.linearize_mclasses(smallers)
837 append("<h4>Subclasses</h4>")
838 append("<ul>")
839 for sub in smallers do
840 if sub == mclass then continue
841 append("<li>")
842 sub.html_link(self)
843 append("</li>")
844 end
845 append("</ul>")
846 else if direct_smallers.length <= 100 then
847 ctx.mainmodule.linearize_mclasses(direct_smallers)
848 append("<h4>Direct Subclasses Only</h4>")
849 append("<ul>")
850 for sub in direct_smallers do
851 if sub == mclass then continue
852 append("<li>")
853 sub.html_link(self)
854 append("</li>")
855 end
856 append("</ul>")
857 else
858 append("<h4>Too much Subclasses to list</h4>")
859 end
860 append("</nav>")
861 end
862
863 private fun class_doc do
864 # title
865 append("<h1>{mclass.signature}</h1>")
866 append("<div class='subtitle info'>")
867 mclass.html_full_signature(self)
868 append("</div>")
869 # comment
870 var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
871 append("<section class='description'>")
872 process_generate_dot
873 append("</section>")
874 # concerns
875 var concern2meths = new ArrayMap[MModule, Array[MMethodDef]]
876 var sorted_meths = new Array[MMethodDef]
877 var sorted = new Array[MModule]
878 sorted_meths.add_all(meths)
879 ctx.mainmodule.linearize_mpropdefs(sorted_meths)
880 for meth in meths do
881 if inherited.has(meth) then continue
882 var mmodule = meth.mclassdef.mmodule
883 if not concern2meths.has_key(mmodule) then
884 sorted.add(mmodule)
885 concern2meths[mmodule] = new Array[MMethodDef]
886 end
887 concern2meths[mmodule].add(meth)
888 end
889 var sections = new ArrayMap[MModule, Array[MModule]]
890 for mmodule in concern2meths.keys do
891 var owner = mmodule.public_owner
892 if owner == null then owner = mmodule
893 if not sections.has_key(owner) then sections[owner] = new Array[MModule]
894 if owner != mmodule then sections[owner].add(mmodule)
895 end
896 append("<section class='concerns'>")
897 append("<h2 class='section-header'>Concerns</h2>")
898 append("<ul>")
899 for owner, mmodules in sections do
900 var nowner = ctx.mbuilder.mmodule2nmodule[owner]
901 append("<li>")
902 if nowner.short_comment.is_empty then
903 append("<a href=\"#{owner.anchor}\">{owner.name}</a>")
904 else
905 append("<a href=\"#{owner.anchor}\">{owner.name}</a>: {nowner.short_comment}")
906 end
907 if not mmodules.is_empty then
908 append("<ul>")
909 for mmodule in mmodules do
910 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
911 if nmodule.short_comment.is_empty then
912 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a></li>")
913 else
914 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a>: {nmodule.short_comment}</li>")
915 end
916 end
917 append("</ul>")
918 end
919 append("</li>")
920 end
921 append("</ul>")
922 append("</section>")
923 # properties
924 var prop_sorter = new MPropDefNameSorter
925 var lmmodule = new List[MModule]
926 # virtual and formal types
927 var local_vtypes = new Array[MVirtualTypeDef]
928 for vt in vtypes do if not inherited.has(vt) then local_vtypes.add(vt)
929 if local_vtypes.length > 0 or mclass.arity > 0 then
930 append("<section class='types'>")
931 append("<h2>Formal and Virtual Types</h2>")
932 # formal types
933 if mclass.arity > 0 and nclass isa AStdClassdef then
934 for ft, bound in mclass.parameter_types do
935 append("<article id='FT_{ft}'>")
936 append("<h3 class='signature' data-untyped-signature='{ft.to_s}'><span>{ft}: ")
937 bound.html_link(self)
938 append("</span></h3>")
939 append("<div class=\"info\">formal generic type</div>")
940 append("</article>")
941 end
942 end
943 # virtual types
944 prop_sorter.sort(local_vtypes)
945 for prop in local_vtypes do prop.html_full_desc(self)
946 append("</section>")
947 end
948 # constructors
949 var local_consts = new Array[MMethodDef]
950 for const in consts do if not inherited.has(const) then local_consts.add(const)
951 prop_sorter.sort(local_consts)
952 if local_consts.length > 0 then
953 append("<section class='constructors'>")
954 append("<h2 class='section-header'>Constructors</h2>")
955 for prop in local_consts do prop.html_full_desc(self)
956 append("</section>")
957 end
958 # methods
959 if not concern2meths.is_empty then
960 append("<section class='methods'>")
961 append("<h2 class='section-header'>Methods</h2>")
962 for owner, mmodules in sections do
963 append("<a id=\"{owner.anchor}\"></a>")
964 if owner != mclass.intro_mmodule and owner != mclass.public_owner then
965 var nowner = ctx.mbuilder.mmodule2nmodule[owner]
966 append("<h3 class=\"concern-toplevel\">Methods refined in ")
967 owner.html_link(self)
968 append("</h3>")
969 append("<p class=\"concern-doc\">")
970 owner.html_link(self)
971 if not nowner.short_comment.is_empty then
972 append(": {nowner.short_comment}")
973 end
974 append("</p>")
975 end
976 if concern2meths.has_key(owner) then
977 var mmethods = concern2meths[owner]
978 prop_sorter.sort(mmethods)
979 for prop in mmethods do prop.html_full_desc(self)
980 end
981 for mmodule in mmodules do
982 append("<a id=\"{mmodule.anchor}\"></a>")
983 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
984 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
985 append("<p class=\"concern-doc\">")
986 mmodule.html_link(self)
987 if not nmodule.short_comment.is_empty then
988 append(": {nmodule.short_comment}")
989 end
990 append("</p>")
991 end
992 var mmethods = concern2meths[mmodule]
993 prop_sorter.sort(mmethods)
994 for prop in mmethods do prop.html_full_desc(self)
995 end
996 end
997 end
998 # inherited properties
999 if inherited.length > 0 then
1000 var sorted_inherited = new Array[MPropDef]
1001 sorted_inherited.add_all(inherited)
1002 ctx.mainmodule.linearize_mpropdefs(sorted_inherited)
1003 var classes = new ArrayMap[MClass, Array[MPropDef]]
1004 for mmethod in sorted_inherited.reversed do
1005 var mclass = mmethod.mclassdef.mclass
1006 if not classes.has_key(mclass) then classes[mclass] = new Array[MPropDef]
1007 classes[mclass].add(mmethod)
1008 end
1009 append("<h3>Inherited Properties</h3>")
1010 for c, mmethods in classes do
1011 prop_sorter.sort(mmethods)
1012 append("<p>Defined in ")
1013 c.html_link(self)
1014 append(": ")
1015 for i in [0..mmethods.length[ do
1016 var mmethod = mmethods[i]
1017 mmethod.html_link(self)
1018 if i <= mmethods.length - 1 then append(", ")
1019 end
1020 append("</p>")
1021 end
1022 end
1023 append("</section>")
1024 end
1025
1026 private fun process_generate_dot do
1027 var pe = ctx.class_hierarchy[mclass]
1028 var cla = new HashSet[MClass]
1029 var sm = new HashSet[MClass]
1030 var sm2 = new HashSet[MClass]
1031 sm.add(mclass)
1032 while cla.length + sm.length < 10 and sm.length > 0 do
1033 cla.add_all(sm)
1034 sm2.clear
1035 for x in sm do
1036 sm2.add_all(pe.poset[x].direct_smallers)
1037 end
1038 var t = sm
1039 sm = sm2
1040 sm2 = t
1041 end
1042 cla.add_all(pe.greaters)
1043
1044 var op = new Buffer
1045 var name = "dep_{mclass.name}"
1046 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")
1047 for c in cla do
1048 if c == mclass then
1049 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1050 else
1051 op.append("\"{c.name}\"[URL=\"{c.url}\"];\n")
1052 end
1053 for c2 in pe.poset[c].direct_greaters do
1054 if not cla.has(c2) then continue
1055 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1056 end
1057 if not pe.poset[c].direct_smallers.is_empty then
1058 var others = true
1059 for c2 in pe.poset[c].direct_smallers do
1060 if cla.has(c2) then others = false
1061 end
1062 if others then
1063 op.append("\"{c.name}...\"[label=\"\"];\n")
1064 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1065 end
1066 end
1067 end
1068 op.append("\}\n")
1069 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1070 end
1071 end
1072
1073 #
1074 # Model redefs
1075 #
1076
1077 redef class MModule
1078 # URL to nitdoc page
1079 fun url: String do
1080 if url_cache == null then
1081 var res = new Buffer
1082 res.append("module_")
1083 var mowner = public_owner
1084 if mowner != null then
1085 res.append("{public_owner.name}_")
1086 end
1087 res.append("{self.name}.html")
1088 url_cache = res.to_s
1089 end
1090 return url_cache.as(not null)
1091 end
1092 private var url_cache: nullable String
1093
1094 # html anchor id to the module in a nitdoc page
1095 fun anchor: String do
1096 if anchor_cache == null then
1097 var res = new Buffer
1098 res.append("MOD_")
1099 var mowner = public_owner
1100 if mowner != null then
1101 res.append("{public_owner.name}_")
1102 end
1103 res.append(self.name)
1104 anchor_cache = res.to_s
1105 end
1106 return anchor_cache.as(not null)
1107 end
1108 private var anchor_cache: nullable String
1109
1110 # Return a link (html a tag) to the nitdoc module page
1111 fun html_link(page: NitdocPage) do
1112 if html_link_cache == null then
1113 var res = new Buffer
1114 if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
1115 res.append("<a href='{url}' title='{page.ctx.mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>")
1116 else
1117 res.append("<a href='{url}'>{name}</a>")
1118 end
1119 html_link_cache = res.to_s
1120 end
1121 page.append(html_link_cache.as(not null))
1122 end
1123 private var html_link_cache: nullable String
1124
1125 # Return the module signature decorated with html
1126 fun html_signature(page: NitdocPage) do
1127 page.append("<span>module ")
1128 html_full_namespace(page)
1129 page.append("</span>")
1130 end
1131
1132 # Return the module full namespace decorated with html
1133 fun html_full_namespace(page: NitdocPage) do
1134 page.append("<span>")
1135 var mowner = public_owner
1136 if mowner != null then
1137 public_owner.html_namespace(page)
1138 page.append("::")
1139 end
1140 html_link(page)
1141 page.append("</span>")
1142 end
1143
1144 # Return the module full namespace decorated with html
1145 fun html_namespace(page: NitdocPage) do
1146 page.append("<span>")
1147 var mowner = public_owner
1148 if mowner != null then
1149 public_owner.html_namespace(page)
1150 else
1151 html_link(page)
1152 end
1153 page.append("</span>")
1154 end
1155
1156 # Return the full comment of the module decorated with html
1157 fun html_full_comment(page: NitdocPage) do
1158 if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
1159 page.append("<div id='description'>")
1160 page.append("<pre class='text_label'>{page.ctx.mbuilder.mmodule2nmodule[self].full_comment}</pre>")
1161 page.append("</div>")
1162 end
1163 end
1164 end
1165
1166 redef class MClass
1167 # Return the module signature decorated with html
1168 fun html_full_signature(page: NitdocPage) do
1169 if visibility < public_visibility then page.append("{visibility.to_s} ")
1170 page.append("{kind} ")
1171 html_namespace(page)
1172 end
1173
1174 # name with formal parameter
1175 # Foo[A, B]
1176 private fun signature: String do
1177 if arity > 0 then
1178 return "{name}[{intro.parameter_names.join(", ")}]"
1179 else
1180 return name
1181 end
1182 end
1183
1184 # Return a link (html a tag) to the nitdoc class page
1185 fun html_link(page: NitdocPage) do
1186 if html_link_cache == null then
1187 var res = new Buffer
1188 res.append("<a href='{url}'")
1189 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1190 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1191 if nclass isa AStdClassdef then
1192 res.append(" title=\"{nclass.short_comment}\"")
1193 end
1194 end
1195 res.append(">{signature}</a>")
1196 html_link_cache = res.to_s
1197 end
1198 page.append(html_link_cache.as(not null))
1199 end
1200 private var html_link_cache: nullable String
1201
1202 # Return the class namespace decorated with html
1203 fun html_namespace(page: NitdocPage) do
1204 intro_mmodule.html_namespace(page)
1205 page.append("::<span>")
1206 html_link(page)
1207 page.append("</span>")
1208 end
1209
1210 fun url: String do
1211 return "class_{public_owner}_{name}.html"
1212 end
1213
1214 # Escape name for html output
1215 redef fun name do return super.html_escape
1216 end
1217
1218 redef class MProperty
1219 # Return the property namespace decorated with html
1220 fun html_namespace(page: NitdocPage) do
1221 intro_mclassdef.mclass.html_namespace(page)
1222 page.append("::<span>")
1223 intro.html_link(page)
1224 page.append("</span>")
1225 end
1226
1227 # Escape name for html output
1228 redef fun name do return super.html_escape
1229 end
1230
1231 redef class MType
1232 fun html_link(page: NitdocPage) is abstract
1233 end
1234
1235 redef class MClassType
1236 redef fun html_link(page) do mclass.html_link(page)
1237 end
1238
1239 redef class MNullableType
1240 redef fun html_link(page) do
1241 page.append("nullable ")
1242 mtype.html_link(page)
1243 end
1244 end
1245
1246 redef class MGenericType
1247 redef fun html_link(page) do
1248 page.append("<a href='{mclass.url}'>{mclass.name}</a>[")
1249 for i in [0..arguments.length[ do
1250 arguments[i].html_link(page)
1251 if i < arguments.length - 1 then page.append(", ")
1252 end
1253 page.append("]")
1254 end
1255 end
1256
1257 redef class MParameterType
1258 redef fun html_link(page) do
1259 var name = mclass.intro.parameter_names[rank]
1260 page.append("<a href='{mclass.url}#FT_{name}' title='formal type'>{name}</a>")
1261 end
1262 end
1263
1264 redef class MVirtualType
1265 redef fun html_link(page) do mproperty.intro.html_link(page)
1266 end
1267
1268 redef class MClassDef
1269 # Return the classdef namespace decorated with html
1270 fun html_namespace(page: NitdocPage) do
1271 mmodule.html_full_namespace(page)
1272 page.append("::<span>")
1273 mclass.html_link(page)
1274 page.append("</span>")
1275 end
1276 end
1277
1278 redef class MPropDef
1279 fun url: String do
1280 if url_cache == null then
1281 url_cache = "{mclassdef.mclass.url}#{anchor}"
1282 end
1283 return url_cache.as(not null)
1284 end
1285 private var url_cache: nullable String
1286
1287 fun anchor: String do
1288 if anchor_cache == null then
1289 anchor_cache = "PROP_{mclassdef.mclass.public_owner.name}_{mproperty.name}"
1290 end
1291 return anchor_cache.as(not null)
1292 end
1293 private var anchor_cache: nullable String
1294
1295 # Return a link (html a tag) to the nitdoc class page
1296 fun html_link(page: NitdocPage) do
1297 if html_link_cache == null then
1298 var res = new Buffer
1299 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1300 var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
1301 res.append("<a href=\"{url}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>")
1302 else
1303 res.append("<a href=\"{url}\">{mproperty.name}</a>")
1304 end
1305 html_link_cache = res.to_s
1306 end
1307 page.append(html_link_cache.as(not null))
1308 end
1309 private var html_link_cache: nullable String
1310
1311 # Return a list item for the mpropdef
1312 private fun html_list_item(page: NitdocPage) do
1313 if is_intro then
1314 page.append("<li class='intro'>")
1315 page.append("<span title='introduction'>I</span>&nbsp;")
1316 else
1317 page.append("<li class='redef'>")
1318 page.append("<span title='redefinition'>R</span>&nbsp;")
1319 end
1320 html_link(page)
1321 page.append("(")
1322 mclassdef.mclass.html_link(page)
1323 page.append(")")
1324 page.append("</li>")
1325 end
1326
1327 # Return a list item for the mpropdef
1328 private fun html_sidebar_item(page: NitdocClass) do
1329 if is_intro and mclassdef.mclass == page.mclass then
1330 page.append("<li class='intro'>")
1331 page.append("<span title='Introduced'>I</span>")
1332 else if is_intro and mclassdef.mclass != page.mclass then
1333 page.append("<li class='inherit'>")
1334 page.append("<span title='Inherited'>H</span>")
1335 else
1336 page.append("<li class='redef'>")
1337 page.append("<span title='Redefined'>R</span>")
1338 end
1339 html_link(page)
1340 page.append("</li>")
1341 end
1342
1343 private fun html_full_desc(page: NitdocClass) is abstract
1344 private fun html_info(page: NitdocClass) is abstract
1345
1346 fun full_name: String do
1347 return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}"
1348 end
1349
1350 private fun html_inheritance(page: NitdocClass) do
1351 # definitions block
1352 page.append("<p class='info'>")
1353 page.ctx.mainmodule.linearize_mpropdefs(mproperty.mpropdefs)
1354 var previous_defs = new Array[MPropDef]
1355 var next_defs = new Array[MPropDef]
1356 var self_passed = false
1357 for def in mproperty.mpropdefs do
1358 if def == self then
1359 self_passed = true
1360 continue
1361 end
1362 if not self_passed then
1363 if def.mclassdef.mclass.in_hierarchy(page.ctx.mainmodule) < page.mclass then continue
1364 if def.is_intro then continue
1365 previous_defs.add(def)
1366 else
1367 if page.mclass.in_hierarchy(page.ctx.mainmodule) < def.mclassdef.mclass then continue
1368 next_defs.add(def)
1369 end
1370 end
1371 page.append("defined by ")
1372 mclassdef.mmodule.html_full_namespace(page)
1373 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1374 page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[self].location)}")
1375 end
1376 if not is_intro then
1377 page.append(", introduced by ")
1378 mproperty.intro.mclassdef.mclass.html_link(page)
1379 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1380 page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[self].location)}")
1381 end
1382 end
1383 if not previous_defs.is_empty then
1384 page.append(", inherited from ")
1385 for i in [0..previous_defs.length[ do
1386 var def = previous_defs[i]
1387 def.mclassdef.mclass.html_link(page)
1388 if page.ctx.mbuilder.mpropdef2npropdef.has_key(def) then
1389 page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[def].location)}")
1390 end
1391
1392 if i < previous_defs.length - 1 then page.append(", ")
1393 end
1394 end
1395 if not next_defs.is_empty then
1396 page.append(", redefined by ")
1397 for i in [0..next_defs.length[ do
1398 var def = next_defs[i]
1399 def.mclassdef.mclass.html_link(page)
1400 if page.ctx.mbuilder.mpropdef2npropdef.has_key(def) then
1401 page.append(" {page.show_source(page.ctx.mbuilder.mpropdef2npropdef[def].location)}")
1402 end
1403 if i < next_defs.length - 1 then page.append(", ")
1404 end
1405 end
1406 page.append(".</p>")
1407 end
1408
1409 private fun html_comment(page: NitdocClass) do
1410 if not page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then return
1411 var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
1412 page.append("<div class='description'>")
1413 if not is_intro and page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then
1414 var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro]
1415 page.append("<p class='info'>from ")
1416 mproperty.html_namespace(page)
1417 page.append("</p>")
1418 if intro_nprop.full_comment == "" then
1419 page.append("<span class=\"noComment\">No comment</span>")
1420 else
1421 page.append("<pre>{intro_nprop.full_comment}</pre>")
1422 end
1423 page.append("<p class='info'>from ")
1424 mclassdef.html_namespace(page)
1425 page.append("</p>")
1426 end
1427 if nprop.full_comment == "" then
1428 page.append("<span class=\"noComment\">No comment</span>")
1429 else
1430 page.append("<pre>{nprop.full_comment}</pre>")
1431 end
1432 html_inheritance(page)
1433 page.append("</div>")
1434 end
1435 end
1436
1437 redef class MMethodDef
1438 redef fun html_full_desc(page) do
1439 var classes = new Array[String]
1440 var is_redef = mproperty.intro_mclassdef.mclass != page.mclass
1441 if mproperty.is_init then
1442 classes.add("init")
1443 else
1444 classes.add("fun")
1445 end
1446 if is_redef then classes.add("redef")
1447 classes.add(mproperty.visibility.to_s)
1448 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1449 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1450 page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}{msignature.untyped_signature(page)}'>")
1451 page.append("<span>{mproperty.name}")
1452 msignature.html_signature(page)
1453 page.append("</span></h3>")
1454 else
1455 page.append("<h3 class='signature' data-untyped-signature='init{msignature.untyped_signature(page)}'>")
1456 page.append("<span>init")
1457 msignature.html_signature(page)
1458 page.append("</span></h3>")
1459 end
1460 html_info(page)
1461 html_comment(page)
1462 page.append("</article>")
1463 end
1464
1465 redef fun html_info(page) do
1466 page.append("<div class='info'>")
1467 if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ")
1468 if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ")
1469 if mproperty.is_init then
1470 page.append("init ")
1471 else
1472 page.append("fun ")
1473 end
1474 mproperty.html_namespace(page)
1475 page.append("</div>")
1476 end
1477 end
1478
1479 redef class MVirtualTypeDef
1480 redef fun html_full_desc(page) do
1481 var is_redef = mproperty.intro_mclassdef.mclass != page.mclass
1482 var classes = new Array[String]
1483 classes.add("type")
1484 if is_redef then classes.add("redef")
1485 classes.add(mproperty.visibility.to_s)
1486 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1487 page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}'><span>{mproperty.name}: ")
1488 bound.html_link(page)
1489 page.append("</span></h3>")
1490 html_info(page)
1491 html_comment(page)
1492 page.append("</article>")
1493 end
1494
1495 redef fun html_info(page) do
1496 page.append("<div class='info'>")
1497 if mproperty.intro_mclassdef.mclass != page.mclass then page.append("redef ")
1498 page.append("type ")
1499 mproperty.html_namespace(page)
1500 page.append("</div>")
1501 end
1502 end
1503
1504 redef class MSignature
1505 private fun html_signature(page: NitdocPage) do
1506 if not mparameters.is_empty then
1507 page.append("(")
1508 for i in [0..mparameters.length[ do
1509 mparameters[i].html_link(page)
1510 if i < mparameters.length - 1 then page.append(", ")
1511 end
1512 page.append(")")
1513 end
1514 if return_mtype != null then
1515 page.append(": ")
1516 return_mtype.html_link(page)
1517 end
1518 end
1519
1520 private fun untyped_signature(page: NitdocPage): String do
1521 var res = new Buffer
1522 if not mparameters.is_empty then
1523 res.append("(")
1524 for i in [0..mparameters.length[ do
1525 res.append(mparameters[i].name)
1526 if i < mparameters.length - 1 then res.append(", ")
1527 end
1528 res.append(")")
1529 end
1530 return res.to_s
1531 end
1532 end
1533
1534 redef class MParameter
1535 private fun html_link(page: NitdocPage) do
1536 page.append("{name}: ")
1537 mtype.html_link(page)
1538 if is_vararg then page.append("...")
1539 end
1540 end
1541
1542 #
1543 # Nodes redefs
1544 #
1545
1546 redef class AModule
1547 private fun short_comment: String do
1548 if n_moduledecl != null and n_moduledecl.n_doc != null then
1549 return n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1550 end
1551 return ""
1552 end
1553
1554 private fun full_comment: String do
1555 var res = new Buffer
1556 if n_moduledecl != null and n_moduledecl.n_doc != null then
1557 for t in n_moduledecl.n_doc.n_comment do
1558 res.append(t.text.substring_from(1).html_escape)
1559 end
1560 end
1561 return res.to_s
1562 end
1563 end
1564
1565 redef class AStdClassdef
1566 private fun short_comment: String do
1567 if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1568 return ""
1569 end
1570
1571 private fun full_comment: String do
1572 var res = new Buffer
1573 if n_doc != null then
1574 for t in n_doc.n_comment do res.append(t.text.substring_from(1).html_escape)
1575 end
1576 return res.to_s
1577 end
1578 end
1579
1580 redef class APropdef
1581 private fun short_comment: String do
1582 if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1583 return ""
1584 end
1585
1586 private fun full_comment: String do
1587 var res = new Buffer
1588 if n_doc != null then
1589 for t in n_doc.n_comment do res.append(t.text.substring_from(1).html_escape)
1590 end
1591 return res.to_s
1592 end
1593 end
1594
1595 var nitdoc = new NitdocContext
1596 nitdoc.generate_nitdoc