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