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