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