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