nitdoc: workaround for a broken assertion
[nit.git] / src / nitdoc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # The main module of the nitdoc program
18 package nitdoc
19
20 import syntax
21 private import utils
22 import abstracttool
23
24
25 # Store knowledge and facilities to generate files
26 class DocContext
27 special AbstractCompiler
28 # Destination directory
29 readable writable var _dir: String = "."
30
31 # Content of a generated file
32 var _stage_context: StageContext = new StageContext(null)
33
34 # Add a string in the content
35 fun add(s: String) do
36 _stage_context.content.add(s)
37 _stage_context.validate = true
38 end
39
40 # Add a string in the content iff some other string are added
41 fun stage(s: String) do _stage_context.content.add(s)
42
43 # Create a new stage in the content
44 fun open_stage do _stage_context = new StageContext(_stage_context)
45
46 # Close the current stage in the content
47 fun close_stage
48 do
49 var s = _stage_context.parent
50 if _stage_context.validate then
51 s.content.add_all(_stage_context.content)
52 s.validate = true
53 end
54 assert s != null
55 _stage_context = s
56 end
57
58 # Write the content to a new file
59 fun write_to(filename: String)
60 do
61 var f = new OFStream.open(filename)
62 for s in _stage_context.content do
63 f.write(s)
64 end
65 f.close
66 end
67
68 # Currently computed module
69 readable var _module: nullable MMSrcModule
70
71 # Is the current directory module computed as a simple modude ?
72 readable writable var _inside_mode: Bool = false
73
74 # Is the current module computed as a intruded one ?
75 readable writable var _intrude_mode: Bool = false
76
77 # Compued introducing entities (for the index)
78 var _entities: Array[MMEntity] = new Array[MMEntity]
79
80 # Register an entity (for the index)
81 fun register(e: MMEntity)
82 do
83 _entities.add(e)
84 if e isa MMSrcModule then
85 _module = e
86 end
87 end
88
89 # Start a new file
90 fun clear
91 do
92 _stage_context = new StageContext(null)
93 end
94
95 # Generate common files (frames, index, overview)
96 fun extract_other_doc
97 do
98 info("Generating other files",1)
99 _module = null
100 inside_mode = false
101 intrude_mode = false
102 clear
103 add("<html><body>\n")
104 add("<a href=\"overview.html\" target=\"mainFrame\">Overview</a><br/>\n")
105 add("<a href=\"index-1.html\" target=\"mainFrame\">Index</a><br/>\n")
106 var modules = modules.to_a
107 sort(modules)
108
109 var rootdirs = new Array[MMDirectory]
110 for m in modules do
111 var md = m.directory
112 if md.parent == null and not rootdirs.has(md) then
113 rootdirs.add(md)
114 end
115 end
116
117 var done = new Array[MMModule]
118 for root in rootdirs do
119 var dirstack = [root]
120 var curdir = root
121 add("{root.name}<br/>\n")
122 var indent = "&nbsp; "
123 while not dirstack.is_empty do
124 var redo = false
125 for m in modules do
126 if done.has(m) then continue
127 var md = m.directory
128 if md.owner == m and md.parent == curdir then
129 # It's a directory module
130 add("{indent}<a href=\"{m}.html\" target=\"mainFrame\">{m}</a><br/>\n")
131 curdir = md
132 dirstack.push(curdir)
133 indent = "&nbsp " * dirstack.length
134 redo = true
135 break # restart to preserve alphabetic order
136 else if md == curdir then
137 if md.owner == m then
138 add("{indent}<a href=\"{m}_.html\" target=\"mainFrame\">{m}</a><br/>\n")
139 else
140 add("{indent}<a href=\"{m}.html\" target=\"mainFrame\">{m}</a><br/>\n")
141 end
142 done.add(m)
143 redo = true
144 end
145 end
146 if not redo then
147 dirstack.pop
148 if not dirstack.is_empty then
149 curdir = dirstack[dirstack.length-1]
150 indent = "&nbsp " * dirstack.length
151 end
152 end
153 end
154 end
155 add("</body></html>\n")
156 write_to("{dir}/menu-frame.html")
157
158 clear
159 add_header("Index")
160 add("<dl>\n")
161 sort(_entities)
162 for e in _entities do
163 add("<dt><b>{e.html_link(self)}</b> - {e.prototype_head(self)} <b>{e}</b>{e.prototype_body(self)} {e.locate(self)}<dd>{e.short_doc}\n")
164 end
165 add("</dl></body></html>\n")
166 write_to("{dir}/index-1.html")
167
168 clear
169 add_header("Overview")
170 add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
171 add("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\"><big>Overview of all Modules</big></th><tr>\n")
172 for m in modules do
173 add("<tr><td width=\"20%\" align=\"right\">{m.html_link(self)}</td><td>{m.short_doc}</td><tr>\n")
174 end
175 add("</table></body></html>\n")
176 write_to("{dir}/overview.html")
177
178 clear
179 add("<html>\n<frameset cols=\"20%,80%\">\n<frame src=\"menu-frame.html\" name=\"menuFrame\" title=\"Menu\">\n<frame src=\"overview.html\" name=\"mainFrame\" title=\"Main\" scrolling=\"yes\">\n</frameset></html>\n")
180 write_to("{dir}/index.html")
181 end
182
183 fun add_header(title: String)
184 do
185 add("<html><head><title>{title}</title></head>\n<body>\n")
186 add("<table border=\"0\" width=\"100%\" cellpadding=\"1\" cellspacing=\"0\"><tr><td bgcolor=\"#eeeeff\">\n")
187 add("<a href=\"overview.html\"><b>Overview</b></a>&nbsp; <a href=\"index-1.html\"><b>Index</b></a>&nbsp; <a href=\"index.html\" target=\"_top\"><b>With Frames</b></a>\n")
188 add("</td></tr></table>")
189 add("Visibility: ")
190 if (not inside_mode and not intrude_mode) or module == null then
191 add("<b>Public</b>&nbsp; ")
192 else
193 add("<a href=\"{module}.html\"><b>Public</b></a>&nbsp; ")
194 end
195 if inside_mode or module == null then
196 add("<b>Inside</b>&nbsp; ")
197 else if module.directory.owner != module then
198 add("<strike><b>Inside</b></strike>&nbsp; ")
199 else
200 add("<a href=\"{module}_.html\"><b>Inside</b></a>&nbsp; ")
201 end
202 if intrude_mode or module == null then
203 add("<b>Intrude</b>&nbsp; ")
204 else
205 add("<a href=\"{module}__.html\"><b>Intrude</b></a>&nbsp; ")
206 end
207 add("<br/>")
208 end
209
210 # Sorter of entities in alphabetical order
211 var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity]
212
213 # Sort entities in the alphabetical order
214 fun sort(array: Array[MMEntity])
215 do
216 _sorter.sort(array)
217 end
218
219 readable writable var _owned_modules: Array[MMModule] = new Array[MMModule]
220
221 # Return the known_owner for current module
222 # if inside_mode is set, it could be a different result
223 fun known_owner_of(m: MMModule): MMModule
224 do
225 var module = module
226 if module == null then return m
227 var res = module.known_owner_of(m)
228 if not inside_mode and not intrude_mode and res.directory.owner == module then
229 return module
230 else
231 return res
232 end
233 end
234
235 readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
236
237 redef fun perform_work(mods)
238 do
239 dir.mkdir
240
241 for mod in modules do
242 assert mod isa MMSrcModule
243 mod.extract_module_doc(self)
244 end
245 self.extract_other_doc
246 end
247
248 init
249 do
250 keep_ast = true
251 super("nitdoc")
252 option_context.add_option(opt_dir)
253 end
254
255 redef fun process_options
256 do
257 super
258 var d = opt_dir.value
259 if d != null then dir = d
260 end
261 end
262
263 # Conditionnal part of the text content of a DocContext
264 class StageContext
265 # Content of the current stage
266 readable var _content: Array[String] = new Array[String]
267
268 # Is a normal string already added?
269 readable writable var _validate: Bool = false
270
271 # Parent stage is any
272 readable var _parent: nullable StageContext = null
273
274 init(parent: nullable StageContext) do _parent = parent
275 end
276
277
278 # Efficiently sort object with their to_s method
279 class AlphaSorter[E: Object]
280 special AbstractSorter[E]
281 redef fun compare(a, b)
282 do
283 var sa: String
284 var sb: String
285 var d = _dico
286 if d.has_key(a) then
287 sa = d[a]
288 else
289 sa = a.to_s
290 d[a] = sa
291 end
292 if d.has_key(b) then
293 sb = d[b]
294 else
295 sb = b.to_s
296 d[b] = sb
297 end
298 return sa <=> sb
299 end
300
301 # Keep track of to_s values
302 var _dico: HashMap[Object, String] = new HashMap[Object, String]
303
304 init do end
305 end
306
307 # Generalization of metamodel entities
308 class MMEntity
309 # Return a link to
310 fun html_link(dctx: DocContext): String is abstract
311
312 # Is the entity should appear in the generaed doc
313 fun need_doc(dctx: DocContext): Bool is abstract
314
315 # Return a one liner description
316 fun short_doc: String do return "&nbsp;"
317
318 # The doc node from the AST
319 # Return null is none
320 fun doc: nullable ADoc do return null
321
322 # Human redable location of the entity (module/class/property)
323 fun locate(dctx: DocContext): String do return ""
324
325 # Part of the prototype before the name (kind, modifiers, qualifier)
326 fun prototype_head(dctx: DocContext): String is abstract
327
328 # Part of the property after the name (signature, modifiers)
329 fun prototype_body(dctx: DocContext): String do return ""
330 end
331
332 redef class MMModule
333 special MMEntity
334 redef fun html_link(dctx) do
335 if dctx.module == self then
336 return "{self}"
337 else
338 return "<a href=\"{self}.html\">{self}</a>"
339 end
340 end
341 redef fun need_doc(dctx) do return true
342 redef fun prototype_head(dctx) do return "module "
343
344 var _known_owner_of_cache: Map[MMModule, MMModule] = new HashMap[MMModule, MMModule]
345
346 # Return the owner of `module` from the point of view of `self`
347 fun known_owner_of(module: MMModule): MMModule
348 do
349 if _known_owner_of_cache.has_key(module) then return _known_owner_of_cache[module]
350 var res = module
351 # is module is publicly imported by self?
352 if mhe < module and visibility_for(module) != 0 then
353 res = known_owner_of_intern(module, self, false)
354 else
355 # Return the canonnical owner of module from the point of view of self
356 res = module.owner(self)
357 end
358 _known_owner_of_cache[module] = res
359 return res
360 end
361
362 # Return the most general module that own self
363 fun owner(from: MMModule): MMModule
364 do
365 var res = self
366 var d: nullable MMDirectory = directory
367 while d != null and d != from.directory do
368 var o = d.owner
369 if o != null and o.mhe <= res then res = o
370 d = d.parent
371 end
372 return res
373 end
374
375 # ???
376 private fun known_owner_of_intern(module: MMModule, from: MMModule, as_owner: Bool): MMModule
377 do
378 if module == self then return self
379 var candidates = new Array[MMModule]
380 for m in explicit_imported_modules do
381 if from.visibility_for(m) == 0 then continue
382 if not m.mhe <= module then continue
383 candidates.add(m.known_owner_of_intern(module, from, true))
384 end
385 # FIXME: I do not know what this does
386 if candidates.is_empty then return module.owner(from)
387 var max = candidates.first
388 for m in candidates do
389 if max.mhe < m then max = m
390 end
391 if as_owner and max.directory.owner == self then
392 return self
393 else
394 return max
395 end
396 end
397
398 end
399
400 redef class MMLocalProperty
401 special MMEntity
402 # Anchor of the property description in the module html file
403 fun html_anchor: String
404 do
405 return "PROP_{local_class}_{cmangle(name)}"
406 end
407
408 redef fun html_link(dctx)
409 do
410 var m = module
411 if not need_doc(dctx) then m = global.intro.module
412 m = dctx.known_owner_of(m)
413 if m == dctx.module then
414 return "<a href=\"#{html_anchor}\">{self}</a>"
415 else
416 return "<a href=\"{m}.html#{html_anchor}\">{self}</a>"
417 end
418 end
419
420 # Kind of property (fun, attr, etc.)
421 fun kind: String is abstract
422
423 redef fun locate(dctx)
424 do
425 return "in {module.html_link(dctx)}::{local_class.html_link(dctx)}"
426 end
427
428 fun known_intro_class(dctx: DocContext): MMLocalClass
429 do
430 var mod = dctx.known_owner_of(global.intro.local_class.module)
431 var cla = mod[global.intro.local_class.global]
432 return cla
433 end
434
435 redef fun prototype_head(dctx)
436 do
437 var res = new Buffer
438 var intro_class = known_intro_class(dctx)
439 var is_redef = local_class != intro_class
440
441 if is_redef then res.append("redef ")
442 if global.visibility_level == 2 then
443 res.append("protected ")
444 else if global.visibility_level == 3 then
445 res.append("private ")
446 end
447 res.append(kind)
448 if is_redef then
449 var gp = global.intro
450 if intro_class.global != local_class.global then
451 res.append(" {module[intro_class.global].html_link(dctx)}::")
452 else if intro_class.module != module then
453 res.append(" {intro_class.module.html_link(dctx)}::")
454 end
455 end
456 return res.to_s
457 end
458
459 redef fun prototype_body(dctx)
460 do
461 var res = new Buffer
462 res.append(signature.to_html(dctx, true))
463 var s = self
464 if s isa MMMethod then
465 if s.is_abstract then
466 res.append(" is abstract")
467 else if s.is_intern then
468 res.append(" is intern")
469 end
470 end
471 return res.to_s
472 end
473
474 redef fun need_doc(dctx)
475 do
476 if global.visibility_level >= 3 or self isa MMAttribute then
477 if not dctx.intrude_mode then return false
478 if dctx.module.visibility_for(module) == 0 then return false
479 end
480 if global.intro == self then
481 return true
482 end
483 return doc != null
484 end
485
486 redef fun short_doc
487 do
488 var d = doc
489 if d != null then
490 return d.short
491 else if global.intro == self then
492 return "&nbsp;"
493 else
494 return global.intro.short_doc
495 end
496 end
497
498 redef fun doc
499 do
500 var n = node
501 if n == null or not n isa APropdef then
502 return null
503 end
504 var d = n.n_doc
505 if d == null then
506 return null
507 end
508 if d.n_comment.is_empty then
509 return null
510 else
511 return d
512 end
513 end
514 end
515 redef class MMMethod
516 redef fun kind do return if global.is_init then "init" else "fun"
517 end
518 redef class MMAttribute
519 redef fun kind do return "var"
520 end
521 redef class MMTypeProperty
522 redef fun kind do return "type"
523 end
524
525 redef class MMSrcModule
526 # Extract and generate html file for the module
527 fun extract_module_doc(dctx: DocContext)
528 do
529 dctx.info("Generating HTML for module {name}",1)
530 dctx.register(self)
531
532 dctx.clear
533 extract_module_doc_inside(dctx)
534 dctx.write_to("{dctx.dir}/{name}.html")
535
536 dctx.intrude_mode = true
537 dctx.clear
538 extract_module_doc_inside(dctx)
539 dctx.write_to("{dctx.dir}/{name}__.html")
540 dctx.intrude_mode = false
541
542 if directory.owner == self then
543 dctx.inside_mode = true
544 dctx.clear
545 extract_module_doc_inside(dctx)
546 dctx.write_to("{dctx.dir}/{name}_.html")
547 dctx.inside_mode = false
548 end
549 end
550
551 fun extract_module_doc_inside(dctx: DocContext)
552 do
553 dctx.add_header("Module {self}")
554 dctx.add("<h1>Module {self}</h1>\n<dl>")
555 var s = ""
556 var d: nullable MMDirectory = directory
557 while d == null do
558 if d.owner != null and (d.owner != self or dctx.inside_mode or dctx.intrude_mode) then
559 s = "{d.owner.html_link(dctx)}::{s}"
560 end
561 d = d.parent
562 end
563 dctx.add("{s}<br/>{prototype_head(dctx)}<b>{self}</b>{prototype_body(dctx)}<br/>\n")
564
565 var strs = new Array[String]
566 var intrude_modules = new Array[MMModule]
567 var public_modules = new Array[MMModule]
568 var private_modules = new Array[MMModule]
569 var owned_modules = dctx.owned_modules
570 owned_modules.clear
571 for m in mhe.greaters do
572 var v = visibility_for(m)
573 if not dctx.inside_mode and not dctx.intrude_mode and m.directory.owner == self then
574 if v >= 2 then owned_modules.add(m)
575 continue
576 end
577 if v == 3 then
578 intrude_modules.add(m)
579 else if v == 2 then
580 public_modules.add(m)
581 else if v == 1 then
582 private_modules.add(m)
583 end
584 end
585 if not intrude_modules.is_empty then
586 var mods = mhe.order.select_smallests(intrude_modules)
587 for i in mods do strs.add(i.html_link(dctx))
588 dctx.add("<dt>Intruded modules: <dd>{strs.join(", ")}\n")
589 end
590 if not public_modules.is_empty then
591 strs.clear
592 var mods = mhe.order.select_smallests(public_modules)
593 for i in mods do strs.add(i.html_link(dctx))
594 dctx.add("<dt>Imported modules: <dd>{strs.join(", ")}\n")
595 end
596 if not private_modules.is_empty then
597 strs.clear
598 var mods = mhe.order.select_smallests(private_modules)
599 for i in mods do strs.add(i.html_link(dctx))
600 dctx.add("<dt>Privatly imported modules: <dd>{strs.join(", ")}\n")
601 end
602 dctx.add("</dl>\n")
603
604 var doc = doc
605 if doc != null then dctx.add("<pre>{doc.to_html}</pre>\n")
606
607 var new_classes = new Array[MMLocalClass]
608 for c in local_classes do
609 if c.need_doc(dctx) then
610 new_classes.add(c)
611 if c.global.intro == c then
612 dctx.register(c)
613 end
614 else
615 for m in owned_modules do
616 if m.global_classes.has(c.global) then
617 var mc = m[c.global]
618 if mc.need_doc(dctx) then
619 new_classes.add(c)
620 break
621 end
622 end
623 end
624 end
625 end
626
627 if not new_classes.is_empty then
628 dctx.sort(new_classes)
629 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
630 dctx.add("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\"><big>Class Summary of {self}</big></th><tr>\n")
631 for c in new_classes do
632 dctx.add("<tr><td width=\"20%\" align=\"right\">{c.prototype_head(dctx)}</td><td><b>{c.html_link(dctx)}</b>{c.prototype_body(dctx)}<br/>{c.short_doc}</td><tr>\n")
633 end
634 dctx.add("</table><br/>\n")
635 end
636
637 if not new_classes.is_empty then
638 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
639 dctx.add("<tr bgcolor=\"#CCCCFF\"><th><big>Class Detail of {self}</big></th><tr>\n")
640 dctx.add("</table>\n")
641
642 for c in new_classes do
643 c.extract_class_doc(dctx)
644 end
645 end
646
647 dctx.add("</body></html>\n")
648 end
649
650 redef fun short_doc
651 do
652 var d = doc
653 if d != null then
654 return d.short
655 else
656 return "&nbsp;"
657 end
658 end
659
660 redef fun doc
661 do
662 var n = node
663 if n.n_packagedecl == null then
664 return null
665 end
666 var np = n.n_packagedecl
667 var d = np.n_doc
668 if d == null then
669 return null
670 end
671 if d.n_comment.is_empty then
672 return null
673 else
674 return d
675 end
676 end
677 end
678
679 redef class ADoc
680 # Html transcription of the doc
681 fun to_html: String
682 do
683 var res = new Buffer
684 for c in n_comment do
685 res.append(c.text.substring_from(1))
686 end
687 return res.to_s
688 end
689
690 # Oneliner transcription of the doc
691 fun short: String
692 do
693 return n_comment.first.text.substring_from(1)
694 end
695 end
696
697 redef class MMLocalClass
698 special MMEntity
699 # Anchor of the class description in the module html file
700 fun html_anchor: String do return "CLASS_{self}"
701
702 redef fun html_link(dctx)
703 do
704 var m = module
705 if not need_doc(dctx) then m = global.module
706 m = dctx.known_owner_of(m)
707 if m == dctx.module then
708 return "<a href=\"#{html_anchor}\">{self}</a>"
709 else
710 return "<a href=\"{m}.html#{html_anchor}\">{self}</a>"
711 end
712 end
713
714 redef fun short_doc do return global.intro.short_doc
715
716 redef fun doc do return global.intro.doc
717
718 redef fun need_doc(dctx) do
719 if module == dctx.module then
720 for m in dctx.owned_modules do
721 if m.global_classes.has(global) then
722 var c = m[global]
723 if c.need_doc(dctx) then return true
724 end
725 end
726 end
727 return false
728 end
729
730 redef fun locate(dctx) do return "in {module.html_link(dctx)}"
731
732 fun known_intro(dctx: DocContext): MMLocalClass do return dctx.known_owner_of(global.intro.module)[global]
733
734 redef fun prototype_head(dctx)
735 do
736 var res = new Buffer
737 var ki = known_intro(dctx)
738 var is_redef = ki != self
739 if is_redef then res.append("redef ")
740 if global.visibility_level == 3 then res.append("private ")
741 res.append("class ")
742 if is_redef then res.append("{ki.module.html_link(dctx)}::")
743 return res.to_s
744 end
745
746 redef fun prototype_body(dctx)
747 do
748 var res = new Buffer
749 if arity > 0 then
750 res.append("[")
751 for i in [0..arity[ do
752 var t = get_formal(i)
753 res.append(t.name.to_s)
754 res.append(": ")
755 res.append(t.bound.html_link(dctx))
756 end
757 res.append("]")
758 end
759 return res.to_s
760 end
761
762 # Extract the doc of a class
763 fun extract_class_doc(dctx: DocContext)
764 do
765 dctx.add("<a name=\"{html_anchor}\"></a><h2>{self}</h2><small>{module.html_link(dctx)}::</small><br/>{prototype_head(dctx)}<b>{self}</b>{prototype_body(dctx)}\n")
766 dctx.add("<blockquote>\n")
767 dctx.add("<dl>\n")
768
769 var sup2 = new Array[String]
770 var intro_module = dctx.known_owner_of(global.module)
771 if intro_module != module then
772 dctx.add("<dt>Refine {self} from: <dd>{intro_module.html_link(dctx)}\n")
773 sup2.clear
774 var mods = new Array[MMModule]
775 for c in crhe.greaters do
776 if c.need_doc(dctx) then
777 var km = dctx.known_owner_of(c.module)
778 if km != module and km != intro_module and not mods.has(km) then
779 mods.add(km)
780 end
781 end
782 end
783 for c in crhe.linear_extension do
784 if mods.has(c.module) then sup2.add(c.module.html_link(dctx))
785 end
786 if not sup2.is_empty then dctx.add("<dt>Previous refinements in: <dd>{sup2.join(", ")}\n")
787 end
788 if not cshe.greaters.is_empty then
789 sup2.clear
790 var clas = new Array[MMLocalClass]
791 for c in cshe.direct_greaters do
792 sup2.add(c.html_link(dctx))
793 end
794 dctx.add("<dt>Direct superclasses: <dd>{sup2.join(", ")}\n")
795 sup2.clear
796 for c in cshe.linear_extension do
797 if c != self then sup2.add(c.html_link(dctx))
798 end
799 dctx.add("<dt>All superclasses: <dd>{sup2.join(", ")}\n")
800 end
801 if not cshe.direct_smallers.is_empty then
802 sup2.clear
803 for c in cshe.direct_smallers do
804 sup2.add(c.html_link(dctx))
805 end
806 dctx.add("<dt>Direct subclasses: <dd>{sup2.join(", ")}\n")
807 end
808 sup2.clear
809 for c in crhe.smallers do
810 c.compute_super_classes
811 for c2 in c.module.local_classes do
812 if not c2 isa MMConcreteClass then continue
813 c2.compute_super_classes
814 c2.compute_ancestors
815 c2.inherit_global_properties
816 end
817 for c2 in c.cshe.direct_smallers do
818 if c2.global.intro == c2 then
819 sup2.add("{c2.html_link(dctx)}")
820 end
821 end
822 end
823 if not sup2.is_empty then
824 dctx.add("<dt>Other direct subclasses in known modules: <dd>{sup2.join(", ")}\n")
825 end
826 sup2.clear
827 for c in crhe.order do
828 if not module.mhe <= c.module and c.need_doc(dctx) then
829 sup2.add(c.module.html_link(dctx))
830 end
831 end
832 if not sup2.is_empty then
833 dctx.add("<dt>Refinements in known modules: <dd>{sup2.join(", ")}\n")
834 end
835 dctx.add("</dl>\n")
836
837 var doc = doc
838 if doc != null then
839 dctx.add("<pre>{doc.to_html}</pre>\n")
840 end
841
842 var details = new Array[Array[MMLocalProperty]]
843 for i in [0..4[ do details.add(property_summary(dctx, i))
844 for i in [0..4[ do property_detail(dctx, i, details[i])
845
846 dctx.add("</blockquote><hr/>\n")
847 end
848
849 fun pass_name(pass: Int): String
850 do
851 var names = once ["Virtual Types", "Consructors", "Methods", "Attributes"]
852 return names[pass]
853 end
854
855 fun accept_prop(p: MMLocalProperty, pass: Int): Bool
856 do
857 if pass == 0 then
858 return p isa MMTypeProperty
859 else if pass == 1 then
860 return p.global.is_init
861 else if pass == 2 then
862 return p isa MMMethod and not p.global.is_init
863 else if pass == 3 then
864 return p isa MMAttribute
865 end
866 abort
867 end
868
869 fun property_summary(dctx: DocContext, pass: Int): Array[MMLocalProperty]
870 do
871 var passname = pass_name(pass)
872 dctx.open_stage
873 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
874 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\">{passname} Summary of {self}</th></tr>\n")
875
876 var new_props = new Array[MMLocalProperty]
877 for g in global_properties do
878 if not accept_prop(g.intro, pass) then continue
879 if module.visibility_for(g.intro.module) < g.visibility_level then continue
880 var p = self[g]
881 if p.local_class != self or not p.need_doc(dctx) then
882 var cla = new Array[MMLocalClass]
883 for m in dctx.owned_modules do
884 if not m.global_classes.has(global) then continue
885 var c = m[global]
886 if not c isa MMConcreteClass then continue
887 if not c.has_global_property(g) then continue
888 var p2 = c[g]
889 if p2.local_class != c or not p2.need_doc(dctx) then continue
890 cla.add(c)
891 end
892 if cla.is_empty then continue
893 cla = crhe.order.select_smallests(cla)
894 end
895
896 new_props.add(p)
897 if p.global.intro == p then
898 dctx.register(p)
899 end
900 end
901 dctx.sort(new_props)
902 for p in new_props do
903 dctx.add("<tr><td width=\"20%\" align=\"right\">{p.prototype_head(dctx)}</td><td><b>{p.html_link(dctx)}</b>{p.prototype_body(dctx)}<br/>&nbsp;&nbsp;&nbsp;&nbsp;{p.short_doc}</td></tr>\n")
904 end
905 dctx.stage("</table><br/>\n")
906
907 dctx.open_stage
908 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
909 if pass != 1 then
910 # skip pass 1 because constructors are not inherited
911 var cmap = new HashMap[MMLocalClass, Array[MMLocalProperty]]
912 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
913 for c in che.greaters do
914 if c isa MMSrcLocalClass then
915 var km = dctx.known_owner_of(c.module)
916 var kc = km[c.global]
917 if kc == self then continue
918 var props: Array[MMLocalProperty]
919 if km == module then
920 if cmap.has_key(kc) then
921 props = cmap[kc]
922 else
923 props = new Array[MMLocalProperty]
924 cmap[kc] = props
925 end
926 else
927 if mmap.has_key(km) then
928 props = mmap[km]
929 else
930 props = new Array[MMLocalProperty]
931 mmap[km] = props
932 end
933 end
934 for g in c.global_properties do
935 var p = c[g]
936 if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
937 props.add(kc[g])
938 end
939 end
940 end
941 end
942 dctx.open_stage
943 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Inherited {passname}</small></th><tr>\n")
944 for c in cshe.linear_extension do
945 if not cmap.has_key(c) then continue
946 var props = cmap[c]
947 if props.is_empty then continue
948 dctx.sort(props)
949 var properties = new Array[String]
950 for p in props do properties.add(p.html_link(dctx))
951 dctx.add("<tr><td width=\"20%\"><small>from {c.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
952 end
953 dctx.close_stage
954
955 dctx.open_stage
956 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Imported {passname}</small></th><tr>\n")
957 for m in module.mhe.linear_extension do
958 if not mmap.has_key(m) then continue
959 var props = mmap[m]
960 if props.is_empty then continue
961 dctx.sort(props)
962 var properties = new Array[String]
963 for p in props do properties.add(p.html_link(dctx))
964 dctx.add("<tr><td width=\"20%\"><small>from {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
965 end
966 dctx.close_stage
967 end
968
969 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
970 for c in crhe.order do
971 if module.mhe <= c.module or dctx.owned_modules.has(c.module) or not c isa MMSrcLocalClass then continue
972 var km = dctx.known_owner_of(c.module)
973 if module.mhe <= km then continue
974 var kc = km[c.global]
975 var props: Array[MMLocalProperty]
976 if mmap.has_key(km) then
977 props = mmap[km]
978 else
979 props = new Array[MMLocalProperty]
980 mmap[km] = props
981 end
982 for g in c.global_properties do
983 var p = c[g]
984 if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
985 var kp = kc[g]
986 if not props.has(kp) then props.add(kp)
987 end
988 end
989 # c.properties_inherited_from(dctx, self, pass)
990 end
991 dctx.open_stage
992 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Added {passname} in known modules</small></th><tr>\n")
993 for c in crhe.order do
994 var m = c.module
995 if not mmap.has_key(m) then continue
996 var props = mmap[m]
997 if props.is_empty then continue
998 dctx.sort(props)
999 var properties = new Array[String]
1000 for p in props do properties.add(p.html_link(dctx))
1001 dctx.add("<tr><td width=\"20%\"><small>in {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1002 end
1003 dctx.close_stage
1004 dctx.stage("</table><br/><br/>\n")
1005 dctx.close_stage
1006
1007 dctx.close_stage
1008 return new_props
1009 end
1010
1011 fun property_detail(dctx: DocContext, pass: Int, new_props: Array[MMLocalProperty])
1012 do
1013 var passname = pass_name(pass)
1014 dctx.open_stage
1015 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
1016 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th>{passname} Detail of {self}</th><tr>\n")
1017 dctx.stage("</table>\n")
1018
1019 dctx.open_stage
1020 for p in new_props do
1021 dctx.add("<a name=\"{p.html_anchor}\"></a><h3>{p}</h3><p><small>{p.module.html_link(dctx)}::{p.local_class.html_link(dctx)}::</small><br/>{p.prototype_head(dctx)} <b>{p.name}</b>{p.prototype_body(dctx)}</p>\n")
1022 dctx.add("<blockquote>")
1023 var doc = p.doc
1024 if doc != null then
1025 dctx.add("<pre>{doc.to_html}</pre>\n")
1026 end
1027 dctx.stage("</blockquote>\n")
1028 dctx.close_stage
1029
1030 dctx.open_stage
1031 dctx.stage("<hr/>\n")
1032 end
1033 dctx.close_stage
1034
1035 dctx.close_stage
1036 end
1037
1038 # Add rows for properties inheriterd to some heirs
1039 fun properties_inherited_from(dctx: DocContext, heir: MMLocalClass, pass: Int)
1040 do
1041 var properties = new Array[String]
1042 for g in global_properties do
1043 var p = self[g]
1044 if p.local_class == self and p.need_doc(dctx) and accept_prop(p, pass) then
1045 properties.add(p.html_link(dctx))
1046 end
1047 end
1048 if not properties.is_empty then
1049 var s: String
1050 if heir.global == global then
1051 s = module.html_link(dctx)
1052 else
1053 s = self.html_link(dctx)
1054 end
1055 dctx.add("<tr><td width=\"20%\"><small>in {s}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1056 end
1057 end
1058 end
1059
1060 redef class MMSrcLocalClass
1061 redef fun short_doc
1062 do
1063 var d = doc
1064 if d != null then
1065 return d.short
1066 else if global.intro == self then
1067 return "&nbsp;"
1068 else
1069 var bc = global.intro
1070 return bc.short_doc
1071 end
1072 end
1073
1074 redef fun doc
1075 do
1076 var n = node
1077 if not n isa AStdClassdef then
1078 return null
1079 end
1080 var d = n.n_doc
1081 if d == null then
1082 return null
1083 end
1084 if d.n_comment.is_empty then
1085 return null
1086 else
1087 return d
1088 end
1089 end
1090
1091 redef fun need_doc(dctx)
1092 do
1093 if global.visibility_level >= 3 then
1094 if not dctx.intrude_mode then return false
1095 if dctx.module.visibility_for(module) == 0 then return false
1096 end
1097 if global.intro == self then
1098 return true
1099 end
1100 for p in src_local_properties do
1101 if p.need_doc(dctx) then
1102 return true
1103 end
1104 end
1105 return super
1106 end
1107 end
1108
1109 redef class MMSignature
1110 # Htlm transcription of the signature (with nested links)
1111 fun to_html(dctx: DocContext, with_closure: Bool): String
1112 do
1113 var res = new Buffer
1114 if arity > 0 then
1115 res.append("(")
1116 res.append(self[0].html_link(dctx))
1117 for i in [1..arity[ do
1118 res.append(", ")
1119 res.append(self[i].html_link(dctx))
1120 end
1121 res.append(")")
1122 end
1123 if return_type != null then
1124 res.append(": ")
1125 res.append(return_type.html_link(dctx))
1126 end
1127 if with_closure then
1128 for c in closures do
1129 res.append(" ")
1130 if c.is_optional then res.append("[")
1131 if c.is_break then res.append("break ")
1132 res.append("!{c.name}")
1133 res.append(c.signature.to_html(dctx, false))
1134 if c.is_optional then res.append("]")
1135 end
1136 end
1137 return res.to_s
1138 end
1139 end
1140
1141 redef class MMType
1142 # Htlm transcription of the type (with nested links)
1143 fun html_link(dctx: DocContext): String do return to_s
1144 end
1145
1146 redef class MMTypeSimpleClass
1147 redef fun html_link(dctx) do return local_class.html_link(dctx)
1148 end
1149
1150 redef class MMTypeGeneric
1151 redef fun html_link(dctx)
1152 do
1153 var res = new Buffer
1154 res.append(local_class.html_link(dctx))
1155 res.append("[")
1156 res.append(params[0].html_link(dctx))
1157 for i in [1..params.length[ do
1158 res.append(", ")
1159 res.append(params[i].html_link(dctx))
1160 end
1161 res.append("]")
1162 return res.to_s
1163 end
1164 end
1165
1166 var c = new DocContext
1167 c.exec_cmd_line