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