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