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