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