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