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