ni_nitdoc: clean mclass description generation
[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 # title
849 add("h1").text(mclass.to_s)
850 open("div").add_class("subtitle")
851 var subtitle = ""
852 if mclass.visibility is none_visibility then subtitle += "private "
853 subtitle += "{mclass.kind} {mclass.public_owner.namespace(mbuilder)}::{mclass}"
854 add_html(subtitle)
855 close("div")
856 # comment
857 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
858 add_html("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
859 open("section").add_class("description")
860 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>")
861 process_generate_dot
862 close("section")
863 # concerns
864 var sorted = new Array[MModule]
865 sorted.add_all(mclass.concerns.keys)
866 var sorterp = new ComparableSorter[MModule]
867 sorterp.sort(sorted)
868 open("section").add_class("concerns")
869 add("h2").add_class("section-header").text("Concerns")
870 open("ul")
871 for owner in sorted do
872 var nmodule = mbuilder.mmodule2nmodule[owner]
873 var childs = mclass.concerns[owner]
874 open("li")
875 add_html("<a href=\"#MOD_{owner.name}\">{owner.name}</a>: {nmodule.short_comment}")
876 if not childs is null then
877 open("ul")
878 var sortedc = childs.to_a
879 var sorterpc = new ComparableSorter[MModule]
880 sorterpc.sort(sortedc)
881 for child in sortedc do
882 var nchild = mbuilder.mmodule2nmodule[child]
883 add_html("<li><a href=\"#MOD_{child.name}\">{child.name}</a>: {nchild.short_comment} </li>")
884 end
885 close("ul")
886 end
887 close("li")
888 end
889 close("ul")
890 close("section")
891 # properties
892 var sorterprop = new ComparableSorter[MProperty]
893 var sorterc = new ComparableSorter[MClass]
894 var lmmodule = new List[MModule]
895 # virtual and formal types
896 if mclass.virtual_types.length > 0 or mclass.arity > 0 then
897 open("section").add_class("types")
898 add("h2").text("Formal and Virtual Types")
899 if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop)
900 #TODO this is incorrect
901 if mclass.arity > 0 and nclass isa AStdClassdef then
902 for prop in nclass.n_formaldefs do
903 open("article").attr("id", "FT_Object_{prop.collect_text}")
904 open("h3").add_class("signature").text("{prop.collect_text}: nullable ")
905 add_html("<a title=\"The root of the class hierarchy.\" href=\"Object.html\">Object</a>")
906 close("h3")
907 add_html("<div class=\"info\">formal generic type</div>")
908 close("article")
909 end
910 end
911 close("section")
912 end
913 # constructors
914 if mclass.constructors.length > 0 then
915 var sortedc = mclass.constructors.to_a
916 sorterprop.sort(sortedc)
917 open("section").add_class("constructors")
918 add("h2").add_class("section-header").text("Constructors")
919 for prop in sortedc do description(prop)
920 close("section")
921 end
922 # methods
923 open("section").add_class("methods")
924 add("h2").add_class("section-header").text("Methods")
925 for mmodule, mmethods in mclass.all_methods do
926 var nmodule = mbuilder.mmodule2nmodule[mmodule]
927 add_html("<a id=\"MOD_{mmodule.name}\"></a>")
928 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
929 if mclass.has_mmodule(mmodule) then
930 add_html("<p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
931 else
932 add_html("<h3 class=\"concern-toplevel\">Methods refined in {mmodule.link(nmodule)}</h3><p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
933 end
934 end
935 var sortedc = mmethods.to_a
936 sorterprop.sort(sortedc)
937 for prop in sortedc do description(prop)
938 end
939 # inherited methods
940 if mclass.inherited_methods.length > 0 then
941 var sortedc = new Array[MClass]
942 sortedc.add_all(mclass.inherited.keys)
943 sorterc.sort(sortedc)
944 add("h3").text("Inherited Methods")
945 for imclass in sortedc do
946 var inclass = mbuilder.mclassdef2nclassdef[imclass.intro].as(AStdClassdef)
947 var sortedp = mclass.inherited[imclass].to_a
948 sorterprop.sort(sortedp)
949 open("p")
950 add_html("Defined in {imclass.link(inclass)}: ")
951 for method in sortedp do
952 #TODO link to inherited propdef
953 add_html("<a href=\"\">{method.name}</a>")
954 if method != sortedp.last then add_html(", ")
955 end
956 close("p")
957 end
958 end
959 close("section")
960 end
961
962 fun description(prop: MProperty) do
963 if not mbuilder.mpropdef2npropdef.has_key(prop.intro) then return
964 var nprop = mbuilder.mpropdef2npropdef[prop.intro]
965 if not nprop isa AMethPropdef then return
966 var classes = new Array[String]
967 if nprop isa AInitPropdef then
968 classes.add("init")
969 else
970 classes.add("fun")
971 end
972 if prop.is_redef then classes.add("redef")
973 if prop.visibility == none_visibility then
974 classes.add("private")
975 else if prop.visibility == protected_visibility then
976 classes.add("protected")
977 else
978 classes.add("public")
979 end
980 open("article").add_classes(classes).attr("id", "{prop.anchor}")
981 var sign = prop.name
982 open("h3").add_class("signature")
983 add_html("{prop.name}{nprop.signature}")
984 close("h3")
985 open("div").add_class("info")
986 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>")
987 close("div")
988 open("div").add_class("description")
989 if nprop.comment == "" then
990 add_html("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
991 else
992 add_html("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
993 end
994 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>")
995 open("p")
996 if prop.local_class != mclass then
997 var mredef = prop.local_class.intro_mmodule
998 var nredef = mbuilder.mmodule2nmodule[mredef]
999 add_html("inherited from {mredef.link(nredef)} ")
1000 end
1001 #TODO display show code if doc github
1002 var mintro = prop.intro_mclassdef.mmodule
1003 var nintro = mbuilder.mmodule2nmodule[mintro]
1004 add_html("defined by the module {mintro.link(nintro)}{if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
1005
1006 for parent in mclass.parents do
1007 var mparent = parent.intro_mmodule
1008 var nparent = mbuilder.mmodule2nmodule[mparent]
1009 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>.")
1010 end
1011 close("p")
1012 close("div")
1013
1014 close("article")
1015 end
1016
1017 fun process_generate_dot do
1018 var pe = nitdoc.class_hierarchy[mclass]
1019 var cla = new HashSet[MClass]
1020 var sm = new HashSet[MClass]
1021 var sm2 = new HashSet[MClass]
1022 sm.add(mclass)
1023 while cla.length + sm.length < 10 and sm.length > 0 do
1024 cla.add_all(sm)
1025 sm2.clear
1026 for x in sm do
1027 sm2.add_all(pe.poset[x].direct_smallers)
1028 end
1029 var t = sm
1030 sm = sm2
1031 sm2 = t
1032 end
1033 cla.add_all(pe.greaters)
1034
1035 var op = new Buffer
1036 var name = "dep_{mclass.name}"
1037 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")
1038 for c in cla do
1039 if c == mclass then
1040 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1041 else
1042 op.append("\"{c.name}\"[URL=\"{c.name}.html\"];\n")
1043 end
1044 for c2 in pe.poset[c].direct_greaters do
1045 if not cla.has(c2) then continue
1046 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1047 end
1048 if not pe.poset[c].direct_smallers.is_empty then
1049 var others = true
1050 for c2 in pe.poset[c].direct_smallers do
1051 if cla.has(c2) then others = false
1052 end
1053 if others then
1054 op.append("\"{c.name}...\"[label=\"\"];\n")
1055 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1056 end
1057 end
1058 end
1059 op.append("\}\n")
1060 generate_dot(op.to_s, name, "Dependency graph for class {mclass.name}")
1061 end
1062 end
1063
1064 redef class AModule
1065 private fun comment: String do
1066 var ret = new Buffer
1067 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
1068 if n_moduledecl.n_doc is null then return ""
1069 for t in n_moduledecl.n_doc.n_comment do
1070 ret.append(t.text.substring_from(1))
1071 end
1072 return ret.to_s
1073 end
1074
1075 private fun short_comment: String do
1076 var ret = new Buffer
1077 if n_moduledecl != null and n_moduledecl.n_doc != null then
1078 ret.append(n_moduledecl.n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1079 end
1080 return ret.to_s
1081 end
1082 end
1083
1084 redef class MModule
1085 super Comparable
1086 redef type OTHER: MModule
1087 redef fun <(other: OTHER): Bool do return self.name < other.name
1088
1089 # Get the list of all methods in a module
1090 fun imported_methods: Set[MMethod] do
1091 var methods = new HashSet[MMethod]
1092 for mclass in imported_mclasses do
1093 for method in mclass.intro_methods do
1094 methods.add(method)
1095 end
1096 end
1097 return methods
1098 end
1099
1100 # Get the list aof all refined methods in a module
1101 fun redef_methods: Set[MMethod] do
1102 var methods = new HashSet[MMethod]
1103 for mclass in redef_mclasses do
1104 for method in mclass.intro_methods do
1105 methods.add(method)
1106 end
1107 end
1108 return methods
1109 end
1110
1111 # Return a link (html a tag) to the nitdoc module page
1112 fun link(amodule: AModule): String do
1113 return "<a href=\"{name}.html\" title=\"{amodule.short_comment}\">{name}</a>"
1114 end
1115
1116 # Return the module namespace decorated with html
1117 fun namespace(mbuilder: ModelBuilder): String do
1118 var str = new Buffer
1119 var mowner = public_owner
1120 if mowner != null then
1121 var nowner = mbuilder.mmodule2nmodule[mowner]
1122 str.append(public_owner.link(nowner))
1123 str.append("::")
1124 end
1125 var nmodule = mbuilder.mmodule2nmodule[self]
1126 str.append(self.link(nmodule))
1127 return str.to_s
1128 end
1129 end
1130 redef class MPropDef
1131 super Comparable
1132 redef type OTHER: MPropDef
1133 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1134
1135 # Return a link (html a tag) to the nitdoc class page
1136 fun link(nprop: APropdef): String do
1137 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\" title=\"{nprop.short_comment}\">{mproperty}</a>"
1138 end
1139 end
1140
1141 redef class MProperty
1142 super Comparable
1143 redef type OTHER: MProperty
1144 redef fun <(other: OTHER): Bool do return self.name < other.name
1145
1146 var is_redef: Bool
1147 var apropdef: nullable APropdef
1148
1149 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1150 do
1151 super
1152 is_redef = false
1153 end
1154
1155 fun local_class: MClass do
1156 var classdef = self.intro_mclassdef
1157 return classdef.mclass
1158 end
1159
1160 fun anchor: String do
1161 return "PROP_{c_name}"
1162 end
1163
1164 end
1165
1166 redef class MClass
1167 super Comparable
1168 redef type OTHER: MClass
1169 redef fun <(other: OTHER): Bool do return self.name < other.name
1170
1171 # Add type parameters
1172 redef fun to_s do
1173 if arity > 0 then
1174 return "{name}[{intro.parameter_names.join(", ")}]"
1175 else
1176 return name
1177 end
1178 end
1179
1180 # Return a link (html a tag) to the nitdoc class page
1181 fun link(aclass: AStdClassdef): String do
1182 return "<a href=\"{name}.html\" title=\"{aclass.short_comment}\">{self}</a>"
1183 end
1184
1185 # Associate all MMethods to each MModule concerns
1186 fun all_methods: HashMap[MModule, Set[MMethod]] do
1187 var hm = new HashMap[MModule, Set[MMethod]]
1188 for mmodule, childs in concerns do
1189 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1190 for prop in intro_methods do
1191 if mmodule == prop.intro_mclassdef.mmodule then
1192 prop.is_redef = false
1193 hm[mmodule].add(prop)
1194 end
1195 end
1196 for prop in redef_methods do
1197 if mmodule == prop.intro_mclassdef.mmodule then
1198 prop.is_redef = true
1199 hm[mmodule].add(prop)
1200 end
1201 end
1202
1203 if childs != null then
1204 for child in childs do
1205 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1206 for prop in intro_methods do
1207 if child == prop.intro_mclassdef.mmodule then
1208 prop.is_redef = false
1209 hm[child].add(prop)
1210 end
1211 end
1212 for prop in redef_methods do
1213 if child == prop.intro_mclassdef.mmodule then
1214 prop.is_redef = true
1215 hm[child].add(prop)
1216 end
1217 end
1218 end
1219 end
1220 end
1221 return hm
1222 end
1223
1224 fun public_owner: MModule do
1225 var owner = intro_mmodule
1226 if owner.public_owner is null then
1227 return owner
1228 else
1229 return owner.public_owner.as(not null)
1230 end
1231 end
1232
1233 # Associate MClass to all MMethod include in 'inherited_methods'
1234 fun inherited: HashMap[MClass, Set[MMethod]] do
1235 var hm = new HashMap[MClass, Set[MMethod]]
1236 for method in inherited_methods do
1237 var mclass = method.intro_mclassdef.mclass
1238 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1239 hm[mclass].add(method)
1240 end
1241 return hm
1242 end
1243
1244 # Return true if MModule concern contain subMModule
1245 fun has_mmodule(sub: MModule): Bool do
1246 for mmodule, childs in concerns do
1247 if childs is null then continue
1248 if childs.has(sub) then return true
1249 end
1250 return false
1251 end
1252
1253 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1254 for const in constructors do
1255 if mprop2npropdef.has_key(const)then
1256 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1257 end
1258 end
1259
1260 for intro in intro_methods do
1261 if mprop2npropdef.has_key(intro)then
1262 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1263 end
1264 end
1265
1266 for rd in redef_methods do
1267 if mprop2npropdef.has_key(rd)then
1268 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1269 end
1270 end
1271 end
1272
1273 fun link_anchor: String do
1274 return "{name}.html"
1275 end
1276
1277 end
1278
1279 redef class AStdClassdef
1280 private fun comment: String do
1281 var ret = new Buffer
1282 if n_doc != null then
1283 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1284 end
1285 return ret.to_s
1286 end
1287
1288 private fun short_comment: String do
1289 var ret = new Buffer
1290 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1291 return ret.to_s
1292 end
1293 end
1294
1295 redef class ASignature
1296 redef fun to_s do
1297 #TODO closures
1298 var ret = ""
1299 if not n_params.is_empty then
1300 ret = "{ret}({n_params.join(", ")})"
1301 end
1302 if n_type != null and n_type.to_s != "" then ret += ": {n_type.to_s}"
1303 return ret
1304 end
1305 end
1306
1307 redef class AParam
1308 redef fun to_s do
1309 var ret = "{n_id.text}"
1310 if n_type != null then
1311 ret = "{ret}: {n_type.to_s}"
1312 if n_dotdotdot != null then ret = "{ret}..."
1313 end
1314 return ret
1315 end
1316 end
1317
1318 redef class AType
1319 redef fun to_s do
1320 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1321 if n_kwnullable != null then ret = "nullable {ret}"
1322 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1323 return ret
1324 end
1325 end
1326
1327 redef class APropdef
1328 private fun short_comment: String is abstract
1329 private fun signature: String is abstract
1330 private fun comment: String is abstract
1331 end
1332
1333 redef class AAttrPropdef
1334 redef fun short_comment do
1335 var ret = new Buffer
1336 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(1))
1337 return ret.to_s
1338 end
1339 end
1340
1341 redef class AMethPropdef
1342 redef fun short_comment do
1343 var ret = new Buffer
1344 if n_doc != null then ret.append(n_doc.n_comment.first.text.substring_from(2).replace("\n", ""))
1345 return ret.to_s
1346 end
1347
1348 redef fun signature: String do
1349 var sign = ""
1350 if n_signature != null then sign = "{n_signature.to_s}"
1351 return sign
1352 end
1353
1354 redef private fun comment: String do
1355 var ret = new Buffer
1356 if n_doc != null then
1357 for t in n_doc.n_comment do ret.append(t.text.substring_from(1))
1358 end
1359 return ret.to_s
1360 end
1361 end
1362
1363 redef class MClassDef
1364 private fun namespace(mclass: MClass): String do
1365
1366 if mmodule.public_owner is null then
1367 return "{mmodule.full_name}::{mclass.name}"
1368 else if mclass is self.mclass then
1369 return "{mmodule.public_owner.name}::{mclass.name}"
1370 else
1371 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1372 end
1373 end
1374 end
1375
1376 redef class Set[E]
1377 fun last: E do
1378 return to_a[length-1]
1379 end
1380 end
1381
1382 # Create a tool context to handle options and paths
1383 var toolcontext = new ToolContext
1384
1385 # Here we launch the nit index
1386 var nitdoc = new Nitdoc(toolcontext)
1387 nitdoc.start