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