lib: Split collections into readable and writable
[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 var mc = m[c.global]
609 if mc != null and mc.need_doc(dctx) then
610 new_classes.add(c)
611 break
612 end
613 end
614 end
615 end
616
617 if not new_classes.is_empty then
618 dctx.sort(new_classes)
619 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
620 dctx.add("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\"><big>Class Summary of {self}</big></th><tr>\n")
621 for c in new_classes do
622 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")
623 end
624 dctx.add("</table><br/>\n")
625 end
626
627 if not new_classes.is_empty then
628 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
629 dctx.add("<tr bgcolor=\"#CCCCFF\"><th><big>Class Detail of {self}</big></th><tr>\n")
630 dctx.add("</table>\n")
631
632 for c in new_classes do
633 c.extract_class_doc(dctx)
634 end
635 end
636
637 dctx.add("</body></html>\n")
638 end
639
640 redef meth short_doc
641 do
642 var d = doc
643 if d != null then
644 return d.short
645 else
646 return "&nbsp;"
647 end
648 end
649
650 redef meth doc
651 do
652 var n = node
653 if not n isa AModule then
654 return null
655 end
656 assert n isa AModule
657 if n.n_packagedecl == null then
658 return null
659 end
660 var np = n.n_packagedecl
661 assert np isa APackagedecl
662 var d = np.n_doc
663 assert d isa ADoc
664 if d == null then
665 return null
666 end
667 if d.n_comment.is_empty then
668 return null
669 else
670 return d
671 end
672 end
673 end
674
675 redef class ADoc
676 # Html transcription of the doc
677 meth to_html: String
678 do
679 var res = new Buffer
680 for c in n_comment do
681 res.append(c.text.substring_from(1))
682 end
683 return res.to_s
684 end
685
686 # Oneliner transcription of the doc
687 meth short: String
688 do
689 return n_comment.first.text.substring_from(1)
690 end
691 end
692
693 redef class MMLocalClass
694 special MMEntity
695 # Anchor of the class description in the module html file
696 meth html_anchor: String do return "CLASS_{self}"
697
698 redef meth html_link(dctx)
699 do
700 var m = module
701 if not need_doc(dctx) then m = global.module
702 var m = dctx.known_owner_of(m)
703 if m == dctx.module then
704 return "<a href=\"#{html_anchor}\">{self}</a>"
705 else
706 return "<a href=\"{m}.html#{html_anchor}\">{self}</a>"
707 end
708 end
709
710 redef meth short_doc do return global.intro.short_doc
711
712 redef meth doc do return global.intro.doc
713
714 redef meth need_doc(dctx) do
715 if module == dctx.module then
716 for m in dctx.owned_modules do
717 var c = m[global]
718 if c != null and c.need_doc(dctx) then return true
719 end
720 end
721 return false
722 end
723
724 redef meth locate(dctx) do return "in {module.html_link(dctx)}"
725
726 meth known_intro(dctx: DocContext): MMLocalClass do return dctx.known_owner_of(global.intro.module)[global]
727
728 redef meth prototype_head(dctx)
729 do
730 var res = new Buffer
731 var ki = known_intro(dctx)
732 var is_redef = ki != self
733 if is_redef then res.append("redef ")
734 if global.visibility_level == 3 then res.append("private ")
735 res.append("class ")
736 if is_redef then res.append("{ki.module.html_link(dctx)}::")
737 return res.to_s
738 end
739
740 redef meth prototype_body(dctx)
741 do
742 var res = new Buffer
743 if arity > 0 then
744 res.append("[")
745 for i in [0..arity[ do
746 var t = get_formal(i)
747 res.append(t.name.to_s)
748 res.append(": ")
749 res.append(t.bound.html_link(dctx))
750 end
751 res.append("]")
752 end
753 return res.to_s
754 end
755
756 # Extract the doc of a class
757 meth extract_class_doc(dctx: DocContext)
758 do
759 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")
760 dctx.add("<blockquote>\n")
761 dctx.add("<dl>\n")
762
763 var sup2 = new Array[String]
764 var intro_module = dctx.known_owner_of(global.module)
765 if intro_module != module then
766 dctx.add("<dt>Refine {self} from: <dd>{intro_module.html_link(dctx)}\n")
767 sup2.clear
768 var mods = new Array[MMModule]
769 for c in crhe.greaters do
770 if c.need_doc(dctx) then
771 var km = dctx.known_owner_of(c.module)
772 if km != module and km != intro_module and not mods.has(km) then
773 mods.add(km)
774 end
775 end
776 end
777 for c in crhe.linear_extension do
778 if mods.has(c.module) then sup2.add(c.module.html_link(dctx))
779 end
780 if not sup2.is_empty then dctx.add("<dt>Previous refinements in: <dd>{sup2.join(", ")}\n")
781 end
782 if not cshe.greaters.is_empty then
783 sup2.clear
784 var clas = new Array[MMLocalClass]
785 for c in cshe.direct_greaters do
786 sup2.add(c.html_link(dctx))
787 end
788 dctx.add("<dt>Direct superclasses: <dd>{sup2.join(", ")}\n")
789 sup2.clear
790 for c in cshe.linear_extension do
791 if c != self then sup2.add(c.html_link(dctx))
792 end
793 dctx.add("<dt>All superclasses: <dd>{sup2.join(", ")}\n")
794 end
795 if not cshe.direct_smallers.is_empty then
796 sup2.clear
797 for c in cshe.direct_smallers do
798 sup2.add(c.html_link(dctx))
799 end
800 dctx.add("<dt>Direct subclasses: <dd>{sup2.join(", ")}\n")
801 end
802 sup2.clear
803 for c in crhe.smallers do
804 c.compute_super_classes
805 for c2 in c.module.local_classes do
806 if not c2 isa MMConcreteClass then continue
807 c2.compute_super_classes
808 c2.compute_ancestors
809 c2.inherit_global_properties
810 end
811 for c2 in c.cshe.direct_smallers do
812 if c2.global.intro == c2 then
813 sup2.add("{c2.html_link(dctx)}")
814 end
815 end
816 end
817 if not sup2.is_empty then
818 dctx.add("<dt>Other direct subclasses in known modules: <dd>{sup2.join(", ")}\n")
819 end
820 sup2.clear
821 for c in crhe.order do
822 if not module.mhe <= c.module and c.need_doc(dctx) then
823 sup2.add(c.module.html_link(dctx))
824 end
825 end
826 if not sup2.is_empty then
827 dctx.add("<dt>Refinements in known modules: <dd>{sup2.join(", ")}\n")
828 end
829 dctx.add("</dl>\n")
830
831 var doc = doc
832 if doc != null then
833 dctx.add("<pre>{doc.to_html}</pre>\n")
834 end
835
836 var details = new Array[Array[MMLocalProperty]]
837 for i in [0..4[ do details.add(property_summary(dctx, i))
838 for i in [0..4[ do property_detail(dctx, i, details[i])
839
840 dctx.add("</blockquote><hr/>\n")
841 end
842
843 meth pass_name(pass: Int): String
844 do
845 var names = once ["Virtual Types", "Consructors", "Methods", "Attributes"]
846 return names[pass]
847 end
848
849 meth accept_prop(p: MMLocalProperty, pass: Int): Bool
850 do
851 if pass == 0 then
852 return p isa MMTypeProperty
853 else if pass == 1 then
854 return p.global.is_init
855 else if pass == 2 then
856 return p isa MMMethod and not p.global.is_init
857 else if pass == 3 then
858 return p isa MMAttribute
859 end
860 abort
861 end
862
863 meth property_summary(dctx: DocContext, pass: Int): Array[MMLocalProperty]
864 do
865 var passname = pass_name(pass)
866 dctx.open_stage
867 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
868 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\">{passname} Summary of {self}</th></tr>\n")
869
870 var new_props = new Array[MMLocalProperty]
871 for g in global_properties do
872 if not accept_prop(g.intro, pass) then continue
873 if module.visibility_for(g.intro.module) < g.visibility_level then continue
874 var p = self[g]
875 if p.local_class != self or not p.need_doc(dctx) then
876 var cla = new Array[MMLocalClass]
877 for m in dctx.owned_modules do
878 var c = m[global]
879 if c == null or not c isa MMConcreteClass then continue
880 var p2 = c[g]
881 if p2 == null or p2.local_class != c or not p2.need_doc(dctx) then continue
882 cla.add(c)
883 end
884 if cla.is_empty then continue
885 cla = crhe.order.select_smallests(cla)
886 end
887
888 new_props.add(p)
889 if p.global.intro == p then
890 dctx.register(p)
891 end
892 end
893 dctx.sort(new_props)
894 for p in new_props do
895 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")
896 end
897 dctx.stage("</table><br/>\n")
898
899 dctx.open_stage
900 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
901 if pass != 1 then
902 # skip pass 1 because constructors are not inherited
903 var cmap = new HashMap[MMLocalClass, Array[MMLocalProperty]]
904 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
905 var props = new Array[MMLocalClass]
906 for c in che.greaters do
907 if c isa MMSrcLocalClass then
908 var km = dctx.known_owner_of(c.module)
909 var kc = km[c.global]
910 if kc == self or not c isa MMConcreteClass then continue
911 var props: Array[MMLocalProperty]
912 if km == module then
913 if cmap.has_key(kc) then
914 props = cmap[kc]
915 else
916 props = new Array[MMLocalProperty]
917 cmap[kc] = props
918 end
919 else
920 if mmap.has_key(km) then
921 props = mmap[km]
922 else
923 props = new Array[MMLocalProperty]
924 mmap[km] = props
925 end
926 end
927 for g in c.global_properties do
928 var p = c[g]
929 if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
930 props.add(kc[g])
931 end
932 end
933 end
934 end
935 dctx.open_stage
936 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Inherited {passname}</small></th><tr>\n")
937 for c in cshe.linear_extension do
938 if not cmap.has_key(c) then continue
939 var props = cmap[c]
940 if props.is_empty then continue
941 dctx.sort(props)
942 var properties = new Array[String]
943 for p in props do properties.add(p.html_link(dctx))
944 dctx.add("<tr><td width=\"20%\"><small>from {c.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
945 end
946 dctx.close_stage
947
948 dctx.open_stage
949 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Imported {passname}</small></th><tr>\n")
950 for m in module.mhe.linear_extension do
951 if not mmap.has_key(m) then continue
952 var props = mmap[m]
953 if props.is_empty then continue
954 dctx.sort(props)
955 var properties = new Array[String]
956 for p in props do properties.add(p.html_link(dctx))
957 dctx.add("<tr><td width=\"20%\"><small>from {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
958 end
959 dctx.close_stage
960 end
961
962 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
963 var props = new Array[MMLocalClass]
964 for c in crhe.order do
965 if module.mhe <= c.module or dctx.owned_modules.has(c.module) or not c isa MMSrcLocalClass then continue
966 var km = dctx.known_owner_of(c.module)
967 if module.mhe <= km then continue
968 var kc = km[c.global]
969 var props: Array[MMLocalProperty]
970 if mmap.has_key(km) then
971 props = mmap[km]
972 else
973 props = new Array[MMLocalProperty]
974 mmap[km] = props
975 end
976 for g in c.global_properties do
977 var p = c[g]
978 if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
979 var kp = kc[g]
980 if not props.has(kp) then props.add(kp)
981 end
982 end
983 # c.properties_inherited_from(dctx, self, pass)
984 end
985 dctx.open_stage
986 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Added {passname} in known modules</small></th><tr>\n")
987 for c in crhe.order do
988 var m = c.module
989 if not mmap.has_key(m) then continue
990 var props = mmap[m]
991 if props.is_empty then continue
992 dctx.sort(props)
993 var properties = new Array[String]
994 for p in props do properties.add(p.html_link(dctx))
995 dctx.add("<tr><td width=\"20%\"><small>in {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
996 end
997 dctx.close_stage
998 dctx.stage("</table><br/><br/>\n")
999 dctx.close_stage
1000
1001 dctx.close_stage
1002 return new_props
1003 end
1004
1005 meth property_detail(dctx: DocContext, pass: Int, new_props: Array[MMLocalProperty])
1006 do
1007 var passname = pass_name(pass)
1008 dctx.open_stage
1009 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
1010 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th>{passname} Detail of {self}</th><tr>\n")
1011 dctx.stage("</table>\n")
1012
1013 dctx.open_stage
1014 for p in new_props do
1015 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")
1016 dctx.add("<blockquote>")
1017 var doc = p.doc
1018 if doc != null then
1019 dctx.add("<pre>{doc.to_html}</pre>\n")
1020 end
1021 dctx.stage("</blockquote>\n")
1022 dctx.close_stage
1023
1024 dctx.open_stage
1025 dctx.stage("<hr/>\n")
1026 end
1027 dctx.close_stage
1028
1029 dctx.close_stage
1030 end
1031
1032 # Add rows for properties inheriterd to some heirs
1033 meth properties_inherited_from(dctx: DocContext, heir: MMLocalClass, pass: Int)
1034 do
1035 var properties = new Array[String]
1036 for g in global_properties do
1037 var p = self[g]
1038 if p.local_class == self and p.need_doc(dctx) and accept_prop(p, pass) then
1039 properties.add(p.html_link(dctx))
1040 end
1041 end
1042 if not properties.is_empty then
1043 var s: String
1044 if heir.global == global then
1045 s = module.html_link(dctx)
1046 else
1047 s = self.html_link(dctx)
1048 end
1049 dctx.add("<tr><td width=\"20%\"><small>in {s}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1050 end
1051 end
1052 end
1053
1054 redef class MMSrcLocalClass
1055 redef meth short_doc
1056 do
1057 var d = doc
1058 if d != null then
1059 return d.short
1060 else if global.intro == self then
1061 return "&nbsp;"
1062 else
1063 var bc = global.intro
1064 return bc.short_doc
1065 end
1066 end
1067
1068 redef meth doc
1069 do
1070 var n = nodes.first
1071 if not n isa AClassdef then
1072 return null
1073 end
1074 assert n isa AClassdef
1075 var d = n.n_doc
1076 assert d isa ADoc
1077 if d == null then
1078 return null
1079 end
1080 if d.n_comment.is_empty then
1081 return null
1082 else
1083 return d
1084 end
1085 end
1086
1087 redef meth need_doc(dctx)
1088 do
1089 if global.visibility_level >= 3 then
1090 if not dctx.intrude_mode then return false
1091 if dctx.module.visibility_for(module) == 0 then return false
1092 end
1093 if global.intro == self then
1094 return true
1095 end
1096 for p in src_local_properties do
1097 if p.need_doc(dctx) then
1098 return true
1099 end
1100 end
1101 return super
1102 end
1103 end
1104
1105 redef class MMSignature
1106 # Htlm transcription of the signature (with nested links)
1107 meth to_html(dctx: DocContext): String
1108 do
1109 var res = new Buffer
1110 if arity > 0 then
1111 res.append("(")
1112 res.append(self[0].html_link(dctx))
1113 for i in [1..arity[ do
1114 res.append(", ")
1115 res.append(self[i].html_link(dctx))
1116 end
1117 res.append(")")
1118 end
1119 if return_type != null then
1120 res.append(": ")
1121 res.append(return_type.html_link(dctx))
1122 end
1123 return res.to_s
1124 end
1125 end
1126
1127 redef class MMType
1128 # Htlm transcription of the type (with nested links)
1129 meth html_link(dctx: DocContext): String do return to_s
1130 end
1131
1132 redef class MMTypeSimpleClass
1133 redef meth html_link(dctx) do return local_class.html_link(dctx)
1134 end
1135
1136 redef class MMTypeGeneric
1137 redef meth html_link(dctx)
1138 do
1139 var res = new Buffer
1140 res.append(local_class.html_link(dctx))
1141 res.append("[")
1142 res.append(params[0].html_link(dctx))
1143 for i in [1..params.length[ do
1144 res.append(", ")
1145 res.append(params[i].html_link(dctx))
1146 end
1147 res.append("]")
1148 return res.to_s
1149 end
1150 end
1151
1152 var c = new DocContext
1153 c.exec_cmd_line