ni_nitdoc: better comment display
[nit.git] / src / ni_nitdoc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 module ni_nitdoc
18
19 import model_utils
20 import abstract_compiler
21 import html
22
23 class Nitdoc
24 private var toolcontext: ToolContext
25 private var model: Model
26 private var modelbuilder: ModelBuilder
27 private var mainmodule: MModule
28 private var class_hierarchy: POSet[MClass]
29 private var arguments: Array[String]
30 private var output_dir: nullable String
31 private var dot_dir: nullable String
32 private var share_dir: nullable String
33 private var source: nullable String
34
35 private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir")
36 private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
37 private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir")
38 private var opt_nodot = new OptionBool("Do not generate graphes with graphiviz", "--no-dot")
39
40 init(toolcontext: ToolContext) do
41 # We need a model to collect stufs
42 self.toolcontext = toolcontext
43 self.arguments = toolcontext.option_context.rest
44 toolcontext.option_context.options.clear
45 toolcontext.option_context.add_option(opt_dir)
46 toolcontext.option_context.add_option(opt_source)
47 toolcontext.option_context.add_option(opt_sharedir)
48 toolcontext.option_context.add_option(opt_nodot)
49 toolcontext.process_options
50 process_options
51
52 if arguments.length < 1 then
53 toolcontext.option_context.usage
54 exit(1)
55 end
56
57 model = new Model
58 modelbuilder = new ModelBuilder(model, toolcontext)
59
60 # Here we load an process std modules
61 var mmodules = modelbuilder.parse_and_build([arguments.first])
62 if mmodules.is_empty then return
63 modelbuilder.full_propdef_semantic_analysis
64 assert mmodules.length == 1
65 self.mainmodule = mmodules.first
66 self.class_hierarchy = mainmodule.flatten_mclass_hierarchy
67 end
68
69 private fun process_options do
70 if not opt_dir.value is null then
71 output_dir = opt_dir.value
72 else
73 output_dir = "doc"
74 end
75 if not opt_sharedir.value is null then
76 share_dir = opt_sharedir.value
77 else
78 var dir = "NIT_DIR".environ
79 if dir.is_empty then
80 dir = "{sys.program_name.dirname}/../share/nitdoc"
81 else
82 dir = "{dir}/share/nitdoc"
83 end
84 share_dir = dir
85 if share_dir is null then
86 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
87 abort
88 end
89 dir = "{share_dir.to_s}/scripts/js-facilities.js"
90 if share_dir is null then
91 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
92 abort
93 end
94 end
95 if not opt_source.value is null then
96 source = ""
97 else
98 source = opt_source.value
99 end
100 end
101
102 fun start do
103 if arguments.length == 1 then
104 # Create destination dir if it's necessary
105 if not output_dir.file_exists then output_dir.mkdir
106 sys.system("cp -r {share_dir.to_s}/* {output_dir.to_s}/")
107 self.dot_dir = null
108 if not opt_nodot.value then self.dot_dir = output_dir.to_s
109 overview
110 #fullindex
111 modules
112 classes
113 #quicksearch_list
114 end
115 end
116
117 fun overview do
118 var overviewpage = new NitdocOverview(modelbuilder, dot_dir)
119 overviewpage.save("{output_dir.to_s}/index.html")
120 end
121
122 fun fullindex do
123 var fullindex = new NitdocFullindex(model.mmodules)
124 fullindex.save("{output_dir.to_s}/full-index.html")
125 end
126
127 fun modules do
128 for mmodule in model.mmodules do
129 var modulepage = new NitdocModule(mmodule, modelbuilder, dot_dir)
130 modulepage.save("{output_dir.to_s}/{mmodule.name}.html")
131 end
132 end
133
134 fun classes do
135 for mclass in modelbuilder.model.mclasses do
136 var classpage = new NitdocClass(mclass, self, dot_dir, source)
137 classpage.save("{output_dir.to_s}/{mclass.name}.html")
138 end
139 end
140
141 # Generate QuickSearch file
142 fun quicksearch_list do
143 var file = new OFStream.open("{output_dir.to_s}/quicksearch-list.js")
144 var content = new Buffer
145 content.append("var entries = \{ ")
146 for prop in model.mproperties do
147 if not prop isa MMethod then continue
148 content.append("\"{prop.name}\": [")
149 for propdef in prop.mpropdefs do
150 content.append("\{txt: \"{propdef.mproperty.full_name}\", url:\"{propdef.mproperty.anchor}\" \}")
151 if not propdef is prop.mpropdefs.last then content.append(", ")
152 end
153 content.append("]")
154 content.append(", ")
155 end
156
157 for mclass in model.mclasses do
158 content.append("\"{mclass.name}\": [")
159 for mclassdef in mclass.mclassdefs do
160 content.append("\{txt: \"{mclassdef.mclass.full_name}\", url:\"{mclass.link_anchor}\" \}")
161 if not mclassdef is mclass.mclassdefs.last then content.append(", ")
162 end
163 content.append("]")
164 if not mclass is model.mclasses.last then content.append(", ")
165 end
166
167 content.append(" \};")
168 file.write(content.to_s)
169 file.close
170 end
171
172 end
173
174 # Nitdoc base page
175 abstract class NitdocPage
176 super HTMLPage
177
178 var dot_dir: nullable String
179 var source: nullable String
180
181 redef fun head do
182 add("meta").attr("charset", "utf-8")
183 add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
184 add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
185 add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
186 add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
187 end
188
189 redef fun body do
190 header
191 open("div").add_class("page")
192 content
193 close("div")
194 footer
195 end
196
197 fun menu is abstract
198
199 fun header do
200 open("header")
201 open("nav").add_class("main")
202 open("ul")
203 menu
204 open("li").attr("id", "liGitHub")
205 open("a").add_class("btn").attr("id", "logGitHub")
206 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
207 close("a")
208 open("div").add_class("popover bottom")
209 add("div").add_class("arrow").text(" ")
210 open("div").add_class("githubTitle")
211 add("h3").text("Github Sign In")
212 close("div")
213 open("div")
214 add("label").attr("id", "lbloginGit").text("Username")
215 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
216 open("label").attr("id", "logginMessage").text("Hello ")
217 open("a").attr("id", "githubAccount")
218 add("strong").attr("id", "nickName").text(" ")
219 close("a")
220 close("label")
221 close("div")
222 open("div")
223 add("label").attr("id", "lbpasswordGit").text("Password")
224 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
225 open("div").attr("id", "listBranches")
226 add("label").attr("id", "lbBranches").text("Branch")
227 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
228 close("div")
229 close("div")
230 open("div")
231 add("label").attr("id", "lbrepositoryGit").text("Repository")
232 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
233 close("div")
234 open("div")
235 add("label").attr("id", "lbbranchGit").text("Branch")
236 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
237 close("div")
238 open("div")
239 add("a").attr("id", "signIn").text("Sign In")
240 close("div")
241 close("div")
242 close("li")
243 close("ul")
244 close("nav")
245 close("header")
246 end
247
248 fun content is abstract
249
250 fun footer do
251 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
252 end
253
254 # Generate a clickable graphviz image using a dot content
255 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 open("article").add_class("graph")
263 add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
264 close("article")
265 var fmap = new IFStream.open("{output_dir}/{name}.map")
266 add_html(fmap.read_all)
267 fmap.close
268 end
269
270 # Add a (source) link for a given location
271 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 href=\"{source.to_s}\">show code</a>)"
284 end
285 end
286 end
287
288 # The overview page
289 class NitdocOverview
290 super NitdocPage
291 private var mbuilder: ModelBuilder
292 private var mmodules = new Array[MModule]
293
294 init(mbuilder: ModelBuilder, dot_dir: nullable String) do
295 self.mbuilder = mbuilder
296 self.dot_dir = dot_dir
297 # get modules
298 var mmodules = new HashSet[MModule]
299 for mmodule in mbuilder.model.mmodules do
300 var owner = mmodule.public_owner
301 if owner != null then
302 mmodules.add(owner)
303 else
304 mmodules.add(mmodule)
305 end
306 end
307 # sort modules
308 var sorter = new ComparableSorter[MModule]
309 self.mmodules.add_all(mmodules)
310 sorter.sort(self.mmodules)
311 end
312
313 redef fun head do
314 super
315 add("title").text("Overview | Nit Standard Library")
316 end
317
318 redef fun menu do
319 add("li").add_class("current").text("Overview")
320 open("li")
321 add("a").attr("href", "full-index.html").text("Full Index")
322 close("li")
323 end
324
325 redef fun content do
326 open("div").add_class("content fullpage")
327 add("h1").text("Nit Standard Library")
328 open("article").add_class("overview")
329 add_html("<p>Documentation for the standard library of Nit<br />Version jenkins-component=stdlib-19<br />Date: TODAY</p>")
330 close("article")
331 open("article").add_class("overview")
332 # module list
333 add("h2").text("Modules")
334 open("ul")
335 for mmodule in mmodules do
336 var amodule = mbuilder.mmodule2nmodule[mmodule]
337 open("li")
338 add_html("{mmodule.link(amodule)}&nbsp;{amodule.short_comment}")
339 close("li")
340 end
341 close("ul")
342 # module graph
343 process_generate_dot
344 close("article")
345 close("div")
346 end
347
348 fun process_generate_dot do
349 var op = new Buffer
350 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")
351 for mmodule in mmodules do
352 op.append("\"{mmodule.name}\"[URL=\"{mmodule.name}.html\"];\n")
353 for imported in mmodule.in_importation.direct_greaters do
354 if imported.direct_owner == null then
355 op.append("\"{mmodule.name}\"->\"{imported.name}\";\n")
356 end
357 end
358 end
359 op.append("\}\n")
360 generate_dot(op.to_s, "dep", "Modules hierarchy")
361 end
362 end
363
364 # The full index page
365 class NitdocFullindex
366 super NitdocPage
367
368 private var mmodules: Array[MModule]
369
370 init(mmodules: Array[MModule]) do
371 self.mmodules = mmodules
372 self.dot_dir = null
373 end
374
375 redef fun head do
376 super
377 add("title").text("Full Index | Nit Standard Library")
378 end
379
380 redef fun menu do
381 open("li")
382 add("a").attr("href", "index.html").text("Overview")
383 close("li")
384 add("li").add_class("current").text("Full Index")
385 end
386
387 redef fun content do
388 open("div").add_class("content fullpage")
389 add("h1").text("Full Index")
390 module_column
391 classes_column
392 properties_column
393 close("div")
394 end
395
396 # Add to content modules column
397 fun module_column do
398 var ls = new List[nullable MModule]
399 var sorted = mmodules
400 var sorterp = new ComparableSorter[MModule]
401 sorterp.sort(sorted)
402 open("article").add_class("modules filterable")
403 add("h2").text("Modules")
404 open("ul")
405 for mmodule in sorted do
406 if mmodule.public_owner != null and not ls.has(mmodule.public_owner) then
407 ls.add(mmodule.public_owner)
408 open("li")
409 add("a").attr("href", "{mmodule.public_owner.name}.html").text(mmodule.public_owner.name)
410 close("li")
411 end
412 end
413 close("ul")
414 close("article")
415 end
416
417 # Add to content classes modules
418 fun classes_column do
419 var sorted = mmodules.first.imported_mclasses.to_a
420 var sorterp = new ComparableSorter[MClass]
421 sorterp.sort(sorted)
422 open("article").add_class("classes filterable")
423 add("h2").text("Classes")
424 open("ul")
425
426 for mclass in sorted do
427 open("li")
428 add("a").attr("href", "{mclass}.html").text(mclass.name)
429 close("li")
430 end
431
432 close("ul")
433 close("article")
434 end
435
436 # Insert the properties column of fullindex page
437 fun properties_column do
438 open("article").add_class("properties filterable")
439 add("h2").text("Properties")
440 open("ul")
441 var sorted_imported = mmodules.first.imported_methods.to_a
442 var sorted_redef = mmodules.first.redef_methods.to_a
443 var sorterp = new ComparableSorter[MProperty]
444 sorterp.sort(sorted_imported)
445 sorterp.sort(sorted_redef)
446
447 for method in sorted_imported do
448 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
449 open("li").add_class("intro")
450 add("span").attr("title", "introduction").text("I")
451 add_html("&nbsp;")
452 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
453 close("li")
454 end
455
456 for method in sorted_redef do
457 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
458 open("li").add_class("redef")
459 add("span").attr("title", "redefinition").text("R")
460 add_html("&nbsp;")
461 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
462 close("li")
463 end
464
465 close("ul")
466 close("article")
467 end
468
469 end
470
471 # A module page
472 class NitdocModule
473 super NitdocPage
474
475 private var mmodule: MModule
476 private var mbuilder: ModelBuilder
477
478 init(mmodule: MModule, mbuilder: ModelBuilder, dot_dir: nullable String) do
479 self.mmodule = mmodule
480 self.mbuilder = mbuilder
481 self.dot_dir = dot_dir
482 end
483
484 redef fun head do
485 super
486 var amodule = mbuilder.mmodule2nmodule[mmodule]
487 add("title").text("{mmodule.name} module | {amodule.short_comment}")
488 end
489
490 redef fun menu do
491 open("li")
492 add("a").attr("href", "index.html").text("Overview")
493 close("li")
494 add("li").add_class("current").text(mmodule.name)
495 open("li")
496 add("a").attr("href", "full-index.html").text("Full Index")
497 close("li")
498 end
499
500 redef fun content do
501 sidebar
502 open("div").add_class("content")
503 add("h1").text(mmodule.name)
504 add("div").add_class("subtitle")
505 add_html("module {mmodule.namespace(mbuilder)}")
506 module_comment
507 process_generate_dot
508 classes
509 properties
510 close("div")
511 end
512
513 # Insert module comment in the content
514 fun module_comment do
515 var amodule = mbuilder.mmodule2nmodule[mmodule]
516 var doc = amodule.comment
517 open("div").attr("id", "description")
518 add("pre").add_class("text_label").text(doc)
519 add("textarea").add_class("edit").attr("rows", "1").attr("cols", "76").attr("id", "fileContent").text(" ")
520 add("a").attr("id", "cancelBtn").text("Cancel")
521 add("a").attr("id", "commitBtn").text("Commit")
522 add("pre").add_class("text_label").attr("id", "preSave").attr("type", "2")
523 close("div")
524 end
525
526 fun process_generate_dot do
527 var name = "dep_{mmodule.name}"
528 var op = new Buffer
529 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")
530 for m in mmodule.in_importation.poset do
531 var public_owner = m.public_owner
532 if public_owner == null then
533 public_owner = m
534 if m == mmodule then
535 op.append("\"{m.name}\"[shape=box,margin=0.03];\n")
536 else
537 op.append("\"{m.name}\"[URL=\"{m.name}.html\"];\n")
538 end
539 end
540 for imported in m.in_importation.direct_greaters do
541 if imported.public_owner == null then
542 op.append("\"{public_owner.name}\"->\"{imported.name}\";\n")
543 end
544 end
545 end
546 op.append("\}\n")
547 generate_dot(op.to_s, name, "Dependency graph for module {mmodule.name}")
548 end
549
550 fun sidebar do
551 var amodule = mbuilder.mmodule2nmodule[mmodule]
552 open("div").add_class("menu")
553 open("nav")
554 add("h3").text("Module Hierarchy")
555 var dependencies = new Array[MModule]
556 for dep in mmodule.in_importation.greaters do
557 if dep == mmodule or dep.public_owner != null then continue
558 dependencies.add(dep)
559 end
560 if dependencies.length > 0 then
561 add("h4").text("All dependencies")
562 display_module_list(dependencies)
563 end
564 var clients = new Array[MModule]
565 for dep in mmodule.in_importation.smallers do
566 if dep == mmodule or dep.public_owner != null then continue
567 clients.add(dep)
568 end
569 if clients.length > 0 then
570 add("h4").text("All clients")
571 display_module_list(clients)
572 end
573 close("nav")
574 if mmodule.in_nesting.direct_greaters.length > 0 then
575 open("nav")
576 add("h3").text("Nested Modules")
577 display_module_list(mmodule.in_nesting.direct_greaters.to_a)
578 close("nav")
579 end
580 close("div")
581 end
582
583 private fun display_module_list(list: Array[MModule]) do
584 open("ul")
585 var sorter = new ComparableSorter[MModule]
586 sorter.sort(list)
587 for m in list do
588 var am = mbuilder.mmodule2nmodule[m]
589 open("li")
590 add_html(m.link(am))
591 close("li")
592 end
593 close("ul")
594 end
595
596 fun classes do
597 var amodule = mbuilder.mmodule2nmodule[mmodule]
598 var intro_mclasses = mmodule.intro_mclasses
599 var redef_mclasses = mmodule.redef_mclasses
600 var all_mclasses = new HashSet[MClass]
601 for m in mmodule.in_nesting.greaters do
602 all_mclasses.add_all(m.intro_mclasses)
603 all_mclasses.add_all(m.redef_mclasses)
604 end
605 all_mclasses.add_all(intro_mclasses)
606 all_mclasses.add_all(redef_mclasses)
607
608 var sorted = new Array[MClass]
609 sorted.add_all(all_mclasses)
610 var sorter = new ComparableSorter[MClass]
611 sorter.sort(sorted)
612 open("div").add_class("module")
613 open("article").add_class("classes filterable")
614 add("h2").text("Classes")
615 open("ul")
616 for c in sorted do
617 var nclass = mbuilder.mclassdef2nclassdef[c.intro].as(AStdClassdef)
618 if redef_mclasses.has(c) and c.intro_mmodule.public_owner != mmodule then
619 open("li").add_class("redef")
620 add("span").attr("title", "refined in this module").text("R ")
621 else
622 open("li").add_class("intro")
623 add("span").attr("title", "introduced in this module").text("I ")
624 end
625 add_html(c.link(nclass))
626 close("li")
627 end
628 close("ul")
629 close("article")
630 close("div")
631 end
632
633 fun properties do
634 var amodule = mbuilder.mmodule2nmodule[mmodule]
635 var mpropdefs = new HashSet[MPropDef]
636 for m in mmodule.in_nesting.greaters do
637 for c in m.mclassdefs do mpropdefs.add_all(c.mpropdefs)
638 end
639 for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs)
640 var sorted = mpropdefs.to_a
641 var sorter = new ComparableSorter[MPropDef]
642 sorter.sort(sorted)
643 open("article").add_class("properties filterable")
644 add("h2").text("Properties")
645 open("ul")
646 for p in sorted do
647 if p isa MAttributeDef then continue
648 if p.mproperty.visibility <= none_visibility then continue
649 if not mbuilder.mpropdef2npropdef.has_key(p) then continue
650 var nprop = mbuilder.mpropdef2npropdef[p]
651 if p.is_intro then
652 open("li").add_class("intro")
653 add("span").attr("title", "introduction").text("I")
654 else
655 open("li").add_class("redef")
656 add("span").attr("title", "redefinition").text("R")
657 end
658 add_html("&nbsp;{p.link(nprop)} ({p.mclassdef.mclass.name})")
659 close("li")
660 end
661 close("ul")
662 close("article")
663 end
664 end
665
666 # A class page
667 class NitdocClass
668 super NitdocPage
669
670 private var mclass: MClass
671 private var mbuilder: ModelBuilder
672 private var nitdoc: Nitdoc
673
674 init(mclass: MClass, nitdoc: Nitdoc, dot_dir: nullable String, source: nullable String) do
675 self.mclass = mclass
676 self.mbuilder = nitdoc.modelbuilder
677 self.nitdoc = nitdoc
678 self.dot_dir = dot_dir
679 self.source = source
680 end
681
682 redef fun head do
683 super
684 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
685 if nclass isa AStdClassdef then
686 add("title").text("{mclass.name} class | {nclass.short_comment}")
687 else
688 add("title").text("{mclass.name} class")
689 end
690 end
691
692 redef fun menu do
693 open("li")
694 add("a").attr("href", "index.html").text("Overview")
695 close("li")
696 open("li")
697 var public_owner = mclass.public_owner
698 if public_owner is null then
699 var am = mbuilder.mmodule2nmodule[mclass.intro_mmodule]
700 add_html(mclass.intro_mmodule.link(am))
701 else
702 var am = mbuilder.mmodule2nmodule[public_owner]
703 add_html(public_owner.link(am))
704 end
705 close("li")
706 add("li").add_class("current").text(mclass.name)
707 open("li")
708 add("a").attr("href", "full-index.html").text("Full Index")
709 close("li")
710 end
711
712 redef fun content do
713 open("div").add_class("menu")
714 properties_column
715 inheritance_column
716 close("div")
717 open("div").add_class("content")
718 class_doc
719 close("div")
720 end
721
722 fun properties_column do
723 var sorter = new ComparableSorter[MPropDef]
724 open("nav").add_class("properties filterable")
725 add("h3").text("Properties")
726 # get properties
727 var vtypes = new HashSet[MVirtualTypeDef]
728 var consts = new HashSet[MMethodDef]
729 var meths = new HashSet[MMethodDef]
730 for mclassdef in mclass.mclassdefs do
731 for mpropdef in mclassdef.mpropdefs do
732 if not mbuilder.mpropdef2npropdef.has_key(mpropdef) then continue
733 if mpropdef.mproperty.visibility <= none_visibility then continue
734 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
735 if mpropdef isa MMethodDef then
736 if mpropdef.mproperty.is_init then
737 consts.add(mpropdef)
738 else
739 meths.add(mpropdef)
740 end
741 end
742 end
743 end
744 for mprop in mclass.inherited_methods do
745 var mpropdef = mprop.intro
746 if not mbuilder.mpropdef2npropdef.has_key(mpropdef) then continue
747 if mprop.visibility <= none_visibility then continue
748 if mprop.intro_mclassdef.mclass.name == "Object" then continue
749 meths.add(mpropdef)
750 end
751 # virtual types
752 if vtypes.length > 0 then
753 var vts = new Array[MVirtualTypeDef]
754 vts.add_all(vtypes)
755 sorter.sort(vts)
756 add("h4").text("Virtual Types")
757 display_mpropdef_list(vts)
758 end
759 if consts.length > 0 then
760 var cts = new Array[MMethodDef]
761 cts.add_all(consts)
762 sorter.sort(cts)
763 add("h4").text("Constructors")
764 display_mpropdef_list(cts)
765 end
766 if meths.length > 0 then
767 var mts = new Array[MMethodDef]
768 mts.add_all(meths)
769 sorter.sort(mts)
770 add("h4").text("Methods")
771 display_mpropdef_list(mts)
772 end
773 close("nav")
774 end
775
776 private fun display_mpropdef_list(list: Array[MPropDef]) do
777 open("ul")
778 for prop in list do
779 var nprop = mbuilder.mpropdef2npropdef[prop]
780 if prop.is_intro and prop.mproperty.intro_mclassdef.mclass != mclass then
781 open("li").add_class("inherit")
782 add("span").attr("title", "Inherited").text("H")
783 else if prop.is_intro then
784 open("li").add_class("intro")
785 add("span").attr("title", "Introduced").text("I")
786 else
787 open("li").add_class("redef")
788 add("span").attr("title", "Redefined").text("R")
789 end
790 add_html(prop.link(nprop))
791 close("li")
792 end
793 close("ul")
794 end
795
796 fun inheritance_column do
797 var sorted = new Array[MClass]
798 var sorterp = new ComparableSorter[MClass]
799 open("nav")
800 add("h3").text("Inheritance")
801 if mclass.ancestors.length > 1 then
802 sorted = mclass.ancestors.to_a
803 sorterp.sort(sorted)
804 add("h4").text("Superclasses")
805 open("ul")
806 for sup in sorted do
807 if sup == mclass then continue
808 open("li")
809 add("a").attr("href", "{sup.name}.html").text(sup.name)
810 close("li")
811 end
812 close("ul")
813 end
814
815 if mclass.descendants.length <= 1 then
816 add("h4").text("No Known Subclasses")
817 else if mclass.descendants.length <= 100 then
818 sorted = mclass.descendants.to_a
819 sorterp.sort(sorted)
820 add("h4").text("Subclasses")
821 open("ul")
822 for sub in sorted do
823 if sub == mclass then continue
824 open("li")
825 add("a").attr("href", "{sub.name}.html").text(sub.name)
826 close("li")
827 end
828 close("ul")
829 else if mclass.children.length <= 100 then
830 sorted = mclass.children.to_a
831 sorterp.sort(sorted)
832 add("h4").text("Direct Subclasses Only")
833 open("ul")
834 for sub in sorted do
835 if sub == mclass then continue
836 open("li")
837 add("a").attr("href", "{sub.name}.html").text(sub.name)
838 close("li")
839 end
840 close("ul")
841 else
842 add("h4").text("Too much Subclasses to list")
843 end
844 close("nav")
845 end
846
847 fun class_doc do
848 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
849 var sorted = new Array[MModule]
850 sorted.add_all(mclass.concerns.keys)
851 var sorterp = new ComparableSorter[MModule]
852 var sorterprop = new ComparableSorter[MProperty]
853 var sorterc = new ComparableSorter[MClass]
854 sorterp.sort(sorted)
855 var subtitle = ""
856 var lmmodule = new List[MModule]
857 # Insert the subtitle part
858 add("h1").text(mclass.to_s)
859 open("div").add_class("subtitle")
860 if mclass.visibility is none_visibility then subtitle += "private "
861 subtitle += "{mclass.kind} {mclass.public_owner.namespace(mbuilder)}::{mclass}"
862 add_html(subtitle)
863 close("div")
864 add_html("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
865 # We add the class description
866 open("section").add_class("description")
867 if nclass isa AStdClassdef and not nclass.comment.is_empty then add_html("<pre class=\"text_label\" title=\"122\" name=\"\" tag=\"{mclass.mclassdefs.first.location.to_s}\" type=\"2\">{nclass.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>")
868 process_generate_dot
869 close("section")
870 open("section").add_class("concerns")
871 add("h2").add_class("section-header").text("Concerns")
872 open("ul")
873 for owner in sorted do
874 var nmodule = mbuilder.mmodule2nmodule[owner]
875 var childs = mclass.concerns[owner]
876 open("li")
877 add_html("<a href=\"#MOD_{owner.name}\">{owner.name}</a>: {nmodule.short_comment}")
878 if not childs is null then
879 open("ul")
880 var sortedc = childs.to_a
881 var sorterpc = new ComparableSorter[MModule]
882 sorterpc.sort(sortedc)
883 for child in sortedc do
884 var nchild = mbuilder.mmodule2nmodule[child]
885 add_html("<li><a href=\"#MOD_{child.name}\">{child.name}</a>: {nchild.short_comment} </li>")
886 end
887 close("ul")
888 end
889 close("li")
890 end
891 close("ul")
892 close("section")
893 # Insert virtual types if there is almost one
894 if mclass.virtual_types.length > 0 or mclass.arity > 0 then
895 open("section").add_class("types")
896 add("h2").text("Formal and Virtual Types")
897 if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop)
898 if mclass.arity > 0 and nclass isa AStdClassdef then
899 for prop in nclass.n_formaldefs do
900 open("article").attr("id", "FT_Object_{prop.collect_text}")
901 open("h3").add_class("signature").text("{prop.collect_text}: nullable ")
902 add_html("<a title=\"The root of the class hierarchy.\" href=\"Object.html\">Object</a>")
903 close("h3")
904 add_html("<div class=\"info\">formal generic type</div>")
905 close("article")
906 end
907 end
908 close("section")
909 end
910 # constructors
911 if mclass.constructors.length > 0 then
912 var sortedc = mclass.constructors.to_a
913 sorterprop.sort(sortedc)
914 open("section").add_class("constructors")
915 add("h2").add_class("section-header").text("Constructors")
916 for prop in sortedc do description(prop)
917 close("section")
918 end
919 # methods
920 open("section").add_class("methods")
921 add("h2").add_class("section-header").text("Methods")
922 for mmodule, mmethods in mclass.all_methods do
923 var nmodule = mbuilder.mmodule2nmodule[mmodule]
924 add_html("<a id=\"MOD_{mmodule.name}\"></a>")
925 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
926 if mclass.has_mmodule(mmodule) then
927 add_html("<p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
928 else
929 add_html("<h3 class=\"concern-toplevel\">Methods refined in {mmodule.link(nmodule)}</h3><p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
930 end
931 end
932 var sortedc = mmethods.to_a
933 sorterprop.sort(sortedc)
934 for prop in sortedc do description(prop)
935 end
936 # inherited methods
937 if mclass.inherited_methods.length > 0 then
938 var sortedc = new Array[MClass]
939 sortedc.add_all(mclass.inherited.keys)
940 sorterc.sort(sortedc)
941 add("h3").text("Inherited Methods")
942 for imclass in sortedc do
943 var inclass = mbuilder.mclassdef2nclassdef[imclass.intro].as(AStdClassdef)
944 var sortedp = mclass.inherited[imclass].to_a
945 sorterprop.sort(sortedp)
946 open("p")
947 add_html("Defined in {imclass.link(inclass)}: ")
948 for method in sortedp do
949 #TODO link to inherited propdef
950 add_html("<a href=\"\">{method.name}</a>")
951 if method != sortedp.last then add_html(", ")
952 end
953 close("p")
954 end
955 end
956 close("section")
957 end
958
959 fun description(prop: MProperty) do
960 if not mbuilder.mpropdef2npropdef.has_key(prop.intro) then return
961 var nprop = mbuilder.mpropdef2npropdef[prop.intro]
962 if not nprop isa AMethPropdef then return
963 var classes = new Array[String]
964 if nprop isa AInitPropdef then
965 classes.add("init")
966 else
967 classes.add("fun")
968 end
969 if prop.is_redef then classes.add("redef")
970 if prop.visibility == none_visibility then
971 classes.add("private")
972 else if prop.visibility == protected_visibility then
973 classes.add("protected")
974 else
975 classes.add("public")
976 end
977 open("article").add_classes(classes).attr("id", "{prop.anchor}")
978 var sign = prop.name
979 open("h3").add_class("signature")
980 add_html("{prop.name}{nprop.signature}")
981 close("h3")
982 open("div").add_class("info")
983 add_html("{if prop.is_redef then "redef" else ""} fun {prop.intro_mclassdef.namespace(mclass)}::{prop.name}</div><div style=\"float: right;\"><a id=\"lblDiffCommit\"></a>")
984 close("div")
985 open("div").add_class("description")
986 if nprop.comment == "" then
987 add_html("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
988 else
989 add_html("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
990 end
991 add_html("<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>")
992 open("p")
993 if prop.local_class != mclass then
994 var mredef = prop.local_class.intro_mmodule
995 var nredef = mbuilder.mmodule2nmodule[mredef]
996 add_html("inherited from {mredef.link(nredef)} ")
997 end
998 #TODO display show code if doc github
999 var mintro = prop.intro_mclassdef.mmodule
1000 var nintro = mbuilder.mmodule2nmodule[mintro]
1001 add_html("defined by the module {mintro.link(nintro)}{if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
1002
1003 for parent in mclass.parents do
1004 var mparent = parent.intro_mmodule
1005 var nparent = mbuilder.mmodule2nmodule[mparent]
1006 if prop isa MMethod then if parent.constructors.has(prop) then add_html(" Previously defined by: {mparent.link(nparent)} for <a href=\"{parent.name}.html\">{parent.name}</a>.")
1007 end
1008 close("p")
1009 close("div")
1010
1011 close("article")
1012 end
1013
1014 fun process_generate_dot do
1015 var pe = nitdoc.class_hierarchy[mclass]
1016 var cla = new HashSet[MClass]
1017 var sm = new HashSet[MClass]
1018 var sm2 = new HashSet[MClass]
1019 sm.add(mclass)
1020 while cla.length + sm.length < 10 and sm.length > 0 do
1021 cla.add_all(sm)
1022 sm2.clear
1023 for x in sm do
1024 sm2.add_all(pe.poset[x].direct_smallers)
1025 end
1026 var t = sm
1027 sm = sm2
1028 sm2 = t
1029 end
1030 cla.add_all(pe.greaters)
1031
1032 var op = new Buffer
1033 var name = "dep_{mclass.name}"
1034 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")
1035 for c in cla do
1036 if c == mclass then
1037 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1038 else
1039 op.append("\"{c.name}\"[URL=\"{c.name}.html\"];\n")
1040 end
1041 for c2 in pe.poset[c].direct_greaters do
1042 if not cla.has(c2) then continue
1043 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1044 end
1045 if not pe.poset[c].direct_smallers.is_empty then
1046 var others = true
1047 for c2 in pe.poset[c].direct_smallers do
1048 if cla.has(c2) then others = false
1049 end
1050 if others then
1051 op.append("\"{c.name}...\"[label=\"\"];\n")
1052 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1053 end
1054 end
1055 end
1056 op.append("\}\n")
1057 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1058 end
1059 end
1060
1061 redef class AModule
1062 private fun comment: String do
1063 var ret = new Buffer
1064 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
1065 if n_moduledecl.n_doc is null then return ""
1066 for t in n_moduledecl.n_doc.n_comment do
1067 ret.append(t.text.substring_from(1))
1068 end
1069 return ret.to_s
1070 end
1071
1072 private fun short_comment: String do
1073 var ret = new Buffer
1074 if n_moduledecl != null and n_moduledecl.n_doc != null then
1075 ret.append(n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1076 end
1077 return ret.to_s
1078 end
1079 end
1080
1081 redef class MModule
1082 super Comparable
1083 redef type OTHER: MModule
1084 redef fun <(other: OTHER): Bool do return self.name < other.name
1085
1086 # Get the list of all methods in a module
1087 fun imported_methods: Set[MMethod] do
1088 var methods = new HashSet[MMethod]
1089 for mclass in imported_mclasses do
1090 for method in mclass.intro_methods do
1091 methods.add(method)
1092 end
1093 end
1094 return methods
1095 end
1096
1097 # Get the list aof all refined methods in a module
1098 fun redef_methods: Set[MMethod] do
1099 var methods = new HashSet[MMethod]
1100 for mclass in redef_mclasses do
1101 for method in mclass.intro_methods do
1102 methods.add(method)
1103 end
1104 end
1105 return methods
1106 end
1107
1108 # Return a link (html a tag) to the nitdoc module page
1109 fun link(amodule: AModule): String do
1110 return "<a href=\"{name}.html\" title=\"{amodule.short_comment}\">{name}</a>"
1111 end
1112
1113 # Return the module namespace decorated with html
1114 fun namespace(mbuilder: ModelBuilder): String do
1115 var str = new Buffer
1116 var mowner = public_owner
1117 if mowner != null then
1118 var nowner = mbuilder.mmodule2nmodule[mowner]
1119 str.append(public_owner.link(nowner))
1120 str.append("::")
1121 end
1122 var nmodule = mbuilder.mmodule2nmodule[self]
1123 str.append(self.link(nmodule))
1124 return str.to_s
1125 end
1126 end
1127 redef class MPropDef
1128 super Comparable
1129 redef type OTHER: MPropDef
1130 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1131
1132 # Return a link (html a tag) to the nitdoc class page
1133 fun link(nprop: APropdef): String do
1134 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\" title=\"{nprop.short_comment}\">{mproperty}</a>"
1135 end
1136 end
1137
1138 redef class MProperty
1139 super Comparable
1140 redef type OTHER: MProperty
1141 redef fun <(other: OTHER): Bool do return self.name < other.name
1142
1143 var is_redef: Bool
1144 var apropdef: nullable APropdef
1145
1146 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1147 do
1148 super
1149 is_redef = false
1150 end
1151
1152 fun local_class: MClass do
1153 var classdef = self.intro_mclassdef
1154 return classdef.mclass
1155 end
1156
1157 fun anchor: String do
1158 return "PROP_{c_name}"
1159 end
1160
1161 end
1162
1163 redef class MClass
1164 super Comparable
1165 redef type OTHER: MClass
1166 redef fun <(other: OTHER): Bool do return self.name < other.name
1167
1168 # Add type parameters
1169 redef fun to_s do
1170 if arity > 0 then
1171 return "{name}[{intro.parameter_names.join(", ")}]"
1172 else
1173 return name
1174 end
1175 end
1176
1177 # Return a link (html a tag) to the nitdoc class page
1178 fun link(aclass: AStdClassdef): String do
1179 return "<a href=\"{name}.html\" title=\"{aclass.short_comment}\">{self}</a>"
1180 end
1181
1182 # Associate all MMethods to each MModule concerns
1183 fun all_methods: HashMap[MModule, Set[MMethod]] do
1184 var hm = new HashMap[MModule, Set[MMethod]]
1185 for mmodule, childs in concerns do
1186 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1187 for prop in intro_methods do
1188 if mmodule == prop.intro_mclassdef.mmodule then
1189 prop.is_redef = false
1190 hm[mmodule].add(prop)
1191 end
1192 end
1193 for prop in redef_methods do
1194 if mmodule == prop.intro_mclassdef.mmodule then
1195 prop.is_redef = true
1196 hm[mmodule].add(prop)
1197 end
1198 end
1199
1200 if childs != null then
1201 for child in childs do
1202 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1203 for prop in intro_methods do
1204 if child == prop.intro_mclassdef.mmodule then
1205 prop.is_redef = false
1206 hm[child].add(prop)
1207 end
1208 end
1209 for prop in redef_methods do
1210 if child == prop.intro_mclassdef.mmodule then
1211 prop.is_redef = true
1212 hm[child].add(prop)
1213 end
1214 end
1215 end
1216 end
1217 end
1218 return hm
1219 end
1220
1221 fun public_owner: MModule do
1222 var owner = intro_mmodule
1223 if owner.public_owner is null then
1224 return owner
1225 else
1226 return owner.public_owner.as(not null)
1227 end
1228 end
1229
1230 # Associate MClass to all MMethod include in 'inherited_methods'
1231 fun inherited: HashMap[MClass, Set[MMethod]] do
1232 var hm = new HashMap[MClass, Set[MMethod]]
1233 for method in inherited_methods do
1234 var mclass = method.intro_mclassdef.mclass
1235 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1236 hm[mclass].add(method)
1237 end
1238 return hm
1239 end
1240
1241 # Return true if MModule concern contain subMModule
1242 fun has_mmodule(sub: MModule): Bool do
1243 for mmodule, childs in concerns do
1244 if childs is null then continue
1245 if childs.has(sub) then return true
1246 end
1247 return false
1248 end
1249
1250 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1251 for const in constructors do
1252 if mprop2npropdef.has_key(const)then
1253 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1254 end
1255 end
1256
1257 for intro in intro_methods do
1258 if mprop2npropdef.has_key(intro)then
1259 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1260 end
1261 end
1262
1263 for rd in redef_methods do
1264 if mprop2npropdef.has_key(rd)then
1265 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1266 end
1267 end
1268 end
1269
1270 fun link_anchor: String do
1271 return "{name}.html"
1272 end
1273
1274 end
1275
1276 redef class AStdClassdef
1277 private fun comment: String do
1278 var ret = new Buffer
1279 if n_doc != null then
1280 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1281 end
1282 return ret.to_s
1283 end
1284
1285 private fun short_comment: String do
1286 var ret = new Buffer
1287 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1288 return ret.to_s
1289 end
1290 end
1291
1292 redef class ASignature
1293 redef fun to_s do
1294 #TODO closures
1295 var ret = ""
1296 if not n_params.is_empty then
1297 ret = "{ret}({n_params.join(", ")})"
1298 end
1299 if n_type != null and n_type.to_s != "" then ret += ": {n_type.to_s}"
1300 return ret
1301 end
1302 end
1303
1304 redef class AParam
1305 redef fun to_s do
1306 var ret = "{n_id.text}"
1307 if n_type != null then
1308 ret = "{ret}: {n_type.to_s}"
1309 if n_dotdotdot != null then ret = "{ret}..."
1310 end
1311 return ret
1312 end
1313 end
1314
1315 redef class AType
1316 redef fun to_s do
1317 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1318 if n_kwnullable != null then ret = "nullable {ret}"
1319 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1320 return ret
1321 end
1322 end
1323
1324 redef class APropdef
1325 private fun short_comment: String is abstract
1326 private fun signature: String is abstract
1327 private fun comment: String is abstract
1328 end
1329
1330 redef class AAttrPropdef
1331 redef fun short_comment do
1332 var ret = new Buffer
1333 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(1))
1334 return ret.to_s
1335 end
1336 end
1337
1338 redef class AMethPropdef
1339 redef fun short_comment do
1340 var ret = new Buffer
1341 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1342 return ret.to_s
1343 end
1344
1345 redef fun signature: String do
1346 var sign = ""
1347 if n_signature != null then sign = "{n_signature.to_s}"
1348 return sign
1349 end
1350
1351 redef private fun comment: String do
1352 var ret = new Buffer
1353 if n_doc != null then
1354 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1355 end
1356 return ret.to_s
1357 end
1358 end
1359
1360 redef class MClassDef
1361 private fun namespace(mclass: MClass): String do
1362
1363 if mmodule.public_owner is null then
1364 return "{mmodule.full_name}::{mclass.name}"
1365 else if mclass is self.mclass then
1366 return "{mmodule.public_owner.name}::{mclass.name}"
1367 else
1368 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1369 end
1370 end
1371 end
1372
1373 redef class Set[E]
1374 fun last: E do
1375 return to_a[length-1]
1376 end
1377 end
1378
1379 # Create a tool context to handle options and paths
1380 var toolcontext = new ToolContext
1381
1382 # Here we launch the nit index
1383 var nitdoc = new Nitdoc(toolcontext)
1384 nitdoc.start