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