ni_nitdoc: display VT 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
643 init(mclass: MClass, nitdoc: Nitdoc, dot_dir: nullable String, source: nullable String) do
644 self.mclass = mclass
645 self.mbuilder = nitdoc.modelbuilder
646 self.nitdoc = nitdoc
647 self.dot_dir = dot_dir
648 self.source = source
649 end
650
651 redef fun head do
652 super
653 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
654 if nclass isa AStdClassdef then
655 append("<title>{mclass.name} class | {nclass.short_comment}</title>")
656 else
657 append("<title>{mclass.name} class</title>")
658 end
659 end
660
661 redef fun menu do
662 append("<li><a href='index.html'>Overview</a></li>")
663 var public_owner = mclass.public_owner
664 if public_owner is null then
665 append("<li>{mclass.intro_mmodule.link(mbuilder)}</li>")
666 else
667 append("<li>{public_owner.link(mbuilder)}</li>")
668 end
669 append("<li class='current'>{mclass.name}</li>")
670 append("<li><a href='full-index.html'>Full Index</a></li>")
671 end
672
673 redef fun content do
674 append("<div class='menu'>")
675 properties_column
676 inheritance_column
677 append("</div>")
678 append("<div class='content'>")
679 class_doc
680 append("</div>")
681 end
682
683 fun properties_column do
684 var sorter = new ComparableSorter[MPropDef]
685 append("<nav class='properties filterable'>")
686 append("<h3>Properties</h3>")
687 # get intro and redef properties
688 var vtypes = new HashSet[MVirtualTypeDef]
689 var consts = new HashSet[MMethodDef]
690 var meths = new HashSet[MMethodDef]
691 for mclassdef in mclass.mclassdefs do
692 for mpropdef in mclassdef.mpropdefs do
693 if mpropdef.mproperty.visibility <= none_visibility then continue
694 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
695 if mpropdef isa MMethodDef then
696 if mpropdef.mproperty.is_init then
697 consts.add(mpropdef)
698 else
699 meths.add(mpropdef)
700 end
701 end
702 end
703 end
704 # get inherited properties
705 for mprop in mclass.inherited_methods do
706 var mpropdef = mprop.intro
707 if mprop.visibility <= none_visibility then continue
708 if mprop.intro_mclassdef.mclass.name == "Object" then continue
709 meths.add(mpropdef)
710 end
711 # virtual types
712 if vtypes.length > 0 then
713 var vts = new Array[MVirtualTypeDef]
714 vts.add_all(vtypes)
715 sorter.sort(vts)
716 append("<h4>Virtual Types</h4>")
717 append("<ul>")
718 for mprop in vts do
719 append(mprop.html_sidebar_item(self))
720 end
721 append("</ul>")
722 end
723 # constructors
724 if consts.length > 0 then
725 var cts = new Array[MMethodDef]
726 cts.add_all(consts)
727 sorter.sort(cts)
728 append("<h4>Constructors</h4>")
729 append("<ul>")
730 for mprop in cts do
731 append(mprop.html_sidebar_item(self))
732 end
733 append("</ul>")
734 end
735 # methods
736 if meths.length > 0 then
737 var mts = new Array[MMethodDef]
738 mts.add_all(meths)
739 sorter.sort(mts)
740 append("<h4>Methods</h4>")
741 append("<ul>")
742 for mprop in mts do
743 append(mprop.html_sidebar_item(self))
744 end
745 append("</ul>")
746 end
747 append("</nav>")
748 end
749
750 fun inheritance_column do
751 var sorted = new Array[MClass]
752 var sorterp = new ComparableSorter[MClass]
753 append("<nav>")
754 append("<h3>Inheritance</h3>")
755 if mclass.ancestors.length > 1 then
756 sorted = mclass.ancestors.to_a
757 sorterp.sort(sorted)
758 append("<h4>Superclasses</h4>")
759 append("<ul>")
760 for sup in sorted do
761 if sup == mclass then continue
762 append("<li><a href='{sup.name}.html'>{sup.name}</a></li>")
763 end
764 append("</ul>")
765 end
766
767 if mclass.descendants.length <= 1 then
768 append("<h4>No Known Subclasses</h4>")
769 else if mclass.descendants.length <= 100 then
770 sorted = mclass.descendants.to_a
771 sorterp.sort(sorted)
772 append("<h4>Subclasses</h4>")
773 append("<ul>")
774 for sub in sorted do
775 if sub == mclass then continue
776 append("<li><a href='{sub.name}.html'>{sub.name}</a></li>")
777 end
778 append("</ul>")
779 else if mclass.children.length <= 100 then
780 sorted = mclass.children.to_a
781 sorterp.sort(sorted)
782 append("<h4>Direct Subclasses Only</h4>")
783 append("<ul>")
784 for sub in sorted do
785 if sub == mclass then continue
786 append("<li><a href='{sub.name}.html'>{sub.name}</a></li>")
787 end
788 append("</ul>")
789 else
790 append("<h4>Too much Subclasses to list</h4>")
791 end
792 append("</nav>")
793 end
794
795 fun class_doc do
796 # title
797 append("<h1>{mclass.html_signature}</h1>")
798 append("<div class='subtitle'>")
799 var subtitle = ""
800 if mclass.visibility is none_visibility then subtitle += "private "
801 subtitle += "{mclass.kind} {mclass.public_owner.html_namespace(mbuilder)}::{mclass}"
802 append(subtitle)
803 append("</div>")
804 # comment
805 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
806 append("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
807 append("<section class='description'>")
808 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>")
809 process_generate_dot
810 append("</section>")
811 # concerns
812 var sorted = new Array[MModule]
813 sorted.add_all(mclass.concerns.keys)
814 var sorterp = new ComparableSorter[MModule]
815 sorterp.sort(sorted)
816 append("<section class='concerns'>")
817 append("<h2 class='section-header'>Concerns</h2>")
818 append("<ul>")
819 for owner in sorted do
820 var nmodule = mbuilder.mmodule2nmodule[owner]
821 var childs = mclass.concerns[owner]
822 append("<li>")
823 append("<a href=\"#MOD_{owner.name}\">{owner.name}</a>: {nmodule.short_comment}")
824 if not childs is null then
825 append("<ul>")
826 var sortedc = childs.to_a
827 var sorterpc = new ComparableSorter[MModule]
828 sorterpc.sort(sortedc)
829 for child in sortedc do
830 var nchild = mbuilder.mmodule2nmodule[child]
831 append("<li><a href=\"#MOD_{child.name}\">{child.name}</a>: {nchild.short_comment} </li>")
832 end
833 append("</ul>")
834 end
835 append("</li>")
836 end
837 append("</ul>")
838 append("</section>")
839 # properties
840 var sorterprop = new ComparableSorter[MProperty]
841 var sorterc = new ComparableSorter[MClass]
842 var lmmodule = new List[MModule]
843 # virtual and formal types
844 if mclass.virtual_types.length > 0 or mclass.arity > 0 then
845 append("<section class='types'>")
846 append("<h2>Formal and Virtual Types</h2>")
847 if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop)
848 if mclass.arity > 0 and nclass isa AStdClassdef then
849 for ft, bound in mclass.parameter_types do
850 append("<article id='{ft}'>")
851 append("<h3 class='signature'>{ft}: {bound.link(mbuilder)}</h3>")
852 append("<div class=\"info\">formal generic type</div>")
853 append("</article>")
854 end
855 end
856 append("</section>")
857 end
858 # constructors
859 if mclass.constructors.length > 0 then
860 var sortedc = mclass.constructors.to_a
861 sorterprop.sort(sortedc)
862 append("<section class='constructors'>")
863 append("<h2 class='section-header'>Constructors</h2>")
864 for prop in sortedc do description(prop)
865 append("</section>")
866 end
867 # methods
868 append("<section class='methods'>")
869 append("<h2 class='section-header'>Methods</h2>")
870 for mmodule, mmethods in mclass.all_methods do
871 var nmodule = mbuilder.mmodule2nmodule[mmodule]
872 append("<a id=\"MOD_{mmodule.name}\"></a>")
873 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
874 if mclass.has_mmodule(mmodule) then
875 append("<p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
876 else
877 append("<h3 class=\"concern-toplevel\">Methods refined in {mmodule.link(mbuilder)}</h3><p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
878 end
879 end
880 var sortedc = mmethods.to_a
881 sorterprop.sort(sortedc)
882 for prop in sortedc do description(prop)
883 end
884 # inherited methods
885 if mclass.inherited_methods.length > 0 then
886 var sortedc = new Array[MClass]
887 sortedc.add_all(mclass.inherited.keys)
888 sorterc.sort(sortedc)
889 append("<h3>Inherited Methods</h3>")
890 for imclass in sortedc do
891 var sortedp = mclass.inherited[imclass].to_a
892 sorterprop.sort(sortedp)
893 append("<p>Defined in {imclass.link(mbuilder)}: ")
894 for method in sortedp do
895 #TODO link to inherited propdef
896 append("<a href=\"\">{method.name}</a>")
897 if method != sortedp.last then append(", ")
898 end
899 append("</p>")
900 end
901 end
902 append("</section>")
903 end
904
905 fun description(prop: MProperty) do
906 if not mbuilder.mpropdef2npropdef.has_key(prop.intro) then return
907 var nprop = mbuilder.mpropdef2npropdef[prop.intro]
908 if not nprop isa AMethPropdef then return
909 var classes = new Array[String]
910 if nprop isa AInitPropdef then
911 classes.add("init")
912 else
913 classes.add("fun")
914 end
915 if prop.is_redef then classes.add("redef")
916 if prop.visibility == none_visibility then
917 classes.add("private")
918 else if prop.visibility == protected_visibility then
919 classes.add("protected")
920 else
921 classes.add("public")
922 end
923 append("<article class='{classes.join(" ")}' id='{prop.anchor}'>")
924 var sign = prop.name
925 append("<h3 class='signature'>{prop.name}{nprop.signature}</h3>")
926 append("<div class='info'>")
927 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>")
928 append("</div>")
929 append("<div class='description'>")
930 if nprop.comment == "" then
931 append("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
932 else
933 append("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
934 end
935 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>")
936 append("<p>")
937 if prop.local_class != mclass then
938 var mredef = prop.local_class.intro_mmodule
939 append("inherited from {mredef.link(mbuilder)} ")
940 end
941 #TODO display show code if doc github
942 var mintro = prop.intro_mclassdef.mmodule
943 append("defined by the module {mintro.link(mbuilder)}{if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
944
945 for parent in mclass.parents do
946 var mparent = parent.intro_mmodule
947 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>.")
948 end
949 append("</p>")
950 append("</div>")
951 append("</article>")
952 end
953
954 fun process_generate_dot do
955 var pe = nitdoc.class_hierarchy[mclass]
956 var cla = new HashSet[MClass]
957 var sm = new HashSet[MClass]
958 var sm2 = new HashSet[MClass]
959 sm.add(mclass)
960 while cla.length + sm.length < 10 and sm.length > 0 do
961 cla.add_all(sm)
962 sm2.clear
963 for x in sm do
964 sm2.add_all(pe.poset[x].direct_smallers)
965 end
966 var t = sm
967 sm = sm2
968 sm2 = t
969 end
970 cla.add_all(pe.greaters)
971
972 var op = new Buffer
973 var name = "dep_{mclass.name}"
974 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")
975 for c in cla do
976 if c == mclass then
977 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
978 else
979 op.append("\"{c.name}\"[URL=\"{c.name}.html\"];\n")
980 end
981 for c2 in pe.poset[c].direct_greaters do
982 if not cla.has(c2) then continue
983 op.append("\"{c.name}\"->\"{c2.name}\";\n")
984 end
985 if not pe.poset[c].direct_smallers.is_empty then
986 var others = true
987 for c2 in pe.poset[c].direct_smallers do
988 if cla.has(c2) then others = false
989 end
990 if others then
991 op.append("\"{c.name}...\"[label=\"\"];\n")
992 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
993 end
994 end
995 end
996 op.append("\}\n")
997 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
998 end
999 end
1000
1001 redef class AModule
1002 private fun comment: String do
1003 var ret = new Buffer
1004 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
1005 if n_moduledecl.n_doc is null then return ""
1006 for t in n_moduledecl.n_doc.n_comment do
1007 ret.append(t.text.substring_from(1))
1008 end
1009 return ret.to_s.html_escape
1010 end
1011
1012 private fun short_comment: String do
1013 var ret = new Buffer
1014 if n_moduledecl != null and n_moduledecl.n_doc != null then
1015 ret.append(n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1016 end
1017 return ret.to_s.html_escape
1018 end
1019 end
1020
1021 redef class MModule
1022 super Comparable
1023 redef type OTHER: MModule
1024 redef fun <(other: OTHER): Bool do return self.name < other.name
1025
1026 # Get the list of all methods in a module
1027 fun imported_methods: Set[MMethod] do
1028 var methods = new HashSet[MMethod]
1029 for mclass in imported_mclasses do
1030 for method in mclass.intro_methods do
1031 methods.add(method)
1032 end
1033 end
1034 return methods
1035 end
1036
1037 # Get the list aof all refined methods in a module
1038 fun redef_methods: Set[MMethod] do
1039 var methods = new HashSet[MMethod]
1040 for mclass in redef_mclasses do
1041 for method in mclass.intro_methods do
1042 methods.add(method)
1043 end
1044 end
1045 return methods
1046 end
1047
1048 # Return a link (html a tag) to the nitdoc module page
1049 fun link(mbuilder: ModelBuilder): String do
1050 return "<a href='{name}.html' title='{mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>"
1051 end
1052
1053 # Return the module signature decorated with html
1054 fun html_signature(mbuilder: ModelBuilder): String do
1055 return "<span>module {html_namespace(mbuilder)}</span>"
1056 end
1057
1058 # Return the module namespace decorated with html
1059 fun html_namespace(mbuilder: ModelBuilder): String do
1060 var res = new Buffer
1061 res.append("<span>")
1062 var mowner = public_owner
1063 if mowner != null then
1064 res.append(public_owner.html_namespace(mbuilder))
1065 res.append("::")
1066 end
1067 res.append(self.link(mbuilder))
1068 res.append("</span>")
1069 return res.to_s
1070 end
1071
1072 # Return the full comment of the module decorated with html
1073 fun html_full_comment(mbuilder: ModelBuilder): String do
1074 var res = new Buffer
1075 res.append("<div id='description'>")
1076 res.append("<pre class='text_label'>{mbuilder.mmodule2nmodule[self].comment}</pre>")
1077 res.append("<textarea class='edit' rows='1' cols='76' id='fileContent'></textarea>")
1078 res.append("<a id='cancelBtn'>Cancel</a>")
1079 res.append("<a id='commitBtn'>Commit</a>")
1080 res.append("<pre class='text_label' id='preSave' type='2'></pre>")
1081 res.append("</div>")
1082 return res.to_s
1083 end
1084 end
1085 redef class MPropDef
1086 super Comparable
1087 redef type OTHER: MPropDef
1088 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1089
1090 # Return a link (html a tag) to the nitdoc class page
1091 fun link(mbuilder: ModelBuilder): String do
1092 if mbuilder.mpropdef2npropdef.has_key(self) then
1093 var nprop = mbuilder.mpropdef2npropdef[self]
1094 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>"
1095 else
1096 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\">{mproperty.name}</a>"
1097 end
1098 end
1099
1100 # Return a list item for the mpropdef
1101 fun html_list_item(mbuilder: ModelBuilder): String do
1102 var res = new Buffer
1103 if is_intro then
1104 res.append("<li class='intro'>")
1105 res.append("<span title='introduction'>I</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.name})")
1106 res.append("</li>")
1107 else
1108 res.append("<li class='redef'>")
1109 res.append("<span title='redefinition'>R</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.name})")
1110 res.append("</li>")
1111 end
1112 return res.to_s
1113 end
1114
1115 # Return a list item for the mpropdef
1116 fun html_sidebar_item(page: NitdocClass): String do
1117 var res = new Buffer
1118 if is_intro and mclassdef.mclass == page.mclass then
1119 res.append("<li class='intro'>")
1120 res.append("<span title='Introduced'>I</span>")
1121 else if is_intro and mclassdef.mclass != page.mclass then
1122 res.append("<li class='inherit'>")
1123 res.append("<span title='Inherited'>H</span>")
1124 else
1125 res.append("<li class='redef'>")
1126 res.append("<span title='Redefined'>R</span>")
1127 end
1128 res.append(link(page.mbuilder))
1129 res.append("</li>")
1130 return res.to_s
1131 end
1132 end
1133
1134 redef class MProperty
1135 super Comparable
1136 redef type OTHER: MProperty
1137 redef fun <(other: OTHER): Bool do return self.name < other.name
1138
1139 var is_redef: Bool
1140 var apropdef: nullable APropdef
1141
1142 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1143 do
1144 super
1145 is_redef = false
1146 end
1147
1148 fun local_class: MClass do
1149 var classdef = self.intro_mclassdef
1150 return classdef.mclass
1151 end
1152
1153 fun anchor: String do
1154 return "PROP_{c_name}"
1155 end
1156
1157 # Escape name for html output
1158 redef fun name do return super.html_escape
1159 end
1160
1161 redef class MClass
1162 super Comparable
1163 redef type OTHER: MClass
1164 redef fun <(other: OTHER): Bool do return self.name < other.name
1165
1166 # Add type parameters
1167 fun html_signature: String do
1168 if arity > 0 then
1169 return "{name}[{intro.parameter_names.join(", ")}]"
1170 else
1171 return name
1172 end
1173 end
1174
1175 # Return a link (html a tag) to the nitdoc class page
1176 fun link(mbuilder: ModelBuilder): String do
1177 if mbuilder.mclassdef2nclassdef.has_key(intro) then
1178 var nclass = mbuilder.mclassdef2nclassdef[intro]
1179 if nclass isa AStdClassdef then
1180 return "<a href='{name}.html' title=\"{nclass.short_comment}\">{html_signature}</a>"
1181 else
1182 return "<a href='{name}.html'>{html_signature}</a>"
1183 end
1184 else
1185 return "<a href='{name}.html'>{html_signature}</a>"
1186 end
1187 end
1188
1189 # Associate all MMethods to each MModule concerns
1190 fun all_methods: HashMap[MModule, Set[MMethod]] do
1191 var hm = new HashMap[MModule, Set[MMethod]]
1192 for mmodule, childs in concerns do
1193 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1194 for prop in intro_methods do
1195 if mmodule == prop.intro_mclassdef.mmodule then
1196 prop.is_redef = false
1197 hm[mmodule].add(prop)
1198 end
1199 end
1200 for prop in redef_methods do
1201 if mmodule == prop.intro_mclassdef.mmodule then
1202 prop.is_redef = true
1203 hm[mmodule].add(prop)
1204 end
1205 end
1206
1207 if childs != null then
1208 for child in childs do
1209 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1210 for prop in intro_methods do
1211 if child == prop.intro_mclassdef.mmodule then
1212 prop.is_redef = false
1213 hm[child].add(prop)
1214 end
1215 end
1216 for prop in redef_methods do
1217 if child == prop.intro_mclassdef.mmodule then
1218 prop.is_redef = true
1219 hm[child].add(prop)
1220 end
1221 end
1222 end
1223 end
1224 end
1225 return hm
1226 end
1227
1228 fun public_owner: MModule do
1229 var owner = intro_mmodule
1230 if owner.public_owner is null then
1231 return owner
1232 else
1233 return owner.public_owner.as(not null)
1234 end
1235 end
1236
1237 # Associate MClass to all MMethod include in 'inherited_methods'
1238 fun inherited: HashMap[MClass, Set[MMethod]] do
1239 var hm = new HashMap[MClass, Set[MMethod]]
1240 for method in inherited_methods do
1241 var mclass = method.intro_mclassdef.mclass
1242 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1243 hm[mclass].add(method)
1244 end
1245 return hm
1246 end
1247
1248 # Return true if MModule concern contain subMModule
1249 fun has_mmodule(sub: MModule): Bool do
1250 for mmodule, childs in concerns do
1251 if childs is null then continue
1252 if childs.has(sub) then return true
1253 end
1254 return false
1255 end
1256
1257 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1258 for const in constructors do
1259 if mprop2npropdef.has_key(const)then
1260 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1261 end
1262 end
1263
1264 for intro in intro_methods do
1265 if mprop2npropdef.has_key(intro)then
1266 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1267 end
1268 end
1269
1270 for rd in redef_methods do
1271 if mprop2npropdef.has_key(rd)then
1272 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1273 end
1274 end
1275 end
1276
1277 fun link_anchor: String do
1278 return "{name}.html"
1279 end
1280
1281 # Escape name for html output
1282 redef fun name do return super.html_escape
1283 end
1284
1285 redef class AStdClassdef
1286 private fun comment: String do
1287 var ret = new Buffer
1288 if n_doc != null then
1289 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1290 end
1291 return ret.to_s.html_escape
1292 end
1293
1294 private fun short_comment: String do
1295 var ret = new Buffer
1296 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1297 return ret.to_s.html_escape
1298 end
1299 end
1300
1301 redef class ASignature
1302 redef fun to_s do
1303 #TODO closures
1304 var ret = ""
1305 if not n_params.is_empty then
1306 ret = "{ret}({n_params.join(", ")})"
1307 end
1308 if n_type != null and n_type.to_s != "" then ret += ": {n_type.to_s}"
1309 return ret
1310 end
1311 end
1312
1313 redef class AParam
1314 redef fun to_s do
1315 var ret = "{n_id.text}"
1316 if n_type != null then
1317 ret = "{ret}: {n_type.to_s}"
1318 if n_dotdotdot != null then ret = "{ret}..."
1319 end
1320 return ret
1321 end
1322 end
1323
1324 redef class MType
1325 fun link(mbuilder: ModelBuilder): String is abstract
1326 end
1327
1328 redef class MClassType
1329 redef fun link(mbuilder) do return mclass.link(mbuilder)
1330 end
1331
1332 redef class MNullableType
1333 redef fun link(mbuilder) do return "nullable {mtype.link(mbuilder)}"
1334 end
1335
1336 redef class AType
1337 fun link: String do
1338 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1339 if n_kwnullable != null then ret = "nullable {ret}"
1340 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1341 return ret
1342 end
1343
1344 fun name: String do return n_id.text.html_escape
1345 end
1346
1347 redef class APropdef
1348 private fun short_comment: String is abstract
1349 private fun signature: String is abstract
1350 private fun comment: String is abstract
1351 end
1352
1353 redef class AAttrPropdef
1354 redef fun short_comment do
1355 var ret = new Buffer
1356 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(1))
1357 return ret.to_s.html_escape
1358 end
1359 end
1360
1361 redef class AMethPropdef
1362 redef fun short_comment do
1363 var ret = new Buffer
1364 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1365 return ret.to_s.html_escape
1366 end
1367
1368 redef fun signature: String do
1369 var sign = ""
1370 if n_signature != null then sign = "{n_signature.to_s}"
1371 return sign
1372 end
1373
1374 redef private fun comment: String do
1375 var ret = new Buffer
1376 if n_doc != null then
1377 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1378 end
1379 return ret.to_s.html_escape
1380 end
1381 end
1382
1383 redef class MClassDef
1384 private fun namespace(mclass: MClass): String do
1385
1386 if mmodule.public_owner is null then
1387 return "{mmodule.full_name}::{mclass.name}"
1388 else if mclass is self.mclass then
1389 return "{mmodule.public_owner.name}::{mclass.name}"
1390 else
1391 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1392 end
1393 end
1394 end
1395
1396 redef class Set[E]
1397 fun last: E do
1398 return to_a[length-1]
1399 end
1400 end
1401
1402 # Create a tool context to handle options and paths
1403 var toolcontext = new ToolContext
1404
1405 # Here we launch the nit index
1406 var nitdoc = new Nitdoc(toolcontext)
1407 nitdoc.start