ni_nitdoc: display doc in <div>, not in <pre>
[nit.git] / src / ni_nitdoc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Documentation generator for the nit language.
16 # Generate API documentation in HTML format from nit source code.
17 module ni_nitdoc
18
19 import model_utils
20 import modelize_property
21 import markdown
22
23 # The NitdocContext contains all the knowledge used for doc generation
24 class NitdocContext
25
26 private var toolcontext = new ToolContext
27 private var model: Model
28 private var mbuilder: ModelBuilder
29 private var mainmodule: MModule
30 private var class_hierarchy: POSet[MClass]
31 private var arguments: Array[String]
32 private var output_dir: nullable String
33 private var dot_dir: nullable String
34 private var share_dir: nullable String
35 private var source: nullable String
36 private var min_visibility: MVisibility
37
38 private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir")
39 private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
40 private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir")
41 private var opt_nodot = new OptionBool("Do not generate graphes with graphiviz", "--no-dot")
42 private var opt_private: OptionBool = new OptionBool("Generate the private API", "--private")
43
44 private var opt_custom_title: OptionString = new OptionString("Title displayed in the top of the Overview page and as suffix of all page names", "--custom-title")
45 private var opt_custom_menu_items: OptionString = new OptionString("Items displayed in menu before the 'Overview' item (Each item must be enclosed in 'li' tags)", "--custom-menu-items")
46 private var opt_custom_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text")
47 private var opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text")
48
49 init do
50 toolcontext.option_context.add_option(opt_dir)
51 toolcontext.option_context.add_option(opt_source)
52 toolcontext.option_context.add_option(opt_sharedir)
53 toolcontext.option_context.add_option(opt_nodot)
54 toolcontext.option_context.add_option(opt_private)
55 toolcontext.option_context.add_option(opt_custom_title)
56 toolcontext.option_context.add_option(opt_custom_footer_text)
57 toolcontext.option_context.add_option(opt_custom_overview_text)
58 toolcontext.option_context.add_option(opt_custom_menu_items)
59 toolcontext.process_options
60 self.arguments = toolcontext.option_context.rest
61
62 if arguments.length < 1 then
63 print "usage: nitdoc [options] file..."
64 toolcontext.option_context.usage
65 exit(1)
66 end
67
68 model = new Model
69 mbuilder = new ModelBuilder(model, toolcontext)
70 # Here we load an process all modules passed on the command line
71 var mmodules = mbuilder.parse(arguments)
72 if mmodules.is_empty then return
73 mbuilder.run_phases
74
75 if mmodules.length == 1 then
76 mainmodule = mmodules.first
77 else
78 # We need a main module, so we build it by importing all modules
79 mainmodule = new MModule(model, null, "<main>", new Location(null, 0, 0, 0, 0))
80 mainmodule.set_imported_mmodules(mmodules)
81 end
82 self.class_hierarchy = mainmodule.flatten_mclass_hierarchy
83 self.process_options
84 end
85
86 private fun process_options do
87 if not opt_dir.value is null then
88 output_dir = opt_dir.value
89 else
90 output_dir = "doc"
91 end
92 if not opt_sharedir.value is null then
93 share_dir = opt_sharedir.value
94 else
95 var dir = "NIT_DIR".environ
96 if dir.is_empty then
97 dir = "{sys.program_name.dirname}/../share/nitdoc"
98 else
99 dir = "{dir}/share/nitdoc"
100 end
101 share_dir = dir
102 if share_dir is null then
103 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
104 abort
105 end
106 dir = "{share_dir.to_s}/scripts/js-facilities.js"
107 if share_dir is null then
108 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
109 abort
110 end
111
112 if opt_private.value then
113 min_visibility = none_visibility
114 else
115 min_visibility = protected_visibility
116 end
117 end
118 source = opt_source.value
119 end
120
121 fun generate_nitdoc do
122 # Create destination dir if it's necessary
123 if not output_dir.file_exists then output_dir.mkdir
124 sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/")
125 self.dot_dir = null
126 if not opt_nodot.value then self.dot_dir = output_dir.to_s
127 overview
128 search
129 modules
130 classes
131 quicksearch_list
132 end
133
134 private fun overview do
135 var overviewpage = new NitdocOverview(self, dot_dir)
136 overviewpage.save("{output_dir.to_s}/index.html")
137 end
138
139 private fun search do
140 var searchpage = new NitdocSearch(self)
141 searchpage.save("{output_dir.to_s}/search.html")
142 end
143
144 private fun modules do
145 for mmodule in model.mmodules do
146 if mmodule.name == "<main>" then continue
147 var modulepage = new NitdocModule(mmodule, self, dot_dir)
148 modulepage.save("{output_dir.to_s}/{mmodule.url}")
149 end
150 end
151
152 private fun classes do
153 for mclass in mbuilder.model.mclasses do
154 var classpage = new NitdocClass(mclass, self, dot_dir, source)
155 classpage.save("{output_dir.to_s}/{mclass.url}")
156 end
157 end
158
159 private fun quicksearch_list do
160 var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js")
161 file.write("var entries = \{ ")
162 for mmodule in model.mmodules do
163 file.write("\"{mmodule.name}\": [")
164 file.write("\{txt: \"{mmodule.name}\", url:\"{mmodule.url}\" \},")
165 file.write("],")
166 end
167 for mclass in model.mclasses do
168 if mclass.visibility < min_visibility then continue
169 file.write("\"{mclass.name}\": [")
170 file.write("\{txt: \"{mclass.name}\", url:\"{mclass.url}\" \},")
171 file.write("],")
172 end
173 var name2mprops = new HashMap[String, Set[MPropDef]]
174 for mproperty in model.mproperties do
175 if mproperty.visibility < min_visibility then continue
176 if mproperty isa MAttribute then continue
177 if not name2mprops.has_key(mproperty.name) then name2mprops[mproperty.name] = new HashSet[MPropDef]
178 name2mprops[mproperty.name].add_all(mproperty.mpropdefs)
179 end
180 for mproperty, mpropdefs in name2mprops do
181 file.write("\"{mproperty}\": [")
182 for mpropdef in mpropdefs do
183 file.write("\{txt: \"{mpropdef.full_name}\", url:\"{mpropdef.url}\" \},")
184 end
185 file.write("],")
186 end
187 file.write(" \};")
188 file.close
189 end
190
191 end
192
193 # Nitdoc base page
194 abstract class NitdocPage
195
196 var dot_dir: nullable String
197 var source: nullable String
198 var ctx: NitdocContext
199
200 init(ctx: NitdocContext) do
201 self.ctx = ctx
202 end
203
204 protected fun head do
205 append("<meta charset='utf-8'/>")
206 append("<script type='text/javascript' src='scripts/jquery-1.7.1.min.js'></script>")
207 append("<script type='text/javascript' src='scripts/ZeroClipboard.min.js'></script>")
208 append("<script type='text/javascript' src='quicksearch-list.js'></script>")
209 append("<script type='text/javascript' src='scripts/base64.js'></script>")
210 append("<script type='text/javascript' src='scripts/github.js'></script>")
211 append("<script type='text/javascript' src='scripts/js-facilities.js'></script>")
212 append("<link rel='stylesheet' href='styles/main.css' type='text/css' media='screen'/>")
213 var title = ""
214 if ctx.opt_custom_title.value != null then
215 title = " | {ctx.opt_custom_title.value.to_s}"
216 end
217 append("<title>{self.title}{title}</title>")
218 end
219
220 protected fun menu do
221 if ctx.opt_custom_menu_items.value != null then
222 append(ctx.opt_custom_menu_items.value.to_s)
223 end
224 end
225
226 protected fun title: String is abstract
227
228 protected fun header do
229 append("<header>")
230 append("<nav class='main'>")
231 append("<ul>")
232 menu
233 append("</ul>")
234 append("</nav>")
235 append("</header>")
236 end
237
238 protected fun content is abstract
239
240 protected fun footer do
241 if ctx.opt_custom_footer_text.value != null then
242 append("<footer>{ctx.opt_custom_footer_text.value.to_s}</footer>")
243 end
244 end
245
246 # Generate a clickable graphviz image using a dot content
247 protected fun generate_dot(dot: String, name: String, alt: String) do
248 var output_dir = dot_dir
249 if output_dir == null then return
250 var file = new OFStream.open("{output_dir}/{name}.dot")
251 file.write(dot)
252 file.close
253 sys.system("\{ test -f {output_dir}/{name}.png && test -f {output_dir}/{name}.s.dot && diff {output_dir}/{name}.dot {output_dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {output_dir}/{name}.dot {output_dir}/{name}.s.dot && dot -Tpng -o{output_dir}/{name}.png -Tcmapx -o{output_dir}/{name}.map {output_dir}/{name}.s.dot ; \}")
254 append("<article class='graph'>")
255 append("<img src='{name}.png' usemap='#{name}' style='margin:auto' alt='{alt}'/>")
256 append("</article>")
257 var fmap = new IFStream.open("{output_dir}/{name}.map")
258 append(fmap.read_all)
259 fmap.close
260 end
261
262 # Add a (source) link for a given location
263 protected fun show_source(l: Location): String
264 do
265 if source == null then
266 return "({l.file.filename.simplify_path})"
267 else
268 # THIS IS JUST UGLY ! (but there is no replace yet)
269 var x = source.split_with("%f")
270 source = x.join(l.file.filename.simplify_path)
271 x = source.split_with("%l")
272 source = x.join(l.line_start.to_s)
273 x = source.split_with("%L")
274 source = x.join(l.line_end.to_s)
275 return " (<a target='_blank' title='Show source' href=\"{source.to_s}\">source</a>)"
276 end
277 end
278
279 # Render the page as a html string
280 protected fun render do
281 append("<!DOCTYPE html>")
282 append("<head>")
283 head
284 append("</head>")
285 append("<body>")
286 header
287 append("<div class='page'>")
288 content
289 append("</div>")
290 footer
291 append("</body>")
292 end
293
294 # Append a string to the page
295 fun append(s: String) do out.write(s)
296
297 # Save html page in the specified file
298 fun save(file: String) do
299 self.out = new OFStream.open(file)
300 render
301 self.out.close
302 end
303 private var out: nullable OFStream
304 end
305
306 # The overview page
307 class NitdocOverview
308 super NitdocPage
309 private var mbuilder: ModelBuilder
310 private var mmodules = new Array[MModule]
311
312 init(ctx: NitdocContext, dot_dir: nullable String) do
313 super(ctx)
314 self.mbuilder = ctx.mbuilder
315 self.dot_dir = dot_dir
316 # get modules
317 var mmodules = new HashSet[MModule]
318 for mmodule in mbuilder.model.mmodules do
319 if mmodule.name == "<main>" then continue
320 var owner = mmodule.public_owner
321 if owner != null then
322 mmodules.add(owner)
323 else
324 mmodules.add(mmodule)
325 end
326 end
327 # sort modules
328 var sorter = new MModuleNameSorter
329 self.mmodules.add_all(mmodules)
330 sorter.sort(self.mmodules)
331 end
332
333 redef fun title do return "Overview"
334
335 redef fun menu do
336 super
337 append("<li class='current'>Overview</li>")
338 append("<li><a href='search.html'>Search</a></li>")
339 end
340
341 redef fun content do
342 var footed = ""
343 if ctx.opt_custom_footer_text.value != null then footed = "footed"
344 append("<div class='content fullpage {footed}'>")
345 var title = "Overview"
346 if ctx.opt_custom_title.value != null then
347 title = ctx.opt_custom_title.value.to_s
348 end
349 append("<h1>{title}</h1>")
350 var text = ""
351 if ctx.opt_custom_overview_text.value != null then
352 text = ctx.opt_custom_overview_text.value.to_s
353 end
354 append("<article class='overview'>{text}</article>")
355 append("<article class='overview'>")
356 # module list
357 append("<h2>Modules</h2>")
358 append("<ul>")
359 for mmodule in mmodules do
360 if mbuilder.mmodule2nmodule.has_key(mmodule) then
361 var amodule = mbuilder.mmodule2nmodule[mmodule]
362 append("<li>")
363 mmodule.html_link(self)
364 append("&nbsp;{amodule.short_comment}</li>")
365 end
366 end
367 append("</ul>")
368 # module graph
369 process_generate_dot
370 append("</article>")
371 append("</div>")
372 end
373
374 private fun process_generate_dot do
375 # build poset with public owners
376 var poset = new POSet[MModule]
377 for mmodule in mmodules do
378 poset.add_node(mmodule)
379 for omodule in mmodules do
380 if mmodule == omodule then continue
381 if mmodule.in_importation < omodule then
382 poset.add_node(omodule)
383 poset.add_edge(mmodule, omodule)
384 end
385 end
386 end
387 # build graph
388 var op = new Buffer
389 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")
390 for mmodule in poset do
391 op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
392 for omodule in poset[mmodule].direct_greaters do
393 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
394 end
395 end
396 op.append("\}\n")
397 generate_dot(op.to_s, "dep", "Modules hierarchy")
398 end
399 end
400
401 # The search page
402 class NitdocSearch
403 super NitdocPage
404
405 init(ctx: NitdocContext) do
406 super(ctx)
407 self.dot_dir = null
408 end
409
410 redef fun title do return "Search"
411
412 redef fun menu do
413 super
414 append("<li><a href='index.html'>Overview</a></li>")
415 append("<li class='current'>Search</li>")
416 end
417
418 redef fun content do
419 var footed = ""
420 if ctx.opt_custom_footer_text.value != null then footed = "footed"
421 append("<div class='content fullpage {footed}'>")
422 append("<h1>{title}</h1>")
423 module_column
424 classes_column
425 properties_column
426 append("</div>")
427 end
428
429 # Add to content modules column
430 private fun module_column do
431 var sorted = ctx.mbuilder.model.mmodule_importation_hierarchy.to_a
432 var sorter = new MModuleNameSorter
433 sorter.sort(sorted)
434 append("<article class='modules filterable'>")
435 append("<h2>Modules</h2>")
436 append("<ul>")
437 for mmodule in sorted do
438 append("<li>")
439 mmodule.html_link(self)
440 append("</li>")
441 end
442 append("</ul>")
443 append("</article>")
444 end
445
446 # Add to content classes modules
447 private fun classes_column do
448 var sorted = ctx.mbuilder.model.mclasses
449 var sorter = new MClassNameSorter
450 sorter.sort(sorted)
451 append("<article class='modules filterable'>")
452 append("<h2>Classes</h2>")
453 append("<ul>")
454 for mclass in sorted do
455 if mclass.visibility < ctx.min_visibility then continue
456 append("<li>")
457 mclass.html_link(self)
458 append("</li>")
459 end
460 append("</ul>")
461 append("</article>")
462 end
463
464 # Insert the properties column of fullindex page
465 private fun properties_column do
466 var sorted = ctx.mbuilder.model.mproperties
467 var sorter = new MPropertyNameSorter
468 sorter.sort(sorted)
469 append("<article class='modules filterable'>")
470 append("<h2>Properties</h2>")
471 append("<ul>")
472 for mproperty in sorted do
473 if mproperty.visibility < ctx.min_visibility then continue
474 if mproperty isa MAttribute then continue
475 append("<li>")
476 mproperty.intro.html_link(self)
477 append(" (")
478 mproperty.intro.mclassdef.mclass.html_link(self)
479 append(")</li>")
480 end
481 append("</ul>")
482 append("</article>")
483 end
484
485 end
486
487 # A module page
488 class NitdocModule
489 super NitdocPage
490
491 private var mmodule: MModule
492 private var mbuilder: ModelBuilder
493 private var local_mclasses = new HashSet[MClass]
494 private var intro_mclasses = new HashSet[MClass]
495 private var redef_mclasses = new HashSet[MClass]
496 private var inherited_mclasses = new HashSet[MClass]
497
498 init(mmodule: MModule, ctx: NitdocContext, dot_dir: nullable String) do
499 super(ctx)
500 self.mmodule = mmodule
501 self.mbuilder = ctx.mbuilder
502 self.dot_dir = dot_dir
503 # get local mclasses
504 for m in mmodule.in_nesting.greaters do
505 for mclassdef in m.mclassdefs do
506 if mclassdef.mclass.visibility < ctx.min_visibility then continue
507 if mclassdef.is_intro then
508 intro_mclasses.add(mclassdef.mclass)
509 else
510 redef_mclasses.add(mclassdef.mclass)
511 end
512 local_mclasses.add(mclassdef.mclass)
513 end
514 end
515 # get inherited mclasses
516 for m in mmodule.in_importation.greaters do
517 if m == mmodule then continue
518 for mclassdef in m.mclassdefs do
519 if mclassdef.mclass.visibility < ctx.min_visibility then continue
520 if local_mclasses.has(mclassdef.mclass) then continue
521 inherited_mclasses.add(mclassdef.mclass)
522 end
523 end
524 end
525
526 redef fun title do
527 if mbuilder.mmodule2nmodule.has_key(mmodule) then
528 var nmodule = mbuilder.mmodule2nmodule[mmodule]
529 return "{mmodule.name} module | {nmodule.short_comment}"
530 else
531 return "{mmodule.name} module"
532 end
533 end
534
535 redef fun menu do
536 super
537 append("<li><a href='index.html'>Overview</a></li>")
538 append("<li class='current'>{mmodule.name}</li>")
539 append("<li><a href='search.html'>Search</a></li>")
540 end
541
542 redef fun content do
543 append("<div class='menu'>")
544 classes_column
545 importation_column
546 append("</div>")
547 var footed = ""
548 if ctx.opt_custom_footer_text.value != null then footed = "footed"
549 append("<div class='content {footed}'>")
550 module_doc
551 append("</div>")
552 end
553
554 private fun classes_column do
555 var sorter = new MClassNameSorter
556 var sorted = new Array[MClass]
557 sorted.add_all(local_mclasses)
558 sorter.sort(sorted)
559 if not sorted.is_empty then
560 append("<nav class='properties filterable'>")
561 append("<h3>Classes</h3>")
562 append("<h4>Classes</h4>")
563 append("<ul>")
564 for mclass in sorted do mclass.html_sidebar_item(self)
565 append("</ul>")
566 append("</nav>")
567 end
568 end
569
570 private fun importation_column do
571 append("<nav>")
572 append("<h3>Module Hierarchy</h3>")
573 var dependencies = new Array[MModule]
574 for dep in mmodule.in_importation.greaters do
575 if dep == mmodule or dep.public_owner != null then continue
576 dependencies.add(dep)
577 end
578 if mmodule.in_nesting.direct_greaters.length > 0 then
579 append("<h4>Nested Modules</h4>")
580 display_module_list(mmodule.in_nesting.direct_greaters.to_a)
581 end
582 if dependencies.length > 0 then
583 append("<h4>All dependencies</h4>")
584 display_module_list(dependencies)
585 end
586 var clients = new Array[MModule]
587 for dep in mmodule.in_importation.smallers do
588 if dep.name == "<main>" then continue
589 if dep == mmodule or dep.public_owner != null then continue
590 clients.add(dep)
591 end
592 if clients.length > 0 then
593 append("<h4>All clients</h4>")
594 display_module_list(clients)
595 end
596 append("</nav>")
597 end
598
599 private fun display_module_list(list: Array[MModule]) do
600 append("<ul>")
601 var sorter = new MModuleNameSorter
602 sorter.sort(list)
603 for m in list do
604 append("<li>")
605 m.html_link(self)
606 append("</li>")
607 end
608 append("</ul>")
609 end
610
611 private fun module_doc do
612 # title
613 append("<h1>{mmodule.name}</h1>")
614 append("<div class='subtitle info'>")
615 mmodule.html_signature(self)
616 append("</div>")
617 # comment
618 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
619 append("<section class='description'>")
620 if not nmodule.full_comment.is_empty then append("<div>{nmodule.full_comment}</div>")
621 process_generate_dot
622 append("</section>")
623 # classes
624 var class_sorter = new MClassNameSorter
625 # intro
626 if not intro_mclasses.is_empty then
627 var sorted = new Array[MClass]
628 sorted.add_all(intro_mclasses)
629 class_sorter.sort(sorted)
630 append("<section class='classes'>")
631 append("<h2 class='section-header'>Introduced classes</h2>")
632 for mclass in sorted do mclass.html_full_desc(self)
633 append("</section>")
634 end
635 # redefs
636 var redefs = new Array[MClass]
637 for mclass in redef_mclasses do if not intro_mclasses.has(mclass) then redefs.add(mclass)
638 class_sorter.sort(redefs)
639 if not redefs.is_empty then
640 append("<section class='classes'>")
641 append("<h2 class='section-header'>Refined classes</h2>")
642 for mclass in redefs do mclass.html_full_desc(self)
643 append("</section>")
644 end
645 # inherited properties
646 var inherited = new Array[MClass]
647 inherited.add_all(inherited_mclasses)
648 if inherited_mclasses.length > 0 then
649 var modules2classes = new ArrayMap[MModule, Array[MClass]]
650 for mclass in inherited_mclasses do
651 if not modules2classes.has_key(mclass.intro_mmodule) then modules2classes[mclass.intro_mmodule] = new Array[MClass]
652 modules2classes[mclass.intro_mmodule].add(mclass)
653 end
654 append("<section class='classes'>")
655 append("<h2 class='section-header'>Inherited Classes</h2>")
656 var mmodules = new Array[MModule]
657 mmodules.add_all(modules2classes.keys)
658 var msorter = new MModuleNameSorter
659 msorter.sort(mmodules)
660 for m in mmodules do
661 var mclasses = modules2classes[m]
662 class_sorter.sort(mclasses)
663 append("<p>Defined in ")
664 m.html_link(self)
665 append(": ")
666 for i in [0..mclasses.length[ do
667 var mclass = mclasses[i]
668 mclass.html_link(self)
669 if i <= mclasses.length - 1 then append(", ")
670 end
671 append("</p>")
672 end
673 append("</section>")
674 end
675 end
676
677 private fun process_generate_dot do
678 # build poset with public owners
679 var poset = new POSet[MModule]
680 for mmodule in self.mmodule.in_importation.poset do
681 if mmodule.name == "<main>" then continue
682 if mmodule.public_owner != null then continue
683 if not mmodule.in_importation < self.mmodule and not self.mmodule.in_importation < mmodule and mmodule != self.mmodule then continue
684 poset.add_node(mmodule)
685 for omodule in mmodule.in_importation.poset do
686 if mmodule == omodule then continue
687 if omodule.name == "<main>" then continue
688 if omodule.public_owner != null then continue
689 if mmodule.in_importation < omodule then
690 poset.add_node(omodule)
691 poset.add_edge(mmodule, omodule)
692 end
693 end
694 end
695 # build graph
696 var op = new Buffer
697 var name = "dep_{mmodule.name}"
698 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")
699 for mmodule in poset do
700 if mmodule == self.mmodule then
701 op.append("\"{mmodule.name}\"[shape=box,margin=0.03];\n")
702 else
703 op.append("\"{mmodule.name}\"[URL=\"{mmodule.url}\"];\n")
704 end
705 for omodule in poset[mmodule].direct_greaters do
706 op.append("\"{mmodule.name}\"->\"{omodule.name}\";\n")
707 end
708 end
709 op.append("\}\n")
710 generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
711 end
712 end
713
714 # A class page
715 class NitdocClass
716 super NitdocPage
717
718 private var mclass: MClass
719 private var vtypes = new HashSet[MVirtualTypeDef]
720 private var consts = new HashSet[MMethodDef]
721 private var meths = new HashSet[MMethodDef]
722 private var inherited = new HashSet[MPropDef]
723
724 init(mclass: MClass, ctx: NitdocContext, dot_dir: nullable String, source: nullable String) do
725 super(ctx)
726 self.mclass = mclass
727 self.dot_dir = dot_dir
728 self.source = source
729 # load properties
730 var locals = new HashSet[MProperty]
731 for mclassdef in mclass.mclassdefs do
732 for mpropdef in mclassdef.mpropdefs do
733 if mpropdef.mproperty.visibility < ctx.min_visibility then continue
734 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
735 if mpropdef isa MMethodDef then
736 if mpropdef.mproperty.is_init then
737 consts.add(mpropdef)
738 else
739 meths.add(mpropdef)
740 end
741 end
742 locals.add(mpropdef.mproperty)
743 end
744 end
745 # get inherited properties
746 for pclass in mclass.in_hierarchy(ctx.mainmodule).greaters do
747 if pclass == mclass then continue
748 for pclassdef in pclass.mclassdefs do
749 for mprop in pclassdef.intro_mproperties do
750 var mpropdef = mprop.intro
751 if mprop.visibility < ctx.min_visibility then continue
752 if locals.has(mprop) then continue
753 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
754 if mpropdef isa MMethodDef then
755 if mpropdef.mproperty.is_init then
756 consts.add(mpropdef)
757 else
758 meths.add(mpropdef)
759 end
760 end
761 inherited.add(mpropdef)
762 end
763 end
764 end
765 end
766
767 redef fun title do
768 var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
769 if nclass isa AStdClassdef then
770 return "{mclass.name} class | {nclass.short_comment}"
771 else
772 return "{mclass.name} class"
773 end
774 end
775
776 redef fun menu do
777 super
778 append("<li><a href='index.html'>Overview</a></li>")
779 var public_owner = mclass.public_owner
780 if public_owner is null then
781 append("<li>")
782 mclass.intro_mmodule.html_link(self)
783 append("</li>")
784 else
785 append("<li>")
786 public_owner.html_link(self)
787 append("</li>")
788 end
789 append("<li class='current'>{mclass.name}</li>")
790 append("<li><a href='search.html'>Search</a></li>")
791 end
792
793 redef fun content do
794 append("<div class='menu'>")
795 properties_column
796 inheritance_column
797 append("</div>")
798 var footed = ""
799 if ctx.opt_custom_footer_text.value != null then footed = "footed"
800 append("<div class='content {footed}'>")
801 class_doc
802 append("</div>")
803 end
804
805 private fun properties_column do
806 var sorter = new MPropDefNameSorter
807 append("<nav class='properties filterable'>")
808 append("<h3>Properties</h3>")
809 # virtual types
810 if vtypes.length > 0 then
811 var vts = new Array[MVirtualTypeDef]
812 vts.add_all(vtypes)
813 sorter.sort(vts)
814 append("<h4>Virtual Types</h4>")
815 append("<ul>")
816 for mprop in vts do
817 mprop.html_sidebar_item(self)
818 end
819 append("</ul>")
820 end
821 # constructors
822 if consts.length > 0 then
823 var cts = new Array[MMethodDef]
824 cts.add_all(consts)
825 sorter.sort(cts)
826 append("<h4>Constructors</h4>")
827 append("<ul>")
828 for mprop in cts do
829 if mprop.mproperty.name == "init" and mprop.mclassdef.mclass != mclass then continue
830 mprop.html_sidebar_item(self)
831 end
832 append("</ul>")
833 end
834 # methods
835 if meths.length > 0 then
836 var mts = new Array[MMethodDef]
837 mts.add_all(meths)
838 sorter.sort(mts)
839 append("<h4>Methods</h4>")
840 append("<ul>")
841 for mprop in mts do
842 if mclass.name != "Object" and mprop.mproperty.intro_mclassdef.mclass.name == "Object" and mprop.mproperty.visibility <= protected_visibility then continue
843 mprop.html_sidebar_item(self)
844 end
845 append("</ul>")
846 end
847 append("</nav>")
848 end
849
850 private fun inheritance_column do
851 var sorted = new Array[MClass]
852 var sorterp = new MClassNameSorter
853 append("<nav>")
854 append("<h3>Inheritance</h3>")
855 var greaters = mclass.in_hierarchy(ctx.mainmodule).greaters.to_a
856 if greaters.length > 1 then
857 ctx.mainmodule.linearize_mclasses(greaters)
858 append("<h4>Superclasses</h4>")
859 append("<ul>")
860 for sup in greaters do
861 if sup == mclass then continue
862 append("<li>")
863 sup.html_link(self)
864 append("</li>")
865 end
866 append("</ul>")
867 end
868 var smallers = mclass.in_hierarchy(ctx.mainmodule).smallers.to_a
869 var direct_smallers = mclass.in_hierarchy(ctx.mainmodule).direct_smallers.to_a
870 if smallers.length <= 1 then
871 append("<h4>No Known Subclasses</h4>")
872 else if smallers.length <= 100 then
873 ctx.mainmodule.linearize_mclasses(smallers)
874 append("<h4>Subclasses</h4>")
875 append("<ul>")
876 for sub in smallers do
877 if sub == mclass then continue
878 append("<li>")
879 sub.html_link(self)
880 append("</li>")
881 end
882 append("</ul>")
883 else if direct_smallers.length <= 100 then
884 ctx.mainmodule.linearize_mclasses(direct_smallers)
885 append("<h4>Direct Subclasses Only</h4>")
886 append("<ul>")
887 for sub in direct_smallers do
888 if sub == mclass then continue
889 append("<li>")
890 sub.html_link(self)
891 append("</li>")
892 end
893 append("</ul>")
894 else
895 append("<h4>Too much Subclasses to list</h4>")
896 end
897 append("</nav>")
898 end
899
900 private fun class_doc do
901 # title
902 append("<h1>{mclass.name}{mclass.html_short_signature}</h1>")
903 append("<div class='subtitle info'>")
904 if mclass.visibility < public_visibility then append("{mclass.visibility.to_s} ")
905 append("{mclass.kind.to_s} ")
906 mclass.html_namespace(self)
907 append("{mclass.html_short_signature}</div>")
908 # comment
909 var nclass = ctx.mbuilder.mclassdef2nclassdef[mclass.intro]
910 append("<section class='description'>")
911 if nclass isa AStdClassdef and not nclass.full_comment.is_empty then append("<div>{nclass.full_comment}</div>")
912 process_generate_dot
913 append("</section>")
914 # concerns
915 var concern2meths = new ArrayMap[MModule, Array[MMethodDef]]
916 var sorted_meths = new Array[MMethodDef]
917 var sorted = new Array[MModule]
918 sorted_meths.add_all(meths)
919 ctx.mainmodule.linearize_mpropdefs(sorted_meths)
920 for meth in meths do
921 if inherited.has(meth) then continue
922 var mmodule = meth.mclassdef.mmodule
923 if not concern2meths.has_key(mmodule) then
924 sorted.add(mmodule)
925 concern2meths[mmodule] = new Array[MMethodDef]
926 end
927 concern2meths[mmodule].add(meth)
928 end
929 var sections = new ArrayMap[MModule, Array[MModule]]
930 for mmodule in concern2meths.keys do
931 var owner = mmodule.public_owner
932 if owner == null then owner = mmodule
933 if not sections.has_key(owner) then sections[owner] = new Array[MModule]
934 if owner != mmodule then sections[owner].add(mmodule)
935 end
936 append("<section class='concerns'>")
937 append("<h2 class='section-header'>Concerns</h2>")
938 append("<ul>")
939 for owner, mmodules in sections do
940 var nowner = ctx.mbuilder.mmodule2nmodule[owner]
941 append("<li>")
942 if nowner.short_comment.is_empty then
943 append("<a href=\"#{owner.anchor}\">{owner.name}</a>")
944 else
945 append("<a href=\"#{owner.anchor}\">{owner.name}</a>: {nowner.short_comment}")
946 end
947 if not mmodules.is_empty then
948 append("<ul>")
949 for mmodule in mmodules do
950 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
951 if nmodule.short_comment.is_empty then
952 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a></li>")
953 else
954 append("<li><a href=\"#{mmodule.anchor}\">{mmodule.name}</a>: {nmodule.short_comment}</li>")
955 end
956 end
957 append("</ul>")
958 end
959 append("</li>")
960 end
961 append("</ul>")
962 append("</section>")
963 # properties
964 var prop_sorter = new MPropDefNameSorter
965 var lmmodule = new List[MModule]
966 # virtual and formal types
967 var local_vtypes = new Array[MVirtualTypeDef]
968 for vt in vtypes do if not inherited.has(vt) then local_vtypes.add(vt)
969 if local_vtypes.length > 0 or mclass.arity > 0 then
970 append("<section class='types'>")
971 append("<h2>Formal and Virtual Types</h2>")
972 # formal types
973 if mclass.arity > 0 and nclass isa AStdClassdef then
974 for ft, bound in mclass.parameter_types do
975 append("<article id='FT_{ft}'>")
976 append("<h3 class='signature' data-untyped-signature='{ft.to_s}'><span>{ft}: ")
977 bound.html_link(self)
978 append("</span></h3>")
979 append("<div class=\"info\">formal generic type</div>")
980 append("</article>")
981 end
982 end
983 # virtual types
984 prop_sorter.sort(local_vtypes)
985 for prop in local_vtypes do prop.html_full_desc(self, self.mclass)
986 append("</section>")
987 end
988 # constructors
989 var local_consts = new Array[MMethodDef]
990 for const in consts do if not inherited.has(const) then local_consts.add(const)
991 prop_sorter.sort(local_consts)
992 if local_consts.length > 0 then
993 append("<section class='constructors'>")
994 append("<h2 class='section-header'>Constructors</h2>")
995 for prop in local_consts do prop.html_full_desc(self, self.mclass)
996 append("</section>")
997 end
998 # methods
999 if not concern2meths.is_empty then
1000 append("<section class='methods'>")
1001 append("<h2 class='section-header'>Methods</h2>")
1002 for owner, mmodules in sections do
1003 append("<a id=\"{owner.anchor}\"></a>")
1004 if owner != mclass.intro_mmodule and owner != mclass.public_owner then
1005 var nowner = ctx.mbuilder.mmodule2nmodule[owner]
1006 append("<h3 class=\"concern-toplevel\">Methods refined in ")
1007 owner.html_link(self)
1008 append("</h3>")
1009 append("<p class=\"concern-doc\">")
1010 owner.html_link(self)
1011 if not nowner.short_comment.is_empty then
1012 append(": {nowner.short_comment}")
1013 end
1014 append("</p>")
1015 end
1016 if concern2meths.has_key(owner) then
1017 var mmethods = concern2meths[owner]
1018 prop_sorter.sort(mmethods)
1019 for prop in mmethods do prop.html_full_desc(self, self.mclass)
1020 end
1021 for mmodule in mmodules do
1022 append("<a id=\"{mmodule.anchor}\"></a>")
1023 var nmodule = ctx.mbuilder.mmodule2nmodule[mmodule]
1024 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
1025 append("<p class=\"concern-doc\">")
1026 mmodule.html_link(self)
1027 if not nmodule.short_comment.is_empty then
1028 append(": {nmodule.short_comment}")
1029 end
1030 append("</p>")
1031 end
1032 var mmethods = concern2meths[mmodule]
1033 prop_sorter.sort(mmethods)
1034 for prop in mmethods do prop.html_full_desc(self, self.mclass)
1035 end
1036 end
1037 append("</section>")
1038 end
1039 # inherited properties
1040 if inherited.length > 0 then
1041 var sorted_inherited = new Array[MPropDef]
1042 sorted_inherited.add_all(inherited)
1043 ctx.mainmodule.linearize_mpropdefs(sorted_inherited)
1044 var classes = new ArrayMap[MClass, Array[MPropDef]]
1045 for mmethod in sorted_inherited.reversed do
1046 var mclass = mmethod.mclassdef.mclass
1047 if not classes.has_key(mclass) then classes[mclass] = new Array[MPropDef]
1048 classes[mclass].add(mmethod)
1049 end
1050 append("<section class='inherited'>")
1051 append("<h2 class='section-header'>Inherited Properties</h2>")
1052 for c, mmethods in classes do
1053 prop_sorter.sort(mmethods)
1054 append("<p>Defined in ")
1055 c.html_link(self)
1056 append(": ")
1057 for i in [0..mmethods.length[ do
1058 var mmethod = mmethods[i]
1059 mmethod.html_link(self)
1060 if i <= mmethods.length - 1 then append(", ")
1061 end
1062 append("</p>")
1063 end
1064 append("</section>")
1065 end
1066 end
1067
1068 private fun process_generate_dot do
1069 var pe = ctx.class_hierarchy[mclass]
1070 var cla = new HashSet[MClass]
1071 var sm = new HashSet[MClass]
1072 var sm2 = new HashSet[MClass]
1073 sm.add(mclass)
1074 while cla.length + sm.length < 10 and sm.length > 0 do
1075 cla.add_all(sm)
1076 sm2.clear
1077 for x in sm do
1078 sm2.add_all(pe.poset[x].direct_smallers)
1079 end
1080 var t = sm
1081 sm = sm2
1082 sm2 = t
1083 end
1084 cla.add_all(pe.greaters)
1085
1086 var op = new Buffer
1087 var name = "dep_{mclass.name}"
1088 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")
1089 for c in cla do
1090 if c == mclass then
1091 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1092 else
1093 op.append("\"{c.name}\"[URL=\"{c.url}\"];\n")
1094 end
1095 for c2 in pe.poset[c].direct_greaters do
1096 if not cla.has(c2) then continue
1097 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1098 end
1099 if not pe.poset[c].direct_smallers.is_empty then
1100 var others = true
1101 for c2 in pe.poset[c].direct_smallers do
1102 if cla.has(c2) then others = false
1103 end
1104 if others then
1105 op.append("\"{c.name}...\"[label=\"\"];\n")
1106 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1107 end
1108 end
1109 end
1110 op.append("\}\n")
1111 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1112 end
1113 end
1114
1115 #
1116 # Model redefs
1117 #
1118
1119 redef class MModule
1120 # URL to nitdoc page
1121 fun url: String do
1122 if url_cache == null then
1123 var res = new Buffer
1124 res.append("module_")
1125 var mowner = public_owner
1126 if mowner != null then
1127 res.append("{public_owner.name}_")
1128 end
1129 res.append("{self.name}.html")
1130 url_cache = res.to_s
1131 end
1132 return url_cache.as(not null)
1133 end
1134 private var url_cache: nullable String
1135
1136 # html anchor id to the module in a nitdoc page
1137 fun anchor: String do
1138 if anchor_cache == null then
1139 var res = new Buffer
1140 res.append("MOD_")
1141 var mowner = public_owner
1142 if mowner != null then
1143 res.append("{public_owner.name}_")
1144 end
1145 res.append(self.name)
1146 anchor_cache = res.to_s
1147 end
1148 return anchor_cache.as(not null)
1149 end
1150 private var anchor_cache: nullable String
1151
1152 # Return a link (html a tag) to the nitdoc module page
1153 fun html_link(page: NitdocPage) do
1154 if html_link_cache == null then
1155 var res = new Buffer
1156 if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
1157 res.append("<a href='{url}' title='{page.ctx.mbuilder.mmodule2nmodule[self].short_comment}'>{name}</a>")
1158 else
1159 res.append("<a href='{url}'>{name}</a>")
1160 end
1161 html_link_cache = res.to_s
1162 end
1163 page.append(html_link_cache.as(not null))
1164 end
1165 private var html_link_cache: nullable String
1166
1167 # Return the module signature decorated with html
1168 fun html_signature(page: NitdocPage) do
1169 page.append("<span>module ")
1170 html_full_namespace(page)
1171 page.append("</span>")
1172 end
1173
1174 # Return the module full namespace decorated with html
1175 fun html_full_namespace(page: NitdocPage) do
1176 page.append("<span>")
1177 var mowner = public_owner
1178 if mowner != null then
1179 public_owner.html_namespace(page)
1180 page.append("::")
1181 end
1182 html_link(page)
1183 page.append("</span>")
1184 end
1185
1186 # Return the module full namespace decorated with html
1187 fun html_namespace(page: NitdocPage) do
1188 page.append("<span>")
1189 var mowner = public_owner
1190 if mowner != null then
1191 public_owner.html_namespace(page)
1192 else
1193 html_link(page)
1194 end
1195 page.append("</span>")
1196 end
1197
1198 # Return the full comment of the module decorated with html
1199 fun html_full_comment(page: NitdocPage) do
1200 if page.ctx.mbuilder.mmodule2nmodule.has_key(self) then
1201 page.append("<div id='description'>")
1202 page.append("<div>{page.ctx.mbuilder.mmodule2nmodule[self].full_comment}</div>")
1203 page.append("</div>")
1204 end
1205 end
1206
1207 private fun has_mclassdef_for(mclass: MClass): Bool do
1208 for mmodule in self.in_nesting.greaters do
1209 for mclassdef in mmodule.mclassdefs do
1210 if mclassdef.mclass == mclass then return true
1211 end
1212 end
1213 return false
1214 end
1215
1216 private fun has_mclassdef(mclassdef: MClassDef): Bool do
1217 for mmodule in self.in_nesting.greaters do
1218 for oclassdef in mmodule.mclassdefs do
1219 if mclassdef == oclassdef then return true
1220 end
1221 end
1222 return false
1223 end
1224 end
1225
1226 redef class MClass
1227 # return the generic signature of the class
1228 # [E, F]
1229 private fun html_short_signature: String do
1230 if arity > 0 then
1231 return "[{intro.parameter_names.join(", ")}]"
1232 else
1233 return ""
1234 end
1235 end
1236
1237 # return the generic signature of the class with bounds
1238 # [E: <a>MType</a>, F: <a>MType</a>]
1239 private fun html_signature(page: NitdocPage) do
1240 if arity > 0 then
1241 page.append("[")
1242 for i in [0..intro.parameter_names.length[ do
1243 page.append("{intro.parameter_names[i]}: ")
1244 intro.bound_mtype.arguments[i].html_link(page)
1245 if i < intro.parameter_names.length - 1 then page.append(", ")
1246 end
1247 page.append("]")
1248 end
1249 end
1250
1251 # Return the class namespace decorated with html
1252 private fun html_namespace(page: NitdocPage) do
1253 intro_mmodule.html_namespace(page)
1254 page.append("::<span>")
1255 html_short_link(page)
1256 page.append("</span>")
1257 end
1258
1259 # Return a link (html a tag) to the nitdoc class page
1260 fun html_short_link(page: NitdocPage) do
1261 if html_short_link_cache == null then
1262 var res = new Buffer
1263 res.append("<a href='{url}'")
1264 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1265 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1266 if nclass isa AStdClassdef then
1267 res.append(" title=\"{nclass.short_comment}\"")
1268 end
1269 end
1270 res.append(">{name}</a>")
1271 html_short_link_cache = res.to_s
1272 end
1273 page.append(html_short_link_cache.as(not null))
1274 end
1275 private var html_short_link_cache: nullable String
1276
1277 # Return a link (html a tag) to the nitdoc class page
1278 fun html_link(page: NitdocPage) do
1279 if html_link_cache == null then
1280 var res = new Buffer
1281 res.append("<a href='{url}'")
1282 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1283 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1284 if nclass isa AStdClassdef then
1285 res.append(" title=\"{nclass.short_comment}\"")
1286 end
1287 end
1288 res.append(">{name}{html_short_signature}</a>")
1289 html_link_cache = res.to_s
1290 end
1291 page.append(html_link_cache.as(not null))
1292 end
1293 private var html_link_cache: nullable String
1294
1295 fun html_anchor(page: NitdocPage) do
1296 if html_anchor_cache == null then
1297 var res = new Buffer
1298 res.append("<a href='#{anchor}'")
1299 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(intro) then
1300 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[intro]
1301 if nclass isa AStdClassdef then
1302 res.append(" title=\"{nclass.short_comment}\"")
1303 end
1304 end
1305 res.append(">{name}{html_short_signature}</a>")
1306 html_anchor_cache = res.to_s
1307 end
1308 page.append(html_anchor_cache.as(not null))
1309 end
1310 private var html_anchor_cache: nullable String
1311
1312 fun anchor: String do
1313 if anchor_cache == null then
1314 anchor_cache = "CLASS_{public_owner.name}_{name}"
1315 end
1316 return anchor_cache.as(not null)
1317 end
1318 private var anchor_cache: nullable String
1319
1320 fun url: String do
1321 return "class_{public_owner}_{name}.html"
1322 end
1323
1324 # Escape name for html output
1325 redef fun name do return super.html_escape
1326
1327 # Return a list item for the mclass
1328 private fun html_sidebar_item(page: NitdocModule) do
1329 if page.mmodule.in_nesting.greaters.has(intro.mmodule) then
1330 page.append("<li class='intro'>")
1331 page.append("<span title='Introduced'>I</span>")
1332 html_anchor(page)
1333 else if page.mmodule.has_mclassdef_for(self) then
1334 page.append("<li class='redef'>")
1335 page.append("<span title='Redefined'>R</span>")
1336 html_anchor(page)
1337 else
1338 page.append("<li class='inherit'>")
1339 page.append("<span title='Inherited'>H</span>")
1340 html_link(page)
1341 end
1342 page.append("</li>")
1343 end
1344
1345 private fun html_full_desc(page: NitdocModule) do
1346 var classes = new Array[String]
1347 classes.add(kind.to_s)
1348 if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then classes.add("redef")
1349 classes.add(visibility.to_s)
1350 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1351 page.append("<h3 class='signature' data-untyped-signature='{name}{html_short_signature}'>")
1352 page.append("<span>")
1353 html_short_link(page)
1354 html_signature(page)
1355 page.append("</span></h3>")
1356 html_info(page)
1357 html_comment(page)
1358 html_redefs(page)
1359 page.append("</article>")
1360 end
1361
1362 private fun html_info(page: NitdocModule) do
1363 page.append("<div class='info'>")
1364 if visibility < public_visibility then page.append("{visibility.to_s} ")
1365 if not page.mmodule.in_nesting.greaters.has(intro.mmodule) then page.append("redef ")
1366 page.append("{kind} ")
1367 html_namespace(page)
1368 page.append("{html_short_signature}</div>")
1369 end
1370
1371 private fun html_comment(page: NitdocModule) do
1372 page.mmodule.linearize_mclassdefs(mclassdefs)
1373 page.append("<div class='description'>")
1374 # comments for each mclassdef contained in current mmodule
1375 for mclassdef in mclassdefs do
1376 if not mclassdef.is_intro and not page.mmodule.mclassdefs.has(mclassdef) then continue
1377 if page.ctx.mbuilder.mclassdef2nclassdef.has_key(mclassdef) then
1378 var nclass = page.ctx.mbuilder.mclassdef2nclassdef[mclassdef]
1379 if nclass isa AStdClassdef then
1380 if nclass.full_comment == "" then
1381 page.append("<p class='info inheritance'>")
1382 page.append("<span class=\"noComment\">no comment for </span>")
1383 else
1384 page.append("<div>{nclass.full_comment}</div>")
1385 page.append("<p class='info inheritance'>")
1386 end
1387 if mclassdef.is_intro then
1388 page.append("introduction in ")
1389 else
1390 page.append("refinement in ")
1391 end
1392 mclassdef.mmodule.html_full_namespace(page)
1393 page.append(" {page.show_source(nclass.location)}</p>")
1394 end
1395 end
1396 end
1397 page.append("</div>")
1398 end
1399
1400 private fun html_redefs(page: NitdocModule) do
1401 page.mmodule.linearize_mclassdefs(mclassdefs)
1402 page.append("<div class='refinements'>")
1403 # comments for each mclassdef contained in current mmodule
1404 for mclassdef in mclassdefs do
1405 if not page.mmodule.mclassdefs.has(mclassdef) then continue
1406 if mclassdef.is_intro then continue
1407 for mpropdef in mclassdef.mpropdefs do
1408 if mpropdef isa MAttributeDef then continue
1409 mpropdef.html_full_desc(page, self)
1410 end
1411 end
1412 page.append("</div>")
1413 end
1414 end
1415
1416 redef class MProperty
1417 # Return the property namespace decorated with html
1418 fun html_namespace(page: NitdocPage) do
1419 intro_mclassdef.mclass.html_namespace(page)
1420 page.append(intro_mclassdef.mclass.html_short_signature)
1421 page.append("::<span>")
1422 intro.html_link(page)
1423 page.append("</span>")
1424 end
1425
1426 # Escape name for html output
1427 redef fun name do return super.html_escape
1428 end
1429
1430 redef class MType
1431 fun html_link(page: NitdocPage) is abstract
1432 end
1433
1434 redef class MClassType
1435 redef fun html_link(page) do mclass.html_link(page)
1436 end
1437
1438 redef class MNullableType
1439 redef fun html_link(page) do
1440 page.append("nullable ")
1441 mtype.html_link(page)
1442 end
1443 end
1444
1445 redef class MGenericType
1446 redef fun html_link(page) do
1447 page.append("<a href='{mclass.url}'>{mclass.name}</a>[")
1448 for i in [0..arguments.length[ do
1449 arguments[i].html_link(page)
1450 if i < arguments.length - 1 then page.append(", ")
1451 end
1452 page.append("]")
1453 end
1454 end
1455
1456 redef class MParameterType
1457 redef fun html_link(page) do
1458 var name = mclass.intro.parameter_names[rank]
1459 page.append("<a href='{mclass.url}#FT_{name}' title='formal type'>{name}</a>")
1460 end
1461 end
1462
1463 redef class MVirtualType
1464 redef fun html_link(page) do mproperty.intro.html_link(page)
1465 end
1466
1467 redef class MClassDef
1468 # Return the classdef namespace decorated with html
1469 fun html_namespace(page: NitdocPage) do
1470 mmodule.html_full_namespace(page)
1471 page.append("::<span>")
1472 mclass.html_link(page)
1473 page.append("</span>")
1474 end
1475 end
1476
1477 redef class MPropDef
1478 fun url: String do
1479 if url_cache == null then
1480 url_cache = "{mclassdef.mclass.url}#{anchor}"
1481 end
1482 return url_cache.as(not null)
1483 end
1484 private var url_cache: nullable String
1485
1486 fun anchor: String do
1487 if anchor_cache == null then
1488 anchor_cache = "PROP_{mclassdef.mclass.public_owner.name}_{mproperty.name}"
1489 end
1490 return anchor_cache.as(not null)
1491 end
1492 private var anchor_cache: nullable String
1493
1494 # Return a link (html a tag) to the nitdoc class page
1495 fun html_link(page: NitdocPage) do
1496 if html_link_cache == null then
1497 var res = new Buffer
1498 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1499 var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
1500 res.append("<a href=\"{url}\" title=\"{nprop.short_comment}\">{mproperty.name}</a>")
1501 else
1502 res.append("<a href=\"{url}\">{mproperty.name}</a>")
1503 end
1504 html_link_cache = res.to_s
1505 end
1506 page.append(html_link_cache.as(not null))
1507 end
1508 private var html_link_cache: nullable String
1509
1510 # Return a list item for the mpropdef
1511 private fun html_sidebar_item(page: NitdocClass) do
1512 if is_intro and mclassdef.mclass == page.mclass then
1513 page.append("<li class='intro'>")
1514 page.append("<span title='Introduced'>I</span>")
1515 else if is_intro and mclassdef.mclass != page.mclass then
1516 page.append("<li class='inherit'>")
1517 page.append("<span title='Inherited'>H</span>")
1518 else
1519 page.append("<li class='redef'>")
1520 page.append("<span title='Redefined'>R</span>")
1521 end
1522 html_link(page)
1523 page.append("</li>")
1524 end
1525
1526 private fun html_full_desc(page: NitdocPage, ctx: MClass) is abstract
1527 private fun html_info(page: NitdocPage, ctx: MClass) is abstract
1528
1529 fun full_name: String do
1530 return "{mclassdef.mclass.public_owner.name}::{mclassdef.mclass.name}::{mproperty.name}"
1531 end
1532
1533 private fun html_comment(page: NitdocPage) do
1534 page.append("<div class='description'>")
1535 if not is_intro then
1536 if page.ctx.mbuilder.mpropdef2npropdef.has_key(mproperty.intro) then
1537 var intro_nprop = page.ctx.mbuilder.mpropdef2npropdef[mproperty.intro]
1538 if intro_nprop.full_comment.is_empty then
1539 page.append("<p class='info inheritance'>")
1540 page.append("<span class=\"noComment\">no comment for </span>")
1541 else
1542 page.append("<div>{intro_nprop.full_comment}</div>")
1543 page.append("<p class='info inheritance'>")
1544 end
1545 page.append("introduction in ")
1546 mproperty.intro.mclassdef.html_namespace(page)
1547 page.append(" {page.show_source(intro_nprop.location)}</p>")
1548 end
1549 end
1550 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1551 var nprop = page.ctx.mbuilder.mpropdef2npropdef[self]
1552 if nprop.full_comment == "" then
1553 page.append("<p class='info inheritance'>")
1554 page.append("<span class=\"noComment\">no comment for </span>")
1555 else
1556 page.append("<div>{nprop.full_comment}</div>")
1557 page.append("<p class='info inheritance'>")
1558 end
1559 if is_intro then
1560 page.append("introduction in ")
1561 else
1562 page.append("redefinition in ")
1563 end
1564 mclassdef.html_namespace(page)
1565 page.append(" {page.show_source(nprop.location)}</p>")
1566 end
1567 page.append("</div>")
1568 end
1569 end
1570
1571 redef class MMethodDef
1572 redef fun html_full_desc(page, ctx) do
1573 var classes = new Array[String]
1574 var is_redef = mproperty.intro_mclassdef.mclass != ctx
1575 if mproperty.is_init then
1576 classes.add("init")
1577 else
1578 classes.add("fun")
1579 end
1580 if is_redef then classes.add("redef")
1581 classes.add(mproperty.visibility.to_s)
1582 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1583 if page.ctx.mbuilder.mpropdef2npropdef.has_key(self) then
1584 page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}{msignature.untyped_signature(page)}'>")
1585 page.append("<span>{mproperty.name}")
1586 msignature.html_signature(page)
1587 page.append("</span></h3>")
1588 else
1589 page.append("<h3 class='signature' data-untyped-signature='init{msignature.untyped_signature(page)}'>")
1590 page.append("<span>init")
1591 msignature.html_signature(page)
1592 page.append("</span></h3>")
1593 end
1594 html_info(page, ctx)
1595 html_comment(page)
1596 page.append("</article>")
1597 end
1598
1599 redef fun html_info(page, ctx) do
1600 page.append("<div class='info'>")
1601 if mproperty.visibility < public_visibility then page.append("{mproperty.visibility.to_s} ")
1602 if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ")
1603 if mproperty.is_init then
1604 page.append("init ")
1605 else
1606 page.append("fun ")
1607 end
1608 mproperty.html_namespace(page)
1609 page.append("</div>")
1610 end
1611 end
1612
1613 redef class MVirtualTypeDef
1614 redef fun html_full_desc(page, ctx) do
1615 var is_redef = mproperty.intro_mclassdef.mclass != ctx
1616 var classes = new Array[String]
1617 classes.add("type")
1618 if is_redef then classes.add("redef")
1619 classes.add(mproperty.visibility.to_s)
1620 page.append("<article class='{classes.join(" ")}' id='{anchor}'>")
1621 page.append("<h3 class='signature' data-untyped-signature='{mproperty.name}'><span>{mproperty.name}: ")
1622 bound.html_link(page)
1623 page.append("</span></h3>")
1624 html_info(page, ctx)
1625 html_comment(page)
1626 page.append("</article>")
1627 end
1628
1629 redef fun html_info(page, ctx) do
1630 page.append("<div class='info'>")
1631 if mproperty.intro_mclassdef.mclass != ctx then page.append("redef ")
1632 page.append("type ")
1633 mproperty.html_namespace(page)
1634 page.append("</div>")
1635 end
1636 end
1637
1638 redef class MSignature
1639 private fun html_signature(page: NitdocPage) do
1640 if not mparameters.is_empty then
1641 page.append("(")
1642 for i in [0..mparameters.length[ do
1643 mparameters[i].html_link(page)
1644 if i < mparameters.length - 1 then page.append(", ")
1645 end
1646 page.append(")")
1647 end
1648 if return_mtype != null then
1649 page.append(": ")
1650 return_mtype.html_link(page)
1651 end
1652 end
1653
1654 private fun untyped_signature(page: NitdocPage): String do
1655 var res = new Buffer
1656 if not mparameters.is_empty then
1657 res.append("(")
1658 for i in [0..mparameters.length[ do
1659 res.append(mparameters[i].name)
1660 if i < mparameters.length - 1 then res.append(", ")
1661 end
1662 res.append(")")
1663 end
1664 return res.to_s
1665 end
1666 end
1667
1668 redef class MParameter
1669 private fun html_link(page: NitdocPage) do
1670 page.append("{name}: ")
1671 mtype.html_link(page)
1672 if is_vararg then page.append("...")
1673 end
1674 end
1675
1676 #
1677 # Nodes redefs
1678 #
1679
1680 redef class AModule
1681 private fun short_comment: String do
1682 if n_moduledecl != null and n_moduledecl.n_doc != null then
1683 return n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1684 end
1685 return ""
1686 end
1687
1688 private fun full_comment: String do
1689 if n_moduledecl != null and n_moduledecl.n_doc != null then
1690 return n_moduledecl.n_doc.full_markdown.html
1691 end
1692 return ""
1693 end
1694 end
1695
1696 redef class AStdClassdef
1697 private fun short_comment: String do
1698 if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1699 return ""
1700 end
1701
1702 private fun full_comment: String do
1703 if n_doc != null then return n_doc.full_markdown.html
1704 return ""
1705 end
1706 end
1707
1708 redef class APropdef
1709 private fun short_comment: String do
1710 if n_doc != null then return n_doc.n_comment.first.text.substring_from(2).replace("\n", "").html_escape
1711 return ""
1712 end
1713
1714 private fun full_comment: String do
1715 if n_doc != null then return n_doc.full_markdown.html
1716 return ""
1717 end
1718 end
1719
1720 var nitdoc = new NitdocContext
1721 nitdoc.generate_nitdoc