ni_nitdoc: reorganize classes for better readability
[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 #
1011 # Model redefs
1012 #
1013
1014 redef class MModule
1015 super Comparable
1016 redef type OTHER: MModule
1017 redef fun <(other: OTHER): Bool do return self.name < other.name
1018
1019 # Get the list of all methods in a module
1020 fun imported_methods: Set[MMethod] do
1021 var methods = new HashSet[MMethod]
1022 for mclass in imported_mclasses do
1023 for method in mclass.intro_methods do
1024 methods.add(method)
1025 end
1026 end
1027 return methods
1028 end
1029
1030 # Get the list aof all refined methods in a module
1031 fun redef_methods: Set[MMethod] do
1032 var methods = new HashSet[MMethod]
1033 for mclass in redef_mclasses do
1034 for method in mclass.intro_methods do
1035 methods.add(method)
1036 end
1037 end
1038 return methods
1039 end
1040
1041 # Return a link (html a tag) to the nitdoc module page
1042 fun link(mbuilder: ModelBuilder): String do
1043 return "<a href='{name}.html' title='{mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>"
1044 end
1045
1046 # Return the module signature decorated with html
1047 fun html_signature(mbuilder: ModelBuilder): String do
1048 return "<span>module {html_namespace(mbuilder)}</span>"
1049 end
1050
1051 # Return the module namespace decorated with html
1052 fun html_namespace(mbuilder: ModelBuilder): String do
1053 var res = new Buffer
1054 res.append("<span>")
1055 var mowner = public_owner
1056 if mowner != null then
1057 res.append(public_owner.html_namespace(mbuilder))
1058 res.append("::")
1059 end
1060 res.append(self.link(mbuilder))
1061 res.append("</span>")
1062 return res.to_s
1063 end
1064
1065 # Return the full comment of the module decorated with html
1066 fun html_full_comment(mbuilder: ModelBuilder): String do
1067 var res = new Buffer
1068 res.append("<div id='description'>")
1069 res.append("<pre class='text_label'>{mbuilder.mmodule2nmodule[self].comment}</pre>")
1070 res.append("<textarea class='edit' rows='1' cols='76' id='fileContent'></textarea>")
1071 res.append("<a id='cancelBtn'>Cancel</a>")
1072 res.append("<a id='commitBtn'>Commit</a>")
1073 res.append("<pre class='text_label' id='preSave' type='2'></pre>")
1074 res.append("</div>")
1075 return res.to_s
1076 end
1077 end
1078
1079 redef class MClass
1080 super Comparable
1081 redef type OTHER: MClass
1082 redef fun <(other: OTHER): Bool do return self.name < other.name
1083
1084 # Add type parameters
1085 fun html_signature: String do
1086 if arity > 0 then
1087 return "{name}[{intro.parameter_names.join(", ")}]"
1088 else
1089 return name
1090 end
1091 end
1092
1093 # Return a link (html a tag) to the nitdoc class page
1094 fun link(mbuilder: ModelBuilder): String do
1095 if mbuilder.mclassdef2nclassdef.has_key(intro) then
1096 var nclass = mbuilder.mclassdef2nclassdef[intro]
1097 if nclass isa AStdClassdef then
1098 return "<a href='{name}.html' title=\"{nclass.short_comment}\">{html_signature}</a>"
1099 else
1100 return "<a href='{name}.html'>{html_signature}</a>"
1101 end
1102 else
1103 return "<a href='{name}.html'>{html_signature}</a>"
1104 end
1105 end
1106
1107 # Associate all MMethods to each MModule concerns
1108 fun all_methods: HashMap[MModule, Set[MMethod]] do
1109 var hm = new HashMap[MModule, Set[MMethod]]
1110 for mmodule, childs in concerns do
1111 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1112 for prop in intro_methods do
1113 if mmodule == prop.intro_mclassdef.mmodule then
1114 prop.is_redef = false
1115 hm[mmodule].add(prop)
1116 end
1117 end
1118 for prop in redef_methods do
1119 if mmodule == prop.intro_mclassdef.mmodule then
1120 prop.is_redef = true
1121 hm[mmodule].add(prop)
1122 end
1123 end
1124
1125 if childs != null then
1126 for child in childs do
1127 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1128 for prop in intro_methods do
1129 if child == prop.intro_mclassdef.mmodule then
1130 prop.is_redef = false
1131 hm[child].add(prop)
1132 end
1133 end
1134 for prop in redef_methods do
1135 if child == prop.intro_mclassdef.mmodule then
1136 prop.is_redef = true
1137 hm[child].add(prop)
1138 end
1139 end
1140 end
1141 end
1142 end
1143 return hm
1144 end
1145
1146 fun public_owner: MModule do
1147 var owner = intro_mmodule
1148 if owner.public_owner is null then
1149 return owner
1150 else
1151 return owner.public_owner.as(not null)
1152 end
1153 end
1154
1155 # Associate MClass to all MMethod include in 'inherited_methods'
1156 fun inherited: HashMap[MClass, Set[MMethod]] do
1157 var hm = new HashMap[MClass, Set[MMethod]]
1158 for method in inherited_methods do
1159 var mclass = method.intro_mclassdef.mclass
1160 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1161 hm[mclass].add(method)
1162 end
1163 return hm
1164 end
1165
1166 # Return true if MModule concern contain subMModule
1167 fun has_mmodule(sub: MModule): Bool do
1168 for mmodule, childs in concerns do
1169 if childs is null then continue
1170 if childs.has(sub) then return true
1171 end
1172 return false
1173 end
1174
1175 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1176 for const in constructors do
1177 if mprop2npropdef.has_key(const)then
1178 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1179 end
1180 end
1181
1182 for intro in intro_methods do
1183 if mprop2npropdef.has_key(intro)then
1184 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1185 end
1186 end
1187
1188 for rd in redef_methods do
1189 if mprop2npropdef.has_key(rd)then
1190 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1191 end
1192 end
1193 end
1194
1195 fun link_anchor: String do
1196 return "{name}.html"
1197 end
1198
1199 # Escape name for html output
1200 redef fun name do return super.html_escape
1201 end
1202
1203 redef class MProperty
1204 super Comparable
1205 redef type OTHER: MProperty
1206 redef fun <(other: OTHER): Bool do return self.name < other.name
1207
1208 var is_redef: Bool
1209 var apropdef: nullable APropdef
1210
1211 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1212 do
1213 super
1214 is_redef = false
1215 end
1216
1217 fun local_class: MClass do
1218 var classdef = self.intro_mclassdef
1219 return classdef.mclass
1220 end
1221
1222 fun anchor: String do
1223 return "PROP_{c_name}"
1224 end
1225
1226 # Escape name for html output
1227 redef fun name do return super.html_escape
1228 end
1229
1230 redef class MType
1231 fun link(mbuilder: ModelBuilder): String is abstract
1232 end
1233
1234 redef class MClassType
1235 redef fun link(mbuilder) do return mclass.link(mbuilder)
1236 end
1237
1238 redef class MNullableType
1239 redef fun link(mbuilder) do return "nullable {mtype.link(mbuilder)}"
1240 end
1241
1242 redef class MClassDef
1243 private fun namespace(mclass: MClass): String do
1244
1245 if mmodule.public_owner is null then
1246 return "{mmodule.full_name}::{mclass.name}"
1247 else if mclass is self.mclass then
1248 return "{mmodule.public_owner.name}::{mclass.name}"
1249 else
1250 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1251 end
1252 end
1253 end
1254
1255 redef class MPropDef
1256 super Comparable
1257 redef type OTHER: MPropDef
1258 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1259
1260 # Return a link (html a tag) to the nitdoc class page
1261 fun link(mbuilder: ModelBuilder): String do
1262 if mbuilder.mpropdef2npropdef.has_key(self) then
1263 var nprop = mbuilder.mpropdef2npropdef[self]
1264 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>"
1265 else
1266 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\">{mproperty.name}</a>"
1267 end
1268 end
1269
1270 # Return a list item for the mpropdef
1271 fun html_list_item(mbuilder: ModelBuilder): String do
1272 var res = new Buffer
1273 if is_intro then
1274 res.append("<li class='intro'>")
1275 res.append("<span title='introduction'>I</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.name})")
1276 res.append("</li>")
1277 else
1278 res.append("<li class='redef'>")
1279 res.append("<span title='redefinition'>R</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.name})")
1280 res.append("</li>")
1281 end
1282 return res.to_s
1283 end
1284
1285 # Return a list item for the mpropdef
1286 fun html_sidebar_item(page: NitdocClass): String do
1287 var res = new Buffer
1288 if is_intro and mclassdef.mclass == page.mclass then
1289 res.append("<li class='intro'>")
1290 res.append("<span title='Introduced'>I</span>")
1291 else if is_intro and mclassdef.mclass != page.mclass then
1292 res.append("<li class='inherit'>")
1293 res.append("<span title='Inherited'>H</span>")
1294 else
1295 res.append("<li class='redef'>")
1296 res.append("<span title='Redefined'>R</span>")
1297 end
1298 res.append(link(page.mbuilder))
1299 res.append("</li>")
1300 return res.to_s
1301 end
1302 end
1303
1304 #
1305 # Nodes redefs
1306 #
1307
1308 redef class AModule
1309 private fun comment: String do
1310 var ret = new Buffer
1311 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
1312 if n_moduledecl.n_doc is null then return ""
1313 for t in n_moduledecl.n_doc.n_comment do
1314 ret.append(t.text.substring_from(1))
1315 end
1316 return ret.to_s.html_escape
1317 end
1318
1319 private fun short_comment: String do
1320 var ret = new Buffer
1321 if n_moduledecl != null and n_moduledecl.n_doc != null then
1322 ret.append(n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1323 end
1324 return ret.to_s.html_escape
1325 end
1326 end
1327
1328 redef class ASignature
1329 redef fun to_s do
1330 #TODO closures
1331 var ret = ""
1332 if not n_params.is_empty then
1333 ret = "{ret}({n_params.join(", ")})"
1334 end
1335 if n_type != null and n_type.to_s != "" then ret += ": {n_type.to_s}"
1336 return ret
1337 end
1338 end
1339
1340 redef class AParam
1341 redef fun to_s do
1342 var ret = "{n_id.text}"
1343 if n_type != null then
1344 ret = "{ret}: {n_type.to_s}"
1345 if n_dotdotdot != null then ret = "{ret}..."
1346 end
1347 return ret
1348 end
1349 end
1350
1351 redef class AType
1352 fun link: String do
1353 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1354 if n_kwnullable != null then ret = "nullable {ret}"
1355 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1356 return ret
1357 end
1358
1359 fun name: String do return n_id.text.html_escape
1360 end
1361
1362 redef class AStdClassdef
1363 private fun comment: String do
1364 var ret = new Buffer
1365 if n_doc != null then
1366 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1367 end
1368 return ret.to_s.html_escape
1369 end
1370
1371 private fun short_comment: String 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 end
1377
1378 redef class APropdef
1379 private fun short_comment: String is abstract
1380 private fun signature: String is abstract
1381 private fun comment: String is abstract
1382 end
1383
1384 redef class AAttrPropdef
1385 redef fun short_comment do
1386 var ret = new Buffer
1387 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(1))
1388 return ret.to_s.html_escape
1389 end
1390 end
1391
1392 redef class AMethPropdef
1393 redef fun short_comment do
1394 var ret = new Buffer
1395 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1396 return ret.to_s.html_escape
1397 end
1398
1399 redef fun signature: String do
1400 var sign = ""
1401 if n_signature != null then sign = "{n_signature.to_s}"
1402 return sign
1403 end
1404
1405 redef private fun comment: String do
1406 var ret = new Buffer
1407 if n_doc != null then
1408 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1409 end
1410 return ret.to_s.html_escape
1411 end
1412 end
1413
1414 # Create a tool context to handle options and paths
1415 var toolcontext = new ToolContext
1416
1417 # Here we launch the nit index
1418 var nitdoc = new Nitdoc(toolcontext)
1419 nitdoc.start