Fix nitdoc
[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 redef meth short_doc do return concrete_property.short_doc
412
413 redef meth doc do return concrete_property.doc
414
415 redef meth need_doc(dctx) do return false
416
417 # Kind of property (meth, attr, etc.)
418 meth kind: String is abstract
419
420 redef meth locate(dctx)
421 do
422 return "in {module.html_link(dctx)}::{local_class.html_link(dctx)}"
423 end
424
425 meth known_intro_class(dctx: DocContext): MMLocalClass
426 do
427 var mod = dctx.known_owner_of(global.intro.local_class.module)
428 var cla = mod[global.intro.local_class.global]
429 return cla
430 end
431
432 redef meth prototype_head(dctx)
433 do
434 var res = ""
435 var intro_class = known_intro_class(dctx)
436 var is_redef = local_class != intro_class
437
438 if is_redef then res.append("redef ")
439 if global.visibility_level == 2 then
440 res.append("protected ")
441 else if global.visibility_level == 3 then
442 res.append("private ")
443 end
444 res.append(kind)
445 if is_redef then
446 var gp = global.intro
447 if intro_class.global != local_class.global then
448 res.append(" {module[intro_class.global].html_link(dctx)}::")
449 else if intro_class.module != module then
450 res.append(" {intro_class.module.html_link(dctx)}::")
451 end
452 end
453 return res
454 end
455
456 redef meth prototype_body(dctx)
457 do
458 var res = signature.to_html(dctx)
459 var s = self
460 if s isa MMConcreteProperty then
461 if s.node isa ADeferredMethPropdef then
462 res.append(" is abstract")
463 else if s.node isa AInternMethPropdef then
464 res.append(" is intern")
465 end
466 end
467 return res
468 end
469 end
470 redef class MMMethod
471 redef meth kind do return if global.is_init then "init" else "meth"
472 end
473 redef class MMAttribute
474 redef meth kind do return "attr"
475 end
476 redef class MMTypeProperty
477 redef meth kind do return "type"
478 end
479
480 redef class MMSrcModule
481 # Extract and generate html file for the module
482 meth extract_module_doc(dctx: DocContext)
483 do
484 dctx.register(self)
485
486 dctx.clear
487 extract_module_doc_inside(dctx)
488 dctx.write_to("{dctx.dir}/{name}.html")
489
490 dctx.intrude_mode = true
491 dctx.clear
492 extract_module_doc_inside(dctx)
493 dctx.write_to("{dctx.dir}/{name}__.html")
494 dctx.intrude_mode = false
495
496 if directory.owner == self then
497 dctx.inside_mode = true
498 dctx.clear
499 extract_module_doc_inside(dctx)
500 dctx.write_to("{dctx.dir}/{name}_.html")
501 dctx.inside_mode = false
502 end
503 end
504
505 meth extract_module_doc_inside(dctx: DocContext)
506 do
507 dctx.add_header("Module {self}")
508 dctx.add("<h1>Module {self}</h1>\n<dl>")
509 var s = ""
510 var d = directory
511 while d == null do
512 if d.owner != null and (d.owner != self or dctx.inside_mode or dctx.intrude_mode) then
513 s = "{d.owner.html_link(dctx)}::{s}"
514 end
515 d = d.parent
516 end
517 dctx.add("{s}<br/>{prototype_head(dctx)}<b>{self}</b>{prototype_body(dctx)}<br/>\n")
518
519 var strs = new Array[String]
520 var intrude_modules = new Array[MMModule]
521 var public_modules = new Array[MMModule]
522 var private_modules = new Array[MMModule]
523 var owned_modules = new Array[MMModule]
524 dctx.owned_modules = owned_modules
525 for m in mhe.greaters do
526 var v = visibility_for(m)
527 if not dctx.inside_mode and not dctx.intrude_mode and m.directory.owner == self then
528 if v >= 2 then owned_modules.add(m)
529 continue
530 end
531 if v == 3 then
532 intrude_modules.add(m)
533 else if v == 2 then
534 public_modules.add(m)
535 else if v == 1 then
536 private_modules.add(m)
537 end
538 end
539 if not intrude_modules.is_empty then
540 var mods = mhe.order.select_smallests(intrude_modules)
541 for i in mods do strs.add(i.html_link(dctx))
542 dctx.add("<dt>Intruded modules: <dd>{strs.join(", ")}\n")
543 end
544 if not public_modules.is_empty then
545 strs.clear
546 var mods = mhe.order.select_smallests(public_modules)
547 for i in mods do strs.add(i.html_link(dctx))
548 dctx.add("<dt>Imported modules: <dd>{strs.join(", ")}\n")
549 end
550 if not private_modules.is_empty then
551 strs.clear
552 var mods = mhe.order.select_smallests(private_modules)
553 for i in mods do strs.add(i.html_link(dctx))
554 dctx.add("<dt>Privatly imported modules: <dd>{strs.join(", ")}\n")
555 end
556 dctx.add("</dl>\n")
557
558 var doc = doc
559 if doc != null then dctx.add("<pre>{doc.to_html}</pre>\n")
560
561 var new_classes = new Array[MMLocalClass]
562 for c in local_classes do
563 if c.need_doc(dctx) then
564 new_classes.add(c)
565 if c.global.intro == c then
566 dctx.register(c)
567 end
568 else
569 for m in owned_modules do
570 var mc = m[c.global]
571 if mc != null and mc.need_doc(dctx) then
572 new_classes.add(c)
573 break
574 end
575 end
576 end
577 end
578
579 if not new_classes.is_empty then
580 dctx.sort(new_classes)
581 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
582 dctx.add("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\"><big>Class Summary of {self}</big></th><tr>\n")
583 for c in new_classes do
584 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")
585 end
586 dctx.add("</table><br/>\n")
587 end
588
589 if not new_classes.is_empty then
590 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
591 dctx.add("<tr bgcolor=\"#CCCCFF\"><th><big>Class Detail of {self}</big></th><tr>\n")
592 dctx.add("</table>\n")
593
594 for c in new_classes do
595 c.extract_class_doc(dctx)
596 end
597 end
598
599 dctx.add("</body></html>\n")
600 end
601
602 redef meth short_doc
603 do
604 var d = doc
605 if d != null then
606 return d.short
607 else
608 return "&nbsp;"
609 end
610 end
611
612 redef meth doc
613 do
614 var n = node
615 if not n isa AModule then
616 return null
617 end
618 assert n isa AModule
619 if n.n_packagedecl == null then
620 return null
621 end
622 var np = n.n_packagedecl
623 assert np isa APackagedecl
624 var d = np.n_doc
625 assert d isa ADoc
626 if d == null then
627 return null
628 end
629 if d.n_comment.is_empty then
630 return null
631 else
632 return d
633 end
634 end
635 end
636
637 redef class ADoc
638 # Html transcription of the doc
639 meth to_html: String
640 do
641 var res = new String
642 for c in n_comment do
643 res.append(c.text.substring_from(1))
644 end
645 return res
646 end
647
648 # Oneliner transcription of the doc
649 meth short: String
650 do
651 return n_comment.first.text.substring_from(1)
652 end
653 end
654
655 redef class MMLocalClass
656 special MMEntity
657 # Anchor of the class description in the module html file
658 meth html_anchor: String do return "CLASS_{self}"
659
660 redef meth html_link(dctx)
661 do
662 var m = module
663 if not need_doc(dctx) then m = global.module
664 var m = dctx.known_owner_of(m)
665 if m == dctx.module then
666 return "<a href=\"#{html_anchor}\">{self}</a>"
667 else
668 return "<a href=\"{m}.html#{html_anchor}\">{self}</a>"
669 end
670 end
671
672 redef meth short_doc do return global.intro.short_doc
673
674 redef meth doc do return global.intro.doc
675
676 redef meth need_doc(dctx) do
677 if module == dctx.module then
678 for m in dctx.owned_modules do
679 var c = m[global]
680 if c != null and c.need_doc(dctx) then return true
681 end
682 end
683 return false
684 end
685
686 redef meth locate(dctx) do return "in {module.html_link(dctx)}"
687
688 meth known_intro(dctx: DocContext): MMLocalClass do return dctx.known_owner_of(global.intro.module)[global]
689
690 redef meth prototype_head(dctx)
691 do
692 var res = ""
693 var ki = known_intro(dctx)
694 var is_redef = ki != self
695 if is_redef then res.append("redef ")
696 if global.visibility_level == 3 then res.append("private ")
697 res.append("class ")
698 if is_redef then res.append("{ki.module.html_link(dctx)}::")
699 return res
700 end
701
702 redef meth prototype_body(dctx)
703 do
704 var res = ""
705 if arity > 0 then
706 res.append("[")
707 for i in [0..arity[ do
708 var t = get_formal(i)
709 res.append(t.name.to_s)
710 res.append(": ")
711 res.append(t.bound.html_link(dctx))
712 end
713 res.append("]")
714 end
715 return res
716 end
717
718 # Extract the doc of a class
719 meth extract_class_doc(dctx: DocContext)
720 do
721 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")
722 dctx.add("<blockquote>\n")
723 dctx.add("<dl>\n")
724
725 var sup2 = new Array[String]
726 var intro_module = dctx.known_owner_of(global.module)
727 if intro_module != module then
728 dctx.add("<dt>Refine {self} from: <dd>{intro_module.html_link(dctx)}\n")
729 sup2.clear
730 var mods = new Array[MMModule]
731 for c in crhe.greaters do
732 if c.need_doc(dctx) then
733 var km = dctx.known_owner_of(c.module)
734 if km != module and km != intro_module and not mods.has(km) then
735 mods.add(km)
736 end
737 end
738 end
739 for c in crhe.linear_extension do
740 if mods.has(c.module) then sup2.add(c.module.html_link(dctx))
741 end
742 if not sup2.is_empty then dctx.add("<dt>Previous refinements in: <dd>{sup2.join(", ")}\n")
743 end
744 if not cshe.greaters.is_empty then
745 sup2.clear
746 var clas = new Array[MMLocalClass]
747 for c in cshe.direct_greaters do
748 sup2.add(c.html_link(dctx))
749 end
750 dctx.add("<dt>Direct superclasses: <dd>{sup2.join(", ")}\n")
751 sup2.clear
752 for c in cshe.linear_extension do
753 if c != self then sup2.add(c.html_link(dctx))
754 end
755 dctx.add("<dt>All superclasses: <dd>{sup2.join(", ")}\n")
756 end
757 if not cshe.direct_smallers.is_empty then
758 sup2.clear
759 for c in cshe.direct_smallers do
760 sup2.add(c.html_link(dctx))
761 end
762 dctx.add("<dt>Direct subclasses: <dd>{sup2.join(", ")}\n")
763 end
764 sup2.clear
765 for c in crhe.smallers do
766 c.compute_super_classes
767 for c2 in c.module.local_classes do
768 if not c2 isa MMConcreteClass then continue
769 c2.compute_super_classes
770 c2.compute_ancestors
771 c2.inherit_global_properties
772 end
773 for c2 in c.cshe.direct_smallers do
774 if c2.global.intro == c2 then
775 sup2.add("{c2.html_link(dctx)}")
776 end
777 end
778 end
779 if not sup2.is_empty then
780 dctx.add("<dt>Other direct subclasses in known modules: <dd>{sup2.join(", ")}\n")
781 end
782 sup2.clear
783 for c in crhe.order do
784 if not module.mhe <= c.module and c.need_doc(dctx) then
785 sup2.add(c.module.html_link(dctx))
786 end
787 end
788 if not sup2.is_empty then
789 dctx.add("<dt>Refinements in known modules: <dd>{sup2.join(", ")}\n")
790 end
791 dctx.add("</dl>\n")
792
793 var doc = doc
794 if doc != null then
795 dctx.add("<pre>{doc.to_html}</pre>\n")
796 end
797
798 var details = new Array[Array[MMLocalProperty]]
799 for i in [0..4[ do details.add(property_summary(dctx, i))
800 for i in [0..4[ do property_detail(dctx, i, details[i])
801
802 dctx.add("</blockquote><hr/>\n")
803 end
804
805 meth pass_name(pass: Int): String
806 do
807 var names = once ["Virtual Types", "Consructors", "Methods", "Attributes"]
808 return names[pass]
809 end
810
811 meth accept_prop(p: MMLocalProperty, pass: Int): Bool
812 do
813 if pass == 0 then
814 return p isa MMTypeProperty
815 else if pass == 1 then
816 return p.global.is_init
817 else if pass == 2 then
818 return p isa MMMethod and not p.global.is_init
819 else if pass == 3 then
820 return p isa MMAttribute
821 end
822 abort
823 end
824
825 meth property_summary(dctx: DocContext, pass: Int): Array[MMLocalProperty]
826 do
827 var passname = pass_name(pass)
828 dctx.open_stage
829 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
830 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\">{passname} Summary of {self}</th><tr>\n")
831
832 var new_props = new Array[MMLocalProperty]
833 for g in global_properties do
834 if not accept_prop(g.intro, pass) then continue
835 if module.visibility_for(g.intro.module) < g.visibility_level then continue
836 var p = self[g]
837 if not p.need_doc(dctx) then
838 var cla = new Array[MMLocalClass]
839 for m in dctx.owned_modules do
840 var c = m[global]
841 if c == null or not c isa MMConcreteClass then continue
842 var p2 = c[g]
843 if p2 == null or not p2.need_doc(dctx) then continue
844 cla.add(c)
845 end
846 if cla.is_empty then continue
847 cla = crhe.order.select_smallests(cla)
848 end
849
850 new_props.add(p)
851 if p.global.intro == p then
852 dctx.register(p)
853 end
854 end
855 dctx.sort(new_props)
856 for p in new_props do
857 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")
858 end
859 dctx.stage("</table><br/>\n")
860
861 dctx.open_stage
862 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
863 if pass != 1 then
864 # skip pass 1 because constructors are not inherited
865 var cmap = new HashMap[MMLocalClass, Array[MMLocalProperty]]
866 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
867 var props = new Array[MMLocalClass]
868 for c in che.greaters do
869 if c isa MMSrcLocalClass then
870 var km = dctx.known_owner_of(c.module)
871 var kc = km[c.global]
872 if kc == self or not c isa MMConcreteClass then continue
873 var props: Array[MMLocalProperty]
874 if km == module then
875 if cmap.has_key(kc) then
876 props = cmap[kc]
877 else
878 props = new Array[MMLocalProperty]
879 cmap[kc] = props
880 end
881 else
882 if mmap.has_key(km) then
883 props = mmap[km]
884 else
885 props = new Array[MMLocalProperty]
886 mmap[km] = props
887 end
888 end
889 for g in c.global_properties do
890 var p = c[g]
891 if p.need_doc(dctx) and accept_prop(p, pass) then
892 props.add(kc[g])
893 end
894 end
895 end
896 end
897 dctx.open_stage
898 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Inherited {passname}</small></th><tr>\n")
899 for c in cshe.linear_extension do
900 if not cmap.has_key(c) then continue
901 var props = cmap[c]
902 if props.is_empty then continue
903 dctx.sort(props)
904 var properties = new Array[String]
905 for p in props do properties.add(p.html_link(dctx))
906 dctx.add("<tr><td width=\"20%\"><small>from {c.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
907 end
908 dctx.close_stage
909
910 dctx.open_stage
911 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Imported {passname}</small></th><tr>\n")
912 for m in module.mhe.linear_extension do
913 if not mmap.has_key(m) then continue
914 var props = mmap[m]
915 if props.is_empty then continue
916 dctx.sort(props)
917 var properties = new Array[String]
918 for p in props do properties.add(p.html_link(dctx))
919 dctx.add("<tr><td width=\"20%\"><small>from {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
920 end
921 dctx.close_stage
922 end
923
924 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
925 var props = new Array[MMLocalClass]
926 for c in crhe.order do
927 if module.mhe <= c.module or dctx.owned_modules.has(c.module) or not c isa MMSrcLocalClass then continue
928 var km = dctx.known_owner_of(c.module)
929 if module.mhe <= km then continue
930 var kc = km[c.global]
931 var props: Array[MMLocalProperty]
932 if mmap.has_key(km) then
933 props = mmap[km]
934 else
935 props = new Array[MMLocalProperty]
936 mmap[km] = props
937 end
938 for g in c.global_properties do
939 var p = c[g]
940 if p.need_doc(dctx) and accept_prop(p, pass) then
941 var kp = kc[g]
942 if not props.has(kp) then props.add(kp)
943 end
944 end
945 # c.properties_inherited_from(dctx, self, pass)
946 end
947 dctx.open_stage
948 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Added {passname} in known modules</small></th><tr>\n")
949 for c in crhe.order do
950 var m = c.module
951 if not mmap.has_key(m) then continue
952 var props = mmap[m]
953 if props.is_empty then continue
954 dctx.sort(props)
955 var properties = new Array[String]
956 for p in props do properties.add(p.html_link(dctx))
957 dctx.add("<tr><td width=\"20%\"><small>in {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
958 end
959 dctx.close_stage
960 dctx.stage("</table><br/><br/>\n")
961 dctx.close_stage
962
963 dctx.close_stage
964 return new_props
965 end
966
967 meth property_detail(dctx: DocContext, pass: Int, new_props: Array[MMLocalProperty])
968 do
969 var passname = pass_name(pass)
970 dctx.open_stage
971 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
972 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th>{passname} Detail of {self}</th><tr>\n")
973 dctx.stage("</table>\n")
974
975 dctx.open_stage
976 for p in new_props do
977 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")
978 dctx.add("<blockquote>")
979 var doc = p.doc
980 if doc != null then
981 dctx.add("<pre>{doc.to_html}</pre>\n")
982 end
983 dctx.stage("</blockquote>\n")
984 dctx.close_stage
985
986 dctx.open_stage
987 dctx.stage("<hr/>\n")
988 end
989 dctx.close_stage
990
991 dctx.close_stage
992 end
993
994 # Add rows for properties inheriterd to some heirs
995 meth properties_inherited_from(dctx: DocContext, heir: MMLocalClass, pass: Int)
996 do
997 var properties = new Array[String]
998 for g in global_properties do
999 var p = self[g]
1000 if p.need_doc(dctx) and accept_prop(p, pass) then
1001 properties.add(p.html_link(dctx))
1002 end
1003 end
1004 if not properties.is_empty then
1005 var s: String
1006 if heir.global == global then
1007 s = module.html_link(dctx)
1008 else
1009 s = self.html_link(dctx)
1010 end
1011 dctx.add("<tr><td width=\"20%\"><small>in {s}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1012 end
1013 end
1014 end
1015
1016 redef class MMSrcLocalClass
1017 redef meth short_doc
1018 do
1019 var d = doc
1020 if d != null then
1021 return d.short
1022 else if global.intro == self then
1023 return "&nbsp;"
1024 else
1025 var bc = global.intro
1026 return bc.short_doc
1027 end
1028 end
1029
1030 redef meth doc
1031 do
1032 var n = nodes.first
1033 if not n isa AClassdef then
1034 return null
1035 end
1036 assert n isa AClassdef
1037 var d = n.n_doc
1038 assert d isa ADoc
1039 if d == null then
1040 return null
1041 end
1042 if d.n_comment.is_empty then
1043 return null
1044 else
1045 return d
1046 end
1047 end
1048
1049 redef meth need_doc(dctx)
1050 do
1051 if global.visibility_level >= 3 then
1052 if not dctx.intrude_mode then return false
1053 if dctx.module.visibility_for(module) == 0 then return false
1054 end
1055 if global.intro == self then
1056 return true
1057 end
1058 for p in src_local_properties do
1059 if p.need_doc(dctx) then
1060 return true
1061 end
1062 end
1063 return super
1064 end
1065 end
1066
1067 redef class MMConcreteProperty
1068 redef meth need_doc(dctx)
1069 do
1070 if global.visibility_level >= 3 or self isa MMAttribute then
1071 if not dctx.intrude_mode then return false
1072 if dctx.module.visibility_for(module) == 0 then return false
1073 end
1074 if global.intro == self then
1075 return true
1076 end
1077 return true
1078 end
1079
1080 redef meth short_doc
1081 do
1082 var d = doc
1083 if d != null then
1084 return d.short
1085 else if global.intro == self then
1086 return "&nbsp;"
1087 else
1088 return global.intro.short_doc
1089 end
1090 end
1091
1092 redef meth doc
1093 do
1094 var n = node
1095 if not node isa PPropdef then
1096 return null
1097 end
1098 assert n isa PPropdef
1099 var d = n.n_doc
1100 assert d isa ADoc
1101 if d == null then
1102 return null
1103 end
1104 if d.n_comment.is_empty then
1105 return null
1106 else
1107 return d
1108 end
1109 end
1110 end
1111
1112 redef class MMSignature
1113 # Htlm transcription of the signature (with nested links)
1114 meth to_html(dctx: DocContext): String
1115 do
1116 var res = new String
1117 if arity > 0 then
1118 res.append("(")
1119 res.append(self[0].html_link(dctx))
1120 for i in [1..arity[ do
1121 res.append(", ")
1122 res.append(self[i].html_link(dctx))
1123 end
1124 res.append(")")
1125 end
1126 if return_type != null then
1127 res.append(": ")
1128 res.append(return_type.html_link(dctx))
1129 end
1130 return res
1131 end
1132 end
1133
1134 redef class MMType
1135 # Htlm transcription of the type (with nested links)
1136 meth html_link(dctx: DocContext): String do return to_s
1137 end
1138
1139 redef class MMTypeSimpleClass
1140 redef meth html_link(dctx) do return local_class.html_link(dctx)
1141 end
1142
1143 redef class MMTypeGeneric
1144 redef meth html_link(dctx)
1145 do
1146 var res = local_class.html_link(dctx)
1147 res.append("[")
1148 res.append(params[0].html_link(dctx))
1149 for i in [1..params.length[ do
1150 res.append(", ")
1151 res.append(params[i].html_link(dctx))
1152 end
1153 res.append("]")
1154 return res
1155 end
1156 end
1157
1158 var c = new DocContext
1159 c.exec_cmd_line