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