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