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