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