nitdoc: clean generated html
[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\">{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;<a href=\"{lpi.local_class.html_name}.html#{lpi.html_anchor}\">{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;<a href=\"{lp.local_class.html_name}.html#{lp.html_anchor}\">{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;<a href=\"{lpi.local_class.html_name}.html#{lpi.html_anchor}\">{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;<a href=\"{lp.local_class.html_name}.html#{lp.html_anchor}\">{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 redef fun html_link(dctx)
741 do
742 if not require_doc(dctx) then print "not required {self}"
743 return "<a href=\"{local_class.html_name}.html#{html_anchor}\">{self}</a>"
744 end
745
746 fun html_link_special(dctx: DocContext, lc: MMLocalClass): String
747 do
748 if not require_doc(dctx) then print "not required {self}"
749 return "<a href=\"{lc.html_name}.html#{html_anchor}\">{self}</a>"
750 end
751
752 # Kind of property (fun, attr, etc.)
753 fun kind: String is abstract
754
755 redef fun short_doc
756 do
757 var d = doc
758 if d != null then
759 return d.short
760 else if global.intro == self then
761 return "&nbsp;"
762 else
763 return global.intro.short_doc
764 end
765 end
766
767 redef fun doc
768 do
769 var n = node
770 if n == null or not n isa APropdef then
771 return null
772 end
773 var d = n.n_doc
774 if d == null then
775 return null
776 end
777 if d.n_comment.is_empty then
778 return null
779 else
780 return d
781 end
782 end
783
784 # The most specific module in the nesting hierarchy that exports the intro of self
785 fun intro_module: MMModule
786 do
787 var m = global.intro.mmmodule
788 var mo = m.direct_owner
789 while mo != null and mo.visibility_for(m) >= 2 do
790 m = mo
791 mo = m.direct_owner
792 end
793 return m
794 end
795
796 # Is the intro of self exported by the top-level module ?
797 fun is_toplevel: Bool
798 do
799 var m = intro_module
800 return m == m.toplevel_owner
801 end
802
803 # Return true if the global class must be documented according to the visibility configured
804 fun require_doc(dctx: DocContext): Bool
805 do
806 if global.visibility_level == 3 then return false # Private
807 if dctx.public_only then
808 var m = intro_module
809 if m != m.toplevel_owner then return false # Unexported
810 end
811 return true
812 end
813
814 # Document the global property in the global class lc
815 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
816 do
817 var visibility: String
818 if global.visibility_level == 1 then
819 visibility = "public"
820 else if global.visibility_level == 2 then
821 visibility = "protected"
822 else if global.visibility_level == 3 then
823 visibility = "private"
824 else
825 abort
826 end
827
828 var intro_class = global.intro.local_class
829 var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner
830
831 dctx.add("<article id=\"{html_anchor}\" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\">\n")
832 dctx.add("<h3 class=\"signature\">{name}{signature.to_html(dctx, true)}</h3>\n")
833 dctx.add("<div class=\"info\">\n")
834 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
835
836 if is_redef then
837 dctx.add("redef ")
838 end
839 if not is_toplevel then
840 dctx.add("(unexported) ")
841 end
842 if global.visibility_level == 2 then
843 dctx.add("protected ")
844 else if global.visibility_level == 3 then
845 dctx.add("private ")
846 end
847 dctx.add(kind)
848 dctx.add(" {intro_class.mmmodule.toplevel_owner.name}")
849 if intro_class.global == lc.global then
850 dctx.add("::{lc.name}")
851 else
852 dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}")
853 end
854 if is_redef then
855 dctx.add("::{mmmodule[intro_class.global][global].html_link(dctx)}")
856 else
857 dctx.add("::{name}")
858 end
859 dctx.add("</div>")
860
861 dctx.add("<div class=\"description\">")
862
863 # Collect all refinement of the global property in the same global property
864 var lps = new Array[MMLocalProperty]
865 for l in prhe.greaters_and_self do
866 lps.add(l)
867 end
868
869 var introdoc = false
870 if global.intro.doc != null then
871 for lp in lps do
872 if lp.doc == null then introdoc = true
873 end
874 end
875 if introdoc then
876 dctx.add("<pre>{global.intro.doc.to_html}</pre>")
877 end
878
879 var tlmods = new Array[MMModule]
880 for lp in lps do
881 var bm = lp.mmmodule.toplevel_owner
882 var lcm = lc.global.intro.mmmodule
883 if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner
884 if not tlmods.has(bm) then tlmods.add(bm)
885 end
886
887 for tm in tlmods do
888 # Document the top level property for the current top level module
889 var tlp
890 if tm.global_classes.has(lc.global) then
891 tlp = tm[lc.global][self.global]
892 assert lps.has(tlp)
893 else if tm.global_classes.has(self.local_class.global) then
894 # Self is the inherited property. Process it
895 tlp = tm[self.local_class.global][self.global]
896 assert lps.has(tlp)
897 else
898 # We skip this module since the props defined by the module is
899 continue
900 end
901
902 var tlcm = lc.global.intro.mmmodule.toplevel_owner
903 if not tlcm.mhe <= tm then
904 dctx.add("<h4>In module {tm.html_link(dctx)} :</h4>")
905 end
906
907 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
908
909 var doc = tlp.doc
910 if doc != null and (not introdoc or global.intro.doc != doc) then
911 dctx.add("<pre>{doc.to_html}</pre>")
912 end
913 dctx.add("<p>")
914 if tlp.local_class.global != lc.global then
915 dctx.add("inherited from {tlp.local_class.html_link(dctx)} ")
916 end
917 if tm != tlp.mmmodule then
918 dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ")
919 end
920 var n = tlp.node
921 if n != null then
922 var l = n.location
923 dctx.show_source(l)
924 end
925
926 dctx.open_stage
927 dctx.stage(". previously defined by:")
928 for lp in lps do
929 var tl = lp.mmmodule.toplevel_owner
930 if tl != tm then continue
931 if lp == tlp then continue
932 dctx.add(" {lp.mmmodule.html_link(dctx)}")
933 if lp.local_class.global != lc.global then
934 dctx.add(" for {lp.local_class.html_link(dctx)}")
935 end
936
937 n = lp.node
938 if n != null then
939 var l = n.location
940 dctx.show_source(l)
941 end
942
943 #var doc = lp.doc
944 #if doc != null and (not introdoc or global.intro.doc != doc) then
945 # dctx.add("<pre>{doc.to_html}</pre>")
946 #end
947 end
948 dctx.close_stage
949 dctx.add("</p>")
950 end
951 dctx.add("</div>")
952 dctx.add("</article>")
953 end
954 end
955 redef class MMMethod
956 redef fun kind do return if global.is_init then "init" else "fun"
957 end
958 redef class MMAttribute
959 redef fun kind do return "var"
960 end
961 redef class MMTypeProperty
962 redef fun kind do return "type"
963 end
964
965 redef class Symbol
966 # Replace < and > with html entities
967 redef fun to_s
968 do
969 var ret = super.to_s
970
971 if(ret.has('<')) then
972 var parts = ret.split_with("<")
973 ret = ""
974
975 for i in [0..parts.length[ do
976 ret += parts[i]
977
978 if(i < parts.length - 1) then
979 ret += "&lt;"
980 end
981 end
982 end
983
984 if(ret.has('>')) then
985 var parts = ret.split_with(">")
986 ret = ""
987
988 for i in [0..parts.length[ do
989 ret += parts[i]
990
991 if(i < parts.length - 1) then
992 ret += "&gt;"
993 end
994 end
995 end
996
997 return ret
998 end
999 end
1000
1001 redef class MMSrcModule
1002 redef fun short_doc
1003 do
1004 var d = doc
1005 if d != null then
1006 return d.short
1007 else
1008 return "&nbsp;"
1009 end
1010 end
1011
1012 redef fun doc
1013 do
1014 var n = node
1015 if n.n_moduledecl == null then
1016 return null
1017 end
1018 var np = n.n_moduledecl
1019 var d = np.n_doc
1020 if d == null then
1021 return null
1022 end
1023 if d.n_comment.is_empty then
1024 return null
1025 else
1026 return d
1027 end
1028 end
1029 end
1030
1031 redef class ADoc
1032 # Html transcription of the doc
1033 fun to_html: String
1034 do
1035 var res = new Buffer
1036 for c in n_comment do
1037 res.append(c.text.substring_from(1))
1038 end
1039 return res.to_s
1040 end
1041
1042 # Oneliner transcription of the doc
1043 fun short: String
1044 do
1045 return n_comment.first.text.substring_from(1)
1046 end
1047 end
1048
1049 redef class MMLocalClass
1050 super MMEntity
1051
1052 # Anchor of the class description in the module html file
1053 fun html_anchor: String do return "CLASS_{self}"
1054
1055 fun html_name: String do return "{self}"
1056
1057 redef fun html_link(dctx)
1058 do
1059 if not require_doc(dctx) then print "{dctx.filename}: not required {self}"
1060 return "<a href=\"{html_name}.html\">{self}</a>"
1061 end
1062
1063 redef fun short_doc do return global.intro.short_doc
1064
1065 redef fun doc do return global.intro.doc
1066
1067 fun kind: String
1068 do
1069 if global.is_interface then
1070 return "interface"
1071 else if global.is_abstract then
1072 return "abstract class"
1073 else if global.is_enum then
1074 return "enum"
1075 else
1076 return "class"
1077 end
1078 end
1079
1080 # The most specific module in the nesting hierarchy that exports the intro of self
1081 fun intro_module: MMModule
1082 do
1083 var m = global.intro.mmmodule
1084 var mo = m.direct_owner
1085 while mo != null and mo.visibility_for(m) >= 2 do
1086 m = mo
1087 mo = m.direct_owner
1088 end
1089 return m
1090 end
1091
1092 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1093 do
1094 if p.local_class.global != self.global then
1095 if p.global.intro.local_class.name == "Object".to_symbol then return
1096 if p.global.is_init or p isa MMTypeProperty then
1097 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1098 else
1099 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link(dctx)}</li>\n")
1100 end
1101 else if p.global.intro.local_class.global == self.global then
1102 dctx.add("<li class='intro'><span title='Introduced'>I</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1103 else
1104 dctx.add("<li class='redef'><span title='Redefined'>R</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1105 end
1106 end
1107
1108 # Return true if the global class must be documented according to the visibility configured
1109 fun require_doc(dctx: DocContext): Bool
1110 do
1111 if global.visibility_level == 3 then return false # Private
1112 if dctx.public_only then
1113 var m = intro_module
1114 if m != m.toplevel_owner then return false # Unexported
1115 end
1116 return true
1117 end
1118
1119 # Fill the body for the page associated to the global class
1120 fun file_page_doc(dctx: DocContext)
1121 do
1122 dctx.add("<div class=\"menu\">\n")
1123
1124 var props = new Array[MMLocalProperty]
1125 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1126 var inhs = new Array[MMLocalClass]
1127 for g in global_properties do
1128 var p = self[g]
1129 if not p.require_doc(dctx) then continue
1130 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1131 props.add(p)
1132 else
1133 var lc = mmmodule[p.local_class.global]
1134 if inh.has_key(lc) then
1135 inh[lc].add(p)
1136 else
1137 inh[lc] = [p]
1138 inhs.add(lc)
1139 end
1140 props.add(p)
1141 end
1142 end
1143 dctx.sort(props)
1144
1145 dctx.add("<nav class=\"properties filterable\">\n")
1146 dctx.add("<h3>Properties</h3>\n")
1147 dctx.open_stage
1148 dctx.stage("<h4>Virtual Types</h4>\n<ul>\n")
1149 for p in props do
1150 if p isa MMTypeProperty then
1151 menu_link(dctx, p)
1152 end
1153 end
1154 dctx.stage("</ul>\n")
1155 dctx.close_stage
1156 dctx.open_stage
1157 dctx.stage("<h4>Constructors</h4>\n<ul>\n")
1158 for p in props do
1159 if p.global.is_init_for(self) then
1160 menu_link(dctx, p)
1161 end
1162 end
1163 dctx.stage("</ul>\n")
1164 dctx.close_stage
1165 dctx.open_stage
1166 dctx.stage("<h4>Methods</h4>\n<ul>\n")
1167 for p in props do
1168 if not p.global.is_init and p isa MMMethod then
1169 menu_link(dctx, p)
1170 end
1171 end
1172 dctx.stage("</ul>\n")
1173 dctx.close_stage
1174 dctx.add("</nav>\n")
1175
1176 dctx.add("<nav class=\"inheritance filterable\">\n")
1177 dctx.add("<h3>Inheritance</h3>\n")
1178 dctx.add("<h4>Superclasses</h4>\n<ul>\n")
1179 for lc in cshe.linear_extension do
1180 if lc == self then continue
1181 if not lc.require_doc(dctx) then continue
1182 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1183 end
1184 dctx.add("</ul>\n")
1185 if cshe.smallers.length == 0 then
1186 dctx.add("<h4>No Known Subclasses</h4>\n")
1187 else if cshe.smallers.length <= 100 then
1188 dctx.add("<h4>Subclasses</h4>\n")
1189 dctx.add("<ul>\n")
1190 for lc in cshe.smallers do
1191 if not lc.require_doc(dctx) then continue
1192 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1193 end
1194 dctx.add("</ul>\n")
1195 else if cshe.direct_smallers.length <= 100 then
1196 dctx.add("<h4>Direct Subclasses Only</h4>\n<ul>\n")
1197 for lc in cshe.direct_smallers do
1198 if not lc.require_doc(dctx) then continue
1199 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1200 end
1201 dctx.add("</ul>\n")
1202 else
1203 dctx.add("<h4>Too much Subclasses to list</h4>\n")
1204 end
1205 dctx.add("</nav>\n")
1206
1207 dctx.add("</div>\n")
1208
1209
1210 dctx.add("<div class=\"content\">\n")
1211 dctx.add("<h1>{name}</h1>\n")
1212 dctx.add("<div class='subtitle'>")
1213 if global.visibility_level == 2 then
1214 abort
1215 else if global.visibility_level == 3 then
1216 dctx.add("private ")
1217 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1218 dctx.add("(unexported) ")
1219 end
1220 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div>")
1221
1222 dctx.add("<section class=\"description\">\n")
1223 var doc = doc
1224 if doc != null then
1225 dctx.add("<pre>{doc.to_html}</pre>\n")
1226 end
1227
1228 var cla = new HashSet[MMLocalClass]
1229 var sm = new HashSet[MMLocalClass]
1230 var sm2 = new HashSet[MMLocalClass]
1231 sm.add(self)
1232 while cla.length + sm.length < 10 and sm.length > 0 do
1233 cla.add_all(sm)
1234 sm2.clear
1235 for x in sm do
1236 sm2.add_all(x.cshe.direct_smallers)
1237 end
1238 var t = sm
1239 sm = sm2
1240 sm2 = t
1241 end
1242 cla.add_all(cshe.greaters_and_self)
1243
1244 var op = new Buffer
1245 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")
1246 for c in cla do
1247 if c == self then
1248 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1249 else
1250 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
1251 end
1252 for c2 in c.cshe.direct_greaters do
1253 if not cla.has(c2) then continue
1254 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1255 end
1256 if not c.cshe.direct_smallers.is_empty then
1257 var others = true
1258 for c2 in c.cshe.direct_smallers do
1259 if cla.has(c2) then others = false
1260 end
1261 if others then
1262 op.append("\"{c.name}...\"[label=\"\"];\n")
1263 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1264 end
1265 end
1266 end
1267 op.append("\}\n")
1268 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph for class {name}")
1269
1270
1271 var mods = new Array[MMModule]
1272 mods.add(global.intro.mmmodule.toplevel_owner)
1273 for lc in crhe.greaters do
1274 if not lc isa MMSrcLocalClass then continue
1275 var m = lc.mmmodule.toplevel_owner
1276 if not mods.has(m) then mods.add(m)
1277 end
1278 dctx.sort(mods)
1279 for m in mods do
1280 if m == global.intro.mmmodule.toplevel_owner then
1281 dctx.add("<p>Introduced by {m.html_link(dctx)}")
1282 else
1283 dctx.add("<p>Refined by {m.html_link(dctx)}")
1284 end
1285 dctx.open_stage
1286 dctx.stage(". Definition in:")
1287 for lc in crhe.greaters do
1288 if lc.mmmodule.toplevel_owner != m then continue
1289 dctx.add(" {lc.mmmodule.html_link(dctx)}")
1290 assert lc isa MMSrcLocalClass
1291 var n = lc.node
1292 if n != null then
1293 dctx.show_source(n.location)
1294 end
1295 end
1296 dctx.close_stage
1297 dctx.add("</p>\n")
1298 end
1299 dctx.add("</section>\n")
1300
1301 dctx.open_stage
1302 dctx.stage("<section class=\"types\">\n")
1303 dctx.stage("<h2>Formal and Virtual Types</h2>\n")
1304 for i in [0..arity[ do
1305 var f = get_formal(i)
1306 f.full_documentation(dctx, self)
1307 end
1308 for p in props do
1309 if not p isa MMTypeProperty then continue
1310 p.full_documentation(dctx, self)
1311 end
1312 dctx.stage("</section>\n")
1313 dctx.close_stage
1314
1315 dctx.open_stage
1316 dctx.stage("<section class=\"constructors\">\n")
1317 dctx.stage("<h2 class=\"section-header\">Constructors</h2>\n")
1318 for p in props do
1319 if not p.global.is_init_for(self) then continue
1320 p.full_documentation(dctx, self)
1321 end
1322 dctx.stage("</section>\n")
1323 dctx.close_stage
1324
1325 dctx.open_stage
1326 dctx.stage("<section class=\"methods\">\n")
1327 dctx.stage("<h2 class=\"section-header\">Methods</h2>\n")
1328 for p in props do
1329 if p.global.is_init then continue
1330 if p.local_class.global != self.global then continue
1331 if not p isa MMMethod then continue
1332 p.full_documentation(dctx, self)
1333 end
1334 if not inhs.is_empty then
1335 dctx.open_stage
1336 dctx.stage("<h3>Inherited Methods</h3>\n")
1337 for lc in inhs do
1338 dctx.open_stage
1339 dctx.stage("<p>Defined in {lc.html_link(dctx)}:")
1340 for p in inh[lc] do
1341 if p.global.is_init then continue
1342 if not p isa MMMethod then continue
1343 dctx.add(" {p.html_link(dctx)}")
1344 end
1345 dctx.stage("</p>")
1346 dctx.close_stage
1347 end
1348 dctx.close_stage
1349 end
1350 dctx.add("</section>\n")
1351 dctx.close_stage
1352 dctx.add("</div> <!-- end class {name} -->\n")
1353 end
1354 end
1355
1356 redef class MMSrcLocalClass
1357 redef fun short_doc
1358 do
1359 var d = doc
1360 if d != null then
1361 return d.short
1362 else if global.intro == self then
1363 return "&nbsp;"
1364 else
1365 var bc = global.intro
1366 return bc.short_doc
1367 end
1368 end
1369
1370 redef fun doc
1371 do
1372 var n = node
1373 if not n isa AStdClassdef then
1374 return null
1375 end
1376 var d = n.n_doc
1377 if d == null then
1378 return null
1379 end
1380 if d.n_comment.is_empty then
1381 return null
1382 else
1383 return d
1384 end
1385 end
1386 end
1387
1388 redef class MMSignature
1389 # Htlm transcription of the signature (with nested links)
1390 fun to_html(dctx: DocContext, with_closure: Bool): String
1391 do
1392 var res = new Buffer
1393 if arity > 0 then
1394 res.append("(")
1395 res.append(self.params[0].name.to_s)
1396 res.append(": ")
1397 res.append(self[0].html_link(dctx))
1398 for i in [1..arity[ do
1399 res.append(", ")
1400 res.append(self.params[i].name.to_s)
1401 res.append(": ")
1402 res.append(self[i].html_link(dctx))
1403 end
1404 res.append(")")
1405 end
1406 if return_type != null then
1407 res.append(": ")
1408 res.append(return_type.html_link(dctx))
1409 end
1410 if with_closure then
1411 for c in closures do
1412 res.append(" ")
1413 if c.is_optional then res.append("[")
1414 if c.is_break then res.append("break ")
1415 res.append("!{c.name}")
1416 res.append(c.signature.to_html(dctx, false))
1417 if c.is_optional then res.append("]")
1418 end
1419 end
1420 return res.to_s
1421 end
1422 end
1423
1424 redef class MMType
1425 # Htlm transcription of the type (with nested links)
1426 fun html_link(dctx: DocContext): String do return to_s
1427 end
1428
1429 redef class MMTypeSimpleClass
1430 redef fun html_link(dctx) do return local_class.html_link(dctx)
1431 end
1432
1433 redef class MMTypeGeneric
1434 redef fun html_link(dctx)
1435 do
1436 var res = new Buffer
1437 res.append(local_class.html_link(dctx))
1438 res.append("[")
1439 res.append(params[0].html_link(dctx))
1440 for i in [1..params.length[ do
1441 res.append(", ")
1442 res.append(params[i].html_link(dctx))
1443 end
1444 res.append("]")
1445 return res.to_s
1446 end
1447 end
1448
1449 redef class MMTypeFormalParameter
1450 fun html_anchor: String
1451 do
1452 return "FT_{local_class}_{cmangle(name)}"
1453 end
1454 redef fun html_link(dctx)
1455 do
1456 return "<a href=\"#{html_anchor}\">{name}</a>"
1457 end
1458 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1459 do
1460 dctx.add("<article id=\"{html_anchor}\">\n")
1461 dctx.add("<h3 class=\"signature\">{name}: {bound.html_link(dctx)}</h3>\n")
1462 dctx.add("<div class=\"info\">")
1463 dctx.add("formal generic type")
1464 dctx.add("</div>")
1465 dctx.add("</article>")
1466 end
1467 end
1468
1469 redef class MMNullableType
1470 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1471 end
1472
1473 redef class MMVirtualType
1474 redef fun html_link(dctx) do return property.html_link(dctx)
1475 end
1476
1477 var c = new DocContext
1478 c.exec_cmd_line