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