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