nitdoc: improve what classes to put in main summary
[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 or owned_modules.has(c.global.intro.mmmodule) then
645 var add = true
646 for p in c.cshe.direct_greaters do
647 if p.global.intro.mmmodule == self or owned_modules.has(p.global.intro.mmmodule) then
648 add = false
649 end
650 end
651 if add then
652 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")
653 end
654 else
655 for p in c.local_local_properties do
656 if p.global.is_method and p.global.intro == p then
657 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")
658 end
659 end
660 end
661 end
662 dctx.add("</table><br/>\n")
663 end
664
665 if not new_classes.is_empty then
666 dctx.sort(new_classes)
667 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
668 dctx.add("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\"><big>Class Summary of {self}</big></th><tr>\n")
669 for c in new_classes do
670 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")
671 end
672 dctx.add("</table><br/>\n")
673 end
674
675 if not new_classes.is_empty then
676 dctx.add("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
677 dctx.add("<tr bgcolor=\"#CCCCFF\"><th><big>Class Detail of {self}</big></th><tr>\n")
678 dctx.add("</table>\n")
679
680 for c in new_classes do
681 c.extract_class_doc(dctx)
682 end
683 end
684
685 dctx.add("</body></html>\n")
686 end
687
688 redef fun short_doc
689 do
690 var d = doc
691 if d != null then
692 return d.short
693 else
694 return "&nbsp;"
695 end
696 end
697
698 redef fun doc
699 do
700 var n = node
701 if n.n_moduledecl == null then
702 return null
703 end
704 var np = n.n_moduledecl
705 var d = np.n_doc
706 if d == null then
707 return null
708 end
709 if d.n_comment.is_empty then
710 return null
711 else
712 return d
713 end
714 end
715 end
716
717 redef class ADoc
718 # Html transcription of the doc
719 fun to_html: String
720 do
721 var res = new Buffer
722 for c in n_comment do
723 res.append(c.text.substring_from(1))
724 end
725 return res.to_s
726 end
727
728 # Oneliner transcription of the doc
729 fun short: String
730 do
731 return n_comment.first.text.substring_from(1)
732 end
733 end
734
735 redef class MMLocalClass
736 super MMEntity
737 # Anchor of the class description in the module html file
738 fun html_anchor: String do return "CLASS_{self}"
739
740 redef fun html_link(dctx)
741 do
742 var m = mmmodule
743 if not need_doc(dctx) then m = global.mmmodule
744 m = dctx.known_owner_of(m)
745 if m == dctx.mmmodule then
746 return "<a href=\"#{html_anchor}\">{self}</a>"
747 else
748 return "<a href=\"{m}.html#{html_anchor}\">{self}</a>"
749 end
750 end
751
752 redef fun short_doc do return global.intro.short_doc
753
754 redef fun doc do return global.intro.doc
755
756 redef fun need_doc(dctx) do
757 if mmmodule == dctx.mmmodule then
758 for m in dctx.owned_modules do
759 if m.global_classes.has(global) then
760 var c = m[global]
761 if c.need_doc(dctx) then return true
762 end
763 end
764 end
765 return false
766 end
767
768 redef fun locate(dctx) do return "in {mmmodule.html_link(dctx)}"
769
770 fun known_intro(dctx: DocContext): MMLocalClass do return dctx.known_owner_of(global.intro.mmmodule)[global]
771
772 redef fun prototype_head(dctx)
773 do
774 var res = new Buffer
775 var ki = known_intro(dctx)
776 var is_redef = ki != self
777 if is_redef then res.append("redef ")
778 if global.visibility_level == 3 then res.append("private ")
779 res.append("class ")
780 if is_redef then res.append("{ki.mmmodule.html_link(dctx)}::")
781 return res.to_s
782 end
783
784 redef fun prototype_body(dctx)
785 do
786 var res = new Buffer
787 if arity > 0 then
788 res.append("[")
789 for i in [0..arity[ do
790 var t = get_formal(i)
791 res.append(t.name.to_s)
792 res.append(": ")
793 res.append(t.bound.html_link(dctx))
794 end
795 res.append("]")
796 end
797 return res.to_s
798 end
799
800 # Extract the doc of a class
801 fun extract_class_doc(dctx: DocContext)
802 do
803 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")
804 dctx.add("<blockquote>\n")
805 dctx.add("<dl>\n")
806
807 var sup2 = new Array[String]
808 var intro_module = dctx.known_owner_of(global.mmmodule)
809 if intro_module != mmmodule then
810 dctx.add("<dt>Refine {self} from: <dd>{intro_module.html_link(dctx)}\n")
811 sup2.clear
812 var mods = new Array[MMModule]
813 for c in crhe.greaters do
814 if c.need_doc(dctx) then
815 var km = dctx.known_owner_of(c.mmmodule)
816 if km != mmmodule and km != intro_module and not mods.has(km) then
817 mods.add(km)
818 end
819 end
820 end
821 for c in crhe.linear_extension do
822 if mods.has(c.mmmodule) then sup2.add(c.mmmodule.html_link(dctx))
823 end
824 if not sup2.is_empty then dctx.add("<dt>Previous refinements in: <dd>{sup2.join(", ")}\n")
825 end
826 if not cshe.greaters.is_empty then
827 sup2.clear
828 var clas = new Array[MMLocalClass]
829 for c in cshe.direct_greaters do
830 sup2.add(c.html_link(dctx))
831 end
832 dctx.add("<dt>Direct superclasses: <dd>{sup2.join(", ")}\n")
833 sup2.clear
834 for c in cshe.linear_extension do
835 if c != self then sup2.add(c.html_link(dctx))
836 end
837 dctx.add("<dt>All superclasses: <dd>{sup2.join(", ")}\n")
838 end
839 if not cshe.direct_smallers.is_empty then
840 sup2.clear
841 for c in cshe.direct_smallers do
842 sup2.add(c.html_link(dctx))
843 end
844 dctx.add("<dt>Direct subclasses: <dd>{sup2.join(", ")}\n")
845 end
846 sup2.clear
847 for c in crhe.smallers do
848 c.compute_super_classes
849 for c2 in c.mmmodule.local_classes do
850 if not c2 isa MMConcreteClass then continue
851 c2.compute_super_classes
852 c2.compute_ancestors
853 end
854 for c2 in c.cshe.direct_smallers do
855 if c2.global.intro == c2 then
856 sup2.add("{c2.html_link(dctx)}")
857 end
858 end
859 end
860 if not sup2.is_empty then
861 dctx.add("<dt>Other direct subclasses in known modules: <dd>{sup2.join(", ")}\n")
862 end
863 sup2.clear
864 for c in crhe.order do
865 if not mmmodule.mhe <= c.mmmodule and c.need_doc(dctx) then
866 sup2.add(c.mmmodule.html_link(dctx))
867 end
868 end
869 if not sup2.is_empty then
870 dctx.add("<dt>Refinements in known modules: <dd>{sup2.join(", ")}\n")
871 end
872 dctx.add("</dl>\n")
873
874 var doc = doc
875 if doc != null then
876 dctx.add("<pre>{doc.to_html}</pre>\n")
877 end
878
879 var details = new Array[Array[MMLocalProperty]]
880 for i in [0..4[ do details.add(property_summary(dctx, i))
881 for i in [0..4[ do property_detail(dctx, i, details[i])
882
883 dctx.add("</blockquote><hr/>\n")
884 end
885
886 fun pass_name(pass: Int): String
887 do
888 var names = once ["Virtual Types", "Consructors", "Methods", "Attributes"]
889 return names[pass]
890 end
891
892 fun accept_prop(p: MMLocalProperty, pass: Int): Bool
893 do
894 if pass == 0 then
895 return p isa MMTypeProperty
896 else if pass == 1 then
897 return p.global.is_init
898 else if pass == 2 then
899 return p isa MMMethod and not p.global.is_init
900 else if pass == 3 then
901 return p isa MMAttribute
902 end
903 abort
904 end
905
906 fun property_summary(dctx: DocContext, pass: Int): Array[MMLocalProperty]
907 do
908 var passname = pass_name(pass)
909 dctx.open_stage
910 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
911 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th colspan=\"2\">{passname} Summary of {self}</th></tr>\n")
912
913 var new_props = new Array[MMLocalProperty]
914 for g in global_properties do
915 if not accept_prop(g.intro, pass) then continue
916 if mmmodule.visibility_for(g.intro.mmmodule) < g.visibility_level then continue
917 var p = self[g]
918 if p.local_class != self or not p.need_doc(dctx) then
919 var cla = new Array[MMLocalClass]
920 for m in dctx.owned_modules do
921 if not m.global_classes.has(global) then continue
922 var c = m[global]
923 if not c isa MMConcreteClass then continue
924 if not c.has_global_property(g) then continue
925 var p2 = c[g]
926 if p2.local_class != c or not p2.need_doc(dctx) then continue
927 cla.add(c)
928 end
929 if cla.is_empty then continue
930 cla = crhe.order.select_smallests(cla)
931 end
932
933 new_props.add(p)
934 if p.global.intro == p then
935 dctx.register(p)
936 end
937 end
938 dctx.sort(new_props)
939 for p in new_props do
940 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")
941 end
942 dctx.stage("</table><br/>\n")
943
944 dctx.open_stage
945 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
946 if pass != 1 then
947 # skip pass 1 because constructors are not inherited
948 var cmap = new HashMap[MMLocalClass, Array[MMLocalProperty]]
949 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
950 for c in che.greaters do
951 if c isa MMSrcLocalClass then
952 var km = dctx.known_owner_of(c.mmmodule)
953 var kc = km[c.global]
954 if kc == self then continue
955 var props: Array[MMLocalProperty]
956 if km == mmmodule then
957 if cmap.has_key(kc) then
958 props = cmap[kc]
959 else
960 props = new Array[MMLocalProperty]
961 cmap[kc] = props
962 end
963 else
964 if mmap.has_key(km) then
965 props = mmap[km]
966 else
967 props = new Array[MMLocalProperty]
968 mmap[km] = props
969 end
970 end
971 for g in c.global_properties do
972 var p = c[g]
973 if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
974 props.add(kc[g])
975 end
976 end
977 end
978 end
979 dctx.open_stage
980 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Inherited {passname}</small></th><tr>\n")
981 for c in cshe.linear_extension do
982 if not cmap.has_key(c) then continue
983 var props = cmap[c]
984 if props.is_empty then continue
985 dctx.sort(props)
986 var properties = new Array[String]
987 for p in props do properties.add(p.html_link(dctx))
988 dctx.add("<tr><td width=\"20%\"><small>from {c.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
989 end
990 dctx.close_stage
991
992 dctx.open_stage
993 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Imported {passname}</small></th><tr>\n")
994 for m in mmmodule.mhe.linear_extension do
995 if not mmap.has_key(m) then continue
996 var props = mmap[m]
997 if props.is_empty then continue
998 dctx.sort(props)
999 var properties = new Array[String]
1000 for p in props do properties.add(p.html_link(dctx))
1001 dctx.add("<tr><td width=\"20%\"><small>from {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1002 end
1003 dctx.close_stage
1004 end
1005
1006 var mmap = new HashMap[MMModule, Array[MMLocalProperty]]
1007 for c in crhe.order do
1008 if mmmodule.mhe <= c.mmmodule or dctx.owned_modules.has(c.mmmodule) or not c isa MMSrcLocalClass then continue
1009 var km = dctx.known_owner_of(c.mmmodule)
1010 if mmmodule.mhe <= km then continue
1011 var kc = km[c.global]
1012 var props: Array[MMLocalProperty]
1013 if mmap.has_key(km) then
1014 props = mmap[km]
1015 else
1016 props = new Array[MMLocalProperty]
1017 mmap[km] = props
1018 end
1019 for g in c.global_properties do
1020 var p = c[g]
1021 if p.local_class == c and p.need_doc(dctx) and accept_prop(p, pass) then
1022 var kp = kc[g]
1023 if not props.has(kp) then props.add(kp)
1024 end
1025 end
1026 # c.properties_inherited_from(dctx, self, pass)
1027 end
1028 dctx.open_stage
1029 dctx.stage("<tr bgcolor=\"#EEEEFF\"><th colspan=\"2\"><small>Added {passname} in known modules</small></th><tr>\n")
1030 for c in crhe.order do
1031 var m = c.mmmodule
1032 if not mmap.has_key(m) then continue
1033 var props = mmap[m]
1034 if props.is_empty then continue
1035 dctx.sort(props)
1036 var properties = new Array[String]
1037 for p in props do properties.add(p.html_link(dctx))
1038 dctx.add("<tr><td width=\"20%\"><small>in {m.html_link(dctx)}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1039 end
1040 dctx.close_stage
1041 dctx.stage("</table><br/><br/>\n")
1042 dctx.close_stage
1043
1044 dctx.close_stage
1045 return new_props
1046 end
1047
1048 fun property_detail(dctx: DocContext, pass: Int, new_props: Array[MMLocalProperty])
1049 do
1050 var passname = pass_name(pass)
1051 dctx.open_stage
1052 dctx.stage("<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\">\n")
1053 dctx.stage("<tr bgcolor=\"#CCCCFF\"><th>{passname} Detail of {self}</th><tr>\n")
1054 dctx.stage("</table>\n")
1055
1056 dctx.open_stage
1057 for p in new_props do
1058 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")
1059 dctx.add("<blockquote>")
1060 var doc = p.doc
1061 if doc != null then
1062 dctx.add("<pre>{doc.to_html}</pre>\n")
1063 end
1064 dctx.stage("</blockquote>\n")
1065 dctx.close_stage
1066
1067 dctx.open_stage
1068 dctx.stage("<hr/>\n")
1069 end
1070 dctx.close_stage
1071
1072 dctx.close_stage
1073 end
1074
1075 # Add rows for properties inheriterd to some heirs
1076 fun properties_inherited_from(dctx: DocContext, heir: MMLocalClass, pass: Int)
1077 do
1078 var properties = new Array[String]
1079 for g in global_properties do
1080 var p = self[g]
1081 if p.local_class == self and p.need_doc(dctx) and accept_prop(p, pass) then
1082 properties.add(p.html_link(dctx))
1083 end
1084 end
1085 if not properties.is_empty then
1086 var s: String
1087 if heir.global == global then
1088 s = mmmodule.html_link(dctx)
1089 else
1090 s = self.html_link(dctx)
1091 end
1092 dctx.add("<tr><td width=\"20%\"><small>in {s}</small></td><td><small>{properties.join(", ")}</small></td><tr>\n")
1093 end
1094 end
1095 end
1096
1097 redef class MMSrcLocalClass
1098 redef fun short_doc
1099 do
1100 var d = doc
1101 if d != null then
1102 return d.short
1103 else if global.intro == self then
1104 return "&nbsp;"
1105 else
1106 var bc = global.intro
1107 return bc.short_doc
1108 end
1109 end
1110
1111 redef fun doc
1112 do
1113 var n = node
1114 if not n isa AStdClassdef then
1115 return null
1116 end
1117 var d = n.n_doc
1118 if d == null then
1119 return null
1120 end
1121 if d.n_comment.is_empty then
1122 return null
1123 else
1124 return d
1125 end
1126 end
1127
1128 redef fun need_doc(dctx)
1129 do
1130 if global.visibility_level >= 3 then
1131 if not dctx.intrude_mode then return false
1132 if dctx.mmmodule.visibility_for(mmmodule) == 0 then return false
1133 end
1134 if global.intro == self then
1135 return true
1136 end
1137 for p in src_local_properties do
1138 if p.need_doc(dctx) then
1139 return true
1140 end
1141 end
1142 return super
1143 end
1144 end
1145
1146 redef class MMSignature
1147 # Htlm transcription of the signature (with nested links)
1148 fun to_html(dctx: DocContext, with_closure: Bool): String
1149 do
1150 var res = new Buffer
1151 if arity > 0 then
1152 res.append("(")
1153 res.append(self[0].html_link(dctx))
1154 for i in [1..arity[ do
1155 res.append(", ")
1156 res.append(self[i].html_link(dctx))
1157 end
1158 res.append(")")
1159 end
1160 if return_type != null then
1161 res.append(": ")
1162 res.append(return_type.html_link(dctx))
1163 end
1164 if with_closure then
1165 for c in closures do
1166 res.append(" ")
1167 if c.is_optional then res.append("[")
1168 if c.is_break then res.append("break ")
1169 res.append("!{c.name}")
1170 res.append(c.signature.to_html(dctx, false))
1171 if c.is_optional then res.append("]")
1172 end
1173 end
1174 return res.to_s
1175 end
1176 end
1177
1178 redef class MMType
1179 # Htlm transcription of the type (with nested links)
1180 fun html_link(dctx: DocContext): String do return to_s
1181 end
1182
1183 redef class MMTypeSimpleClass
1184 redef fun html_link(dctx) do return local_class.html_link(dctx)
1185 end
1186
1187 redef class MMTypeGeneric
1188 redef fun html_link(dctx)
1189 do
1190 var res = new Buffer
1191 res.append(local_class.html_link(dctx))
1192 res.append("[")
1193 res.append(params[0].html_link(dctx))
1194 for i in [1..params.length[ do
1195 res.append(", ")
1196 res.append(params[i].html_link(dctx))
1197 end
1198 res.append("]")
1199 return res.to_s
1200 end
1201 end
1202
1203 var c = new DocContext
1204 c.exec_cmd_line