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