ni_nitdoc: cleaned html 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 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
288 var mbuilder: ModelBuilder
289
290 # Init with Array[AModule] to get all ifnormations about each MModule containt in a program
291 # opt_nodot to inform about the graph gen
292 # destination: to know where will be saved dot files
293 init with(mbuilder: ModelBuilder, opt_nodot: Bool, destination: String) do
294 self.mbuilder = mbuilder
295 self.opt_nodot = opt_nodot
296 self.destinationdir = destination
297 end
298
299 redef fun head do
300 super
301 add("title").text("Overview | Nit Standard Library")
302 end
303
304 redef fun menu do
305 add("li").add_class("current").text("Overview")
306 open("li")
307 add("a").attr("href", "full-index.html").text("Full Index")
308 close("li")
309 end
310
311 redef fun content do
312 open("div").add_class("content fullpage")
313 add("h1").text("Nit Standard Library")
314 open("article").add_class("overview")
315 add_html("<p>Documentation for the standard library of Nit<br />Version jenkins-component=stdlib-19<br />Date: TODAY</p>")
316 close("article")
317 open("article").add_class("overview")
318 add("h2").text("Modules")
319 open("ul")
320 add_modules
321 close("ul")
322 process_generate_dot
323 close("article")
324 close("div")
325 end
326
327 fun add_modules do
328 var mmodules = list_mmodules
329 var sorted = new Array[MModule].from(mmodules)
330 var sorter = new ComparableSorter[MModule]
331 sorter.sort(sorted)
332 for mmodule in sorted 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 end
339
340 fun process_generate_dot do
341 var op = new Buffer
342 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")
343 for mmodule in list_mmodules do
344 op.append("\"{mmodule.name}\"[URL=\"{mmodule.name}.html\"];\n")
345 for imported in mmodule.in_importation.direct_greaters do
346 if imported.direct_owner == null then
347 op.append("\"{mmodule.name}\"->\"{imported.name}\";\n")
348 end
349 end
350 end
351 op.append("\}\n")
352 generate_dot(op.to_s, "dep", "Modules hierarchy")
353 end
354
355 private fun list_mmodules: Set[MModule] do
356 var mmodules = new HashSet[MModule]
357 for mmodule in mbuilder.model.mmodules do
358 var owner = mmodule.public_owner
359 if owner != null then
360 mmodules.add(owner)
361 else
362 mmodules.add(mmodule)
363 end
364 end
365 return mmodules
366 end
367 end
368
369 # The full index page
370 class NitdocFullindex
371 super NitdocPage
372
373 var mmodules: Array[MModule]
374
375 init with(mmodules: Array[MModule]) do
376 self.mmodules = mmodules
377 opt_nodot = false
378 destinationdir = ""
379 end
380
381 redef fun head do
382 super
383 add("title").text("Full Index | Nit Standard Library")
384 end
385
386 redef fun menu do
387 open("li")
388 add("a").attr("href", "index.html").text("Overview")
389 close("li")
390 add("li").add_class("current").text("Full Index")
391 end
392
393 redef fun content do
394 open("div").add_class("content fullpage")
395 add("h1").text("Full Index")
396 module_column
397 classes_column
398 properties_column
399 close("div")
400 end
401
402 # Add to content modules column
403 fun module_column do
404 var ls = new List[nullable MModule]
405 var sorted = mmodules
406 var sorterp = new ComparableSorter[MModule]
407 sorterp.sort(sorted)
408 open("article").add_class("modules filterable")
409 add("h2").text("Modules")
410 open("ul")
411 for mmodule in sorted do
412 if mmodule.public_owner != null and not ls.has(mmodule.public_owner) then
413 ls.add(mmodule.public_owner)
414 open("li")
415 add("a").attr("href", "{mmodule.public_owner.name}.html").text(mmodule.public_owner.name)
416 close("li")
417 end
418 end
419 close("ul")
420 close("article")
421 end
422
423 # Add to content classes modules
424 fun classes_column do
425 var sorted = mmodules.first.imported_mclasses.to_a
426 var sorterp = new ComparableSorter[MClass]
427 sorterp.sort(sorted)
428 open("article").add_class("classes filterable")
429 add("h2").text("Classes")
430 open("ul")
431
432 for mclass in sorted do
433 open("li")
434 add("a").attr("href", "{mclass}.html").text(mclass.name)
435 close("li")
436 end
437
438 close("ul")
439 close("article")
440 end
441
442 # Insert the properties column of fullindex page
443 fun properties_column do
444 open("article").add_class("properties filterable")
445 add("h2").text("Properties")
446 open("ul")
447 var sorted_imported = mmodules.first.imported_methods.to_a
448 var sorted_redef = mmodules.first.redef_methods.to_a
449 var sorterp = new ComparableSorter[MProperty]
450 sorterp.sort(sorted_imported)
451 sorterp.sort(sorted_redef)
452
453 for method in sorted_imported do
454 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
455 open("li").add_class("intro")
456 add("span").attr("title", "introduction").text("I")
457 add_html("&nbsp;")
458 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
459 close("li")
460 end
461
462 for method in sorted_redef do
463 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
464 open("li").add_class("redef")
465 add("span").attr("title", "redefinition").text("R")
466 add_html("&nbsp;")
467 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
468 close("li")
469 end
470
471 close("ul")
472 close("article")
473 end
474
475 end
476
477 # A module page
478 class NitdocModule
479 super NitdocPage
480
481 var mmodule: MModule
482 var mbuilder: ModelBuilder
483
484 init with(mmodule: MModule, mbuilder: ModelBuilder) do
485 self.mmodule = mmodule
486 self.mbuilder = mbuilder
487 opt_nodot = false
488 destinationdir = ""
489 end
490
491 redef fun head do
492 super
493 var amodule = mbuilder.mmodule2nmodule[mmodule]
494 add("title").text("{mmodule.name} module | {amodule.short_comment}")
495 end
496
497 redef fun menu do
498 open("li")
499 add("a").attr("href", "index.html").text("Overview")
500 close("li")
501 add("li").add_class("current").text(mmodule.name)
502 open("li")
503 add("a").attr("href", "full-index.html").text("Full Index")
504 close("li")
505 end
506
507 redef fun content do
508 sidebar
509 open("div").add_class("content")
510 add("h1").text(mmodule.name)
511 add("div").add_class("subtitle")
512 add_html("module {mmodule.namespace(mbuilder)}")
513 module_comment
514 #process_generate_dot
515 classes
516 properties
517 close("div")
518 end
519
520 # Insert module comment in the content
521 fun module_comment do
522 var amodule = mbuilder.mmodule2nmodule[mmodule]
523 var doc = amodule.comment
524 open("div").attr("id", "description")
525 add("pre").add_class("text_label").text(doc)
526 add("textarea").add_class("edit").attr("rows", "1").attr("cols", "76").attr("id", "fileContent").text(" ")
527 add("a").attr("id", "cancelBtn").text("Cancel")
528 add("a").attr("id", "commitBtn").text("Commit")
529 add("pre").add_class("text_label").attr("id", "preSave").attr("type", "2")
530 close("div")
531 end
532
533 fun process_generate_dot do
534 var op = new Buffer
535 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")
536 for m in mmodule.in_importation.smallers do
537 op.append("\"{m.name}\"[URL=\"{m.name}.html\"];\n")
538 for imported in m.in_importation.direct_greaters do
539 if imported.direct_owner == null then
540 op.append("\"{m.name}\"->\"{imported.name}\";\n")
541 end
542 end
543 end
544 op.append("\}\n")
545 generate_dot(op.to_s, "dep_{mmodule.name}", "Modules hierarchy")
546 end
547
548 fun sidebar do
549 var amodule = mbuilder.mmodule2nmodule[mmodule]
550 open("div").add_class("menu")
551 open("nav")
552 add("h3").text("Module Hierarchy")
553 var dependencies = new Array[MModule]
554 for dep in mmodule.in_importation.greaters do
555 if dep == mmodule or dep.public_owner != null then continue
556 dependencies.add(dep)
557 end
558 if dependencies.length > 0 then
559 add("h4").text("All dependencies")
560 display_module_list(dependencies)
561 end
562 var clients = new Array[MModule]
563 for dep in mmodule.in_importation.smallers do
564 if dep == mmodule or dep.public_owner != null then continue
565 clients.add(dep)
566 end
567 if clients.length > 0 then
568 add("h4").text("All clients")
569 display_module_list(clients)
570 end
571 close("nav")
572 if mmodule.in_nesting.direct_greaters.length > 0 then
573 open("nav")
574 add("h3").text("Nested Modules")
575 display_module_list(mmodule.in_nesting.direct_greaters.to_a)
576 close("nav")
577 end
578 close("div")
579 end
580
581 private fun display_module_list(list: Array[MModule]) do
582 open("ul")
583 var sorter = new ComparableSorter[MModule]
584 sorter.sort(list)
585 for m in list do
586 var am = mbuilder.mmodule2nmodule[m]
587 open("li")
588 add_html(m.link(am))
589 close("li")
590 end
591 close("ul")
592 end
593
594 fun classes do
595 var amodule = mbuilder.mmodule2nmodule[mmodule]
596 var intro_mclasses = mmodule.intro_mclasses
597 var redef_mclasses = mmodule.redef_mclasses
598 var all_mclasses = new HashSet[MClass]
599 for m in mmodule.in_nesting.greaters do
600 all_mclasses.add_all(m.intro_mclasses)
601 all_mclasses.add_all(m.redef_mclasses)
602 end
603 all_mclasses.add_all(intro_mclasses)
604 all_mclasses.add_all(redef_mclasses)
605
606 var sorted = new Array[MClass]
607 sorted.add_all(all_mclasses)
608 var sorter = new ComparableSorter[MClass]
609 sorter.sort(sorted)
610 open("div").add_class("module")
611 open("article").add_class("classes filterable")
612 add("h2").text("Classes")
613 open("ul")
614 for c in sorted do
615 var nclass = mbuilder.mclassdef2nclassdef[c.intro].as(AStdClassdef)
616 if redef_mclasses.has(c) and c.intro_mmodule.public_owner != mmodule then
617 open("li").add_class("redef")
618 add("span").attr("title", "refined in this module").text("R ")
619 else
620 open("li").add_class("intro")
621 add("span").attr("title", "introduced in this module").text("I ")
622 end
623 add_html(c.link(nclass))
624 close("li")
625 end
626 close("ul")
627 close("article")
628 close("div")
629 end
630
631 fun properties do
632 var amodule = mbuilder.mmodule2nmodule[mmodule]
633 var mpropdefs = new HashSet[MPropDef]
634 for m in mmodule.in_nesting.greaters do
635 for c in m.mclassdefs do mpropdefs.add_all(c.mpropdefs)
636 end
637 for c in mmodule.mclassdefs do mpropdefs.add_all(c.mpropdefs)
638 var sorted = mpropdefs.to_a
639 var sorter = new ComparableSorter[MPropDef]
640 sorter.sort(sorted)
641 open("article").add_class("properties filterable")
642 add("h2").text("Properties")
643 open("ul")
644 for p in sorted do
645 if p isa MAttributeDef then continue
646 if p.mproperty.visibility <= none_visibility then continue
647 if not mbuilder.mpropdef2npropdef.has_key(p) then continue
648 var nprop = mbuilder.mpropdef2npropdef[p]
649 if p.is_intro then
650 open("li").add_class("intro")
651 add("span").attr("title", "introduction").text("I")
652 else
653 open("li").add_class("redef")
654 add("span").attr("title", "redefinition").text("R")
655 end
656 add_html("&nbsp;{p.link(nprop)} ({p.mclassdef.mclass.name})")
657 close("li")
658 end
659 close("ul")
660 close("article")
661 end
662 end
663
664 # A class page
665 class NitdocClass
666 super NitdocPage
667
668 var mclass: MClass
669 var mbuilder: ModelBuilder
670
671 init with(mclass: MClass, mbuilder: ModelBuilder, source: nullable String) do
672 self.mclass = mclass
673 self.mbuilder = mbuilder
674 self.opt_nodot = false
675 self.destinationdir = ""
676 self.source = source
677 end
678
679 redef fun head do
680 super
681 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
682 if nclass isa AStdClassdef then
683 add("title").text("{mclass.name} class | {nclass.short_comment}")
684 else
685 add("title").text("{mclass.name} class")
686 end
687 end
688
689 redef fun menu do
690 open("li")
691 add("a").attr("href", "index.html").text("Overview")
692 close("li")
693 open("li")
694 var public_owner = mclass.public_owner
695 if public_owner is null then
696 var am = mbuilder.mmodule2nmodule[mclass.intro_mmodule]
697 add_html(mclass.intro_mmodule.link(am))
698 else
699 var am = mbuilder.mmodule2nmodule[public_owner]
700 add_html(public_owner.link(am))
701 end
702 close("li")
703 add("li").add_class("current").text(mclass.name)
704 open("li")
705 add("a").attr("href", "full-index.html").text("Full Index")
706 close("li")
707 end
708
709 redef fun content do
710 open("div").add_class("menu")
711 properties_column
712 inheritance_column
713 close("div")
714 open("div").add_class("content")
715 class_doc
716 close("div")
717 end
718
719 fun properties_column do
720 var sorter = new ComparableSorter[MPropDef]
721 open("nav").add_class("properties filterable")
722 add("h3").text("Properties")
723 # get properties
724 var vtypes = new HashSet[MVirtualTypeDef]
725 var consts = new HashSet[MMethodDef]
726 var meths = new HashSet[MMethodDef]
727 for mclassdef in mclass.mclassdefs do
728 for mpropdef in mclassdef.mpropdefs do
729 if not mbuilder.mpropdef2npropdef.has_key(mpropdef) then continue
730 if mpropdef.mproperty.visibility <= none_visibility then continue
731 if mpropdef isa MVirtualTypeDef then vtypes.add(mpropdef)
732 if mpropdef isa MMethodDef then
733 if mpropdef.mproperty.is_init then
734 consts.add(mpropdef)
735 else
736 meths.add(mpropdef)
737 end
738 end
739 end
740 end
741 for mprop in mclass.inherited_methods do
742 var mpropdef = mprop.intro
743 if not mbuilder.mpropdef2npropdef.has_key(mpropdef) then continue
744 if mprop.visibility <= none_visibility then continue
745 if mprop.intro_mclassdef.mclass.name == "Object" then continue
746 meths.add(mpropdef)
747 end
748 # virtual types
749 if vtypes.length > 0 then
750 var vts = new Array[MVirtualTypeDef]
751 vts.add_all(vtypes)
752 sorter.sort(vts)
753 add("h4").text("Virtual Types")
754 display_mpropdef_list(vts)
755 end
756 if consts.length > 0 then
757 var cts = new Array[MMethodDef]
758 cts.add_all(consts)
759 sorter.sort(cts)
760 add("h4").text("Constructors")
761 display_mpropdef_list(cts)
762 end
763 if meths.length > 0 then
764 var mts = new Array[MMethodDef]
765 mts.add_all(meths)
766 sorter.sort(mts)
767 add("h4").text("Methods")
768 display_mpropdef_list(mts)
769 end
770 close("nav")
771 end
772
773 private fun display_mpropdef_list(list: Array[MPropDef]) do
774 open("ul")
775 for prop in list do
776 var nprop = mbuilder.mpropdef2npropdef[prop]
777 if prop.is_intro and prop.mproperty.intro_mclassdef.mclass != mclass then
778 open("li").add_class("inherit")
779 add("span").attr("title", "Inherited").text("H")
780 else if prop.is_intro then
781 open("li").add_class("intro")
782 add("span").attr("title", "Introduced").text("I")
783 else
784 open("li").add_class("redef")
785 add("span").attr("title", "Redefined").text("R")
786 end
787 add_html(prop.link(nprop))
788 close("li")
789 end
790 close("ul")
791 end
792
793 fun inheritance_column do
794 var sorted = new Array[MClass]
795 var sorterp = new ComparableSorter[MClass]
796 open("nav")
797 add("h3").text("Inheritance")
798 if mclass.ancestors.length > 1 then
799 sorted = mclass.ancestors.to_a
800 sorterp.sort(sorted)
801 add("h4").text("Superclasses")
802 open("ul")
803 for sup in sorted do
804 if sup == mclass then continue
805 open("li")
806 add("a").attr("href", "{sup.name}.html").text(sup.name)
807 close("li")
808 end
809 close("ul")
810 end
811
812 if mclass.descendants.length <= 1 then
813 add("h4").text("No Known Subclasses")
814 else if mclass.descendants.length <= 100 then
815 sorted = mclass.descendants.to_a
816 sorterp.sort(sorted)
817 add("h4").text("Subclasses")
818 open("ul")
819 for sub in sorted do
820 if sub == mclass then continue
821 open("li")
822 add("a").attr("href", "{sub.name}.html").text(sub.name)
823 close("li")
824 end
825 close("ul")
826 else if mclass.children.length <= 100 then
827 sorted = mclass.children.to_a
828 sorterp.sort(sorted)
829 add("h4").text("Direct Subclasses Only")
830 open("ul")
831 for sub in sorted do
832 if sub == mclass then continue
833 open("li")
834 add("a").attr("href", "{sub.name}.html").text(sub.name)
835 close("li")
836 end
837 close("ul")
838 else
839 add("h4").text("Too much Subclasses to list")
840 end
841 close("nav")
842 end
843
844 fun class_doc do
845 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
846 var sorted = new Array[MModule]
847 sorted.add_all(mclass.concerns.keys)
848 var sorterp = new ComparableSorter[MModule]
849 var sorterprop = new ComparableSorter[MProperty]
850 var sorterc = new ComparableSorter[MClass]
851 sorterp.sort(sorted)
852 var subtitle = ""
853 var lmmodule = new List[MModule]
854 # Insert the subtitle part
855 add("h1").text(mclass.to_s)
856 open("div").add_class("subtitle")
857 if mclass.visibility is none_visibility then subtitle += "private "
858 subtitle += "{mclass.kind} {mclass.public_owner.namespace(mbuilder)}::{mclass}"
859 add_html(subtitle)
860 close("div")
861 add_html("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
862 # We add the class description
863 open("section").add_class("description")
864 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>")
865 close("section")
866 open("section").add_class("concerns")
867 add("h2").add_class("section-header").text("Concerns")
868 open("ul")
869 for owner in sorted do
870 var nmodule = mbuilder.mmodule2nmodule[owner]
871 var childs = mclass.concerns[owner]
872 open("li")
873 add_html("<a href=\"#MOD_{owner.name}\">{owner.name}</a>: {nmodule.short_comment}")
874 if not childs is null then
875 open("ul")
876 var sortedc = childs.to_a
877 var sorterpc = new ComparableSorter[MModule]
878 sorterpc.sort(sortedc)
879 for child in sortedc do
880 var nchild = mbuilder.mmodule2nmodule[child]
881 add_html("<li><a href=\"#MOD_{child.name}\">{child.name}</a>: {nchild.short_comment} </li>")
882 end
883 close("ul")
884 end
885 close("li")
886 end
887 close("ul")
888 close("section")
889 # Insert virtual types if there is almost one
890 if mclass.virtual_types.length > 0 or mclass.arity > 0 then
891 open("section").add_class("types")
892 add("h2").text("Formal and Virtual Types")
893 if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop)
894 if mclass.arity > 0 and nclass isa AStdClassdef then
895 for prop in nclass.n_formaldefs do
896 open("article").attr("id", "FT_Object_{prop.collect_text}")
897 open("h3").add_class("signature").text("{prop.collect_text}: nullable ")
898 add_html("<a title=\"The root of the class hierarchy.\" href=\"Object.html\">Object</a>")
899 close("h3")
900 add_html("<div class=\"info\">formal generic type</div>")
901 close("article")
902 end
903 end
904 close("section")
905 end
906 # constructors
907 if mclass.constructors.length > 0 then
908 var sortedc = mclass.constructors.to_a
909 sorterprop.sort(sortedc)
910 open("section").add_class("constructors")
911 add("h2").add_class("section-header").text("Constructors")
912 for prop in sortedc do description(prop)
913 close("section")
914 end
915 # methods
916 open("section").add_class("methods")
917 add("h2").add_class("section-header").text("Methods")
918 for mmodule, mmethods in mclass.all_methods do
919 var nmodule = mbuilder.mmodule2nmodule[mmodule]
920 add_html("<a id=\"MOD_{mmodule.name}\"></a>")
921 if mmodule != mclass.intro_mmodule and mmodule != mclass.public_owner then
922 if mclass.has_mmodule(mmodule) then
923 add_html("<p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
924 else
925 add_html("<h3 class=\"concern-toplevel\">Methods refined in {mmodule.link(nmodule)}</h3><p class=\"concern-doc\">{mmodule.name}: {nmodule.short_comment}</p>")
926 end
927 end
928 var sortedc = mmethods.to_a
929 sorterprop.sort(sortedc)
930 for prop in sortedc do description(prop)
931 end
932 # inherited methods
933 if mclass.inherited_methods.length > 0 then
934 var sortedc = new Array[MClass]
935 sortedc.add_all(mclass.inherited.keys)
936 sorterc.sort(sortedc)
937 add("h3").text("Inherited Methods")
938 for imclass in sortedc do
939 var inclass = mbuilder.mclassdef2nclassdef[imclass.intro].as(AStdClassdef)
940 var sortedp = mclass.inherited[imclass].to_a
941 sorterprop.sort(sortedp)
942 open("p")
943 add_html("Defined in {imclass.link(inclass)}: ")
944 for method in sortedp do
945 #TODO link to inherited propdef
946 add_html("<a href=\"\">{method.name}</a>")
947 if method != sortedp.last then add_html(", ")
948 end
949 close("p")
950 end
951 end
952 close("section")
953 end
954
955 fun description(prop: MProperty) do
956 if not mbuilder.mpropdef2npropdef.has_key(prop.intro) then return
957 var nprop = mbuilder.mpropdef2npropdef[prop.intro]
958 if not nprop isa AMethPropdef then return
959 var classes = new Array[String]
960 if nprop isa AInitPropdef then
961 classes.add("init")
962 else
963 classes.add("fun")
964 end
965 if prop.is_redef then classes.add("redef")
966 if prop.visibility == none_visibility then
967 classes.add("private")
968 else if prop.visibility == protected_visibility then
969 classes.add("protected")
970 else
971 classes.add("public")
972 end
973 open("article").add_classes(classes).attr("id", "{prop.anchor}")
974 var sign = prop.name
975 open("h3").add_class("signature")
976 add_html("{prop.name}{nprop.signature}")
977 close("h3")
978 open("div").add_class("info")
979 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>")
980 close("div")
981 open("div").add_class("description")
982 if nprop.comment == "" then
983 add_html("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
984 else
985 add_html("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{nprop.comment}</pre>")
986 end
987 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>")
988 open("p")
989 if prop.local_class != mclass then
990 var mredef = prop.local_class.intro_mmodule
991 var nredef = mbuilder.mmodule2nmodule[mredef]
992 add_html("inherited from {mredef.link(nredef)} ")
993 end
994 #TODO display show code if doc github
995 var mintro = prop.intro_mclassdef.mmodule
996 var nintro = mbuilder.mmodule2nmodule[mintro]
997 add_html("defined by the module {mintro.link(nintro)}{if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
998
999 for parent in mclass.parents do
1000 var mparent = parent.intro_mmodule
1001 var nparent = mbuilder.mmodule2nmodule[mparent]
1002 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>.")
1003 end
1004 close("p")
1005 close("div")
1006
1007 close("article")
1008 end
1009
1010 end
1011
1012 redef class AModule
1013 private fun comment: String do
1014 var ret = ""
1015 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
1016 if n_moduledecl.n_doc is null then return ""
1017 for t in n_moduledecl.n_doc.n_comment do
1018 var txt = t.text
1019 txt = txt.replace("# ", "")
1020 txt = txt.replace("#", "")
1021 ret += txt
1022 end
1023 return ret
1024 end
1025
1026 private fun short_comment: String do
1027 var ret = ""
1028 if n_moduledecl != null and n_moduledecl.n_doc != null then
1029 var txt = n_moduledecl.n_doc.n_comment.first.text
1030 txt = txt.replace("# ", "")
1031 txt = txt.replace("\n", "")
1032 ret += txt
1033 end
1034 return ret
1035 end
1036 end
1037
1038 redef class MModule
1039 super Comparable
1040 redef type OTHER: MModule
1041 redef fun <(other: OTHER): Bool do return self.name < other.name
1042
1043 # Get the list of all methods in a module
1044 fun imported_methods: Set[MMethod] do
1045 var methods = new HashSet[MMethod]
1046 for mclass in imported_mclasses do
1047 for method in mclass.intro_methods do
1048 methods.add(method)
1049 end
1050 end
1051 return methods
1052 end
1053
1054 # Get the list aof all refined methods in a module
1055 fun redef_methods: Set[MMethod] do
1056 var methods = new HashSet[MMethod]
1057 for mclass in redef_mclasses do
1058 for method in mclass.intro_methods do
1059 methods.add(method)
1060 end
1061 end
1062 return methods
1063 end
1064
1065 # Return a link (html a tag) to the nitdoc module page
1066 fun link(amodule: AModule): String do
1067 return "<a href=\"{name}.html\" title=\"{amodule.short_comment}\">{name}</a>"
1068 end
1069
1070 # Return the module namespace decorated with html
1071 fun namespace(mbuilder: ModelBuilder): String do
1072 var str = new Buffer
1073 var mowner = public_owner
1074 if mowner != null then
1075 var nowner = mbuilder.mmodule2nmodule[mowner]
1076 str.append(public_owner.link(nowner))
1077 end
1078 var nmodule = mbuilder.mmodule2nmodule[self]
1079 str.append(self.link(nmodule))
1080 return str.to_s
1081 end
1082 end
1083 redef class MPropDef
1084 super Comparable
1085 redef type OTHER: MPropDef
1086 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1087
1088 # Return a link (html a tag) to the nitdoc class page
1089 fun link(nprop: APropdef): String do
1090 return "<a href=\"{mclassdef.mclass.name}.html#{mproperty.anchor}\" title=\"{nprop.short_comment}\">{mproperty}</a>"
1091 end
1092 end
1093
1094 redef class MProperty
1095 super Comparable
1096 redef type OTHER: MProperty
1097 redef fun <(other: OTHER): Bool do return self.name < other.name
1098
1099 var is_redef: Bool
1100 var apropdef: nullable APropdef
1101
1102 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1103 do
1104 super
1105 is_redef = false
1106 end
1107
1108 fun local_class: MClass do
1109 var classdef = self.intro_mclassdef
1110 return classdef.mclass
1111 end
1112
1113 fun anchor: String do
1114 return "PROP_{c_name}"
1115 end
1116
1117 end
1118
1119 redef class MClass
1120 super Comparable
1121 redef type OTHER: MClass
1122 redef fun <(other: OTHER): Bool do return self.name < other.name
1123
1124 # Add type parameters
1125 redef fun to_s do
1126 if arity > 0 then
1127 return "{name}[{intro.parameter_names.join(", ")}]"
1128 else
1129 return name
1130 end
1131 end
1132
1133 # Return a link (html a tag) to the nitdoc class page
1134 fun link(aclass: AStdClassdef): String do
1135 return "<a href=\"{name}.html\" title=\"{aclass.short_comment}\">{self}</a>"
1136 end
1137
1138 # Associate all MMethods to each MModule concerns
1139 fun all_methods: HashMap[MModule, Set[MMethod]] do
1140 var hm = new HashMap[MModule, Set[MMethod]]
1141 for mmodule, childs in concerns do
1142 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1143 for prop in intro_methods do
1144 if mmodule == prop.intro_mclassdef.mmodule then
1145 prop.is_redef = false
1146 hm[mmodule].add(prop)
1147 end
1148 end
1149 for prop in redef_methods do
1150 if mmodule == prop.intro_mclassdef.mmodule then
1151 prop.is_redef = true
1152 hm[mmodule].add(prop)
1153 end
1154 end
1155
1156 if childs != null then
1157 for child in childs do
1158 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1159 for prop in intro_methods do
1160 if child == prop.intro_mclassdef.mmodule then
1161 prop.is_redef = false
1162 hm[child].add(prop)
1163 end
1164 end
1165 for prop in redef_methods do
1166 if child == prop.intro_mclassdef.mmodule then
1167 prop.is_redef = true
1168 hm[child].add(prop)
1169 end
1170 end
1171 end
1172 end
1173 end
1174 return hm
1175 end
1176
1177 fun public_owner: MModule do
1178 var owner = intro_mmodule
1179 if owner.public_owner is null then
1180 return owner
1181 else
1182 return owner.public_owner.as(not null)
1183 end
1184 end
1185
1186 # Associate MClass to all MMethod include in 'inherited_methods'
1187 fun inherited: HashMap[MClass, Set[MMethod]] do
1188 var hm = new HashMap[MClass, Set[MMethod]]
1189 for method in inherited_methods do
1190 var mclass = method.intro_mclassdef.mclass
1191 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1192 hm[mclass].add(method)
1193 end
1194 return hm
1195 end
1196
1197 # Return true if MModule concern contain subMModule
1198 fun has_mmodule(sub: MModule): Bool do
1199 for mmodule, childs in concerns do
1200 if childs is null then continue
1201 if childs.has(sub) then return true
1202 end
1203 return false
1204 end
1205
1206 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1207 for const in constructors do
1208 if mprop2npropdef.has_key(const)then
1209 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1210 end
1211 end
1212
1213 for intro in intro_methods do
1214 if mprop2npropdef.has_key(intro)then
1215 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1216 end
1217 end
1218
1219 for rd in redef_methods do
1220 if mprop2npropdef.has_key(rd)then
1221 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1222 end
1223 end
1224 end
1225
1226 fun link_anchor: String do
1227 return "{name}.html"
1228 end
1229
1230 end
1231
1232 redef class AStdClassdef
1233 private fun comment: String do
1234 var ret = ""
1235 if n_doc != null then
1236 for t in n_doc.n_comment do
1237 var txt = t.text.replace("# ", "")
1238 txt = txt.replace("#", "")
1239 ret += "{txt}"
1240 end
1241 end
1242 return ret
1243 end
1244
1245 private fun short_comment: String do
1246 var ret = ""
1247 if n_doc != null then
1248 var txt = n_doc.n_comment.first.text
1249 txt = txt.replace("# ", "")
1250 txt = txt.replace("\n", "")
1251 ret += txt
1252 end
1253 return ret
1254 end
1255 end
1256
1257 redef class ASignature
1258 redef fun to_s do
1259 #TODO closures
1260 var ret = ""
1261 if not n_params.is_empty then
1262 ret = "{ret}({n_params.join(", ")})"
1263 end
1264 if n_type != null and n_type.to_s != "" then ret += ": {n_type.to_s}"
1265 return ret
1266 end
1267 end
1268
1269 redef class AParam
1270 redef fun to_s do
1271 var ret = "{n_id.text}"
1272 if n_type != null then
1273 ret = "{ret}: {n_type.to_s}"
1274 if n_dotdotdot != null then ret = "{ret}..."
1275 end
1276 return ret
1277 end
1278 end
1279
1280 redef class AType
1281 redef fun to_s do
1282 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1283 if n_kwnullable != null then ret = "nullable {ret}"
1284 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1285 return ret
1286 end
1287 end
1288
1289 redef class APropdef
1290 private fun short_comment: String is abstract
1291 private fun signature: String is abstract
1292 private fun comment: String is abstract
1293 end
1294
1295 redef class AAttrPropdef
1296 redef fun short_comment do
1297 var ret = ""
1298 if n_doc != null then
1299 var txt = n_doc.n_comment.first.text
1300 txt = txt.replace("# ", "")
1301 txt = txt.replace("\n", "")
1302 ret += txt
1303 end
1304 return ret
1305 end
1306 end
1307
1308 redef class AMethPropdef
1309 redef fun short_comment do
1310 var ret = new Buffer
1311 if n_doc != null then
1312 var txt = n_doc.n_comment.first.text
1313 txt = txt.replace("# ", "")
1314 txt = txt.replace("\n", "")
1315 ret.append(txt)
1316 end
1317 return ret.to_s
1318 end
1319
1320 redef fun signature: String do
1321 var sign = ""
1322 if n_signature != null then sign = "{n_signature.to_s}"
1323 return sign
1324 end
1325
1326 redef private fun comment: String do
1327 var ret = new Buffer
1328 if n_doc != null then
1329 for t in n_doc.n_comment do
1330 var txt = t.text.replace("# ", "")
1331 txt = txt.replace("#", "")
1332 ret.append(txt)
1333 end
1334 end
1335 return ret.to_s
1336 end
1337 end
1338
1339 redef class MClassDef
1340 private fun namespace(mclass: MClass): String do
1341
1342 if mmodule.public_owner is null then
1343 return "{mmodule.full_name}::{mclass.name}"
1344 else if mclass is self.mclass then
1345 return "{mmodule.public_owner.name}::{mclass.name}"
1346 else
1347 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1348 end
1349 end
1350 end
1351
1352 redef class Set[E]
1353 fun last: E do
1354 return to_a[length-1]
1355 end
1356 end
1357
1358 # Create a tool context to handle options and paths
1359 var toolcontext = new ToolContext
1360
1361 # Here we launch the nit index
1362 var nitdoc = new Nitdoc(toolcontext)
1363 nitdoc.start