nitdoc: complete rewrite
[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 = "doc"
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 # Start a new file
69 fun clear
70 do
71 _stage_context = new StageContext(null)
72 end
73
74 # Sorter of entities in alphabetical order
75 var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity]
76
77 # Sort entities in the alphabetical order
78 fun sort(array: Array[MMEntity])
79 do
80 _sorter.sort(array)
81 end
82
83 readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
84 readable var _opt_source: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
85 readable var _opt_public: OptionBool = new OptionBool("Generate only the public API", "--public")
86
87 fun public_only: Bool
88 do
89 if self._opt_public.value == true then return true
90 return false
91 end
92
93 # The current processed filename
94 var filename: String
95
96 # The main virtual module
97 var mainmod: nullable MMVirtualModule
98
99 redef fun perform_work(mods)
100 do
101 mainmod = new MMVirtualModule(self, mods)
102
103 dir.mkdir
104
105 # Compute the set of direct owned nested modules
106 var owns = new HashMap[MMModule, Array[MMModule]]
107 for mod in modules do
108 owns[mod] = new Array[MMModule]# [mod]
109 end
110 for mod in modules do
111 if mod == mainmod then continue
112 var d = mod.directory
113 loop
114 var o = d.owner
115 if o != null and o != mod then
116 owns[o].add(mod)
117 end
118 var dp = d.parent
119 if dp == null or dp == d then break
120 d = dp
121 end
122 end
123
124 # Builds the various module hierarchies
125 var mnh = new PartialOrder[MMModule] # nested module hierarchy
126 var tmh = new PartialOrder[MMModule] # top module import hierrchy
127 var ms = mainmod.mhe.linear_extension.reversed
128 for m in ms do
129 if ms == mainmod then continue
130 m.mnhe_ = mnh.add(m, owns[m])
131 var pub = new Array[MMModule]
132 for m2 in m.mhe.greaters do
133 if m2.toplevel_owner != m2 and m2.toplevel_owner != m.toplevel_owner then continue
134 if m.mnhe <= m2 then continue
135 if m.visibility_for(m2) <= 0 then
136 # nothing
137 else if m.visibility_for(m2) == 1 then
138 else
139 pub.add(m2)
140 end
141 end
142 m.tmhe_ = tmh.add(m, pub)
143 end
144
145 var head = "<script type=\"text/javascript\" src=\"http://moz-concept.com/nitdoc/scripts/jquery-1.7.1.min.js\"></script>\n" +
146 "<script type=\"text/javascript\" src=\"http://moz-concept.com/nitdoc/scripts/js-facilities.js\"></script>\n" +
147 "<link rel=\"stylesheet\" href=\"http://moz-concept.com/nitdoc/styles/main.css\" type=\"text/css\" media=\"screen\">"
148
149 var action_bar = "<header><nav class='main'><ul><li><a href='.'>Overview</a></li><li><a href='full-index.html'>Full Index</a></li></ul></nav></header>\n"
150
151 # generate the index
152 self.filename = "index.html"
153 clear
154 add("<html><head>{head}</head><body>\n")
155 add(action_bar)
156 add("<div class=\"page\">")
157 add("<div class=\"content fullpage\">")
158 add("<h1>Modules</h1>\n<article class='overview'><ul>")
159 var modss = mainmod.mhe.greaters_and_self.to_a
160 sort(modss)
161 for mod in modss do
162 if not mod.is_toplevel then continue
163 if not mod.require_doc(self) then continue
164 assert mod isa MMSrcModule
165 add("<li>{mod.html_link(self)} {mod.short_doc}</li>")
166
167 end
168 add("</ul>")
169
170 var op = new Buffer
171 op.append("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
172 for mod in modss do
173 if not mod.is_toplevel then continue
174 if not mod.require_doc(self) then continue
175 op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n")
176 for mod2 in mod.tmhe.direct_greaters do
177 if not modss.has(mod2) then continue
178 op.append("\"{mod.name}\"->\"{mod2.name}\";\n")
179 end
180 end
181 op.append("\}\n")
182 self.gen_dot(op.to_s, "dep")
183 add("</article></div>")
184 add("<div class='clear'></div>")
185 add("</div>")
186 add("</body></html>\n")
187 write_to("{dir}/index.html")
188
189 # Generate page for modules
190 for mod in modules do
191 if mod == mainmod then continue
192 assert mod isa MMSrcModule
193 if not mod.require_doc(self) then continue
194 self.filename = mod.html_name
195 clear
196 add("<html><head>{head}<title>Module {mod.name}</title></head><body>\n")
197 add(action_bar)
198 add("<div class=\"page\">")
199 mod.file_page_doc(self)
200 add("</div>")
201 add("</body></html>\n")
202 write_to("{dir}/{mod.html_name}.html")
203 end
204
205 # Generate pages for global classes
206 for c in mainmod.local_classes do
207 if not c.require_doc(self) then continue
208 self.filename = c.html_name
209 clear
210 add("<html><head>{head}<title>Class {c.name}</title></head><body>\n")
211 add(action_bar)
212 add("<div class=\"page\">")
213 c.file_page_doc(self)
214 add("</div>")
215 add("</body></html>\n")
216 write_to("{dir}/{c.html_name}.html")
217 end
218
219 self.filename = "fullindex"
220 clear
221 add("<html><head>{head}</head><body>\n")
222 add(action_bar)
223 add("<div class=\"page\">")
224 add("<div class=\"content fullpage\">")
225 mainmod.file_index_page_doc(self)
226 add("</div>")
227 add("</div>")
228 add("</body></html>\n")
229 write_to("{dir}/full-index.html")
230 end
231
232
233 # Add a (source) link fo a given location
234 fun show_source(l: Location)
235 do
236 var s = opt_source.value
237 if s == null then
238 add("in #{l.file.filename}")
239 else
240 # THIS IS JUST UGLY ! (but there is no replace yet)
241 var x = s.split_with("%f")
242 s = x.join(l.file.filename)
243 x = s.split_with("%l")
244 s = x.join(l.line_start.to_s)
245 x = s.split_with("%L")
246 s = x.join(l.line_end.to_s)
247 add(" (<a href=\"{s}\">show code</a>)")
248 end
249 end
250
251 # Generate a clicable graphiz image using a dot content.
252 # `name' refer to the filename (without extension) and the id name of the map.
253 # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...)
254 fun gen_dot(dot: String, name: String)
255 do
256 var f = new OFStream.open("{self.dir}/{name}.dot")
257 f.write(dot)
258 f.close
259 sys.system("\{ test -f {self.dir}/{name}.png && test -f {self.dir}/{name}.s.dot && diff {self.dir}/{name}.dot {self.dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {self.dir}/{name}.dot {self.dir}/{name}.s.dot && dot -Tpng -o{self.dir}/{name}.png -Tcmapx -o{self.dir}/{name}.map {self.dir}/{name}.s.dot ; \}")
260 self.add("<div><img src=\"{name}.png\" usemap=\"#{name}\" style=\"margin:auto\"/></div>")
261 var fmap = new IFStream.open("{self.dir}/{name}.map")
262 self.add(fmap.read_all)
263 fmap.close
264 end
265
266 init
267 do
268 keep_ast = true
269 super("nitdoc")
270 filename = "-unset-"
271 option_context.add_option(opt_public)
272 option_context.add_option(opt_dir)
273 option_context.add_option(opt_source)
274 end
275
276 redef fun process_options
277 do
278 super
279 var d = opt_dir.value
280 if d != null then dir = d
281 end
282
283 redef fun handle_property_conflict(lc, impls)
284 do
285 # THIS IS SO UGLY! See MMVirtualModule
286 if lc.mmmodule == self.mainmod then
287 return # We just accept, so one in impls is arbitrary inherited
288 end
289 super
290 end
291 end
292
293 # A virtual module is used to work as an implicit main module that combine unrelated modules
294 # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled
295 class MMVirtualModule
296 super MMModule
297 init(ctx: MMContext, mods: Array[MMModule])
298 do
299 # We need to compute the whole metamodel since there is no mmbuilder to do it
300 super(" main".to_symbol, mods.first.directory, ctx, new Location(null,0,0,0,0))
301 ctx.add_module(self, mods)
302 for m in mods do
303 self.add_super_module(m, 1)
304 end
305 self.import_global_classes
306 self.import_local_classes
307 for c in self.local_classes do
308 c.compute_super_classes
309 end
310 for c in self.local_classes do
311 c.compute_ancestors
312 end
313
314 end
315 redef fun require_doc(dctx) do return false
316 end
317
318 # Conditionnal part of the text content of a DocContext
319 class StageContext
320 # Content of the current stage
321 readable var _content: Array[String] = new Array[String]
322
323 # Is a normal string already added?
324 readable writable var _validate: Bool = false
325
326 # Parent stage is any
327 readable var _parent: nullable StageContext = null
328
329 init(parent: nullable StageContext) do _parent = parent
330 end
331
332
333 # Efficiently sort object with their to_s method
334 class AlphaSorter[E: Object]
335 super AbstractSorter[E]
336 redef fun compare(a, b)
337 do
338 var sa: String
339 var sb: String
340 var d = _dico
341 if d.has_key(a) then
342 sa = d[a]
343 else
344 sa = a.to_s
345 d[a] = sa
346 end
347 if d.has_key(b) then
348 sb = d[b]
349 else
350 sb = b.to_s
351 d[b] = sb
352 end
353 return sa <=> sb
354 end
355
356 # Keep track of to_s values
357 var _dico: HashMap[Object, String] = new HashMap[Object, String]
358
359 init do end
360 end
361
362 # Generalization of metamodel entities
363 class MMEntity
364 # Return a link to
365 fun html_link(dctx: DocContext): String is abstract
366
367 # Return a one liner description
368 fun short_doc: String do return "&nbsp;"
369
370 # The doc node from the AST
371 # Return null is none
372 fun doc: nullable ADoc do return null
373 end
374
375 redef class MMModule
376 super MMEntity
377 redef fun html_link(dctx) do
378 return "<a href=\"{html_name}.html\">{self}</a>"
379 end
380
381 fun require_doc(dctx: DocContext): Bool
382 do
383 if dctx.public_only and not is_toplevel then return false
384 return true
385 end
386
387 # Return true if the module is a top-level owner or a top-level module
388 fun is_toplevel: Bool
389 do
390 var pd = directory.parent
391 return pd == null or (pd.owner == null and directory.owner == self)
392 end
393
394 # Element in the module nesting tree
395 fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null)
396 var mnhe_: nullable PartialOrderElement[MMModule] = null
397
398 # Element in the top level module importation hierarchy
399 fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null)
400 var tmhe_: nullable PartialOrderElement[MMModule] = null
401
402 fun toplevel_owner: MMModule
403 do
404 var m = self
405 loop
406 var ds = m.mnhe.direct_smallers
407 if ds.length == 0 then return m
408 if ds.length == 1 then m = ds.first else abort
409 end
410 end
411
412 fun html_name: String
413 do
414 return "{name}"
415 end
416
417 fun direct_owner: nullable MMModule
418 do
419 var d = directory
420 while d.owner == self do d = d.parent.as(not null)
421 return d.owner
422 end
423
424 # Fill the body for the page associated to the module
425 fun file_page_doc(dctx: DocContext)
426 do
427 dctx.add("<div class=\"menu\">\n")
428
429 var mods = new Array[MMModule]
430 mods = self.mhe.greaters.to_a
431 dctx.sort(mods)
432
433 dctx.open_stage
434 dctx.stage("<nav>\n")
435 dctx.stage("<h3>Module Hierarchy</h3>\n")
436 dctx.stage("<h4>All dependencies</h4>\n")
437 dctx.stage("<ul>\n")
438 for mod in mods do
439 if not mod.require_doc(dctx) then continue
440 if self.mnhe <= mod then continue # do not want nested stuff
441 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
442 dctx.add("<li>{mod.html_link(dctx)}</li>")
443 end
444 dctx.stage("</ul>\n")
445
446 mods = self.mhe.smallers.to_a
447 dctx.sort(mods)
448 dctx.stage("<h4>All clients</h4>\n")
449 dctx.stage("<ul>\n")
450 for mod in mods do
451 if not mod.require_doc(dctx) then continue
452 if self.mnhe <= mod then continue # do not want nested stuff
453 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
454 dctx.add("<li>{mod.html_link(dctx)}</li>")
455 end
456 dctx.stage("</ul>\n")
457 dctx.stage("</nav>\n")
458 dctx.close_stage
459
460 if not dctx.public_only then
461 mods = self.mnhe.direct_greaters.to_a
462 dctx.sort(mods)
463 dctx.open_stage
464 dctx.stage("<nav>\n")
465 dctx.stage("<h3>Nested Modules</h3><ul>\n")
466 for mod in mods do
467 if not mod.require_doc(dctx) then continue
468 dctx.add("<li>{mod.html_link(dctx)}</li>")
469 end
470 dctx.stage("</ul></nav>\n")
471 dctx.close_stage
472 end
473
474 dctx.add("</div>") # metadata
475
476 dctx.add("<div class=\"content\">\n")
477 dctx.add("<h1>{name}</h1>\n")
478 dctx.add("<div class='subtitle'>module ")
479 for m in mnhe.smallers do
480 dctx.add("{m.html_link(dctx)}::")
481 end
482 dctx.add("{self.name}</div>\n")
483
484 dctx.add("<section class='description'>\n")
485
486 var doc = doc
487 if doc != null then
488 dctx.add("<div id=\"description\">\n")
489 dctx.add("<pre>{doc.to_html}</pre>\n")
490 dctx.add("</div>\n")
491 end
492
493 var op = new Buffer
494 op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
495 var ms = new Array[nullable MMModule]
496 do
497 var m0: nullable MMModule = self
498 while m0 != null do
499 m0 = m0.direct_owner
500 ms.add(m0)
501 end
502 end
503 var cla = new HashSet[MMModule]
504 cla.add(self)
505 for m0 in self.mhe.greaters do
506 if not m0.require_doc(dctx) then continue
507 if self.visibility_for(m0) <= 1 then continue # private or hidden
508 if self.mnhe <= m0 then continue # do not want nested stuff
509 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
510 cla.add(m0)
511 end
512 for m0 in self.mhe.smallers do
513 if not m0.require_doc(dctx) then continue
514 if m0.visibility_for(self) <= 1 then continue # private or hidden
515 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
516 cla.add(m0)
517 end
518 for m0 in self.mnhe.smallers do
519 cla.add(m0)
520 end
521 ms = ms.reversed
522 for m0 in ms do
523 if m0 != null then
524 op.append("subgraph \"cluster_{m0.name}\"\{\n")
525 end
526 for c in cla do
527 if c.direct_owner != m0 then continue
528 if c == self then
529 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
530 else
531 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
532 end
533 end
534 if m0 != null then
535 op.append("\"{m0.name}\"[URL=\"{m0.html_name}.html\"];\n")
536 for c in m0.mhe.direct_greaters do
537 if not cla.has(c) then continue
538 op.append("\"{m0.name}\"->\"{c.name}\";\n")
539 end
540 end
541 end
542 for m0 in ms do
543 # Close the nesting subgraph
544 if m0 != null then
545 op.append("\}\n")
546 end
547 end
548 for c in cla do
549 for c2 in c.tmhe.direct_greaters do
550 if not cla.has(c2) then continue
551 op.append("\"{c.name}\"->\"{c2.name}\";\n")
552 end
553 end
554 op.append("\}\n")
555 dctx.gen_dot(op.to_s, name.to_s)
556 dctx.add("</section>")
557
558 var clas = new Array[MMLocalClass]
559 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
560 var gprops = new Array[MMLocalProperty]
561 do
562 var m = self
563 for g in m.global_classes do
564 var lc = m[g]
565 if not lc.require_doc(dctx) then continue
566 var im = g.intro.mmmodule
567 if self.visibility_for(im) <= 1 then continue # private import or invisible import
568 var keep = false
569 for lc2 in lc.crhe.greaters_and_self do
570 if not lc2 isa MMSrcLocalClass then continue
571 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
572 keep = true
573 end
574 if not keep then continue
575 clas.add(self[g])
576 for gp in lc.global_properties do
577 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
578 var lp = lc[gp]
579 var mp = lp.local_class.mmmodule
580 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
581 lp = self[g][gp]
582 if not lp.require_doc(dctx) then continue
583 if props.has_key(lp.global) then
584 if not props[lp.global].has(lp) then
585 props[lp.global].add(lp)
586 end
587 else
588 props[lp.global] = [lp]
589 gprops.add(lp.global.intro)
590 end
591 end
592 end
593 end
594 dctx.add("<section class=\"module\">\n")
595 dctx.open_stage
596 dctx.stage("<article class=\"classes filterable\">\n")
597 dctx.stage("<h2>Classes</h2>\n")
598 dctx.sort(clas)
599 dctx.stage("<ul>\n")
600 for lc in clas do
601 if self.mnhe <= lc.global.intro.mmmodule then
602 dctx.add("<li class='intro'><span title='introduced in this module'>I</span>&nbsp;")
603 else
604 dctx.add("<li class='redef'><span title='refined in this module'>R</span>&nbsp;")
605 end
606 dctx.add("{lc.html_link(dctx)}</li>\n")
607 end
608 dctx.stage("</ul></article>\n")
609 dctx.close_stage
610
611 dctx.open_stage
612 dctx.stage("<article class=\"properties filterable\">\n")
613 dctx.stage("<h2>Properties</h2>\n")
614 dctx.sort(gprops)
615 dctx.stage("<ul>\n")
616 for lgp in gprops do
617 var gp = lgp.global
618 var lps = props[gp]
619
620 if gp.intro isa MMAttribute then continue
621
622 var lpi = self[gp.intro.local_class.global][gp]
623
624 if lps.has(lpi) then
625 dctx.add("<li class='intro'><span title='introduction in an other module'>I</span>&nbsp;<a href=\"{lpi.local_class.html_name}.html#{lpi.html_anchor}\">{lpi}&nbsp;({lpi.local_class})</a></li>\n")
626 lps.remove(lpi)
627 else
628 dctx.add("<li class='intro'><span title='introduction in this module'>I</span>&nbsp;{lpi}")
629 dctx.add("&nbsp;({lpi.local_class})</li>\n")
630 end
631 if lps.length >= 1 then
632 dctx.sort(lps)
633 for lp in lps do
634 dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;<a href=\"{lp.local_class.html_name}.html#{lp.html_anchor}\">{lp}&nbsp;({lp.local_class})</a></li>")
635 end
636 end
637 end
638 dctx.stage("</ul></article>\n")
639 dctx.close_stage
640
641
642 dctx.add("</div>\n")
643 dctx.add("</div>\n")
644 end
645
646 # Fill the body for the page associated to the full index
647 fun file_index_page_doc(dctx: DocContext)
648 do
649
650 dctx.add("<h1>Full Index</h1>\n")
651
652 var clas = new Array[MMLocalClass]
653 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
654 var gprops = new Array[MMLocalProperty]
655 var mods = new Array[MMModule]
656 for m in mhe.greaters_and_self do
657 if not m.require_doc(dctx) then continue
658 mods.add(m)
659 end
660 for g in global_classes do
661 var lc = self[g]
662 if not lc.require_doc(dctx) then continue
663 clas.add(lc)
664 for gp in lc.global_properties do
665 var lp = lc[gp]
666 if not lp.require_doc(dctx) then continue
667 if props.has_key(lp.global) then
668 if not props[lp.global].has(lp) then
669 props[lp.global].add(lp)
670 end
671 else
672 props[lp.global] = [lp]
673 gprops.add(lp.global.intro)
674 end
675 end
676 end
677 dctx.open_stage
678 dctx.stage("<article class=\"modules filterable\">\n")
679 dctx.stage("<h2>Modules</h2>\n")
680 dctx.sort(mods)
681 dctx.stage("<ul>\n")
682 for m in mods do
683 dctx.add("<li>{m.html_link(dctx)}</li>")
684 end
685 dctx.stage("</ul></article>\n")
686 dctx.close_stage
687
688 dctx.open_stage
689 dctx.stage("<article class=\"classes filterable\">\n")
690 dctx.stage("<h2>Classes</h2>\n")
691 dctx.sort(clas)
692 dctx.stage("<ul>\n")
693 for lc in clas do
694 dctx.add("<li>{lc.html_link(dctx)}</li>")
695 end
696 dctx.stage("</ul></article>\n")
697 dctx.close_stage
698
699 dctx.open_stage
700 dctx.stage("<article class=\"properties filterable\">\n")
701 dctx.stage("<h2>Properties</h2>\n")
702 dctx.sort(gprops)
703 dctx.stage("<ul>\n")
704 for lgp in gprops do
705 var gp = lgp.global
706 var lps = props[gp]
707
708 if gp.intro isa MMAttribute then continue
709
710 var lpi = self[gp.intro.local_class.global][gp]
711
712 lps.remove(lpi)
713 dctx.add("<li class='intro'><span title='introduction'>I</span>&nbsp;<a href=\"{lpi.local_class.html_name}.html#{lpi.html_anchor}\">{lpi}&nbsp;({lpi.local_class})</a></li>\n")
714 if lps.length >= 1 then
715 dctx.sort(lps)
716 for lp in lps do
717 dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;<a href=\"{lp.local_class.html_name}.html#{lp.html_anchor}\">{lp}&nbsp;({lp.local_class})</a></li>\n")
718 end
719 end
720 end
721 dctx.stage("</ul></article>\n")
722 dctx.close_stage
723 end
724 end
725
726 redef class MMLocalProperty
727 super MMEntity
728 # Anchor of the property description in the module html file
729 fun html_anchor: String
730 do
731 return "PROP_{local_class}_{cmangle(name)}"
732 end
733
734 redef fun html_link(dctx)
735 do
736 if not require_doc(dctx) then print "not required {self}"
737 return "<a href=\"{local_class.html_name}.html#{html_anchor}\">{self}</a>"
738 end
739
740 fun html_link_special(dctx: DocContext, lc: MMLocalClass): String
741 do
742 if not require_doc(dctx) then print "not required {self}"
743 return "<a href=\"{lc.html_name}.html#{html_anchor}\">{self}</a>"
744 end
745
746 # Kind of property (fun, attr, etc.)
747 fun kind: String is abstract
748
749 redef fun short_doc
750 do
751 var d = doc
752 if d != null then
753 return d.short
754 else if global.intro == self then
755 return "&nbsp;"
756 else
757 return global.intro.short_doc
758 end
759 end
760
761 redef fun doc
762 do
763 var n = node
764 if n == null or not n isa APropdef then
765 return null
766 end
767 var d = n.n_doc
768 if d == null then
769 return null
770 end
771 if d.n_comment.is_empty then
772 return null
773 else
774 return d
775 end
776 end
777
778 # The most specific module in the nesting hierarchy that exports the intro of self
779 fun intro_module: MMModule
780 do
781 var m = global.intro.mmmodule
782 var mo = m.direct_owner
783 while mo != null and mo.visibility_for(m) >= 2 do
784 m = mo
785 mo = m.direct_owner
786 end
787 return m
788 end
789
790 # Is the intro of self exported by the top-level module ?
791 fun is_toplevel: Bool
792 do
793 var m = intro_module
794 return m == m.toplevel_owner
795 end
796
797 # Return true if the global class must be documented according to the visibility configured
798 fun require_doc(dctx: DocContext): Bool
799 do
800 if global.visibility_level == 3 then return false # Private
801 if dctx.public_only then
802 var m = intro_module
803 if m != m.toplevel_owner then return false # Unexported
804 end
805 return true
806 end
807
808 # Document the global property in the global class lc
809 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
810 do
811 var visibility: String
812 if global.visibility_level == 1 then
813 visibility = "public"
814 else if global.visibility_level == 2 then
815 visibility = "protected"
816 else if global.visibility_level == 3 then
817 visibility = "private"
818 else
819 abort
820 end
821
822 var intro_class = global.intro.local_class
823 var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner
824
825 dctx.add("<article id=\"{html_anchor}\" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\">\n")
826 dctx.add("<h3 class=\"signature\">{name}{signature.to_html(dctx, true)}</h3>\n")
827 dctx.add("<div class=\"info\">\n")
828 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
829
830 if is_redef then
831 dctx.add("redef ")
832 end
833 if not is_toplevel then
834 dctx.add("(unexported) ")
835 end
836 if global.visibility_level == 2 then
837 dctx.add("protected ")
838 else if global.visibility_level == 3 then
839 dctx.add("private ")
840 end
841 dctx.add(kind)
842 dctx.add(" {intro_class.mmmodule.toplevel_owner.name}")
843 if intro_class.global == lc.global then
844 dctx.add("::{lc.name}")
845 else
846 dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}")
847 end
848 if is_redef then
849 dctx.add("::{mmmodule[intro_class.global][global].html_link(dctx)}")
850 else
851 dctx.add("::{name}")
852 end
853 dctx.add("</div>")
854
855 dctx.add("<div class=\"description\">")
856
857 # Collect all refinement of the global property in the same global property
858 var lps = new Array[MMLocalProperty]
859 for l in prhe.greaters_and_self do
860 lps.add(l)
861 end
862
863 var introdoc = false
864 if global.intro.doc != null then
865 for lp in lps do
866 if lp.doc == null then introdoc = true
867 end
868 end
869 if introdoc then
870 dctx.add("<pre>{global.intro.doc.to_html}</pre>")
871 end
872
873 var tlmods = new Array[MMModule]
874 for lp in lps do
875 var bm = lp.mmmodule.toplevel_owner
876 var lcm = lc.global.intro.mmmodule
877 if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner
878 if not tlmods.has(bm) then tlmods.add(bm)
879 end
880
881 for tm in tlmods do
882 # Document the top level property for the current top level module
883 var tlp
884 if tm.global_classes.has(lc.global) then
885 tlp = tm[lc.global][self.global]
886 assert lps.has(tlp)
887 else if tm.global_classes.has(self.local_class.global) then
888 # Self is the inherited property. Process it
889 tlp = tm[self.local_class.global][self.global]
890 assert lps.has(tlp)
891 else
892 # We skip this module since the props defined by the module is
893 continue
894 end
895
896 var tlcm = lc.global.intro.mmmodule.toplevel_owner
897 if not tlcm.mhe <= tm then
898 dctx.add("<h4>In module {tm.html_link(dctx)} :</h4>")
899 end
900
901 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
902
903 var doc = tlp.doc
904 if doc != null and (not introdoc or global.intro.doc != doc) then
905 dctx.add("<pre>{doc.to_html}</pre>")
906 end
907 dctx.add("<p>")
908 if tlp.local_class.global != lc.global then
909 dctx.add("inherited from {tlp.local_class.html_link(dctx)} ")
910 end
911 if tm != tlp.mmmodule then
912 dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ")
913 end
914 var n = tlp.node
915 if n != null then
916 var l = n.location
917 dctx.show_source(l)
918 end
919
920 dctx.open_stage
921 dctx.stage(". previously defined by:")
922 for lp in lps do
923 var tl = lp.mmmodule.toplevel_owner
924 if tl != tm then continue
925 if lp == tlp then continue
926 dctx.add(" {lp.mmmodule.html_link(dctx)}")
927 if lp.local_class.global != lc.global then
928 dctx.add(" for {lp.local_class.html_link(dctx)}")
929 end
930
931 n = lp.node
932 if n != null then
933 var l = n.location
934 dctx.show_source(l)
935 end
936
937 #var doc = lp.doc
938 #if doc != null and (not introdoc or global.intro.doc != doc) then
939 # dctx.add("<pre>{doc.to_html}</pre>")
940 #end
941 end
942 dctx.close_stage
943 dctx.add("</p>")
944 end
945 dctx.add("</div>")
946 dctx.add("</article>")
947 end
948 end
949 redef class MMMethod
950 redef fun kind do return if global.is_init then "init" else "fun"
951 end
952 redef class MMAttribute
953 redef fun kind do return "var"
954 end
955 redef class MMTypeProperty
956 redef fun kind do return "type"
957 end
958
959 redef class MMSrcModule
960 redef fun short_doc
961 do
962 var d = doc
963 if d != null then
964 return d.short
965 else
966 return "&nbsp;"
967 end
968 end
969
970 redef fun doc
971 do
972 var n = node
973 if n.n_moduledecl == null then
974 return null
975 end
976 var np = n.n_moduledecl
977 var d = np.n_doc
978 if d == null then
979 return null
980 end
981 if d.n_comment.is_empty then
982 return null
983 else
984 return d
985 end
986 end
987 end
988
989 redef class ADoc
990 # Html transcription of the doc
991 fun to_html: String
992 do
993 var res = new Buffer
994 for c in n_comment do
995 res.append(c.text.substring_from(1))
996 end
997 return res.to_s
998 end
999
1000 # Oneliner transcription of the doc
1001 fun short: String
1002 do
1003 return n_comment.first.text.substring_from(1)
1004 end
1005 end
1006
1007 redef class MMLocalClass
1008 super MMEntity
1009
1010 # Anchor of the class description in the module html file
1011 fun html_anchor: String do return "CLASS_{self}"
1012
1013 fun html_name: String do return "{self}"
1014
1015 redef fun html_link(dctx)
1016 do
1017 if not require_doc(dctx) then print "{dctx.filename}: not required {self}"
1018 return "<a href=\"{html_name}.html\">{self}</a>"
1019 end
1020
1021 redef fun short_doc do return global.intro.short_doc
1022
1023 redef fun doc do return global.intro.doc
1024
1025 fun kind: String
1026 do
1027 if global.is_interface then
1028 return "interface"
1029 else if global.is_abstract then
1030 return "abstract class"
1031 else if global.is_enum then
1032 return "enum"
1033 else
1034 return "class"
1035 end
1036 end
1037
1038 # The most specific module in the nesting hierarchy that exports the intro of self
1039 fun intro_module: MMModule
1040 do
1041 var m = global.intro.mmmodule
1042 var mo = m.direct_owner
1043 while mo != null and mo.visibility_for(m) >= 2 do
1044 m = mo
1045 mo = m.direct_owner
1046 end
1047 return m
1048 end
1049
1050 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1051 do
1052 if p.local_class.global != self.global then
1053 if p.global.intro.local_class.name == "Object".to_symbol then return
1054 if p.global.is_init or p isa MMTypeProperty then
1055 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1056 else
1057 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link(dctx)}</li>\n")
1058 end
1059 else if p.global.intro.local_class.global == self.global then
1060 dctx.add("<li class='intro'><span title='Introduced'>I</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1061 else
1062 dctx.add("<li class='redef'><span title='Redefined'>R</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1063 end
1064 end
1065
1066 # Return true if the global class must be documented according to the visibility configured
1067 fun require_doc(dctx: DocContext): Bool
1068 do
1069 if global.visibility_level == 3 then return false # Private
1070 if dctx.public_only then
1071 var m = intro_module
1072 if m != m.toplevel_owner then return false # Unexported
1073 end
1074 return true
1075 end
1076
1077 # Fill the body for the page associated to the global class
1078 fun file_page_doc(dctx: DocContext)
1079 do
1080 dctx.add("<div class=\"menu\">\n")
1081
1082 var props = new Array[MMLocalProperty]
1083 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1084 var inhs = new Array[MMLocalClass]
1085 for g in global_properties do
1086 var p = self[g]
1087 if not p.require_doc(dctx) then continue
1088 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1089 props.add(p)
1090 else
1091 var lc = mmmodule[p.local_class.global]
1092 if inh.has_key(lc) then
1093 inh[lc].add(p)
1094 else
1095 inh[lc] = [p]
1096 inhs.add(lc)
1097 end
1098 props.add(p)
1099 end
1100 end
1101 dctx.sort(props)
1102
1103 dctx.add("<nav class=\"properties filterable\">\n")
1104 dctx.add("<h3>Properties</h3>\n")
1105 dctx.open_stage
1106 dctx.stage("<h4>Virtual Types</h4>\n<ul>\n")
1107 for p in props do
1108 if p isa MMTypeProperty then
1109 menu_link(dctx, p)
1110 end
1111 end
1112 dctx.stage("</ul>\n")
1113 dctx.close_stage
1114 dctx.open_stage
1115 dctx.stage("<h4>Constructors</h4>\n<ul>\n")
1116 for p in props do
1117 if p.global.is_init_for(self) then
1118 menu_link(dctx, p)
1119 end
1120 end
1121 dctx.stage("</ul>\n")
1122 dctx.close_stage
1123 dctx.open_stage
1124 dctx.stage("<h4>Methods</h4>\n<ul>\n")
1125 for p in props do
1126 if not p.global.is_init and p isa MMMethod then
1127 menu_link(dctx, p)
1128 end
1129 end
1130 dctx.stage("</ul>\n")
1131 dctx.close_stage
1132 dctx.add("</nav>\n")
1133
1134 dctx.add("<nav class=\"inheritance filterable\">\n")
1135 dctx.add("<h3>Inheritance</h3>\n")
1136 dctx.add("<h4>Superclasses</h3>\n<ul>\n")
1137 for lc in cshe.linear_extension do
1138 if lc == self then continue
1139 if not lc.require_doc(dctx) then continue
1140 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1141 end
1142 dctx.add("</ul>\n")
1143 if cshe.smallers.length == 0 then
1144 dctx.add("<h4>No Known Subclasses</h4>\n")
1145 else if cshe.smallers.length <= 100 then
1146 dctx.add("<h4>Subclasses</h4>\n")
1147 dctx.add("<ul>\n")
1148 for lc in cshe.smallers do
1149 if not lc.require_doc(dctx) then continue
1150 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1151 end
1152 dctx.add("</ul>\n")
1153 else if cshe.direct_smallers.length <= 100 then
1154 dctx.add("<h4>Direct Subclasses Only</h4>\n<ul>\n")
1155 for lc in cshe.direct_smallers do
1156 if not lc.require_doc(dctx) then continue
1157 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1158 end
1159 dctx.add("</ul>\n")
1160 else
1161 dctx.add("<h4>Too much Subclasses to list</h4>\n")
1162 end
1163 dctx.add("</nav>\n")
1164
1165 dctx.add("</div>\n")
1166
1167
1168 dctx.add("<div class=\"content\">\n")
1169 dctx.add("<h1>{name}</h1>\n")
1170 dctx.add("<div class='subtitle'>")
1171 if global.visibility_level == 2 then
1172 abort
1173 else if global.visibility_level == 3 then
1174 dctx.add("private ")
1175 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1176 dctx.add("(unexported) ")
1177 end
1178 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div>")
1179
1180 dctx.add("<section class=\"description\">\n")
1181 var doc = doc
1182 if doc != null then
1183 dctx.add("<pre>{doc.to_html}</pre>\n")
1184 end
1185
1186 var cla = new HashSet[MMLocalClass]
1187 var sm = new HashSet[MMLocalClass]
1188 var sm2 = new HashSet[MMLocalClass]
1189 sm.add(self)
1190 while cla.length + sm.length < 10 and sm.length > 0 do
1191 cla.add_all(sm)
1192 sm2.clear
1193 for x in sm do
1194 sm2.add_all(x.cshe.direct_smallers)
1195 end
1196 var t = sm
1197 sm = sm2
1198 sm2 = t
1199 end
1200 cla.add_all(cshe.greaters_and_self)
1201
1202 var op = new Buffer
1203 op.append("digraph {name} \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
1204 for c in cla do
1205 if c == self then
1206 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1207 else
1208 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
1209 end
1210 for c2 in c.cshe.direct_greaters do
1211 if not cla.has(c2) then continue
1212 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1213 end
1214 if not c.cshe.direct_smallers.is_empty then
1215 var others = true
1216 for c2 in c.cshe.direct_smallers do
1217 if cla.has(c2) then others = false
1218 end
1219 if others then
1220 op.append("\"{c.name}...\"[label=\"\"];\n")
1221 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1222 end
1223 end
1224 end
1225 op.append("\}\n")
1226 dctx.gen_dot(op.to_s, name.to_s)
1227
1228
1229 var mods = new Array[MMModule]
1230 mods.add(global.intro.mmmodule.toplevel_owner)
1231 for lc in crhe.greaters do
1232 if not lc isa MMSrcLocalClass then continue
1233 var m = lc.mmmodule.toplevel_owner
1234 if not mods.has(m) then mods.add(m)
1235 end
1236 dctx.sort(mods)
1237 for m in mods do
1238 if m == global.intro.mmmodule.toplevel_owner then
1239 dctx.add("<p>Introduced by {m.html_link(dctx)}")
1240 else
1241 dctx.add("<p>Refined by {m.html_link(dctx)}")
1242 end
1243 dctx.open_stage
1244 dctx.stage(". Definition in:")
1245 for lc in crhe.greaters do
1246 if lc.mmmodule.toplevel_owner != m then continue
1247 dctx.add(" {lc.mmmodule.html_link(dctx)}")
1248 assert lc isa MMSrcLocalClass
1249 var n = lc.node
1250 if n != null then
1251 dctx.show_source(n.location)
1252 end
1253 end
1254 dctx.close_stage
1255 dctx.add("</p>\n")
1256 end
1257 dctx.add("</ul>\n")
1258
1259 dctx.add("</section>\n")
1260
1261 dctx.open_stage
1262 dctx.stage("<section class=\"types\">\n")
1263 dctx.stage("<h2>Formal and Virtual Types</h2>\n")
1264 for i in [0..arity[ do
1265 var f = get_formal(i)
1266 f.full_documentation(dctx, self)
1267 end
1268 for p in props do
1269 if not p isa MMTypeProperty then continue
1270 p.full_documentation(dctx, self)
1271 end
1272 dctx.stage("</section>\n")
1273 dctx.close_stage
1274
1275 dctx.open_stage
1276 dctx.stage("<section class=\"constructors\">\n")
1277 dctx.stage("<h2 class=\"section-header\">Constructors</h2>\n")
1278 for p in props do
1279 if not p.global.is_init_for(self) then continue
1280 p.full_documentation(dctx, self)
1281 end
1282 dctx.stage("</section>\n")
1283 dctx.close_stage
1284
1285 dctx.open_stage
1286 dctx.stage("<section class=\"methods\">\n")
1287 dctx.stage("<h2 class=\"section-header\">Methods</h2>\n")
1288 for p in props do
1289 if p.global.is_init then continue
1290 if p.local_class.global != self.global then continue
1291 if not p isa MMMethod then continue
1292 p.full_documentation(dctx, self)
1293 end
1294 if not inhs.is_empty then
1295 dctx.open_stage
1296 dctx.stage("<h3>Inherited Methods</h4>\n")
1297 for lc in inhs do
1298 dctx.open_stage
1299 dctx.stage("<p>Defined in {lc.html_link(dctx)}:")
1300 for p in inh[lc] do
1301 if p.global.is_init then continue
1302 if not p isa MMMethod then continue
1303 dctx.add(" {p.html_link(dctx)}")
1304 end
1305 dctx.stage("</p>")
1306 dctx.close_stage
1307 end
1308 dctx.close_stage
1309 end
1310 dctx.add("</section>\n")
1311 dctx.close_stage
1312 dctx.add("</div> <!-- end class {name} -->\n")
1313 end
1314 end
1315
1316 redef class MMSrcLocalClass
1317 redef fun short_doc
1318 do
1319 var d = doc
1320 if d != null then
1321 return d.short
1322 else if global.intro == self then
1323 return "&nbsp;"
1324 else
1325 var bc = global.intro
1326 return bc.short_doc
1327 end
1328 end
1329
1330 redef fun doc
1331 do
1332 var n = node
1333 if not n isa AStdClassdef then
1334 return null
1335 end
1336 var d = n.n_doc
1337 if d == null then
1338 return null
1339 end
1340 if d.n_comment.is_empty then
1341 return null
1342 else
1343 return d
1344 end
1345 end
1346 end
1347
1348 redef class MMSignature
1349 # Htlm transcription of the signature (with nested links)
1350 fun to_html(dctx: DocContext, with_closure: Bool): String
1351 do
1352 var res = new Buffer
1353 if arity > 0 then
1354 res.append("(")
1355 res.append(self.params[0].name.to_s)
1356 res.append(": ")
1357 res.append(self[0].html_link(dctx))
1358 for i in [1..arity[ do
1359 res.append(", ")
1360 res.append(self.params[i].name.to_s)
1361 res.append(": ")
1362 res.append(self[i].html_link(dctx))
1363 end
1364 res.append(")")
1365 end
1366 if return_type != null then
1367 res.append(": ")
1368 res.append(return_type.html_link(dctx))
1369 end
1370 if with_closure then
1371 for c in closures do
1372 res.append(" ")
1373 if c.is_optional then res.append("[")
1374 if c.is_break then res.append("break ")
1375 res.append("!{c.name}")
1376 res.append(c.signature.to_html(dctx, false))
1377 if c.is_optional then res.append("]")
1378 end
1379 end
1380 return res.to_s
1381 end
1382 end
1383
1384 redef class MMType
1385 # Htlm transcription of the type (with nested links)
1386 fun html_link(dctx: DocContext): String do return to_s
1387 end
1388
1389 redef class MMTypeSimpleClass
1390 redef fun html_link(dctx) do return local_class.html_link(dctx)
1391 end
1392
1393 redef class MMTypeGeneric
1394 redef fun html_link(dctx)
1395 do
1396 var res = new Buffer
1397 res.append(local_class.html_link(dctx))
1398 res.append("[")
1399 res.append(params[0].html_link(dctx))
1400 for i in [1..params.length[ do
1401 res.append(", ")
1402 res.append(params[i].html_link(dctx))
1403 end
1404 res.append("]")
1405 return res.to_s
1406 end
1407 end
1408
1409 redef class MMTypeFormalParameter
1410 fun html_anchor: String
1411 do
1412 return "FT_{local_class}_{cmangle(name)}"
1413 end
1414 redef fun html_link(dctx)
1415 do
1416 return "<a href=\"#{html_anchor}\">{name}</a>"
1417 end
1418 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1419 do
1420 dctx.add("<article id=\"{html_anchor}\">\n")
1421 dctx.add("<h3 class=\"signature\">{name}: {bound.html_link(dctx)}</h3>\n")
1422 dctx.add("<div class=\"info\">")
1423 dctx.add("formal generic type")
1424 dctx.add("</div>")
1425 dctx.add("</article>")
1426 end
1427 end
1428
1429 redef class MMNullableType
1430 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1431 end
1432
1433 redef class MMVirtualType
1434 redef fun html_link(dctx) do return property.html_link(dctx)
1435 end
1436
1437 var c = new DocContext
1438 c.exec_cmd_line