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