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