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