ni_nitdoc: added custom title, footer, overview and menu options
[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 private var opt_custom_title: OptionString = new OptionString("Title displayed in the top of the Overview page and as suffix of all page names", "--custom-title")
40 private var opt_custom_menu_items: OptionString = new OptionString("Items displayed in menu before the 'Overview' item (Each item must be enclosed in 'li' tags)", "--custom-menu-items")
41 private var opt_custom_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text")
42 private var opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text")
43 init(toolcontext: ToolContext) do
44 # We need a model to collect stufs
45 self.toolcontext = toolcontext
46 self.arguments = toolcontext.option_context.rest
47 toolcontext.option_context.options.clear
48 toolcontext.option_context.add_option(opt_dir)
49 toolcontext.option_context.add_option(opt_source)
50 toolcontext.option_context.add_option(opt_sharedir)
51 toolcontext.option_context.add_option(opt_nodot)
52 toolcontext.option_context.add_option(opt_custom_title)
53 toolcontext.option_context.add_option(opt_custom_footer_text)
54 toolcontext.option_context.add_option(opt_custom_overview_text)
55 toolcontext.option_context.add_option(opt_custom_menu_items)
56 toolcontext.process_options
57 process_options
58
59 if arguments.length < 1 then
60 toolcontext.option_context.usage
61 exit(1)
62 end
63
64 model = new Model
65 modelbuilder = new ModelBuilder(model, toolcontext)
66 # Here we load an process all modules passed on the command line
67 var mmodules = modelbuilder.parse_and_build(arguments)
68 modelbuilder.full_propdef_semantic_analysis
69 if mmodules.is_empty then return
70
71 if mmodules.length == 1 then
72 mainmodule = mmodules.first
73 else
74 # We need a main module, so we build it by importing all modules
75 mainmodule = new MModule(model, null, "<main>", new Location(null, 0, 0, 0, 0))
76 mainmodule.set_imported_mmodules(mmodules)
77 end
78 self.class_hierarchy = mainmodule.flatten_mclass_hierarchy
79 end
80
81 private fun process_options do
82 if not opt_dir.value is null then
83 output_dir = opt_dir.value
84 else
85 output_dir = "doc"
86 end
87 if not opt_sharedir.value is null then
88 share_dir = opt_sharedir.value
89 else
90 var dir = "NIT_DIR".environ
91 if dir.is_empty then
92 dir = "{sys.program_name.dirname}/../share/nitdoc"
93 else
94 dir = "{dir}/share/nitdoc"
95 end
96 share_dir = dir
97 if share_dir is null then
98 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
99 abort
100 end
101 dir = "{share_dir.to_s}/scripts/js-facilities.js"
102 if share_dir is null then
103 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
104 abort
105 end
106 end
107 if not opt_source.value is null then
108 source = ""
109 else
110 source = opt_source.value
111 end
112 end
113
114 fun start do
115 # Create destination dir if it's necessary
116 if not output_dir.file_exists then output_dir.mkdir
117 sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/")
118 self.dot_dir = null
119 if not opt_nodot.value then self.dot_dir = output_dir.to_s
120 overview
121 fullindex
122 modules
123 classes
124 quicksearch_list
125 end
126
127 fun overview do
128 var overviewpage = new NitdocOverview(self, dot_dir)
129 overviewpage.save("{output_dir.to_s}/index.html")
130 end
131
132 fun fullindex do
133 var fullindex = new NitdocFullindex(self)
134 fullindex.save("{output_dir.to_s}/full-index.html")
135 end
136
137 fun modules do
138 for mmodule in model.mmodules do
139 if mmodule.name == "<main>" then continue
140 var modulepage = new NitdocModule(mmodule, self, dot_dir)
141 modulepage.save("{output_dir.to_s}/{mmodule.url}")
142 end
143 end
144
145 fun classes do
146 for mclass in modelbuilder.model.mclasses do
147 var classpage = new NitdocClass(mclass, self, dot_dir, source)
148 classpage.save("{output_dir.to_s}/{mclass.url}")
149 end
150 end
151
152 # Generate QuickSearch file
153 fun quicksearch_list do
154 var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js")
155 var content = new Buffer
156 content.append("var entries = \{ ")
157
158 for mmodule in model.mmodules do
159 content.append("\"{mmodule.name}\": [")
160 content.append("\{txt: \"{mmodule.name}\", url:\"{mmodule.url}\" \},")
161 content.append("],")
162 end
163 for mclass in model.mclasses do
164 if mclass.visibility <= none_visibility then continue
165 content.append("\"{mclass.name}\": [")
166 content.append("\{txt: \"{mclass.name}\", url:\"{mclass.url}\" \},")
167 content.append("],")
168 end
169 var name2mprops = new HashMap[String, Set[MPropDef]]
170 for mproperty in model.mproperties do
171 if mproperty.visibility <= none_visibility then continue
172 if mproperty isa MAttribute then continue
173 if not name2mprops.has_key(mproperty.name) then name2mprops[mproperty.name] = new HashSet[MPropDef]
174 name2mprops[mproperty.name].add_all(mproperty.mpropdefs)
175 end
176 for mproperty, mpropdefs in name2mprops do
177 content.append("\"{mproperty}\": [")
178 for mpropdef in mpropdefs do
179 content.append("\{txt: \"{mpropdef.full_name}\", url:\"{mpropdef.url}\" \},")
180 end
181 content.append("],")
182 end
183
184 content.append(" \};")
185 file.write(content.to_s)
186 file.close
187 end
188
189 end
190
191 # Nitdoc base page
192 abstract class NitdocPage
193
194 var dot_dir: nullable String
195 var source: nullable String
196 var nitdoc: Nitdoc
197
198 init(nitdoc: Nitdoc) do
199 self.nitdoc = nitdoc
200 end
201
202 fun append(str: String) do html.append(str)
203 var html = new Buffer
204
205 fun head do
206 append("<meta charset='utf-8'/>")
207 append("<script type='text/javascript' src='scripts/jquery-1.7.1.min.js'></script>")
208 append("<script type='text/javascript' src='quicksearch-list.js'></script>")
209 append("<script type='text/javascript' src='scripts/js-facilities.js'></script>")
210 append("<link rel='stylesheet' href='styles/main.css' type='text/css' media='screen'/>")
211 var title = ""
212 if nitdoc.opt_custom_title.value != null then
213 title = " | {nitdoc.opt_custom_title.value.to_s}"
214 end
215 append("<title>{self.title}{title}</title>")
216 end
217
218 fun menu do
219 if nitdoc.opt_custom_menu_items.value != null then
220 append(nitdoc.opt_custom_menu_items.value.to_s)
221 end
222 end
223
224 fun title: String is abstract
225
226 fun header do
227 append("<header>")
228 append("<nav class='main'>")
229 append("<ul>")
230 menu
231 append("<li id='liGitHub'>")
232 append("<a class='btn' id='logGitHub'>")
233 append("<img id='imgGitHub' src='resources/icons/github-icon.png' alt='GitHub'/>")
234 append("</a>")
235 append("<div class='popover bottom'>")
236 append("<div class='arrow'>&nbsp;</div>")
237 append("<div class='githubTitle'>")
238 append("<h3>Github Sign In</h3>")
239 append("</div>")
240 append("<div>")
241 append("<label id='lbloginGit'>Username</label>")
242 append("<input id='loginGit' name='login' type='text'/>")
243 append("<label id='logginMessage'>Hello ")
244 append("<a id='githubAccount'><strong id='nickName'></strong></a>")
245 append("</label>")
246 append("</div>")
247 append("<div>")
248 append("<label id='lbpasswordGit'>Password</label>")
249 append("<input id='passwordGit' name='password' type='password'/>")
250 append("<div id='listBranches'>")
251 append("<label id='lbBranches'>Branch</label>")
252 append("<select class='dropdown' id='dropBranches' name='dropBranches' tabindex='1'></select>")
253 append("</div>")
254 append("</div>")
255 append("<div>")
256 append("<label id='lbrepositoryGit'>Repository</label>")
257 append("<input id='repositoryGit' name='repository' type='text'/>")
258 append("</div>")
259 append("<div>")
260 append("<label id='lbbranchGit'>Branch</label>")
261 append("<input id='branchGit' name='branch' type='text'/>")
262 append("</div>")
263 append("<div>")
264 append("<a id='signIn'>Sign In</a>")
265 append("</div>")
266 append("</div>")
267 append("</li>")
268 append("</ul>")
269 append("</nav>")
270 append("</header>")
271 end
272
273 fun content is abstract
274
275 fun footer do
276 if nitdoc.opt_custom_footer_text.value != null then
277 append("<footer>{nitdoc.opt_custom_footer_text.value.to_s}</footer>")
278 end
279 end
280
281 # Generate a clickable graphviz image using a dot content
282 fun generate_dot(dot: String, name: String, alt: String) do
283 var output_dir = dot_dir
284 if output_dir == null then return
285 var file = new OFStream.open("{output_dir}/{name}.dot")
286 file.write(dot)
287 file.close
288 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 ; \}")
289 append("<article class='graph'>")
290 append("<img src='{name}.png' usemap='#{name}' style='margin:auto' alt='{alt}'/>")
291 append("</article>")
292 var fmap = new IFStream.open("{output_dir}/{name}.map")
293 append(fmap.read_all)
294 fmap.close
295 end
296
297 # Add a (source) link for a given location
298 fun show_source(l: Location): String
299 do
300 if source == null then
301 return "({l.file.filename.simplify_path})"
302 else
303 # THIS IS JUST UGLY ! (but there is no replace yet)
304 var x = source.split_with("%f")
305 source = x.join(l.file.filename.simplify_path)
306 x = source.split_with("%l")
307 source = x.join(l.line_start.to_s)
308 x = source.split_with("%L")
309 source = x.join(l.line_end.to_s)
310 return " (<a href=\"{source.to_s}\">show code</a>)"
311 end
312 end
313
314 # Render the page as a html string
315 fun render: String do
316 append("<!DOCTYPE html>")
317 append("<head>")
318 head
319 append("</head>")
320 append("<body>")
321 header
322 append("<div class='page'>")
323 content
324 append("</div>")
325 footer
326 append("</body>")
327 return html.to_s
328 end
329
330 # Save html page in the specified file
331 fun save(file: String) do
332 var out = new OFStream.open(file)
333 out.write(render)
334 out.close
335 end
336 end
337
338 # The overview page
339 class NitdocOverview
340 super NitdocPage
341 private var mbuilder: ModelBuilder
342 private var mmodules = new Array[MModule]
343
344 init(nitdoc: Nitdoc, dot_dir: nullable String) do
345 super(nitdoc)
346 self.mbuilder = nitdoc.modelbuilder
347 self.dot_dir = dot_dir
348 # get modules
349 var mmodules = new HashSet[MModule]
350 for mmodule in mbuilder.model.mmodules do
351 if mmodule.name == "<main>" then continue
352 var owner = mmodule.public_owner
353 if owner != null then
354 mmodules.add(owner)
355 else
356 mmodules.add(mmodule)
357 end
358 end
359 # sort modules
360 var sorter = new ComparableSorter[MModule]
361 self.mmodules.add_all(mmodules)
362 sorter.sort(self.mmodules)
363 end
364
365 redef fun title do return "Overview"
366
367 redef fun menu do
368 super
369 append("<li class='current'>Overview</li>")
370 append("<li><a href='full-index.html'>Full Index</a></li>")
371 end
372
373 redef fun content do
374 append("<div class='content fullpage'>")
375 var title = "Overview"
376 if nitdoc.opt_custom_title.value != null then
377 title = nitdoc.opt_custom_title.value.to_s
378 end
379 append("<h1>{title}</h1>")
380 var text = ""
381 if nitdoc.opt_custom_overview_text.value != null then
382 text = nitdoc.opt_custom_overview_text.value.to_s
383 end
384 append("<article class='overview'>{text}</article>")
385 append("<article class='overview'>")
386 # module list
387 append("<h2>Modules</h2>")
388 append("<ul>")
389 for mmodule in mmodules do
390 if mbuilder.mmodule2nmodule.has_key(mmodule) then
391 var amodule = mbuilder.mmodule2nmodule[mmodule]
392 append("<li>{mmodule.link(mbuilder)}&nbsp;{amodule.short_comment}</li>")
393 end
394 end
395 append("</ul>")
396 # module graph
397 process_generate_dot
398 append("</article>")
399 append("</div>")
400 end
401
402 fun process_generate_dot do
403 var op = new Buffer
404 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")
405 for mmodule in mmodules do
406 op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
407 for imported in mmodule.in_importation.direct_greaters do
408 if imported.direct_owner == null then
409 op.append("\"{mmodule.name}\"->\"{imported.name}\";\n")
410 end
411 end
412 end
413 op.append("\}\n")
414 generate_dot(op.to_s, "dep", "Modules hierarchy")
415 end
416 end
417
418 # The full index page
419 class NitdocFullindex
420 super NitdocPage
421
422 init(nitdoc: Nitdoc) do
423 super(nitdoc)
424 self.dot_dir = null
425 end
426
427 redef fun title do return "Full Index"
428
429 redef fun menu do
430 super
431 append("<li><a href='index.html'>Overview</a></li>")
432 append("<li class='current'>Full Index</li>")
433 end
434
435 redef fun content do
436 append("<div class='content fullpage'>")
437 append("<h1>Full Index</h1>")
438 module_column
439 classes_column
440 properties_column
441 append("</div>")
442 end
443
444 # Add to content modules column
445 fun module_column do
446 var sorter = new ComparableSorter[MModule]
447 var sorted = new Array[MModule]
448 for mmodule in nitdoc.modelbuilder.model.mmodule_importation_hierarchy do
449 sorted.add(mmodule)
450 end
451 sorter.sort(sorted)
452 append("<article class='modules filterable'>")
453 append("<h2>Modules</h2>")
454 append("<ul>")
455 for mmodule in sorted do
456 append("<li>{mmodule.link(nitdoc.modelbuilder)}</li>")
457 end
458 append("</ul>")
459 append("</article>")
460 end
461
462 # Add to content classes modules
463 fun classes_column do
464 var sorted = nitdoc.modelbuilder.model.mclasses
465 var sorter = new ComparableSorter[MClass]
466 sorter.sort(sorted)
467 append("<article class='modules filterable'>")
468 append("<h2>Classes</h2>")
469 append("<ul>")
470 for mclass in sorted do
471 append("<li>{mclass.link(nitdoc.modelbuilder)}</li>")
472 end
473 append("</ul>")
474 append("</article>")
475 end
476
477 # Insert the properties column of fullindex page
478 fun properties_column do
479 var sorted = nitdoc.modelbuilder.model.mproperties
480 var sorter = new ComparableSorter[MProperty]
481 sorter.sort(sorted)
482 append("<article class='modules filterable'>")
483 append("<h2>Properties</h2>")
484 append("<ul>")
485 for mproperty in sorted do
486 if mproperty isa MAttribute then continue
487 append("<li>{mproperty.intro.link(nitdoc.modelbuilder)} ({mproperty.intro.mclassdef.mclass.link(nitdoc.modelbuilder)})</li>")
488 end
489 append("</ul>")
490 append("</article>")
491 end
492
493 end
494
495 # A module page
496 class NitdocModule
497 super NitdocPage
498
499 private var mmodule: MModule
500 private var mbuilder: ModelBuilder
501
502 init(mmodule: MModule, nitdoc: Nitdoc, dot_dir: nullable String) do
503 super(nitdoc)
504 self.mmodule = mmodule
505 self.mbuilder = nitdoc.modelbuilder
506 self.dot_dir = dot_dir
507 end
508
509 redef fun title do
510 if mbuilder.mmodule2nmodule.has_key(mmodule) then
511 var nmodule = mbuilder.mmodule2nmodule[mmodule]
512 return "{mmodule.name} module | {nmodule.short_comment}"
513 else
514 return "{mmodule.name} module"
515 end
516 end
517
518 redef fun menu do
519 super
520 append("<li><a href='index.html'>Overview</a></li>")
521 append("<li class='current'>{mmodule.name}</li>")
522 append("<li><a href='full-index.html'>Full Index</a></li>")
523 end
524
525 redef fun content do
526 sidebar
527 append("<div class='content'>")
528 append("<h1>{mmodule.name}</h1>")
529 append("<div class='subtitle'>{mmodule.html_signature(mbuilder)}</div>")
530 append(mmodule.html_full_comment(mbuilder))
531 process_generate_dot
532 classes
533 properties
534 append("</div>")
535 end
536
537 fun process_generate_dot do
538 var name = "dep_{mmodule.name}"
539 var op = new Buffer
540 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")
541 for m in mmodule.in_importation.poset do
542 if m.name == "<main>" then continue
543 var public_owner = m.public_owner
544 if public_owner == null then
545 public_owner = m
546 if m == mmodule then
547 op.append("\"{m.name}\"[shape=box,margin=0.03];\n")
548 else
549 op.append("\"{m.name}\"[URL=\"{m.url}\"];\n")
550 end
551 end
552 for imported in m.in_importation.direct_greaters do
553 if imported.name == "<main>" then continue
554 if imported.public_owner == null then
555 op.append("\"{public_owner.name}\"->\"{imported.name}\";\n")
556 end
557 end
558 end
559 op.append("\}\n")
560 generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
561 end
562
563 fun sidebar do
564 append("<div class='menu'>")
565 append("<nav>")
566 append("<h3>Module Hierarchy</h3>")
567 var dependencies = new Array[MModule]
568 for dep in mmodule.in_importation.greaters do
569 if dep == mmodule or dep.public_owner != null then continue
570 dependencies.add(dep)
571 end
572 if dependencies.length > 0 then
573 append("<h4>All dependencies</h4>")
574 display_module_list(dependencies)
575 end
576 var clients = new Array[MModule]
577 for dep in mmodule.in_importation.smallers do
578 if dep == mmodule or dep.public_owner != null then continue
579 clients.add(dep)
580 end
581 if clients.length > 0 then
582 append("<h4>All clients</h4>")
583 display_module_list(clients)
584 end
585 append("</nav>")
586 if mmodule.in_nesting.direct_greaters.length > 0 then
587 append("<nav>")
588 append("<h3>Nested Modules</h3>")
589 display_module_list(mmodule.in_nesting.direct_greaters.to_a)
590 append("</nav>")
591 end
592 append("</div>")
593 end
594
595 private fun display_module_list(list: Array[MModule]) do
596 append("<ul>")
597 var sorter = new ComparableSorter[MModule]
598 sorter.sort(list)
599 for m in list do append("<li>{m.link(mbuilder)}</li>")
600 append("</ul>")
601 end
602
603 # display the class column
604 fun classes do
605 var intro_mclasses = mmodule.intro_mclasses
606 var redef_mclasses = mmodule.redef_mclasses
607 var all_mclasses = new HashSet[MClass]
608 for m in mmodule.in_nesting.greaters do
609 all_mclasses.add_all(m.intro_mclasses)
610 all_mclasses.add_all(m.redef_mclasses)
611 end
612 all_mclasses.add_all(intro_mclasses)
613 all_mclasses.add_all(redef_mclasses)
614
615 var sorted = new Array[MClass]
616 sorted.add_all(all_mclasses)
617 var sorter = new ComparableSorter[MClass]
618 sorter.sort(sorted)
619 append("<div class='module'>")
620 append("<article class='classes filterable'>")
621 append("<h2>Classes</h2>")
622 append("<ul>")
623 for c in sorted do
624 if redef_mclasses.has(c) and c.intro_mmodule.public_owner != mmodule then
625 append("<li class='redef'>")
626 append("<span title='refined in this module'>R </span>")
627 else
628 append("<li class='intro'>")
629 append("<span title='introduced in this module'>I </span>")
630 end
631 append(c.link(mbuilder))
632 append("</li>")
633 end
634 append("</ul>")
635 append("</article>")
636 append("</div>")
637 end
638
639 # display the property column
640 fun properties do
641 # get properties
642 var mpropdefs = new HashSet[MPropDef]
643 for m in mmodule.in_nesting.greaters do
644 for c in m.mclassdefs do mpropdefs.add_all(c.mpropdefs)
645 end
646 for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs)
647 var sorted = mpropdefs.to_a
648 var sorter = new ComparableSorter[MPropDef]
649 sorter.sort(sorted)
650 # display properties in one column
651 append("<article class='properties filterable'>")
652 append("<h2>Properties</h2>")
653 append("<ul>")
654 for mprop in sorted do
655 if mprop isa MAttributeDef then continue
656 if mprop.mproperty.visibility <= none_visibility then continue
657 append(mprop.html_list_item(mbuilder))
658 end
659 append("</ul>")
660 append("</article>")
661 end
662 end
663
664 # A class page
665 class NitdocClass
666 super NitdocPage
667
668 private var mclass: MClass
669 private var mbuilder: ModelBuilder
670 private var vtypes = new HashSet[MVirtualTypeDef]
671 private var consts = new HashSet[MMethodDef]
672 private var meths = new HashSet[MMethodDef]
673 private var inherited = new HashSet[MPropDef]
674
675 init(mclass: MClass, nitdoc: Nitdoc, dot_dir: nullable String, source: nullable String) do
676 super(nitdoc)
677 self.mclass = mclass
678 self.mbuilder = nitdoc.modelbuilder
679 self.dot_dir = dot_dir
680 self.source = source
681 # load properties
682 for mclassdef in mclass.mclassdefs do
683 for mpropdef in mclassdef.mpropdefs do
684 if mpropdef.mproperty.visibility <= none_visibility then continue
685 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
686 if mpropdef isa MMethodDef then
687 if mpropdef.mproperty.is_init then
688 consts.add(mpropdef)
689 else
690 meths.add(mpropdef)
691 end
692 end
693 end
694 end
695 # get inherited properties
696 for mprop in mclass.inherited_mproperties do
697 var mpropdef = mprop.intro
698 if mprop.visibility <= none_visibility then continue
699 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
700 if mpropdef isa MMethodDef then
701 if mpropdef.mproperty.is_init then
702 consts.add(mpropdef)
703 else
704 meths.add(mpropdef)
705 end
706 end
707 inherited.add(mpropdef)
708 end
709 end
710
711 redef fun title do
712 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
713 if nclass isa AStdClassdef then
714 return "{mclass.name} class | {nclass.short_comment}"
715 else
716 return "{mclass.name} class"
717 end
718 end
719
720 redef fun menu do
721 super
722 append("<li><a href='index.html'>Overview</a></li>")
723 var public_owner = mclass.public_owner
724 if public_owner is null then
725 append("<li>{mclass.intro_mmodule.link(mbuilder)}</li>")
726 else
727 append("<li>{public_owner.link(mbuilder)}</li>")
728 end
729 append("<li class='current'>{mclass.name}</li>")
730 append("<li><a href='full-index.html'>Full Index</a></li>")
731 end
732
733 redef fun content do
734 append("<div class='menu'>")
735 properties_column
736 inheritance_column
737 append("</div>")
738 append("<div class='content'>")
739 class_doc
740 append("</div>")
741 end
742
743 fun properties_column do
744 var sorter = new ComparableSorter[MPropDef]
745 append("<nav class='properties filterable'>")
746 append("<h3>Properties</h3>")
747 # virtual types
748 if vtypes.length > 0 then
749 var vts = new Array[MVirtualTypeDef]
750 vts.add_all(vtypes)
751 sorter.sort(vts)
752 append("<h4>Virtual Types</h4>")
753 append("<ul>")
754 for mprop in vts do
755 append(mprop.html_sidebar_item(self))
756 end
757 append("</ul>")
758 end
759 # constructors
760 if consts.length > 0 then
761 var cts = new Array[MMethodDef]
762 cts.add_all(consts)
763 sorter.sort(cts)
764 append("<h4>Constructors</h4>")
765 append("<ul>")
766 for mprop in cts do
767 append(mprop.html_sidebar_item(self))
768 end
769 append("</ul>")
770 end
771 # methods
772 if meths.length > 0 then
773 var mts = new Array[MMethodDef]
774 mts.add_all(meths)
775 sorter.sort(mts)
776 append("<h4>Methods</h4>")
777 append("<ul>")
778 for mprop in mts do
779 if mclass.name != "Object" and mprop.mproperty.intro_mclassdef.mclass.name == "Object" and mprop.mproperty.visibility <= protected_visibility then continue
780 append(mprop.html_sidebar_item(self))
781 end
782 append("</ul>")
783 end
784 append("</nav>")
785 end
786
787 fun inheritance_column do
788 var sorted = new Array[MClass]
789 var sorterp = new ComparableSorter[MClass]
790 append("<nav>")
791 append("<h3>Inheritance</h3>")
792 if mclass.ancestors.length > 1 then
793 sorted = mclass.ancestors.to_a
794 sorterp.sort(sorted)
795 append("<h4>Superclasses</h4>")
796 append("<ul>")
797 for sup in sorted do
798 if sup == mclass then continue
799 append("<li>{sup.link(mbuilder)}</li>")
800 end
801 append("</ul>")
802 end
803
804 if mclass.descendants.length <= 1 then
805 append("<h4>No Known Subclasses</h4>")
806 else if mclass.descendants.length <= 100 then
807 sorted = mclass.descendants.to_a
808 sorterp.sort(sorted)
809 append("<h4>Subclasses</h4>")
810 append("<ul>")
811 for sub in sorted do
812 if sub == mclass then continue
813 append("<li>{sub.link(mbuilder)}</li>")
814 end
815 append("</ul>")
816 else if mclass.children.length <= 100 then
817 sorted = mclass.children.to_a
818 sorterp.sort(sorted)
819 append("<h4>Direct Subclasses Only</h4>")
820 append("<ul>")
821 for sub in sorted do
822 if sub == mclass then continue
823 append("<li>{sub.link(mbuilder)}</li>")
824 end
825 append("</ul>")
826 else
827 append("<h4>Too much Subclasses to list</h4>")
828 end
829 append("</nav>")
830 end
831
832 fun class_doc do
833 # title
834 append("<h1>{mclass.html_signature}</h1>")
835 append("<div class='subtitle info'>{mclass.html_full_signature(mbuilder)}")
836
837 append("</div>")
838 # comment
839 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
840 append("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
841 append("<section class='description'>")
842 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>")
843 process_generate_dot
844 append("</section>")
845 # concerns
846 var concern2meths = new ArrayMap[MModule, Array[MMethodDef]]
847 var sorted_meths = new Array[MMethodDef]
848 var sorted = new Array[MModule]
849 sorted_meths.add_all(meths)
850 nitdoc.mainmodule.linearize_mpropdefs(sorted_meths)
851 for meth in meths do
852 if inherited.has(meth) then continue
853 var mmodule = meth.mclassdef.mmodule
854 if not concern2meths.has_key(mmodule) then
855 sorted.add(mmodule)
856 concern2meths[mmodule] = new Array[MMethodDef]
857 end
858 concern2meths[mmodule].add(meth)
859 end
860 var sections = new ArrayMap[MModule, Array[MModule]]
861 for mmodule in concern2meths.keys do
862 var owner = mmodule.public_owner
863 if owner == null then owner = mmodule
864 if not sections.has_key(owner) then sections[owner] = new Array[MModule]
865 if owner != mmodule then sections[owner].add(mmodule)
866 end
867 append("<section class='concerns'>")
868 append("<h2 class='section-header'>Concerns</h2>")
869 append("<ul>")
870 for owner, mmodules in sections do
871 var nowner = mbuilder.mmodule2nmodule[owner]
872 append("<li>")
873 if nowner.short_comment.is_empty then
874 append("<a href=\"#{owner.anchor}\">{owner.name}</a>")
875 else
876 append("<a href=\"#{owner.anchor}\">{owner.name}</a>: {nowner.short_comment}")
877 end
878 if not mmodules.is_empty then
879 append("<ul>")
880 for mmodule in mmodules do
881 var nmodule = mbuilder.mmodule2nmodule[mmodule]
882 if nmodule.short_comment.is_empty then
883 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a></li>")
884 else
885 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a>: {nmodule.short_comment}</li>")
886 end
887 end
888 append("</ul>")
889 end
890 append("</li>")
891 end
892 append("</ul>")
893 append("</section>")
894 # properties
895 var prop_sorter = new ComparableSorter[MPropDef]
896 var sorterprop = new ComparableSorter[MProperty]
897 var sorterc = new ComparableSorter[MClass]
898 var lmmodule = new List[MModule]
899 # virtual and formal types
900 var local_vtypes = new Array[MVirtualTypeDef]
901 for vt in vtypes do if not inherited.has(vt) then local_vtypes.add(vt)
902 if local_vtypes.length > 0 or mclass.arity > 0 then
903 append("<section class='types'>")
904 append("<h2>Formal and Virtual Types</h2>")
905 # formal types
906 if mclass.arity > 0 and nclass isa AStdClassdef then
907 for ft, bound in mclass.parameter_types do
908 append("<article id='FT_{ft}'>")
909 append("<h3 class='signature'>{ft}: {bound.link(mbuilder)}</h3>")
910 append("<div class=\"info\">formal generic type</div>")
911 append("</article>")
912 end
913 end
914 # virtual types
915 prop_sorter.sort(local_vtypes)
916 for prop in local_vtypes do append(prop.html_full_desc(self))
917 append("</section>")
918 end
919 # constructors
920 var local_consts = new Array[MMethodDef]
921 for const in consts do if not inherited.has(const) then local_consts.add(const)
922 prop_sorter.sort(local_consts)
923 if local_consts.length > 0 then
924 append("<section class='constructors'>")
925 append("<h2 class='section-header'>Constructors</h2>")
926 for prop in local_consts do append(prop.html_full_desc(self))
927 append("</section>")
928 end
929 # methods
930 if not concern2meths.is_empty then
931 append("<section class='methods'>")
932 append("<h2 class='section-header'>Methods</h2>")
933 for owner, mmodules in sections do
934 append("<a id=\"{owner.anchor}\"></a>")
935 if owner != mclass.intro_mmodule and owner != mclass.public_owner then
936 var nowner = mbuilder.mmodule2nmodule[owner]
937 append("<h3 class=\"concern-toplevel\">Methods refined in {owner.link(mbuilder)}</h3>")
938 if nowner.short_comment.is_empty then
939 append("<p class=\"concern-doc\">{owner.link(mbuilder)}</p>")
940 else
941 append("<p class=\"concern-doc\">{owner.link(mbuilder)}: {nowner.short_comment}</p>")
942 end
943 end
944 if concern2meths.has_key(owner) then
945 var mmethods = concern2meths[owner]
946 prop_sorter.sort(mmethods)
947 for prop in mmethods do append(prop.html_full_desc(self))
948 end
949 for mmodule in mmodules do
950 append("<a id=\"{mmodule.anchor}\"></a>")
951 var nmodule = mbuilder.mmodule2nmodule[mmodule]
952 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
953 if nmodule.short_comment.is_empty then
954 append("<p class=\"concern-doc\">{mmodule.link(mbuilder)}</p>")
955 else
956 append("<p class=\"concern-doc\">{mmodule.link(mbuilder)}: {nmodule.short_comment}</p>")
957 end
958 end
959 var mmethods = concern2meths[mmodule]
960 prop_sorter.sort(mmethods)
961 for prop in mmethods do append(prop.html_full_desc(self))
962 end
963 end
964 end
965 # inherited properties
966 if inherited.length > 0 then
967 var sorted_inherited = new Array[MPropDef]
968 sorted_inherited.add_all(inherited)
969 nitdoc.mainmodule.linearize_mpropdefs(sorted_inherited)
970 var classes = new ArrayMap[MClass, Array[MPropDef]]
971 for mmethod in sorted_inherited.reversed do
972 var mclass = mmethod.mclassdef.mclass
973 if not classes.has_key(mclass) then classes[mclass] = new Array[MPropDef]
974 classes[mclass].add(mmethod)
975 end
976 append("<h3>Inherited Properties</h3>")
977 for c, mmethods in classes do
978 prop_sorter.sort(mmethods)
979 append("<p>Defined in {c.link(mbuilder)}: ")
980 for i in [0..mmethods.length[ do
981 var mmethod = mmethods[i]
982 append(mmethod.link(mbuilder))
983 if i <= mmethods.length - 1 then append(", ")
984 end
985 append("</p>")
986 end
987 end
988 append("</section>")
989 end
990
991 fun process_generate_dot do
992 var pe = nitdoc.class_hierarchy[mclass]
993 var cla = new HashSet[MClass]
994 var sm = new HashSet[MClass]
995 var sm2 = new HashSet[MClass]
996 sm.add(mclass)
997 while cla.length + sm.length < 10 and sm.length > 0 do
998 cla.add_all(sm)
999 sm2.clear
1000 for x in sm do
1001 sm2.add_all(pe.poset[x].direct_smallers)
1002 end
1003 var t = sm
1004 sm = sm2
1005 sm2 = t
1006 end
1007 cla.add_all(pe.greaters)
1008
1009 var op = new Buffer
1010 var name = "dep_{mclass.name}"
1011 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")
1012 for c in cla do
1013 if c == mclass then
1014 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1015 else
1016 op.append("\"{c.name}\"[URL=\"{c.url}\"];\n")
1017 end
1018 for c2 in pe.poset[c].direct_greaters do
1019 if not cla.has(c2) then continue
1020 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1021 end
1022 if not pe.poset[c].direct_smallers.is_empty then
1023 var others = true
1024 for c2 in pe.poset[c].direct_smallers do
1025 if cla.has(c2) then others = false
1026 end
1027 if others then
1028 op.append("\"{c.name}...\"[label=\"\"];\n")
1029 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1030 end
1031 end
1032 end
1033 op.append("\}\n")
1034 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1035 end
1036 end
1037
1038 #
1039 # Model redefs
1040 #
1041
1042 redef class MModule
1043 super Comparable
1044 redef type OTHER: MModule
1045 redef fun <(other: OTHER): Bool do return self.name < other.name
1046
1047 # Get the list of all methods in a module
1048 fun imported_methods: Set[MMethod] do
1049 var methods = new HashSet[MMethod]
1050 for mclass in imported_mclasses do
1051 for method in mclass.intro_methods do
1052 methods.add(method)
1053 end
1054 end
1055 return methods
1056 end
1057
1058 # Get the list aof all refined methods in a module
1059 fun redef_methods: Set[MMethod] do
1060 var methods = new HashSet[MMethod]
1061 for mclass in redef_mclasses do
1062 for method in mclass.intro_methods do
1063 methods.add(method)
1064 end
1065 end
1066 return methods
1067 end
1068
1069 # URL to nitdoc page
1070 fun url: String do
1071 var res = new Buffer
1072 res.append("module_")
1073 var mowner = public_owner
1074 if mowner != null then
1075 res.append("{public_owner.name}_")
1076 end
1077 res.append("{self.name}.html")
1078 return res.to_s
1079 end
1080
1081 # html anchor id to the module in a nitdoc page
1082 fun anchor: String do
1083 var res = new Buffer
1084 res.append("MOD_")
1085 var mowner = public_owner
1086 if mowner != null then
1087 res.append("{public_owner.name}_")
1088 end
1089 res.append(self.name)
1090 return res.to_s
1091 end
1092
1093 # Return a link (html a tag) to the nitdoc module page
1094 fun link(mbuilder: ModelBuilder): String do
1095 if mbuilder.mmodule2nmodule.has_key(self) then
1096 return "<a href='{url}' title='{mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>"
1097 else
1098 return "<a href='{url}'>{name}</a>"
1099 end
1100 end
1101
1102 # Return the module signature decorated with html
1103 fun html_signature(mbuilder: ModelBuilder): String do
1104 return "<span>module {html_full_namespace(mbuilder)}</span>"
1105 end
1106
1107 # Return the module full namespace decorated with html
1108 fun html_full_namespace(mbuilder: ModelBuilder): String do
1109 var res = new Buffer
1110 res.append("<span>")
1111 var mowner = public_owner
1112 if mowner != null then
1113 res.append(public_owner.html_namespace(mbuilder))
1114 res.append("::")
1115 end
1116 res.append(self.link(mbuilder))
1117 res.append("</span>")
1118 return res.to_s
1119 end
1120
1121 # Return the module full namespace decorated with html
1122 fun html_namespace(mbuilder: ModelBuilder): String do
1123 var res = new Buffer
1124 res.append("<span>")
1125 var mowner = public_owner
1126 if mowner != null then
1127 res.append(public_owner.html_namespace(mbuilder))
1128 else
1129 res.append(self.link(mbuilder))
1130 end
1131 res.append("</span>")
1132 return res.to_s
1133 end
1134
1135 # Return the full comment of the module decorated with html
1136 fun html_full_comment(mbuilder: ModelBuilder): String do
1137 var res = new Buffer
1138 if mbuilder.mmodule2nmodule.has_key(self) then
1139 res.append("<div id='description'>")
1140 res.append("<pre class='text_label'>{mbuilder.mmodule2nmodule[self].comment}</pre>")
1141 res.append("<textarea class='edit' rows='1' cols='76' id='fileContent'></textarea>")
1142 res.append("<a id='cancelBtn'>Cancel</a>")
1143 res.append("<a id='commitBtn'>Commit</a>")
1144 res.append("<pre class='text_label' id='preSave' type='2'></pre>")
1145 res.append("</div>")
1146 end
1147 return res.to_s
1148 end
1149 end
1150
1151 redef class MClass
1152 super Comparable
1153 redef type OTHER: MClass
1154 redef fun <(other: OTHER): Bool do return self.name < other.name
1155
1156 # Return the module signature decorated with html
1157 fun html_full_signature(mbuilder: ModelBuilder): String do
1158 var res = new Buffer
1159 if visibility <= none_visibility then
1160 res.append("private ")
1161 else if visibility == protected_visibility then
1162 res.append("protected ")
1163 end
1164 res.append("{kind} {html_namespace(mbuilder)}")
1165 return res.to_s
1166 end
1167
1168 # Add type parameters
1169 fun html_signature: String do
1170 if arity > 0 then
1171 return "{name}[{intro.parameter_names.join(", ")}]"
1172 else
1173 return name
1174 end
1175 end
1176
1177 # Return a link (html a tag) to the nitdoc class page
1178 fun link(mbuilder: ModelBuilder): String do
1179 if mbuilder.mclassdef2nclassdef.has_key(intro) then
1180 var nclass = mbuilder.mclassdef2nclassdef[intro]
1181 if nclass isa AStdClassdef then
1182 return "<a href='{url}' title=\"{nclass.short_comment}\">{html_signature}</a>"
1183 else
1184 return "<a href='{url}'>{html_signature}</a>"
1185 end
1186 else
1187 return "<a href='{url}'>{html_signature}</a>"
1188 end
1189 end
1190
1191 # Return the class namespace decorated with html
1192 fun html_namespace(mbuilder: ModelBuilder): String do
1193 var res = new Buffer
1194 res.append(intro_mmodule.html_namespace(mbuilder))
1195 res.append("::<span>{self.link(mbuilder)}</span>")
1196 return res.to_s
1197 end
1198
1199 fun url: String do
1200 return "class_{public_owner}_{c_name}.html"
1201 end
1202
1203 # Escape name for html output
1204 redef fun name do return super.html_escape
1205 end
1206
1207 redef class MProperty
1208 super Comparable
1209 redef type OTHER: MProperty
1210 redef fun <(other: OTHER): Bool do return self.name < other.name
1211
1212 # Return the property namespace decorated with html
1213 fun html_namespace(mbuilder: ModelBuilder): String do
1214 return "{intro_mclassdef.mclass.html_namespace(mbuilder)}::<span>{intro.link(mbuilder)}</span>"
1215 end
1216
1217 # Escape name for html output
1218 redef fun name do return super.html_escape
1219 end
1220
1221 redef class MType
1222 fun link(mbuilder: ModelBuilder): String is abstract
1223 end
1224
1225 redef class MClassType
1226 redef fun link(mbuilder) do return mclass.link(mbuilder)
1227 end
1228
1229 redef class MNullableType
1230 redef fun link(mbuilder) do return "nullable {mtype.link(mbuilder)}"
1231 end
1232
1233 redef class MGenericType
1234 redef fun link(mbuilder) do
1235 var res = new Buffer
1236 res.append("<a href='{mclass.url}'>{mclass.name}</a>[")
1237 for i in [0..arguments.length[ do
1238 res.append(arguments[i].link(mbuilder))
1239 if i < arguments.length - 1 then res.append(", ")
1240 end
1241 res.append("]")
1242 return res.to_s
1243 end
1244 end
1245
1246 redef class MParameterType
1247 redef fun link(mbuilder) do
1248 var name = mclass.intro.parameter_names[rank]
1249 return "<a href='{mclass.url}#FT_{name}' title='formal type'>{name}</a>"
1250 end
1251 end
1252
1253 redef class MVirtualType
1254 redef fun link(mbuilder) do return mproperty.intro.link(mbuilder)
1255 end
1256
1257 redef class MClassDef
1258 # Return the classdef namespace decorated with html
1259 fun html_namespace(mbuilder: ModelBuilder): String do
1260 var res = new Buffer
1261 res.append(mmodule.html_full_namespace(mbuilder))
1262 res.append("::<span>{self.mclass.link(mbuilder)}</span>")
1263 return res.to_s
1264 end
1265 end
1266
1267 redef class MPropDef
1268 super Comparable
1269 redef type OTHER: MPropDef
1270 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1271
1272 fun url: String do return "{mclassdef.mclass.url}#{anchor}"
1273 fun anchor: String do return "PROP_{mclassdef.mclass.public_owner.name}_{c_name}"
1274
1275 # Return a link (html a tag) to the nitdoc class page
1276 fun link(mbuilder: ModelBuilder): String do
1277 if mbuilder.mpropdef2npropdef.has_key(self) then
1278 var nprop = mbuilder.mpropdef2npropdef[self]
1279 return "<a href=\"{url}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>"
1280 else
1281 return "<a href=\"{url}\">{mproperty.name}</a>"
1282 end
1283 end
1284
1285 # Return a list item for the mpropdef
1286 fun html_list_item(mbuilder: ModelBuilder): String do
1287 var res = new Buffer
1288 if is_intro then
1289 res.append("<li class='intro'>")
1290 res.append("<span title='introduction'>I</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.link(mbuilder)})")
1291 res.append("</li>")
1292 else
1293 res.append("<li class='redef'>")
1294 res.append("<span title='redefinition'>R</span>&nbsp;{link(mbuilder)} ({mclassdef.mclass.link(mbuilder)})")
1295 res.append("</li>")
1296 end
1297 return res.to_s
1298 end
1299
1300 # Return a list item for the mpropdef
1301 fun html_sidebar_item(page: NitdocClass): String do
1302 var res = new Buffer
1303 if is_intro and mclassdef.mclass == page.mclass then
1304 res.append("<li class='intro'>")
1305 res.append("<span title='Introduced'>I</span>")
1306 else if is_intro and mclassdef.mclass != page.mclass then
1307 res.append("<li class='inherit'>")
1308 res.append("<span title='Inherited'>H</span>")
1309 else
1310 res.append("<li class='redef'>")
1311 res.append("<span title='Redefined'>R</span>")
1312 end
1313 res.append(link(page.mbuilder))
1314 res.append("</li>")
1315 return res.to_s
1316 end
1317
1318 fun html_full_desc(page: NitdocClass): String is abstract
1319 fun html_info(page: NitdocClass): String is abstract
1320
1321 fun full_name: String do
1322 return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}"
1323 end
1324 end
1325
1326 redef class MMethodDef
1327 redef fun html_full_desc(page) do
1328 if not page.mbuilder.mpropdef2npropdef.has_key(self) then
1329 return ""
1330 end
1331 var res = new Buffer
1332 var mprop = mproperty
1333 var nprop = page.mbuilder.mpropdef2npropdef[self]
1334 var classes = new Array[String]
1335 var is_redef = mprop.intro_mclassdef.mclass != page.mclass
1336 classes.add("fun")
1337 if mprop.is_init then classes.add("init")
1338 if is_redef then classes.add("redef")
1339 if mprop.visibility == none_visibility then
1340 classes.add("private")
1341 else if mprop.visibility == protected_visibility then
1342 classes.add("protected")
1343 else
1344 classes.add("public")
1345 end
1346 res.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1347 if nprop isa AAttrPropdef then
1348 if nprop.mreadpropdef == self then
1349 res.append("<h3 class='signature'>{mprop.name}: {nprop.html_signature(page.mbuilder)}</h3>")
1350 else
1351 res.append("<h3 class='signature'>{mprop.name}(value: {nprop.html_signature(page.mbuilder)})</h3>")
1352 end
1353 else
1354 var intro_nprop = page.mbuilder.mpropdef2npropdef[mprop.intro]
1355 res.append("<h3 class='signature'>{mprop.name}{intro_nprop.html_signature(page.mbuilder)}</h3>")
1356 end
1357 res.append(html_info(page))
1358 res.append("<div class='description'>")
1359 if nprop.comment == "" then
1360 res.append("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
1361 else
1362 res.append("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
1363 end
1364 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>")
1365 # definitions block
1366 res.append("<p class='info'>")
1367 page.nitdoc.mainmodule.linearize_mpropdefs(mprop.mpropdefs)
1368 var previous_defs = new Array[MMethodDef]
1369 var next_defs = new Array[MMethodDef]
1370 var self_passed = false
1371 for def in mprop.mpropdefs do
1372 if def == self then
1373 self_passed = true
1374 continue
1375 end
1376 if not self_passed then
1377 if not page.mclass.ancestors.has(def.mclassdef.mclass) then continue
1378 if def.is_intro then continue
1379 previous_defs.add(def)
1380 else
1381 if not page.mclass.descendants.has(def.mclassdef.mclass) then continue
1382 next_defs.add(def)
1383 end
1384 end
1385 res.append("defined by {mclassdef.mmodule.html_full_namespace(page.mbuilder)}")
1386 if not is_intro then
1387 res.append(", introduced by {mprop.intro.mclassdef.mclass.link(page.mbuilder)}")
1388 end
1389 if not previous_defs.is_empty then
1390 res.append(", inherited from ")
1391 for i in [0..previous_defs.length[ do
1392 res.append(previous_defs[i].mclassdef.mclass.link(page.mbuilder))
1393 if i < previous_defs.length - 1 then res.append(", ")
1394 end
1395 end
1396 if not next_defs.is_empty then
1397 res.append(", redefined by ")
1398 for i in [0..next_defs.length[ do
1399 res.append(next_defs[i].mclassdef.mclass.link(page.mbuilder))
1400 if i < next_defs.length - 1 then res.append(", ")
1401 end
1402 end
1403 res.append(".</p>")
1404 res.append("</div>")
1405 res.append("</article>")
1406 return res.to_s
1407 end
1408
1409 redef fun html_info(page) do
1410 var res = new Buffer
1411 res.append("<div class='info'>")
1412 if mproperty.visibility <= none_visibility then
1413 res.append("private ")
1414 else if mproperty.visibility <= protected_visibility then
1415 res.append("protected ")
1416 end
1417 if mproperty.intro_mclassdef.mclass != page.mclass then res.append("redef ")
1418 res.append("fun {mproperty.html_namespace(page.mbuilder)}")
1419 res.append("</div>")
1420 res.append("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
1421 return res.to_s
1422 end
1423 end
1424
1425 redef class MVirtualTypeDef
1426 redef fun html_full_desc(page) do
1427 var res = new Buffer
1428 var mprop = mproperty
1429 var is_redef = mprop.intro_mclassdef.mclass != page.mclass
1430 var classes = new Array[String]
1431 classes.add("type")
1432 if is_redef then classes.add("redef")
1433 if mprop.visibility == none_visibility then
1434 classes.add("private")
1435 else if mprop.visibility == protected_visibility then
1436 classes.add("protected")
1437 else
1438 classes.add("public")
1439 end
1440 res.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1441 res.append("<h3 class='signature'>{mprop.name}: {bound.link(page.mbuilder)}</h3>")
1442 res.append(html_info(page))
1443 res.append("<div class='description'>")
1444
1445 if page.mbuilder.mpropdef2npropdef.has_key(self) and page.mbuilder.mpropdef2npropdef[self].comment != "" then
1446 var nprop = page.mbuilder.mpropdef2npropdef[self]
1447 res.append("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
1448 else
1449 res.append("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
1450 end
1451 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>")
1452 # definitions block
1453 res.append("<p class='info'>")
1454 page.nitdoc.mainmodule.linearize_mpropdefs(mprop.mpropdefs)
1455 var previous_defs = new Array[MVirtualTypeDef]
1456 var next_defs = new Array[MVirtualTypeDef]
1457 var self_passed = false
1458 for def in mprop.mpropdefs do
1459 if def == self then
1460 self_passed = true
1461 continue
1462 end
1463 if not self_passed then
1464 if not page.mclass.ancestors.has(def.mclassdef.mclass) then continue
1465 if def.is_intro then continue
1466 previous_defs.add(def)
1467 else
1468 if not page.mclass.descendants.has(def.mclassdef.mclass) then continue
1469 next_defs.add(def)
1470 end
1471 end
1472 res.append("defined by {mclassdef.mmodule.html_full_namespace(page.mbuilder)}")
1473 if not is_intro then
1474 res.append(", introduced by {mprop.intro.mclassdef.mclass.link(page.mbuilder)}")
1475 end
1476 if not previous_defs.is_empty then
1477 res.append(", inherited from ")
1478 for i in [0..previous_defs.length[ do
1479 res.append(previous_defs[i].mclassdef.mclass.link(page.mbuilder))
1480 if i < previous_defs.length - 1 then res.append(", ")
1481 end
1482 end
1483 if not next_defs.is_empty then
1484 res.append(", redefined by ")
1485 for i in [0..next_defs.length[ do
1486 res.append(next_defs[i].mclassdef.mclass.link(page.mbuilder))
1487 if i < next_defs.length - 1 then res.append(", ")
1488 end
1489 end
1490 res.append(".</p>")
1491 res.append("</div>")
1492 res.append("</article>")
1493 return res.to_s
1494 end
1495
1496 redef fun html_info(page) do
1497 var res = new Buffer
1498 res.append("<div class='info'>")
1499 if mproperty.intro_mclassdef.mclass != page.mclass then res.append("redef ")
1500 res.append("type {mproperty.html_namespace(page.mbuilder)}")
1501 res.append("</div>")
1502 res.append("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
1503 return res.to_s
1504 end
1505 end
1506
1507 #
1508 # Nodes redefs
1509 #
1510
1511 redef class AModule
1512 private fun comment: String do
1513 var ret = new Buffer
1514 if n_moduledecl == null or n_moduledecl.n_doc == null then return ""
1515 for t in n_moduledecl.n_doc.n_comment do
1516 ret.append(t.text.substring_from(1))
1517 end
1518 return ret.to_s.html_escape
1519 end
1520
1521 private fun short_comment: String do
1522 var ret = new Buffer
1523 if n_moduledecl != null and n_moduledecl.n_doc != null then
1524 ret.append(n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1525 end
1526 return ret.to_s.html_escape
1527 end
1528 end
1529
1530 redef class AStdClassdef
1531 private fun comment: String do
1532 var ret = new Buffer
1533 if n_doc != null then
1534 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1535 end
1536 return ret.to_s.html_escape
1537 end
1538
1539 private fun short_comment: String do
1540 var ret = new Buffer
1541 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1542 return ret.to_s.html_escape
1543 end
1544 end
1545
1546 redef class APropdef
1547 private fun short_comment: String is abstract
1548 private fun html_signature(mbuilder: ModelBuilder): String is abstract
1549 private fun comment: String is abstract
1550 end
1551
1552 redef class AAttrPropdef
1553 redef fun short_comment do
1554 var ret = new Buffer
1555 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1556 return ret.to_s.html_escape
1557 end
1558
1559 redef private fun comment: String do
1560 var ret = new Buffer
1561 if n_doc != null then
1562 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1563 end
1564 return ret.to_s.html_escape
1565 end
1566
1567 redef fun html_signature(mbuilder) do
1568 if n_type != null then return n_type.mtype.link(mbuilder)
1569 return ""
1570 end
1571 end
1572
1573 redef class AMethPropdef
1574 redef fun short_comment do
1575 var ret = new Buffer
1576 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1577 return ret.to_s.html_escape
1578 end
1579
1580 redef private fun comment: String do
1581 var ret = new Buffer
1582 if n_doc != null then
1583 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1584 end
1585 return ret.to_s.html_escape
1586 end
1587
1588 redef fun html_signature(mbuilder) do
1589 if n_signature != null then return n_signature.to_html(mbuilder)
1590 return ""
1591 end
1592 end
1593
1594 redef class ATypePropdef
1595 redef fun short_comment do
1596 var ret = new Buffer
1597 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1598 return ret.to_s.html_escape
1599 end
1600
1601 redef private fun comment: String do
1602 var ret = new Buffer
1603 if n_doc != null then
1604 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1605 end
1606 return ret.to_s.html_escape
1607 end
1608
1609 redef fun html_signature(mbuilder) do
1610 return mpropdef.bound.link(mbuilder)
1611 end
1612 end
1613
1614 redef class ASignature
1615 fun to_html(mbuilder: ModelBuilder): String do
1616 #TODO closures
1617 var res = new Buffer
1618 if not n_params.is_empty then
1619 res.append("(")
1620 for i in [0..n_params.length[ do
1621 res.append(n_params[i].to_html(mbuilder))
1622 if i < n_params.length - 1 then res.append(", ")
1623 end
1624 res.append(")")
1625 end
1626 if n_type != null and n_type.mtype.link(mbuilder) != "" then res.append(": {n_type.mtype.link(mbuilder)}")
1627 return res.to_s
1628 end
1629 end
1630
1631 redef class AParam
1632 fun to_html(mbuilder: ModelBuilder): String do
1633 var ret = "{n_id.text}"
1634 if n_type != null then
1635 ret = "{ret}: {n_type.mtype.link(mbuilder)}"
1636 if n_dotdotdot != null then ret = "{ret}..."
1637 end
1638 return ret
1639 end
1640 end
1641
1642 # Create a tool context to handle options and paths
1643 var toolcontext = new ToolContext
1644
1645 # Here we launch the nit index
1646 var nitdoc = new Nitdoc(toolcontext)
1647 nitdoc.start