ni_nitdoc: display inherited virtual types and constructors in class sidebar
[nit.git] / src / ni_nitdoc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 module ni_nitdoc
18
19 import model_utils
20 import abstract_compiler
21
22 class Nitdoc
23 private var toolcontext: ToolContext
24 private var model: Model
25 private var modelbuilder: ModelBuilder
26 private var mainmodule: MModule
27 private var class_hierarchy: POSet[MClass]
28 private var arguments: Array[String]
29 private var output_dir: nullable String
30 private var dot_dir: nullable String
31 private var share_dir: nullable String
32 private var source: nullable String
33
34 private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir")
35 private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
36 private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir")
37 private var opt_nodot = new OptionBool("Do not generate graphes with graphiviz", "--no-dot")
38
39 init(toolcontext: ToolContext) do
40 # We need a model to collect stufs
41 self.toolcontext = toolcontext
42 self.arguments = toolcontext.option_context.rest
43 toolcontext.option_context.options.clear
44 toolcontext.option_context.add_option(opt_dir)
45 toolcontext.option_context.add_option(opt_source)
46 toolcontext.option_context.add_option(opt_sharedir)
47 toolcontext.option_context.add_option(opt_nodot)
48 toolcontext.process_options
49 process_options
50
51 if arguments.length < 1 then
52 toolcontext.option_context.usage
53 exit(1)
54 end
55
56 model = new Model
57 modelbuilder = new ModelBuilder(model, toolcontext)
58
59 # Here we load an process std modules
60 var mmodules = modelbuilder.parse_and_build([arguments.first])
61 if mmodules.is_empty then return
62 modelbuilder.full_propdef_semantic_analysis
63 assert mmodules.length == 1
64 self.mainmodule = mmodules.first
65 self.class_hierarchy = mainmodule.flatten_mclass_hierarchy
66 end
67
68 private fun process_options do
69 if not opt_dir.value is null then
70 output_dir = opt_dir.value
71 else
72 output_dir = "doc"
73 end
74 if not opt_sharedir.value is null then
75 share_dir = opt_sharedir.value
76 else
77 var dir = "NIT_DIR".environ
78 if dir.is_empty then
79 dir = "{sys.program_name.dirname}/../share/nitdoc"
80 else
81 dir = "{dir}/share/nitdoc"
82 end
83 share_dir = dir
84 if share_dir is null then
85 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
86 abort
87 end
88 dir = "{share_dir.to_s}/scripts/js-facilities.js"
89 if share_dir is null then
90 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
91 abort
92 end
93 end
94 if not opt_source.value is null then
95 source = ""
96 else
97 source = opt_source.value
98 end
99 end
100
101 fun start do
102 if arguments.length == 1 then
103 # Create destination dir if it's necessary
104 if not output_dir.file_exists then output_dir.mkdir
105 sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/")
106 self.dot_dir = null
107 if not opt_nodot.value then self.dot_dir = output_dir.to_s
108 overview
109 #fullindex
110 modules
111 classes
112 #quicksearch_list
113 end
114 end
115
116 fun overview do
117 var overviewpage = new NitdocOverview(modelbuilder, dot_dir)
118 overviewpage.save("{output_dir.to_s}/index.html")
119 end
120
121 fun fullindex do
122 var fullindex = new NitdocFullindex(model.mmodules)
123 fullindex.save("{output_dir.to_s}/full-index.html")
124 end
125
126 fun modules do
127 for mmodule in model.mmodules do
128 var modulepage = new NitdocModule(mmodule, modelbuilder, dot_dir)
129 modulepage.save("{output_dir.to_s}/{mmodule.name}.html")
130 end
131 end
132
133 fun classes do
134 for mclass in modelbuilder.model.mclasses do
135 var classpage = new NitdocClass(mclass, self, dot_dir, source)
136 classpage.save("{output_dir.to_s}/{mclass.name}.html")
137 end
138 end
139
140 # Generate QuickSearch file
141 fun quicksearch_list do
142 var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js")
143 var content = new Buffer
144 content.append("var entries = \{ ")
145 for prop in model.mproperties do
146 if not prop isa MMethod then continue
147 content.append("\"{prop.name}\": [")
148 for propdef in prop.mpropdefs do
149 content.append("\{txt: \"{propdef.mproperty.full_name}\", url:\"{propdef.mproperty.anchor}\" \}")
150 if not propdef is prop.mpropdefs.last then content.append(", ")
151 end
152 content.append("]")
153 content.append(", ")
154 end
155
156 for mclass in model.mclasses do
157 content.append("\"{mclass.name}\": [")
158 for mclassdef in mclass.mclassdefs do
159 content.append("\{txt: \"{mclassdef.mclass.full_name}\", url:\"{mclass.link_anchor}\" \}")
160 if not mclassdef is mclass.mclassdefs.last then content.append(", ")
161 end
162 content.append("]")
163 if not mclass is model.mclasses.last then content.append(", ")
164 end
165
166 content.append(" \};")
167 file.write(content.to_s)
168 file.close
169 end
170
171 end
172
173 # Nitdoc base page
174 abstract class NitdocPage
175
176 var dot_dir: nullable String
177 var source: nullable String
178
179 init do end
180
181 fun append(str: String) do html.append(str)
182 var html = new Buffer
183
184 fun head do
185 append("<meta charset='utf-8'/>")
186 append("<script type='text/javascript' src='scripts/jquery-1.7.1.min.js'></script>")
187 append("<script type='text/javascript' src='quicksearch-list.js'></script>")
188 append("<script type='text/javascript' src='scripts/js-facilities.js'></script>")
189 append("<link rel='stylesheet' href='styles/main.css' type='text/css' media='screen'/>")
190 end
191
192 fun menu is abstract
193
194 fun header do
195 append("<header>")
196 append("<nav class='main'>")
197 append("<ul>")
198 menu
199 append("<li id='liGitHub'>")
200 append("<a class='btn' id='logGitHub'>")
201 append("<img id='imgGitHub' src='resources/icons/github-icon.png' alt='GitHub'/>")
202 append("</a>")
203 append("<div class='popover bottom'>")
204 append("<div class='arrow'>&nbsp;</div>")
205 append("<div class='githubTitle'>")
206 append("<h3>Github Sign In</h3>")
207 append("</div>")
208 append("<div>")
209 append("<label id='lbloginGit'>Username</label>")
210 append("<input id='loginGit' name='login' type='text'/>")
211 append("<label id='logginMessage'>Hello ")
212 append("<a id='githubAccount'><strong id='nickName'></strong></a>")
213 append("</label>")
214 append("</div>")
215 append("<div>")
216 append("<label id='lbpasswordGit'>Password</label>")
217 append("<input id='passwordGit' name='password' type='password'/>")
218 append("<div id='listBranches'>")
219 append("<label id='lbBranches'>Branch</label>")
220 append("<select class='dropdown' id='dropBranches' name='dropBranches' tabindex='1'></select>")
221 append("</div>")
222 append("</div>")
223 append("<div>")
224 append("<label id='lbrepositoryGit'>Repository</label>")
225 append("<input id='repositoryGit' name='repository' type='text'/>")
226 append("</div>")
227 append("<div>")
228 append("<label id='lbbranchGit'>Branch</label>")
229 append("<input id='branchGit' name='branch' type='text'/>")
230 append("</div>")
231 append("<div>")
232 append("<a id='signIn'>Sign In</a>")
233 append("</div>")
234 append("</div>")
235 append("</li>")
236 append("</ul>")
237 append("</nav>")
238 append("</header>")
239 end
240
241 fun content is abstract
242
243 fun footer do
244 append("<footer>Nit standard library. Version jenkins-component=stdlib-19.</footer>")
245 end
246
247 # Generate a clickable graphviz image using a dot content
248 fun generate_dot(dot: String, name: String, alt: String) do
249 var output_dir = dot_dir
250 if output_dir == null then return
251 var file = new OFStream.open("{output_dir}/{name}.dot")
252 file.write(dot)
253 file.close
254 sys.system("\{ test -f {output_dir}/{name}.png && test -f {output_dir}/{name}.s.dot && diff {output_dir}/{name}.dot {output_dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {output_dir}/{name}.dot {output_dir}/{name}.s.dot && dot -Tpng -o{output_dir}/{name}.png -Tcmapx -o{output_dir}/{name}.map {output_dir}/{name}.s.dot ; \}")
255 append("<article class='graph'>")
256 append("<img src='{name}.png' usemap='#{name}' style='margin:auto' alt='{alt}'/>")
257 append("</article>")
258 var fmap = new IFStream.open("{output_dir}/{name}.map")
259 append(fmap.read_all)
260 fmap.close
261 end
262
263 # Add a (source) link for a given location
264 fun show_source(l: Location): String
265 do
266 if source == null then
267 return "({l.file.filename.simplify_path})"
268 else
269 # THIS IS JUST UGLY ! (but there is no replace yet)
270 var x = source.split_with("%f")
271 source = x.join(l.file.filename.simplify_path)
272 x = source.split_with("%l")
273 source = x.join(l.line_start.to_s)
274 x = source.split_with("%L")
275 source = x.join(l.line_end.to_s)
276 return " (<a href=\"{source.to_s}\">show code</a>)"
277 end
278 end
279
280 # Render the page as a html string
281 fun render: String do
282 append("<!DOCTYPE html>")
283 append("<head>")
284 head
285 append("</head>")
286 append("<body>")
287 header
288 append("<div class='page'>")
289 content
290 append("</div>")
291 footer
292 append("</body>")
293 return html.to_s
294 end
295
296 # Save html page in the specified file
297 fun save(file: String) do
298 var out = new OFStream.open(file)
299 out.write(render)
300 out.close
301 end
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(mbuilder: ModelBuilder, dot_dir: nullable String) do
311 self.mbuilder = mbuilder
312 self.dot_dir = dot_dir
313 # get modules
314 var mmodules = new HashSet[MModule]
315 for mmodule in mbuilder.model.mmodules do
316 var owner = mmodule.public_owner
317 if owner != null then
318 mmodules.add(owner)
319 else
320 mmodules.add(mmodule)
321 end
322 end
323 # sort modules
324 var sorter = new ComparableSorter[MModule]
325 self.mmodules.add_all(mmodules)
326 sorter.sort(self.mmodules)
327 end
328
329 redef fun head do
330 super
331 append("<title>Overview | Nit Standard Library</title>")
332 end
333
334 redef fun menu do
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 append("<h1>Nit Standard Library</h1>")
342 append("<article class='overview'><p>Documentation for the standard library of Nit<br />Version jenkins-component=stdlib-19<br />Date: TODAY</p></article>")
343 append("<article class='overview'>")
344 # module list
345 append("<h2>Modules</h2>")
346 append("<ul>")
347 for mmodule in mmodules do
348 var amodule = mbuilder.mmodule2nmodule[mmodule]
349 append("<li>{mmodule.link(mbuilder)}&nbsp;{amodule.short_comment}</li>")
350 end
351 append("</ul>")
352 # module graph
353 process_generate_dot
354 append("</article>")
355 append("</div>")
356 end
357
358 fun process_generate_dot do
359 var op = new Buffer
360 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")
361 for mmodule in mmodules do
362 op.append("\"{mmodule.name}\"[URL=\"{mmodule.name}.html\"];\n")
363 for imported in mmodule.in_importation.direct_greaters do
364 if imported.direct_owner == null then
365 op.append("\"{mmodule.name}\"->\"{imported.name}\";\n")
366 end
367 end
368 end
369 op.append("\}\n")
370 generate_dot(op.to_s, "dep", "Modules hierarchy")
371 end
372 end
373
374 # The full index page
375 class NitdocFullindex
376 super NitdocPage
377
378 private var mmodules: Array[MModule]
379
380 init(mmodules: Array[MModule]) do
381 self.mmodules = mmodules
382 self.dot_dir = null
383 end
384
385 redef fun head do
386 super
387 append("<title>Full Index | Nit Standard Library</title>")
388 end
389
390 redef fun menu do
391 append("<li><a href='index.html'>Overview</a></li>")
392 append("<li class='current'>Full Index</li>")
393 end
394
395 redef fun content do
396 append("<div class='content fullpage'>")
397 append("<h1>Full Index</h1>")
398 module_column
399 classes_column
400 properties_column
401 append("</div>")
402 end
403
404 # Add to content modules column
405 fun module_column do
406 var ls = new List[nullable MModule]
407 var sorted = mmodules
408 var sorterp = new ComparableSorter[MModule]
409 sorterp.sort(sorted)
410 append("<article class='modules filterable'></article>")
411 append("<h2>Modules</h2>")
412 append("<ul>")
413 for mmodule in sorted do
414 if mmodule.public_owner != null and not ls.has(mmodule.public_owner) then
415 ls.add(mmodule.public_owner)
416 append("<li><a href='{mmodule.public_owner.name}.html'>(mmodule.public_owner.name)</a></li>")
417 end
418 end
419 append("</ul>")
420 append("</article>")
421 end
422
423 # Add to content classes modules
424 fun classes_column do
425 var sorted = mmodules.first.imported_mclasses.to_a
426 var sorterp = new ComparableSorter[MClass]
427 sorterp.sort(sorted)
428 append("<article class='classes filterable'>")
429 append("<h2>Classes</h2>")
430 append("<ul>")
431 for mclass in sorted do
432 append("<li><a href='{mclass}.html'>(mclass.name)</a></li>")
433 end
434 append("</ul>")
435 append("</article>")
436 end
437
438 # Insert the properties column of fullindex page
439 fun properties_column do
440 append("<article class='classes filterable'>")
441 append("<h2>Properties</h2>")
442 append("<ul>")
443 var sorted_imported = mmodules.first.imported_methods.to_a
444 var sorted_redef = mmodules.first.redef_methods.to_a
445 var sorterp = new ComparableSorter[MProperty]
446 sorterp.sort(sorted_imported)
447 sorterp.sort(sorted_redef)
448
449 for method in sorted_imported do
450 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
451 append("<li class='intro'</li>")
452 append("<span title='introduction'>I</span>&nbsp;")
453 append("<a href='{method.local_class.name}.html' title=''>{method.name} ({method.local_class.name})</a>")
454 append("</li>")
455 end
456
457 for method in sorted_redef do
458 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
459 append("<li class='redef'>")
460 append("<span title='redefinition'>R</span>&nbsp;")
461 append("<a href='{method.local_class.name}.html' title=''>{method.name} ({method.local_class.name})</span>")
462 append("</li>")
463 end
464 append("</ul>")
465 append("</article>")
466 end
467
468 end
469
470 # A module page
471 class NitdocModule
472 super NitdocPage
473
474 private var mmodule: MModule
475 private var mbuilder: ModelBuilder
476
477 init(mmodule: MModule, mbuilder: ModelBuilder, dot_dir: nullable String) do
478 self.mmodule = mmodule
479 self.mbuilder = mbuilder
480 self.dot_dir = dot_dir
481 end
482
483 redef fun head do
484 super
485 var amodule = mbuilder.mmodule2nmodule[mmodule]
486 append("<title>{mmodule.name} module | {amodule.short_comment}</title>")
487 end
488
489 redef fun menu do
490 append("<li><a href='index.html'>Overview</a></li>")
491 append("<li class='current'>{mmodule.name}</li>")
492 append("<li><a href='full-index.html'>Full Index</a></li>")
493 end
494
495 redef fun content do
496 sidebar
497 append("<div class='content'>")
498 append("<h1>{mmodule.name}</h1>")
499 append("<div class='subtitle'>{mmodule.html_signature(mbuilder)}</div>")
500 append(mmodule.html_full_comment(mbuilder))
501 process_generate_dot
502 classes
503 properties
504 append("</div>")
505 end
506
507 fun process_generate_dot do
508 var name = "dep_{mmodule.name}"
509 var op = new Buffer
510 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")
511 for m in mmodule.in_importation.poset do
512 var public_owner = m.public_owner
513 if public_owner == null then
514 public_owner = m
515 if m == mmodule then
516 op.append("\"{m.name}\"[shape=box,margin=0.03];\n")
517 else
518 op.append("\"{m.name}\"[URL=\"{m.name}.html\"];\n")
519 end
520 end
521 for imported in m.in_importation.direct_greaters do
522 if imported.public_owner == null then
523 op.append("\"{public_owner.name}\"->\"{imported.name}\";\n")
524 end
525 end
526 end
527 op.append("\}\n")
528 generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
529 end
530
531 fun sidebar do
532 var amodule = mbuilder.mmodule2nmodule[mmodule]
533 append("<div class='menu'>")
534 append("<nav>")
535 append("<h3>Module Hierarchy</h3>")
536 var dependencies = new Array[MModule]
537 for dep in mmodule.in_importation.greaters do
538 if dep == mmodule or dep.public_owner != null then continue
539 dependencies.add(dep)
540 end
541 if dependencies.length > 0 then
542 append("<h4>All dependencies</h4>")
543 display_module_list(dependencies)
544 end
545 var clients = new Array[MModule]
546 for dep in mmodule.in_importation.smallers do
547 if dep == mmodule or dep.public_owner != null then continue
548 clients.add(dep)
549 end
550 if clients.length > 0 then
551 append("<h4>All clients</h4>")
552 display_module_list(clients)
553 end
554 append("</nav>")
555 if mmodule.in_nesting.direct_greaters.length > 0 then
556 append("<nav>")
557 append("<h3>Nested Modules</h3>")
558 display_module_list(mmodule.in_nesting.direct_greaters.to_a)
559 append("</nav>")
560 end
561 append("</div>")
562 end
563
564 private fun display_module_list(list: Array[MModule]) do
565 append("<ul>")
566 var sorter = new ComparableSorter[MModule]
567 sorter.sort(list)
568 for m in list do append("<li>{m.link(mbuilder)}</li>")
569 append("</ul>")
570 end
571
572 # display the class column
573 fun classes do
574 var amodule = mbuilder.mmodule2nmodule[mmodule]
575 var intro_mclasses = mmodule.intro_mclasses
576 var redef_mclasses = mmodule.redef_mclasses
577 var all_mclasses = new HashSet[MClass]
578 for m in mmodule.in_nesting.greaters do
579 all_mclasses.add_all(m.intro_mclasses)
580 all_mclasses.add_all(m.redef_mclasses)
581 end
582 all_mclasses.add_all(intro_mclasses)
583 all_mclasses.add_all(redef_mclasses)
584
585 var sorted = new Array[MClass]
586 sorted.add_all(all_mclasses)
587 var sorter = new ComparableSorter[MClass]
588 sorter.sort(sorted)
589 append("<div class='module'>")
590 append("<article class='classes filterable'>")
591 append("<h2>Classes</h2>")
592 append("<ul>")
593 for c in sorted do
594 if redef_mclasses.has(c) and c.intro_mmodule.public_owner != mmodule then
595 append("<li class='redef'>")
596 append("<span title='refined in this module'>R </span>")
597 else
598 append("<li class='intro'>")
599 append("<span title='introduced in this module'>I </span>")
600 end
601 append(c.link(mbuilder))
602 append("</li>")
603 end
604 append("</ul>")
605 append("</article>")
606 append("</div>")
607 end
608
609 # display the property column
610 fun properties do
611 # get properties
612 var amodule = mbuilder.mmodule2nmodule[mmodule]
613 var mpropdefs = new HashSet[MPropDef]
614 for m in mmodule.in_nesting.greaters do
615 for c in m.mclassdefs do mpropdefs.add_all(c.mpropdefs)
616 end
617 for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs)
618 var sorted = mpropdefs.to_a
619 var sorter = new ComparableSorter[MPropDef]
620 sorter.sort(sorted)
621 # display properties in one column
622 append("<article class='properties filterable'>")
623 append("<h2>Properties</h2>")
624 append("<ul>")
625 for mprop in sorted do
626 if mprop isa MAttributeDef then continue
627 if mprop.mproperty.visibility <= none_visibility then continue
628 append(mprop.html_list_item(mbuilder))
629 end
630 append("</ul>")
631 append("</article>")
632 end
633 end
634
635 # A class page
636 class NitdocClass
637 super NitdocPage
638
639 private var mclass: MClass
640 private var mbuilder: ModelBuilder
641 private var nitdoc: Nitdoc
642 private var vtypes = new HashSet[MVirtualTypeDef]
643 private var consts = new HashSet[MMethodDef]
644 private var meths = new HashSet[MMethodDef]
645 private var inherited = new HashSet[MPropDef]
646
647 init(mclass: MClass, nitdoc: Nitdoc, dot_dir: nullable String, source: nullable String) do
648 self.mclass = mclass
649 self.mbuilder = nitdoc.modelbuilder
650 self.nitdoc = nitdoc
651 self.dot_dir = dot_dir
652 self.source = source
653 # load properties
654 for mclassdef in mclass.mclassdefs do
655 for mpropdef in mclassdef.mpropdefs do
656 if mpropdef.mproperty.visibility <= none_visibility then continue
657 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
658 if mpropdef isa MMethodDef then
659 if mpropdef.mproperty.is_init then
660 consts.add(mpropdef)
661 else
662 meths.add(mpropdef)
663 end
664 end
665 end
666 end
667 # get inherited properties
668 for mprop in mclass.inherited_mproperties do
669 var mpropdef = mprop.intro
670 if mprop.visibility <= none_visibility then continue
671 if mprop.intro_mclassdef.mclass.name == "Object" then continue
672 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
673 if mpropdef isa MMethodDef then
674 if mpropdef.mproperty.is_init then
675 consts.add(mpropdef)
676 else
677 meths.add(mpropdef)
678 end
679 end
680 inherited.add(mpropdef)
681 end
682 end
683
684 redef fun head do
685 super
686 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
687 if nclass isa AStdClassdef then
688 append("<title>{mclass.name} class | {nclass.short_comment}</title>")
689 else
690 append("<title>{mclass.name} class</title>")
691 end
692 end
693
694 redef fun menu do
695 append("<li><a href='index.html'>Overview</a></li>")
696 var public_owner = mclass.public_owner
697 if public_owner is null then
698 append("<li>{mclass.intro_mmodule.link(mbuilder)}</li>")
699 else
700 append("<li>{public_owner.link(mbuilder)}</li>")
701 end
702 append("<li class='current'>{mclass.name}</li>")
703 append("<li><a href='full-index.html'>Full Index</a></li>")
704 end
705
706 redef fun content do
707 append("<div class='menu'>")
708 properties_column
709 inheritance_column
710 append("</div>")
711 append("<div class='content'>")
712 class_doc
713 append("</div>")
714 end
715
716 fun properties_column do
717 var sorter = new ComparableSorter[MPropDef]
718 append("<nav class='properties filterable'>")
719 append("<h3>Properties</h3>")
720 # virtual types
721 if vtypes.length > 0 then
722 var vts = new Array[MVirtualTypeDef]
723 vts.add_all(vtypes)
724 sorter.sort(vts)
725 append("<h4>Virtual Types</h4>")
726 append("<ul>")
727 for mprop in vts do
728 append(mprop.html_sidebar_item(self))
729 end
730 append("</ul>")
731 end
732 # constructors
733 if consts.length > 0 then
734 var cts = new Array[MMethodDef]
735 cts.add_all(consts)
736 sorter.sort(cts)
737 append("<h4>Constructors</h4>")
738 append("<ul>")
739 for mprop in cts do
740 append(mprop.html_sidebar_item(self))
741 end
742 append("</ul>")
743 end
744 # methods
745 if meths.length > 0 then
746 var mts = new Array[MMethodDef]
747 mts.add_all(meths)
748 sorter.sort(mts)
749 append("<h4>Methods</h4>")
750 append("<ul>")
751 for mprop in mts do
752 append(mprop.html_sidebar_item(self))
753 end
754 append("</ul>")
755 end
756 append("</nav>")
757 end
758
759 fun inheritance_column do
760 var sorted = new Array[MClass]
761 var sorterp = new ComparableSorter[MClass]
762 append("<nav>")
763 append("<h3>Inheritance</h3>")
764 if mclass.ancestors.length > 1 then
765 sorted = mclass.ancestors.to_a
766 sorterp.sort(sorted)
767 append("<h4>Superclasses</h4>")
768 append("<ul>")
769 for sup in sorted do
770 if sup == mclass then continue
771 append("<li><a href='{sup.name}.html'>{sup.name}</a></li>")
772 end
773 append("</ul>")
774 end
775
776 if mclass.descendants.length <= 1 then
777 append("<h4>No Known Subclasses</h4>")
778 else if mclass.descendants.length <= 100 then
779 sorted = mclass.descendants.to_a
780 sorterp.sort(sorted)
781 append("<h4>Subclasses</h4>")
782 append("<ul>")
783 for sub in sorted do
784 if sub == mclass then continue
785 append("<li><a href='{sub.name}.html'>{sub.name}</a></li>")
786 end
787 append("</ul>")
788 else if mclass.children.length <= 100 then
789 sorted = mclass.children.to_a
790 sorterp.sort(sorted)
791 append("<h4>Direct Subclasses Only</h4>")
792 append("<ul>")
793 for sub in sorted do
794 if sub == mclass then continue
795 append("<li><a href='{sub.name}.html'>{sub.name}</a></li>")
796 end
797 append("</ul>")
798 else
799 append("<h4>Too much Subclasses to list</h4>")
800 end
801 append("</nav>")
802 end
803
804 fun class_doc do
805 # title
806 append("<h1>{mclass.html_signature}</h1>")
807 append("<div class='subtitle'>")
808 var subtitle = ""
809 if mclass.visibility is none_visibility then subtitle += "private "
810 subtitle += "{mclass.kind} {mclass.public_owner.html_namespace(mbuilder)}::{mclass}"
811 append(subtitle)
812 append("</div>")
813 # comment
814 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
815 append("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
816 append("<section class='description'>")
817 if nclass isa AStdClassdef and not nclass.comment.is_empty then append("<pre class=\"text_label\" title=\"122\" name=\"\" tag=\"{mclass.mclassdefs.first.location.to_s}\" type=\"2\">{nclass.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>")
818 process_generate_dot
819 append("</section>")
820 # concerns
821 var sorted = new Array[MModule]
822 sorted.add_all(mclass.concerns.keys)
823 var sorterp = new ComparableSorter[MModule]
824 sorterp.sort(sorted)
825 append("<section class='concerns'>")
826 append("<h2 class='section-header'>Concerns</h2>")
827 append("<ul>")
828 for owner in sorted do
829 var nmodule = mbuilder.mmodule2nmodule[owner]
830 var childs = mclass.concerns[owner]
831 append("<li>")
832 append("<a href=\"#MOD_{owner.name}\">{owner.name}</a>: {nmodule.short_comment}")
833 if not childs is null then
834 append("<ul>")
835 var sortedc = childs.to_a
836 var sorterpc = new ComparableSorter[MModule]
837 sorterpc.sort(sortedc)
838 for child in sortedc do
839 var nchild = mbuilder.mmodule2nmodule[child]
840 append("<li><a href=\"#MOD_{child.name}\">{child.name}</a>: {nchild.short_comment} </li>")
841 end
842 append("</ul>")
843 end
844 append("</li>")
845 end
846 append("</ul>")
847 append("</section>")
848 # properties
849 var sorterprop = new ComparableSorter[MProperty]
850 var sorterc = new ComparableSorter[MClass]
851 var lmmodule = new List[MModule]
852 # virtual and formal types
853 if mclass.virtual_types.length > 0 or mclass.arity > 0 then
854 append("<section class='types'>")
855 append("<h2>Formal and Virtual Types</h2>")
856 if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop)
857 if mclass.arity > 0 and nclass isa AStdClassdef then
858 for ft, bound in mclass.parameter_types do
859 append("<article id='{ft}'>")
860 append("<h3 class='signature'>{ft}: {bound.link(mbuilder)}</h3>")
861 append("<div class=\"info\">formal generic type</div>")
862 append("</article>")
863 end
864 end
865 append("</section>")
866 end
867 # constructors
868 if mclass.constructors.length > 0 then
869 var sortedc = mclass.constructors.to_a
870 sorterprop.sort(sortedc)
871 append("<section class='constructors'>")
872 append("<h2 class='section-header'>Constructors</h2>")
873 for prop in sortedc do description(prop)
874 append("</section>")
875 end
876 # methods
877 append("<section class='methods'>")
878 append("<h2 class='section-header'>Methods</h2>")
879 for mmodule, mmethods in mclass.all_methods do
880 var nmodule = mbuilder.mmodule2nmodule[mmodule]
881 append("<a id=\"MOD_{mmodule.name}\"></a>")
882 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
883 if mclass.has_mmodule(mmodule) then
884 append("<p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
885 else
886 append("<h3 class=\"concern-toplevel\">Methods refined in {mmodule.link(mbuilder)}</h3><p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
887 end
888 end
889 var sortedc = mmethods.to_a
890 sorterprop.sort(sortedc)
891 for prop in sortedc do description(prop)
892 end
893 # inherited methods
894 if mclass.inherited_methods.length > 0 then
895 var sortedc = new Array[MClass]
896 sortedc.add_all(mclass.inherited.keys)
897 sorterc.sort(sortedc)
898 append("<h3>Inherited Methods</h3>")
899 for imclass in sortedc do
900 var sortedp = mclass.inherited[imclass].to_a
901 sorterprop.sort(sortedp)
902 append("<p>Defined in {imclass.link(mbuilder)}: ")
903 for method in sortedp do
904 #TODO link to inherited propdef
905 append("<a href=\"\">{method.name}</a>")
906 if method != sortedp.last then append(", ")
907 end
908 append("</p>")
909 end
910 end
911 append("</section>")
912 end
913
914 fun description(prop: MProperty) do
915 if not mbuilder.mpropdef2npropdef.has_key(prop.intro) then return
916 var nprop = mbuilder.mpropdef2npropdef[prop.intro]
917 if not nprop isa AMethPropdef then return
918 var classes = new Array[String]
919 if nprop isa AInitPropdef then
920 classes.add("init")
921 else
922 classes.add("fun")
923 end
924 if prop.is_redef then classes.add("redef")
925 if prop.visibility == none_visibility then
926 classes.add("private")
927 else if prop.visibility == protected_visibility then
928 classes.add("protected")
929 else
930 classes.add("public")
931 end
932 append("<article class='{classes.join(" ")}' id='{prop.anchor}'>")
933 var sign = prop.name
934 append("<h3 class='signature'>{prop.name}{nprop.signature}</h3>")
935 append("<div class='info'>")
936 append("{if prop.is_redef then "redef" else ""} fun {prop.intro_mclassdef.namespace(mclass)}::{prop.name}</div><div style=\"float: right;\"><a id=\"lblDiffCommit\"></a>")
937 append("</div>")
938 append("<div class='description'>")
939 if nprop.comment == "" then
940 append("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
941 else
942 append("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
943 end
944 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>")
945 append("<p>")
946 if prop.local_class != mclass then
947 var mredef = prop.local_class.intro_mmodule
948 append("inherited from {mredef.link(mbuilder)} ")
949 end
950 #TODO display show code if doc github
951 var mintro = prop.intro_mclassdef.mmodule
952 append("defined by the module {mintro.link(mbuilder)}{if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
953
954 for parent in mclass.parents do
955 var mparent = parent.intro_mmodule
956 if prop isa MMethod then if parent.constructors.has(prop) then append(" Previously defined by: {mparent.link(mbuilder)} for <a href=\"{parent.name}.html\">{parent.name}</a>.")
957 end
958 append("</p>")
959 append("</div>")
960 append("</article>")
961 end
962
963 fun process_generate_dot do
964 var pe = nitdoc.class_hierarchy[mclass]
965 var cla = new HashSet[MClass]
966 var sm = new HashSet[MClass]
967 var sm2 = new HashSet[MClass]
968 sm.add(mclass)
969 while cla.length + sm.length < 10 and sm.length > 0 do
970 cla.add_all(sm)
971 sm2.clear
972 for x in sm do
973 sm2.add_all(pe.poset[x].direct_smallers)
974 end
975 var t = sm
976 sm = sm2
977 sm2 = t
978 end
979 cla.add_all(pe.greaters)
980
981 var op = new Buffer
982 var name = "dep_{mclass.name}"
983 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")
984 for c in cla do
985 if c == mclass then
986 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
987 else
988 op.append("\"{c.name}\"[URL=\"{c.name}.html\"];\n")
989 end
990 for c2 in pe.poset[c].direct_greaters do
991 if not cla.has(c2) then continue
992 op.append("\"{c.name}\"->\"{c2.name}\";\n")
993 end
994 if not pe.poset[c].direct_smallers.is_empty then
995 var others = true
996 for c2 in pe.poset[c].direct_smallers do
997 if cla.has(c2) then others = false
998 end
999 if others then
1000 op.append("\"{c.name}...\"[label=\"\"];\n")
1001 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1002 end
1003 end
1004 end
1005 op.append("\}\n")
1006 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1007 end
1008 end
1009
1010 redef class AModule
1011 private fun comment: String do
1012 var ret = new Buffer
1013 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
1014 if n_moduledecl.n_doc is null then return ""
1015 for t in n_moduledecl.n_doc.n_comment do
1016 ret.append(t.text.substring_from(1))
1017 end
1018 return ret.to_s.html_escape
1019 end
1020
1021 private fun short_comment: String do
1022 var ret = new Buffer
1023 if n_moduledecl != null and n_moduledecl.n_doc != null then
1024 ret.append(n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1025 end
1026 return ret.to_s.html_escape
1027 end
1028 end
1029
1030 redef class MModule
1031 super Comparable
1032 redef type OTHER: MModule
1033 redef fun <(other: OTHER): Bool do return self.name < other.name
1034
1035 # Get the list of all methods in a module
1036 fun imported_methods: Set[MMethod] do
1037 var methods = new HashSet[MMethod]
1038 for mclass in imported_mclasses do
1039 for method in mclass.intro_methods do
1040 methods.add(method)
1041 end
1042 end
1043 return methods
1044 end
1045
1046 # Get the list aof all refined methods in a module
1047 fun redef_methods: Set[MMethod] do
1048 var methods = new HashSet[MMethod]
1049 for mclass in redef_mclasses do
1050 for method in mclass.intro_methods do
1051 methods.add(method)
1052 end
1053 end
1054 return methods
1055 end
1056
1057 # Return a link (html a tag) to the nitdoc module page
1058 fun link(mbuilder: ModelBuilder): String do
1059 return "<a href='{name}.html' title='{mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>"
1060 end
1061
1062 # Return the module signature decorated with html
1063 fun html_signature(mbuilder: ModelBuilder): String do
1064 return "<span>module {html_namespace(mbuilder)}</span>"
1065 end
1066
1067 # Return the module namespace decorated with html
1068 fun html_namespace(mbuilder: ModelBuilder): String do
1069 var res = new Buffer
1070 res.append("<span>")
1071 var mowner = public_owner
1072 if mowner != null then
1073 res.append(public_owner.html_namespace(mbuilder))
1074 res.append("::")
1075 end
1076 res.append(self.link(mbuilder))
1077 res.append("</span>")
1078 return res.to_s
1079 end
1080
1081 # Return the full comment of the module decorated with html
1082 fun html_full_comment(mbuilder: ModelBuilder): String do
1083 var res = new Buffer
1084 res.append("<div id='description'>")
1085 res.append("<pre class='text_label'>{mbuilder.mmodule2nmodule[self].comment}</pre>")
1086 res.append("<textarea class='edit' rows='1' cols='76' id='fileContent'></textarea>")
1087 res.append("<a id='cancelBtn'>Cancel</a>")
1088 res.append("<a id='commitBtn'>Commit</a>")
1089 res.append("<pre class='text_label' id='preSave' type='2'></pre>")
1090 res.append("</div>")
1091 return res.to_s
1092 end
1093 end
1094 redef class MPropDef
1095 super Comparable
1096 redef type OTHER: MPropDef
1097 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1098
1099 # Return a link (html a tag) to the nitdoc class page
1100 fun link(mbuilder: ModelBuilder): String do
1101 if mbuilder.mpropdef2npropdef.has_key(self) then
1102 var nprop = mbuilder.mpropdef2npropdef[self]
1103 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>"
1104 else
1105 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\">{mproperty.name}</a>"
1106 end
1107 end
1108
1109 # Return a list item for the mpropdef
1110 fun html_list_item(mbuilder: ModelBuilder): String do
1111 var res = new Buffer
1112 if is_intro then
1113 res.append("<li class='intro'>")
1114 res.append("<span title='introduction'>I</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.name})")
1115 res.append("</li>")
1116 else
1117 res.append("<li class='redef'>")
1118 res.append("<span title='redefinition'>R</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.name})")
1119 res.append("</li>")
1120 end
1121 return res.to_s
1122 end
1123
1124 # Return a list item for the mpropdef
1125 fun html_sidebar_item(page: NitdocClass): String do
1126 var res = new Buffer
1127 if is_intro and mclassdef.mclass == page.mclass then
1128 res.append("<li class='intro'>")
1129 res.append("<span title='Introduced'>I</span>")
1130 else if is_intro and mclassdef.mclass != page.mclass then
1131 res.append("<li class='inherit'>")
1132 res.append("<span title='Inherited'>H</span>")
1133 else
1134 res.append("<li class='redef'>")
1135 res.append("<span title='Redefined'>R</span>")
1136 end
1137 res.append(link(page.mbuilder))
1138 res.append("</li>")
1139 return res.to_s
1140 end
1141 end
1142
1143 redef class MProperty
1144 super Comparable
1145 redef type OTHER: MProperty
1146 redef fun <(other: OTHER): Bool do return self.name < other.name
1147
1148 var is_redef: Bool
1149 var apropdef: nullable APropdef
1150
1151 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1152 do
1153 super
1154 is_redef = false
1155 end
1156
1157 fun local_class: MClass do
1158 var classdef = self.intro_mclassdef
1159 return classdef.mclass
1160 end
1161
1162 fun anchor: String do
1163 return "PROP_{c_name}"
1164 end
1165
1166 # Escape name for html output
1167 redef fun name do return super.html_escape
1168 end
1169
1170 redef class MClass
1171 super Comparable
1172 redef type OTHER: MClass
1173 redef fun <(other: OTHER): Bool do return self.name < other.name
1174
1175 # Add type parameters
1176 fun html_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 link(mbuilder: ModelBuilder): String do
1186 if mbuilder.mclassdef2nclassdef.has_key(intro) then
1187 var nclass = mbuilder.mclassdef2nclassdef[intro]
1188 if nclass isa AStdClassdef then
1189 return "<a href='{name}.html' title=\"{nclass.short_comment}\">{html_signature}</a>"
1190 else
1191 return "<a href='{name}.html'>{html_signature}</a>"
1192 end
1193 else
1194 return "<a href='{name}.html'>{html_signature}</a>"
1195 end
1196 end
1197
1198 # Associate all MMethods to each MModule concerns
1199 fun all_methods: HashMap[MModule, Set[MMethod]] do
1200 var hm = new HashMap[MModule, Set[MMethod]]
1201 for mmodule, childs in concerns do
1202 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1203 for prop in intro_methods do
1204 if mmodule == prop.intro_mclassdef.mmodule then
1205 prop.is_redef = false
1206 hm[mmodule].add(prop)
1207 end
1208 end
1209 for prop in redef_methods do
1210 if mmodule == prop.intro_mclassdef.mmodule then
1211 prop.is_redef = true
1212 hm[mmodule].add(prop)
1213 end
1214 end
1215
1216 if childs != null then
1217 for child in childs do
1218 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1219 for prop in intro_methods do
1220 if child == prop.intro_mclassdef.mmodule then
1221 prop.is_redef = false
1222 hm[child].add(prop)
1223 end
1224 end
1225 for prop in redef_methods do
1226 if child == prop.intro_mclassdef.mmodule then
1227 prop.is_redef = true
1228 hm[child].add(prop)
1229 end
1230 end
1231 end
1232 end
1233 end
1234 return hm
1235 end
1236
1237 fun public_owner: MModule do
1238 var owner = intro_mmodule
1239 if owner.public_owner is null then
1240 return owner
1241 else
1242 return owner.public_owner.as(not null)
1243 end
1244 end
1245
1246 # Associate MClass to all MMethod include in 'inherited_methods'
1247 fun inherited: HashMap[MClass, Set[MMethod]] do
1248 var hm = new HashMap[MClass, Set[MMethod]]
1249 for method in inherited_methods do
1250 var mclass = method.intro_mclassdef.mclass
1251 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1252 hm[mclass].add(method)
1253 end
1254 return hm
1255 end
1256
1257 # Return true if MModule concern contain subMModule
1258 fun has_mmodule(sub: MModule): Bool do
1259 for mmodule, childs in concerns do
1260 if childs is null then continue
1261 if childs.has(sub) then return true
1262 end
1263 return false
1264 end
1265
1266 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1267 for const in constructors do
1268 if mprop2npropdef.has_key(const)then
1269 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1270 end
1271 end
1272
1273 for intro in intro_methods do
1274 if mprop2npropdef.has_key(intro)then
1275 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1276 end
1277 end
1278
1279 for rd in redef_methods do
1280 if mprop2npropdef.has_key(rd)then
1281 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1282 end
1283 end
1284 end
1285
1286 fun link_anchor: String do
1287 return "{name}.html"
1288 end
1289
1290 # Escape name for html output
1291 redef fun name do return super.html_escape
1292 end
1293
1294 redef class AStdClassdef
1295 private fun comment: String do
1296 var ret = new Buffer
1297 if n_doc != null then
1298 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1299 end
1300 return ret.to_s.html_escape
1301 end
1302
1303 private fun short_comment: String do
1304 var ret = new Buffer
1305 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1306 return ret.to_s.html_escape
1307 end
1308 end
1309
1310 redef class ASignature
1311 redef fun to_s do
1312 #TODO closures
1313 var ret = ""
1314 if not n_params.is_empty then
1315 ret = "{ret}({n_params.join(", ")})"
1316 end
1317 if n_type != null and n_type.to_s != "" then ret += ": {n_type.to_s}"
1318 return ret
1319 end
1320 end
1321
1322 redef class AParam
1323 redef fun to_s do
1324 var ret = "{n_id.text}"
1325 if n_type != null then
1326 ret = "{ret}: {n_type.to_s}"
1327 if n_dotdotdot != null then ret = "{ret}..."
1328 end
1329 return ret
1330 end
1331 end
1332
1333 redef class MType
1334 fun link(mbuilder: ModelBuilder): String is abstract
1335 end
1336
1337 redef class MClassType
1338 redef fun link(mbuilder) do return mclass.link(mbuilder)
1339 end
1340
1341 redef class MNullableType
1342 redef fun link(mbuilder) do return "nullable {mtype.link(mbuilder)}"
1343 end
1344
1345 redef class AType
1346 fun link: String do
1347 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1348 if n_kwnullable != null then ret = "nullable {ret}"
1349 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1350 return ret
1351 end
1352
1353 fun name: String do return n_id.text.html_escape
1354 end
1355
1356 redef class APropdef
1357 private fun short_comment: String is abstract
1358 private fun signature: String is abstract
1359 private fun comment: String is abstract
1360 end
1361
1362 redef class AAttrPropdef
1363 redef fun short_comment do
1364 var ret = new Buffer
1365 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(1))
1366 return ret.to_s.html_escape
1367 end
1368 end
1369
1370 redef class AMethPropdef
1371 redef fun short_comment do
1372 var ret = new Buffer
1373 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1374 return ret.to_s.html_escape
1375 end
1376
1377 redef fun signature: String do
1378 var sign = ""
1379 if n_signature != null then sign = "{n_signature.to_s}"
1380 return sign
1381 end
1382
1383 redef private fun comment: String do
1384 var ret = new Buffer
1385 if n_doc != null then
1386 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1387 end
1388 return ret.to_s.html_escape
1389 end
1390 end
1391
1392 redef class MClassDef
1393 private fun namespace(mclass: MClass): String do
1394
1395 if mmodule.public_owner is null then
1396 return "{mmodule.full_name}::{mclass.name}"
1397 else if mclass is self.mclass then
1398 return "{mmodule.public_owner.name}::{mclass.name}"
1399 else
1400 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1401 end
1402 end
1403 end
1404
1405 redef class Set[E]
1406 fun last: E do
1407 return to_a[length-1]
1408 end
1409 end
1410
1411 # Create a tool context to handle options and paths
1412 var toolcontext = new ToolContext
1413
1414 # Here we launch the nit index
1415 var nitdoc = new Nitdoc(toolcontext)
1416 nitdoc.start