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