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