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