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