nitdoc: make the menu follows the scroll.
[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("in {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 return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
469 end
470
471 redef fun json_entry(dctx) do
472 return "\{txt:\"{self.qualified_name}\",url:\"{html_name}.html\"\},"
473 end
474
475 redef fun qualified_name do
476 var buffer = new Buffer
477 for m in mnhe.smallers do
478 buffer.append("{m.html_name}::")
479 end
480 buffer.append("{self.name}")
481 return buffer.to_s
482 end
483
484 fun require_doc(dctx: DocContext): Bool
485 do
486 if dctx.public_only and not is_toplevel then return false
487 return true
488 end
489
490 # Return true if the module is a top-level owner or a top-level module
491 fun is_toplevel: Bool
492 do
493 var pd = directory.parent
494 return pd == null or (pd.owner == null and directory.owner == self)
495 end
496
497 # Element in the module nesting tree
498 fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null)
499 var mnhe_: nullable PartialOrderElement[MMModule] = null
500
501 # Element in the top level module importation hierarchy
502 fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null)
503 var tmhe_: nullable PartialOrderElement[MMModule] = null
504
505 fun toplevel_owner: MMModule
506 do
507 var m = self
508 loop
509 var ds = m.mnhe.direct_smallers
510 if ds.length == 0 then return m
511 if ds.length == 1 then m = ds.first else abort
512 end
513 end
514
515 fun html_name: String
516 do
517 return "{name}"
518 end
519
520 fun direct_owner: nullable MMModule
521 do
522 var d = directory
523 while d.owner == self do d = d.parent.as(not null)
524 return d.owner
525 end
526
527 # Fill the body for the page associated to the module
528 fun file_page_doc(dctx: DocContext)
529 do
530 dctx.add("<div class=\"menu\">\n")
531
532 var mods = new Array[MMModule]
533 mods = self.mhe.greaters.to_a
534 dctx.sort(mods)
535
536 dctx.open_stage
537 dctx.stage("<nav>\n")
538 dctx.stage("<h3>Module Hierarchy</h3>\n")
539 dctx.stage("<h4>All dependencies</h4>\n")
540 dctx.stage("<ul>\n")
541 for mod in mods do
542 if not mod.require_doc(dctx) then continue
543 if self.mnhe <= mod then continue # do not want nested stuff
544 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
545 dctx.add("<li>{mod.html_link(dctx)}</li>")
546 end
547 dctx.stage("</ul>\n")
548
549 mods = self.mhe.smallers.to_a
550 dctx.sort(mods)
551 dctx.stage("<h4>All clients</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 dctx.stage("</nav>\n")
561 dctx.close_stage
562
563 if not dctx.public_only then
564 mods = self.mnhe.direct_greaters.to_a
565 dctx.sort(mods)
566 dctx.open_stage
567 dctx.stage("<nav>\n")
568 dctx.stage("<h3>Nested Modules</h3><ul>\n")
569 for mod in mods do
570 if not mod.require_doc(dctx) then continue
571 dctx.add("<li>{mod.html_link(dctx)}</li>")
572 end
573 dctx.stage("</ul></nav>\n")
574 dctx.close_stage
575 end
576
577 dctx.add("</div>") # metadata
578
579 dctx.add("<div class=\"content\">\n")
580 dctx.add("<h1>{name}</h1>\n")
581 dctx.add("<div class='subtitle'>module ")
582 for m in mnhe.smallers do
583 dctx.add("{m.html_link(dctx)}::")
584 end
585 dctx.add("{self.name}</div>\n")
586
587 dctx.add("<section class='description'>\n")
588
589 var doc = doc
590 if doc != null then
591 dctx.add("<div id=\"description\">\n")
592 dctx.add("<pre>{doc.to_html}</pre>\n")
593 dctx.add("</div>\n")
594 end
595
596 var op = new Buffer
597 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")
598 var ms = new Array[nullable MMModule]
599 do
600 var m0: nullable MMModule = self
601 while m0 != null do
602 m0 = m0.direct_owner
603 ms.add(m0)
604 end
605 end
606 var cla = new HashSet[MMModule]
607 cla.add(self)
608 for m0 in self.mhe.greaters do
609 if not m0.require_doc(dctx) then continue
610 if self.visibility_for(m0) <= 1 then continue # private or hidden
611 if self.mnhe <= m0 then continue # do not want nested stuff
612 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
613 cla.add(m0)
614 end
615 for m0 in self.mhe.smallers do
616 if not m0.require_doc(dctx) then continue
617 if m0.visibility_for(self) <= 1 then continue # private or hidden
618 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
619 cla.add(m0)
620 end
621 for m0 in self.mnhe.smallers do
622 cla.add(m0)
623 end
624 ms = ms.reversed
625 for m0 in ms do
626 if m0 != null then
627 op.append("subgraph \"cluster_{m0.name}\"\{\n")
628 end
629 for c in cla do
630 if c.direct_owner != m0 then continue
631 if c == self then
632 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
633 else
634 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
635 end
636 end
637 if m0 != null then
638 op.append("\"{m0.name}\"[URL=\"{m0.html_name}.html\"];\n")
639 for c in m0.mhe.direct_greaters do
640 if not cla.has(c) then continue
641 op.append("\"{m0.name}\"->\"{c.name}\";\n")
642 end
643 end
644 end
645 for m0 in ms do
646 # Close the nesting subgraph
647 if m0 != null then
648 op.append("\}\n")
649 end
650 end
651 for c in cla do
652 for c2 in c.tmhe.direct_greaters do
653 if not cla.has(c2) then continue
654 op.append("\"{c.name}\"->\"{c2.name}\";\n")
655 end
656 end
657 op.append("\}\n")
658 dctx.gen_dot(op.to_s, name.to_s, "Dependency graph for module {name}")
659 dctx.add("</section>")
660
661 var clas = new Array[MMLocalClass]
662 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
663 var gprops = new Array[MMLocalProperty]
664 do
665 var m = self
666 for g in m.global_classes do
667 var lc = m[g]
668 if not lc.require_doc(dctx) then continue
669 var im = g.intro.mmmodule
670 if self.visibility_for(im) <= 1 then continue # private import or invisible import
671 var keep = false
672 for lc2 in lc.crhe.greaters_and_self do
673 if not lc2 isa MMSrcLocalClass then continue
674 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
675 keep = true
676 end
677 if not keep then continue
678 clas.add(self[g])
679 lc.compute_super_classes
680 for gp in lc.global_properties do
681 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
682 var lp = lc[gp]
683 var mp = lp.local_class.mmmodule
684 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
685 lp = self[g][gp]
686 if not lp.require_doc(dctx) then continue
687 if props.has_key(lp.global) then
688 if not props[lp.global].has(lp) then
689 props[lp.global].add(lp)
690 end
691 else
692 props[lp.global] = [lp]
693 gprops.add(lp.global.intro)
694 end
695 end
696 end
697 end
698 dctx.add("<section class=\"module\">\n")
699 dctx.open_stage
700 dctx.stage("<article class=\"classes filterable\">\n")
701 dctx.stage("<h2>Classes</h2>\n")
702 dctx.sort(clas)
703 dctx.stage("<ul>\n")
704 for lc in clas do
705 if self.mnhe <= lc.global.intro.mmmodule then
706 dctx.add("<li class='intro'><span title='introduced in this module'>I</span>&nbsp;")
707 else
708 dctx.add("<li class='redef'><span title='refined in this module'>R</span>&nbsp;")
709 end
710 dctx.add("{lc.html_link(dctx)}</li>\n")
711 end
712 dctx.stage("</ul></article>\n")
713 dctx.close_stage
714
715 dctx.open_stage
716 dctx.stage("<article class=\"properties filterable\">\n")
717 dctx.stage("<h2>Properties</h2>\n")
718 dctx.sort(gprops)
719 dctx.stage("<ul>\n")
720 for lgp in gprops do
721 var gp = lgp.global
722 var lps = props[gp]
723
724 if gp.intro isa MMAttribute then continue
725
726 var lpi = self[gp.intro.local_class.global][gp]
727
728 if lps.has(lpi) then
729 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")
730 lps.remove(lpi)
731 else
732 dctx.add("<li class='intro'><span title='introduction in this module'>I</span>&nbsp;{lpi.html_name}")
733 dctx.add("&nbsp;({lpi.local_class})</li>\n")
734 end
735 if lps.length >= 1 then
736 dctx.sort(lps)
737 for lp in lps do
738 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>")
739 end
740 end
741 end
742 dctx.stage("</ul></article>\n")
743 dctx.close_stage
744 dctx.add("</section>\n")
745 dctx.add("</div>\n")
746 end
747
748 # Fill the body for the page associated to the full index
749 fun file_index_page_doc(dctx: DocContext)
750 do
751
752 dctx.add("<h1>Full Index</h1>\n")
753
754 var clas = new Array[MMLocalClass]
755 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
756 var gprops = new Array[MMLocalProperty]
757 var mods = new Array[MMModule]
758 for m in mhe.greaters_and_self do
759 if not m.require_doc(dctx) then continue
760 mods.add(m)
761 end
762 for g in global_classes do
763 var lc = self[g]
764 if not lc.require_doc(dctx) then continue
765 clas.add(lc)
766 for gp in lc.global_properties do
767 var lp = lc[gp]
768 if not lp.require_doc(dctx) then continue
769 if props.has_key(lp.global) then
770 if not props[lp.global].has(lp) then
771 props[lp.global].add(lp)
772 end
773 else
774 props[lp.global] = [lp]
775 gprops.add(lp.global.intro)
776 end
777 end
778 end
779 dctx.open_stage
780 dctx.stage("<article class=\"modules filterable\">\n")
781 dctx.stage("<h2>Modules</h2>\n")
782 dctx.sort(mods)
783 dctx.stage("<ul>\n")
784 for m in mods do
785 dctx.add("<li>{m.html_link(dctx)}</li>")
786 end
787 dctx.stage("</ul></article>\n")
788 dctx.close_stage
789
790 dctx.open_stage
791 dctx.stage("<article class=\"classes filterable\">\n")
792 dctx.stage("<h2>Classes</h2>\n")
793 dctx.sort(clas)
794 dctx.stage("<ul>\n")
795 for lc in clas do
796 dctx.add("<li>{lc.html_link(dctx)}</li>")
797 end
798 dctx.stage("</ul></article>\n")
799 dctx.close_stage
800
801 dctx.open_stage
802 dctx.stage("<article class=\"properties filterable\">\n")
803 dctx.stage("<h2>Properties</h2>\n")
804 dctx.sort(gprops)
805 dctx.stage("<ul>\n")
806 for lgp in gprops do
807 var gp = lgp.global
808 var lps = props[gp]
809
810 if gp.intro isa MMAttribute then continue
811
812 var lpi = self[gp.intro.local_class.global][gp]
813
814 lps.remove(lpi)
815 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")
816 if lps.length >= 1 then
817 dctx.sort(lps)
818 for lp in lps do
819 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")
820 end
821 end
822 end
823 dctx.stage("</ul></article>\n")
824 dctx.close_stage
825 end
826
827 # Fill the quicksearch list JSON object
828 fun file_quicksearch_list_doc(dctx: DocContext)
829 do
830 var entities = new HashMap[String, Array[MMEntity]]
831 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
832 for m in mhe.greaters_and_self do
833 if not m.require_doc(dctx) then continue
834 var a = new Array[MMEntity]
835 a.add(m)
836 entities[m.html_name] = a
837 end
838 for g in global_classes do
839 var lc = self[g]
840 if not lc.require_doc(dctx) then continue
841 var a = new Array[MMEntity]
842 a.add(lc)
843 entities[lc.html_name] = a
844 for gp in lc.global_properties do
845 var lp = lc[gp]
846 if not lp.require_doc(dctx) then continue
847 if lp.kind == "var" then continue
848 if props.has_key(lp.global) then
849 if not props[lp.global].has(lp) then
850 props[lp.global].add(lp)
851 end
852 else
853 props[lp.global] = [lp]
854 end
855 end
856 end
857
858 for k, v in props do
859 entities[k.short_name] = v
860 end
861
862 var keys = entities.keys.to_a
863 var sorter = new AlphaSorter[String]
864 sorter.sort(keys)
865
866 dctx.open_stage
867 dctx.stage("var entries = \{")
868 for key in keys do
869 dctx.add("\"{key}\": [")
870 for entity in entities[key] do
871 dctx.add(entity.json_entry(dctx))
872 end
873 dctx.add("],")
874 end
875 dctx.stage("\};")
876 dctx.close_stage
877 end
878 end
879
880 redef class MMGlobalProperty
881 # Return the short name of the property
882 fun short_name: String do
883 return self.intro.html_name
884 end
885 end
886
887 redef class MMLocalProperty
888 super MMEntity
889 # Anchor of the property description in the module html file
890 fun html_anchor: String
891 do
892 return "PROP_{local_class}_{cmangle(name)}"
893 end
894
895 redef fun json_entry(dctx) do
896 return "\{txt:\"{qualified_name}\",url:\"{local_class.html_name}.html#{html_anchor}\"\},"
897 end
898
899 redef fun qualified_name do
900 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
901 end
902
903 fun html_open_link(dctx: DocContext): String
904 do
905 if not require_doc(dctx) then print "not required {self}"
906 var title = "{html_name}{signature.to_s}"
907 if short_doc != "&nbsp;" then
908 title += " #{short_doc}"
909 end
910 return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">"
911 end
912
913 fun html_name: String
914 do
915 return self.name.to_s.html_escape
916 end
917
918 redef fun html_link(dctx)
919 do
920 if not require_doc(dctx) then print "not required {self}"
921 var title = "{html_name}{signature.to_s}"
922 if short_doc != "&nbsp;" then
923 title += " #{short_doc}"
924 end
925 return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
926 end
927
928 fun html_link_special(dctx: DocContext, lc: MMLocalClass): String
929 do
930 if not require_doc(dctx) then print "not required {self}"
931 var title = "{html_name}{signature_for(lc.get_type)}"
932 if short_doc != "&nbsp;" then
933 title += " #{short_doc}"
934 end
935 return "<a href=\"{lc.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
936 end
937
938 # Kind of property (fun, attr, etc.)
939 fun kind: String is abstract
940
941 redef fun short_doc
942 do
943 var d = doc
944 if d != null then
945 return d.short
946 else if global.intro == self then
947 return "&nbsp;"
948 else
949 return global.intro.short_doc
950 end
951 end
952
953 redef fun doc
954 do
955 var n = node
956 if n == null or not n isa APropdef then
957 return null
958 end
959 var d = n.n_doc
960 if d == null then
961 return null
962 end
963 if d.n_comment.is_empty then
964 return null
965 else
966 return d
967 end
968 end
969
970 # The most specific module in the nesting hierarchy that exports the intro of self
971 fun intro_module: MMModule
972 do
973 var m = global.intro.mmmodule
974 var mo = m.direct_owner
975 while mo != null and mo.visibility_for(m) >= 2 do
976 m = mo
977 mo = m.direct_owner
978 end
979 return m
980 end
981
982 # Is the intro of self exported by the top-level module ?
983 fun is_toplevel: Bool
984 do
985 var m = intro_module
986 return m == m.toplevel_owner
987 end
988
989 # Return true if the global property must be documented according to the visibility configured
990 fun require_doc(dctx: DocContext): Bool
991 do
992 if global.visibility_level == 3 and not dctx.with_private then return false # Private
993 if dctx.public_only then
994 var m = intro_module
995 if m != m.toplevel_owner then return false # Unexported
996 end
997 return true
998 end
999
1000 # Document the global property in the global class lc
1001 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1002 do
1003 var visibility: String
1004 if global.visibility_level == 1 then
1005 visibility = "public"
1006 else if global.visibility_level == 2 then
1007 visibility = "protected"
1008 else if global.visibility_level == 3 then
1009 visibility = "private"
1010 else
1011 abort
1012 end
1013
1014 var intro_class = global.intro.local_class
1015 var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner
1016
1017 dctx.add("<article id=\"{html_anchor}\" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\">\n")
1018 dctx.add("<h3 class=\"signature\">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1019 dctx.add("<div class=\"info\">\n")
1020 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
1021
1022 if is_redef then
1023 dctx.add("redef ")
1024 end
1025 if not is_toplevel then
1026 dctx.add("(unexported) ")
1027 end
1028 if global.visibility_level == 2 then
1029 dctx.add("protected ")
1030 else if global.visibility_level == 3 then
1031 dctx.add("private ")
1032 end
1033 dctx.add(kind)
1034 dctx.add(" {intro_class.mmmodule.toplevel_owner.name}")
1035 if intro_class.global == lc.global then
1036 dctx.add("::{lc.name}")
1037 else
1038 dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}")
1039 end
1040 if is_redef then
1041 dctx.add("::{mmmodule[intro_class.global][global].html_link(dctx)}")
1042 else
1043 dctx.add("::{html_name}")
1044 end
1045 dctx.add("</div>")
1046
1047 dctx.add("<div class=\"description\">")
1048
1049 # Collect all refinement of the global property in the same global property
1050 var lps = new Array[MMLocalProperty]
1051 for l in prhe.greaters_and_self do
1052 lps.add(l)
1053 end
1054
1055 var introdoc = false
1056 if global.intro.doc != null then
1057 for lp in lps do
1058 if lp.doc == null then introdoc = true
1059 end
1060 end
1061 if introdoc then
1062 dctx.add("<pre>{global.intro.doc.to_html}</pre>")
1063 end
1064
1065 var tlmods = new Array[MMModule]
1066 for lp in lps do
1067 var bm = lp.mmmodule.toplevel_owner
1068 var lcm = lc.global.intro.mmmodule
1069 if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner
1070 if not tlmods.has(bm) then tlmods.add(bm)
1071 end
1072
1073 for tm in tlmods do
1074 # Document the top level property for the current top level module
1075 var tlp
1076 if tm.global_classes.has(lc.global) then
1077 tlp = tm[lc.global][self.global]
1078 assert lps.has(tlp)
1079 else if tm.global_classes.has(self.local_class.global) then
1080 # Self is the inherited property. Process it
1081 tlp = tm[self.local_class.global][self.global]
1082 assert lps.has(tlp)
1083 else
1084 # We skip this module since the props defined by the module is
1085 continue
1086 end
1087
1088 var tlcm = lc.global.intro.mmmodule.toplevel_owner
1089 if not tlcm.mhe <= tm then
1090 dctx.add("<h4>In module {tm.html_link(dctx)} :</h4>")
1091 end
1092
1093 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
1094
1095 var doc = tlp.doc
1096 if doc != null and (not introdoc or global.intro.doc != doc) then
1097 dctx.add("<pre>{doc.to_html}</pre>")
1098 end
1099 dctx.add("<p>")
1100 if tlp.local_class.global != lc.global then
1101 dctx.add("inherited from {tlp.local_class.html_link(dctx)} ")
1102 end
1103 if tm != tlp.mmmodule then
1104 dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1105 end
1106 var n = tlp.node
1107 if n != null then
1108 var l = n.location
1109 dctx.show_source(l)
1110 end
1111
1112 dctx.open_stage
1113 dctx.stage(". previously defined by:")
1114 for lp in lps do
1115 var tl = lp.mmmodule.toplevel_owner
1116 if tl != tm then continue
1117 if lp == tlp then continue
1118 dctx.add(" {lp.mmmodule.html_link(dctx)}")
1119 if lp.local_class.global != lc.global then
1120 dctx.add(" for {lp.local_class.html_link(dctx)} ")
1121 end
1122
1123 n = lp.node
1124 if n != null then
1125 var l = n.location
1126 dctx.show_source(l)
1127 end
1128
1129 #var doc = lp.doc
1130 #if doc != null and (not introdoc or global.intro.doc != doc) then
1131 # dctx.add("<pre>{doc.to_html}</pre>")
1132 #end
1133 end
1134 dctx.close_stage
1135 dctx.add("</p>")
1136 end
1137 dctx.add("</div>")
1138 dctx.add("</article>")
1139 end
1140 end
1141 redef class MMMethod
1142 redef fun kind do return if global.is_init then "init" else "fun"
1143 end
1144 redef class MMAttribute
1145 redef fun kind do return "var"
1146 end
1147 redef class MMTypeProperty
1148 redef fun kind do return "type"
1149 end
1150
1151 redef class MMSrcModule
1152 redef fun short_doc
1153 do
1154 var d = doc
1155 if d != null then
1156 return d.short
1157 else
1158 return "&nbsp;"
1159 end
1160 end
1161
1162 redef fun doc
1163 do
1164 var n = node
1165 if n.n_moduledecl == null then
1166 return null
1167 end
1168 var np = n.n_moduledecl
1169 var d = np.n_doc
1170 if d == null then
1171 return null
1172 end
1173 if d.n_comment.is_empty then
1174 return null
1175 else
1176 return d
1177 end
1178 end
1179 end
1180
1181 redef class ADoc
1182 # Html transcription of the doc
1183 fun to_html: String
1184 do
1185 var res = new Buffer
1186 for c in n_comment do
1187 res.append(c.text.substring_from(1))
1188 end
1189 return res.to_s.html_escape
1190 end
1191
1192 # Oneliner transcription of the doc
1193 fun short: String
1194 do
1195 return n_comment.first.text.substring_from(1).html_escape
1196 end
1197 end
1198
1199 redef class MMLocalClass
1200 super MMEntity
1201
1202 # Anchor of the class description in the module html file
1203 fun html_anchor: String do return "CLASS_{self}"
1204
1205 fun html_name: String do return "{self}"
1206
1207 redef fun html_link(dctx)
1208 do
1209 if not require_doc(dctx) then print "{dctx.filename}: not required {self}"
1210 return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
1211 end
1212
1213 redef fun json_entry(dctx) do
1214 return "\{txt:\"{qualified_name}\",url:\"{html_name}.html\"\},"
1215 end
1216
1217 redef fun qualified_name do
1218 return "{intro_module.qualified_name}::{html_name}"
1219 end
1220
1221 redef fun short_doc do return global.intro.short_doc
1222
1223 redef fun doc do return global.intro.doc
1224
1225 fun kind: String
1226 do
1227 if global.is_interface then
1228 return "interface"
1229 else if global.is_abstract then
1230 return "abstract class"
1231 else if global.is_enum then
1232 return "enum"
1233 else
1234 return "class"
1235 end
1236 end
1237
1238 # The most specific module in the nesting hierarchy that exports the intro of self
1239 fun intro_module: MMModule
1240 do
1241 var m = global.intro.mmmodule
1242 var mo = m.direct_owner
1243 while mo != null and mo.visibility_for(m) >= 2 do
1244 m = mo
1245 mo = m.direct_owner
1246 end
1247 return m
1248 end
1249
1250 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1251 do
1252 if p.local_class.global != self.global then
1253 if p.global.intro.local_class.name == "Object".to_symbol then return
1254 if p.global.is_init or p isa MMTypeProperty then
1255 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1256 else
1257 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link(dctx)}</li>\n")
1258 end
1259 else if p.global.intro.local_class.global == self.global then
1260 dctx.add("<li class='intro'><span title='Introduced'>I</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1261 else
1262 dctx.add("<li class='redef'><span title='Redefined'>R</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1263 end
1264 end
1265
1266 # Return true if the global class must be documented according to the visibility configured
1267 fun require_doc(dctx: DocContext): Bool
1268 do
1269 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1270 if dctx.public_only then
1271 var m = intro_module
1272 if m != m.toplevel_owner then return false # Unexported
1273 end
1274 return true
1275 end
1276
1277 # Fill the body for the page associated to the global class
1278 fun file_page_doc(dctx: DocContext)
1279 do
1280 dctx.add("<div class=\"menu\">\n")
1281
1282 var props = new Array[MMLocalProperty]
1283 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1284 var inhs = new Array[MMLocalClass]
1285 for g in global_properties do
1286 var p = self[g]
1287 if not p.require_doc(dctx) then continue
1288 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1289 props.add(p)
1290 else
1291 var lc = mmmodule[p.local_class.global]
1292 if inh.has_key(lc) then
1293 inh[lc].add(p)
1294 else
1295 inh[lc] = [p]
1296 inhs.add(lc)
1297 end
1298 props.add(p)
1299 end
1300 end
1301 dctx.sort(props)
1302
1303 dctx.add("<nav class=\"properties filterable\">\n")
1304 dctx.add("<h3>Properties</h3>\n")
1305 dctx.open_stage
1306 dctx.stage("<h4>Virtual Types</h4>\n<ul>\n")
1307 for p in props do
1308 if p isa MMTypeProperty then
1309 menu_link(dctx, p)
1310 end
1311 end
1312 dctx.stage("</ul>\n")
1313 dctx.close_stage
1314 dctx.open_stage
1315 dctx.stage("<h4>Constructors</h4>\n<ul>\n")
1316 for p in props do
1317 if p.global.is_init_for(self) then
1318 menu_link(dctx, p)
1319 end
1320 end
1321 dctx.stage("</ul>\n")
1322 dctx.close_stage
1323 dctx.open_stage
1324 dctx.stage("<h4>Methods</h4>\n<ul>\n")
1325 for p in props do
1326 if not p.global.is_init and p isa MMMethod then
1327 menu_link(dctx, p)
1328 end
1329 end
1330 dctx.stage("</ul>\n")
1331 dctx.close_stage
1332 dctx.add("</nav>\n")
1333
1334 dctx.add("<nav class=\"inheritance filterable\">\n")
1335 dctx.add("<h3>Inheritance</h3>\n")
1336 dctx.add("<h4>Superclasses</h4>\n<ul>\n")
1337 for lc in cshe.linear_extension do
1338 if lc == self then continue
1339 if not lc.require_doc(dctx) then continue
1340 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1341 end
1342 dctx.add("</ul>\n")
1343 if cshe.smallers.length == 0 then
1344 dctx.add("<h4>No Known Subclasses</h4>\n")
1345 else if cshe.smallers.length <= 100 then
1346 dctx.add("<h4>Subclasses</h4>\n")
1347 dctx.add("<ul>\n")
1348 for lc in cshe.smallers do
1349 if not lc.require_doc(dctx) then continue
1350 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1351 end
1352 dctx.add("</ul>\n")
1353 else if cshe.direct_smallers.length <= 100 then
1354 dctx.add("<h4>Direct Subclasses Only</h4>\n<ul>\n")
1355 for lc in cshe.direct_smallers do
1356 if not lc.require_doc(dctx) then continue
1357 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1358 end
1359 dctx.add("</ul>\n")
1360 else
1361 dctx.add("<h4>Too much Subclasses to list</h4>\n")
1362 end
1363 dctx.add("</nav>\n")
1364
1365 dctx.add("</div>\n")
1366
1367
1368 dctx.add("<div class=\"content\">\n")
1369 dctx.add("<h1>{name}</h1>\n")
1370 dctx.add("<div class='subtitle'>")
1371 if global.visibility_level == 2 then
1372 abort
1373 else if global.visibility_level == 3 then
1374 dctx.add("private ")
1375 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1376 dctx.add("(unexported) ")
1377 end
1378 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div>")
1379
1380 dctx.add("<section class=\"description\">\n")
1381 var doc = doc
1382 if doc != null then
1383 dctx.add("<pre>{doc.to_html}</pre>\n")
1384 end
1385
1386 var cla = new HashSet[MMLocalClass]
1387 var sm = new HashSet[MMLocalClass]
1388 var sm2 = new HashSet[MMLocalClass]
1389 sm.add(self)
1390 while cla.length + sm.length < 10 and sm.length > 0 do
1391 cla.add_all(sm)
1392 sm2.clear
1393 for x in sm do
1394 sm2.add_all(x.cshe.direct_smallers)
1395 end
1396 var t = sm
1397 sm = sm2
1398 sm2 = t
1399 end
1400 cla.add_all(cshe.greaters_and_self)
1401
1402 var op = new Buffer
1403 var name = "class_{name}"
1404 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")
1405 for c in cla do
1406 if c == self then
1407 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1408 else
1409 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
1410 end
1411 for c2 in c.cshe.direct_greaters do
1412 if not cla.has(c2) then continue
1413 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1414 end
1415 if not c.cshe.direct_smallers.is_empty then
1416 var others = true
1417 for c2 in c.cshe.direct_smallers do
1418 if cla.has(c2) then others = false
1419 end
1420 if others then
1421 op.append("\"{c.name}...\"[label=\"\"];\n")
1422 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1423 end
1424 end
1425 end
1426 op.append("\}\n")
1427 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph for class {name}")
1428
1429
1430 var mods = new Array[MMModule]
1431 mods.add(global.intro.mmmodule.toplevel_owner)
1432 for lc in crhe.greaters do
1433 if not lc isa MMSrcLocalClass then continue
1434 var m = lc.mmmodule.toplevel_owner
1435 if not mods.has(m) then mods.add(m)
1436 end
1437 dctx.sort(mods)
1438 for m in mods do
1439 if m == global.intro.mmmodule.toplevel_owner then
1440 dctx.add("<p>Introduced by {m.html_link(dctx)}")
1441 else
1442 dctx.add("<p>Refined by {m.html_link(dctx)}")
1443 end
1444 dctx.open_stage
1445 dctx.stage(". Definition in:")
1446 for lc in crhe.greaters do
1447 if lc.mmmodule.toplevel_owner != m then continue
1448 dctx.add(" {lc.mmmodule.html_link(dctx)} ")
1449 assert lc isa MMSrcLocalClass
1450 var n = lc.node
1451 if n != null then
1452 dctx.show_source(n.location)
1453 end
1454 end
1455 dctx.close_stage
1456 dctx.add("</p>\n")
1457 end
1458 dctx.add("</section>\n")
1459
1460 dctx.open_stage
1461 dctx.stage("<section class=\"types\">\n")
1462 dctx.stage("<h2>Formal and Virtual Types</h2>\n")
1463 for i in [0..arity[ do
1464 var f = get_formal(i)
1465 f.full_documentation(dctx, self)
1466 end
1467 for p in props do
1468 if not p isa MMTypeProperty then continue
1469 p.full_documentation(dctx, self)
1470 end
1471 dctx.stage("</section>\n")
1472 dctx.close_stage
1473
1474 dctx.open_stage
1475 dctx.stage("<section class=\"constructors\">\n")
1476 dctx.stage("<h2 class=\"section-header\">Constructors</h2>\n")
1477 for p in props do
1478 if not p.global.is_init_for(self) then continue
1479 p.full_documentation(dctx, self)
1480 end
1481 dctx.stage("</section>\n")
1482 dctx.close_stage
1483
1484 dctx.open_stage
1485 dctx.stage("<section class=\"methods\">\n")
1486 dctx.stage("<h2 class=\"section-header\">Methods</h2>\n")
1487 for p in props do
1488 if p.global.is_init then continue
1489 if p.local_class.global != self.global then continue
1490 if not p isa MMMethod then continue
1491 p.full_documentation(dctx, self)
1492 end
1493 if not inhs.is_empty then
1494 dctx.open_stage
1495 dctx.stage("<h3>Inherited Methods</h3>\n")
1496 for lc in inhs do
1497 dctx.open_stage
1498 dctx.stage("<p>Defined in {lc.html_link(dctx)}:")
1499 for p in inh[lc] do
1500 if p.global.is_init then continue
1501 if not p isa MMMethod then continue
1502 dctx.add(" {p.html_link(dctx)}")
1503 end
1504 dctx.stage("</p>")
1505 dctx.close_stage
1506 end
1507 dctx.close_stage
1508 end
1509 dctx.add("</section>\n")
1510 dctx.close_stage
1511 dctx.add("</div> <!-- end class {name} -->\n")
1512 end
1513 end
1514
1515 redef class MMSrcLocalClass
1516 redef fun short_doc
1517 do
1518 var d = doc
1519 if d != null then
1520 return d.short
1521 else if global.intro == self then
1522 return "&nbsp;"
1523 else
1524 var bc = global.intro
1525 return bc.short_doc
1526 end
1527 end
1528
1529 redef fun doc
1530 do
1531 var n = node
1532 if not n isa AStdClassdef then
1533 return null
1534 end
1535 var d = n.n_doc
1536 if d == null then
1537 return null
1538 end
1539 if d.n_comment.is_empty then
1540 return null
1541 else
1542 return d
1543 end
1544 end
1545 end
1546
1547 redef class MMSignature
1548 # Htlm transcription of the signature (with nested links)
1549 fun to_html(dctx: DocContext, with_closure: Bool): String
1550 do
1551 var res = new Buffer
1552 if arity > 0 then
1553 res.append("(")
1554 for i in [0..arity[ do
1555 if i > 0 then res.append(", ")
1556 res.append(self.params[i].name.to_s)
1557 res.append(": ")
1558 res.append(self[i].html_link(dctx))
1559 if self.vararg_rank == i then
1560 res.append("...")
1561 end
1562 end
1563 res.append(")")
1564 end
1565 if return_type != null then
1566 res.append(": ")
1567 res.append(return_type.html_link(dctx))
1568 end
1569 if with_closure then
1570 for c in closures do
1571 res.append(" ")
1572 if c.is_optional then res.append("[")
1573 if c.is_break then res.append("break ")
1574 res.append("!{c.name}")
1575 res.append(c.signature.to_html(dctx, false))
1576 if c.is_optional then res.append("]")
1577 end
1578 end
1579 return res.to_s
1580 end
1581 end
1582
1583 redef class MMType
1584 # Htlm transcription of the type (with nested links)
1585 fun html_link(dctx: DocContext): String do return to_s
1586 end
1587
1588 redef class MMTypeSimpleClass
1589 redef fun html_link(dctx) do return local_class.html_link(dctx)
1590 end
1591
1592 redef class MMTypeGeneric
1593 redef fun html_link(dctx)
1594 do
1595 var res = new Buffer
1596 res.append(local_class.html_link(dctx))
1597 res.append("[")
1598 res.append(params[0].html_link(dctx))
1599 for i in [1..params.length[ do
1600 res.append(", ")
1601 res.append(params[i].html_link(dctx))
1602 end
1603 res.append("]")
1604 return res.to_s
1605 end
1606 end
1607
1608 redef class MMTypeFormalParameter
1609 fun html_anchor: String
1610 do
1611 return "FT_{local_class}_{cmangle(name)}"
1612 end
1613 redef fun html_link(dctx)
1614 do
1615 return "<a href=\"#{html_anchor}\">{name}</a>"
1616 end
1617 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1618 do
1619 dctx.add("<article id=\"{html_anchor}\">\n")
1620 dctx.add("<h3 class=\"signature\">{name}: {bound.html_link(dctx)}</h3>\n")
1621 dctx.add("<div class=\"info\">")
1622 dctx.add("formal generic type")
1623 dctx.add("</div>")
1624 dctx.add("</article>")
1625 end
1626 end
1627
1628 redef class MMNullableType
1629 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1630 end
1631
1632 redef class MMVirtualType
1633 redef fun html_link(dctx) do return property.html_link(dctx)
1634 end
1635
1636 var c = new DocContext
1637 c.exec_cmd_line