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