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