ni_nitdoc: factorized html lnk to modules
[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.name}.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.name}")
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 name = c.name
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("a").attr("href", "{name}.html").text(name)
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.name)
837 open("div").add_class("subtitle")
838 if mclass.visibility is none_visibility then subtitle += "private "
839 var nowner = mbuilder.mmodule2nmodule[mclass.public_owner]
840 subtitle += "{mclass.kind} {mclass.public_owner.link(nowner)}::{mclass.name}"
841 add_html(subtitle)
842 close("div")
843 add_html("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
844 # We add the class description
845 open("section").add_class("description")
846 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>")
847 close("section")
848 open("section").add_class("concerns")
849 add("h2").add_class("section-header").text("Concerns")
850 open("ul")
851 for owner in sorted do
852 var nmodule = mbuilder.mmodule2nmodule[owner]
853 var childs = mclass.concerns[owner]
854 open("li")
855 add_html("<a href=\"#MOD_{owner.name}\">{owner.name}</a>: {nmodule.short_comment}")
856 if not childs is null then
857 open("ul")
858 var sortedc = childs.to_a
859 var sorterpc = new ComparableSorter[MModule]
860 sorterpc.sort(sortedc)
861 for child in sortedc do
862 var nchild = mbuilder.mmodule2nmodule[child]
863 add_html("<li><a href=\"#MOD_{child.name}\">{child.name}</a>: {nchild.short_comment} </li>")
864 end
865 close("ul")
866 end
867 close("li")
868 end
869 close("ul")
870 close("section")
871 # Insert virtual types if there is almost one
872 if mclass.virtual_types.length > 0 or mclass.arity > 0 then
873 open("section").add_class("types")
874 add("h2").text("Formal and Virtual Types")
875 if mclass.virtual_types.length > 0 then for prop in mclass.virtual_types do description(prop)
876 if mclass.arity > 0 and nclass isa AStdClassdef then
877 for prop in nclass.n_formaldefs do
878 open("article").attr("id", "FT_Object_{prop.collect_text}")
879 open("h3").add_class("signature").text("{prop.collect_text}: nullable ")
880 add_html("<a title=\"The root of the class hierarchy.\" href=\"Object.html\">Object</a>")
881 close("h3")
882 add_html("<div class=\"info\">formal generic type</div>")
883 close("article")
884 end
885 end
886 close("section")
887 end
888 # Insert constructors if there is almost one
889 if mclass.constructors.length > 0 then
890 var sortedc = mclass.constructors.to_a
891 sorterprop.sort(sortedc)
892 open("section").add_class("constructors")
893 add("h2").add_class("section-header").text("Constructors")
894 for prop in sortedc do description(prop)
895 close("section")
896 end
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 # Insert 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 i_mclass in sortedc do
920 var sortedp = mclass.inherited[i_mclass].to_a
921 sorterprop.sort(sortedp)
922 open("p")
923 add_html("Defined in <a href=\"{i_mclass.name}.html\">{i_mclass.name}</a>: ")
924 for method in sortedp do
925 add_html("<a href=\"{method.link_anchor}\">{method.name}</a>")
926 if method != sortedp.last then add_html(", ")
927 end
928 close("p")
929 end
930 end
931 close("section")
932 end
933
934 # Insert description tags for 'prop'
935 fun description(prop: MProperty) do
936 open("article").add_class("fun public {if prop.is_redef then "redef" else ""}").attr("id", "{prop.anchor}")
937 var sign = prop.name
938 if prop.apropdef != null then sign += prop.apropdef.signature
939 add_html("<h3 class=\"signature\">{sign}</h3>")
940 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>")
941
942 open("div").add_class("description")
943 if prop.apropdef is null or prop.apropdef.comment == "" then
944 add_html("<a class=\"newComment\" title=\"32\" tag=\"\">New Comment</a>")
945 else
946 add_html("<pre class=\"text_label\" title=\"\" name=\"\" tag=\"\" type=\"1\">{prop.apropdef.comment}</pre>")
947 end
948 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>")
949 open("p")
950 if prop.local_class != mclass then
951 var mredef = prop.local_class.intro_mmodule
952 var nredef = mbuilder.mmodule2nmodule[mredef]
953 add_html("inherited from {mredef.link(nredef)} ")
954 end
955 #TODO display show code if doc github
956 var mintro = prop.intro_mclassdef.mmodule
957 var nintro = mbuilder.mmodule2nmodule[mintro]
958 add_html("defined by the module {mintro.link(nintro)}{if prop.apropdef is null then "" else show_source(prop.apropdef.location)}.")
959
960 for parent in mclass.parents do
961 var mparent = parent.intro_mmodule
962 var nparent = mbuilder.mmodule2nmodule[mparent]
963 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>.")
964 end
965 close("p")
966 close("div")
967
968 close("article")
969 end
970
971 end
972
973 redef class AModule
974 private fun comment: String do
975 var ret = ""
976 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
977 if n_moduledecl.n_doc is null then return ""
978 for t in n_moduledecl.n_doc.n_comment do
979 var txt = t.text
980 txt = txt.replace("# ", "")
981 txt = txt.replace("#", "")
982 ret += txt
983 end
984 return ret
985 end
986
987 private fun short_comment: String do
988 var ret = ""
989 if n_moduledecl != null and n_moduledecl.n_doc != null then
990 var txt = n_moduledecl.n_doc.n_comment.first.text
991 txt = txt.replace("# ", "")
992 txt = txt.replace("\n", "")
993 ret += txt
994 end
995 return ret
996 end
997 end
998
999 redef class MModule
1000 super Comparable
1001 redef type OTHER: MModule
1002 redef fun <(other: OTHER): Bool do return self.name < other.name
1003
1004 # Get the list of all methods in a module
1005 fun imported_methods: Set[MMethod] do
1006 var methods = new HashSet[MMethod]
1007 for mclass in imported_mclasses do
1008 for method in mclass.intro_methods do
1009 methods.add(method)
1010 end
1011 end
1012 return methods
1013 end
1014
1015 # Get the list aof all refined methods in a module
1016 fun redef_methods: Set[MMethod] do
1017 var methods = new HashSet[MMethod]
1018 for mclass in redef_mclasses do
1019 for method in mclass.intro_methods do
1020 methods.add(method)
1021 end
1022 end
1023 return methods
1024 end
1025
1026 # Return a link (html a tag) to the nitdoc module page
1027 fun link(amodule: AModule): String do
1028 return "<a href=\"{name}.html\" title=\"{amodule.short_comment}\">{name}</a>"
1029 end
1030 end
1031 redef class MPropDef
1032 super Comparable
1033 redef type OTHER: MPropDef
1034 redef fun <(other: OTHER): Bool do return self.mproperty.name < other.mproperty.name
1035 end
1036
1037 redef class MProperty
1038 super Comparable
1039 redef type OTHER: MProperty
1040 redef fun <(other: OTHER): Bool do return self.name < other.name
1041
1042 var is_redef: Bool
1043 var apropdef: nullable APropdef
1044
1045 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
1046 do
1047 super
1048 is_redef = false
1049 end
1050
1051 fun local_class: MClass do
1052 var classdef = self.intro_mclassdef
1053 return classdef.mclass
1054 end
1055
1056 fun class_text: String do
1057 return local_class.name
1058 end
1059
1060 fun link_anchor: String do
1061 return "{class_text}.html#{anchor}"
1062 end
1063
1064 fun anchor: String do
1065 return "PROP_{c_name}"
1066 end
1067
1068 end
1069
1070 redef class MClass
1071 super Comparable
1072 redef type OTHER: MClass
1073 redef fun <(other: OTHER): Bool do return self.name < other.name
1074
1075 # Associate all MMethods to each MModule concerns
1076 fun all_methods: HashMap[MModule, Set[MMethod]] do
1077 var hm = new HashMap[MModule, Set[MMethod]]
1078 for mmodule, childs in concerns do
1079 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
1080 for prop in intro_methods do
1081 if mmodule == prop.intro_mclassdef.mmodule then
1082 prop.is_redef = false
1083 hm[mmodule].add(prop)
1084 end
1085 end
1086 for prop in redef_methods do
1087 if mmodule == prop.intro_mclassdef.mmodule then
1088 prop.is_redef = true
1089 hm[mmodule].add(prop)
1090 end
1091 end
1092
1093 if childs != null then
1094 for child in childs do
1095 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
1096 for prop in intro_methods do
1097 if child == prop.intro_mclassdef.mmodule then
1098 prop.is_redef = false
1099 hm[child].add(prop)
1100 end
1101 end
1102 for prop in redef_methods do
1103 if child == prop.intro_mclassdef.mmodule then
1104 prop.is_redef = true
1105 hm[child].add(prop)
1106 end
1107 end
1108 end
1109 end
1110 end
1111 return hm
1112 end
1113
1114 fun public_owner: MModule do
1115 var owner = intro_mmodule
1116 if owner.public_owner is null then
1117 return owner
1118 else
1119 return owner.public_owner.as(not null)
1120 end
1121 end
1122
1123 # Associate MClass to all MMethod include in 'inherited_methods'
1124 fun inherited: HashMap[MClass, Set[MMethod]] do
1125 var hm = new HashMap[MClass, Set[MMethod]]
1126 for method in inherited_methods do
1127 var mclass = method.intro_mclassdef.mclass
1128 if not hm.has_key(mclass) then hm[mclass] = new HashSet[MMethod]
1129 hm[mclass].add(method)
1130 end
1131 return hm
1132 end
1133
1134 # Return true if MModule concern contain subMModule
1135 fun has_mmodule(sub: MModule): Bool do
1136 for mmodule, childs in concerns do
1137 if childs is null then continue
1138 if childs.has(sub) then return true
1139 end
1140 return false
1141 end
1142
1143 fun mmethod(mprop2npropdef: Map[MProperty, APropdef]) do
1144 for const in constructors do
1145 if mprop2npropdef.has_key(const)then
1146 const.apropdef = mprop2npropdef[const].as(AMethPropdef)
1147 end
1148 end
1149
1150 for intro in intro_methods do
1151 if mprop2npropdef.has_key(intro)then
1152 if mprop2npropdef[intro] isa AMethPropdef then intro.apropdef = mprop2npropdef[intro].as(AMethPropdef)
1153 end
1154 end
1155
1156 for rd in redef_methods do
1157 if mprop2npropdef.has_key(rd)then
1158 if mprop2npropdef[rd] isa AMethPropdef then rd.apropdef = mprop2npropdef[rd].as(AMethPropdef)
1159 end
1160 end
1161 end
1162
1163 fun link_anchor: String do
1164 return "{name}.html"
1165 end
1166
1167 end
1168
1169 redef class AStdClassdef
1170 private fun comment: String do
1171 var ret = ""
1172 if n_doc != null then
1173 for t in n_doc.n_comment do
1174 var txt = t.text.replace("# ", "")
1175 txt = txt.replace("#", "")
1176 ret += "{txt}"
1177 end
1178 end
1179 return ret
1180 end
1181
1182 private fun short_comment: String do
1183 var ret = ""
1184 if n_doc != null then
1185 var txt = n_doc.n_comment.first.text
1186 txt = txt.replace("# ", "")
1187 txt = txt.replace("\n", "")
1188 ret += txt
1189 end
1190 return ret
1191 end
1192 end
1193
1194 redef class ASignature
1195 redef fun to_s do
1196 #TODO closures
1197 var ret = ""
1198 if not n_params.is_empty then
1199 ret = "{ret}({n_params.join(", ")})"
1200 end
1201 if n_type != null and n_type.to_s != "" then ret += " {n_type.to_s}"
1202 return ret
1203 end
1204 end
1205
1206 redef class AParam
1207 redef fun to_s do
1208 var ret = "{n_id.text}"
1209 if n_type != null then
1210 ret = "{ret}: {n_type.to_s}"
1211 if n_dotdotdot != null then ret = "{ret}..."
1212 end
1213 return ret
1214 end
1215 end
1216
1217 redef class AType
1218 redef fun to_s do
1219 var ret = "<a href=\"{n_id.text}.html\">{n_id.text}</a>"
1220 if n_kwnullable != null then ret = "nullable {ret}"
1221 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
1222 return ret
1223 end
1224 end
1225
1226 redef class APropdef
1227 private fun short_comment: String is abstract
1228 private fun signature: String is abstract
1229 private fun comment: String is abstract
1230 end
1231
1232 redef class AAttrPropdef
1233 redef fun short_comment do
1234 var ret = ""
1235 if n_doc != null then
1236 var txt = n_doc.n_comment.first.text
1237 txt = txt.replace("# ", "")
1238 txt = txt.replace("\n", "")
1239 ret += txt
1240 end
1241 return ret
1242 end
1243 end
1244
1245 redef class AMethPropdef
1246 redef fun short_comment do
1247 var ret = ""
1248 if n_doc != null then
1249 var txt = n_doc.n_comment.first.text
1250 txt = txt.replace("# ", "")
1251 txt = txt.replace("\n", "")
1252 ret += txt
1253 end
1254 return ret
1255 end
1256
1257 redef fun signature: String do
1258 var sign = ""
1259 if n_signature != null then sign = " {n_signature.to_s}"
1260 return sign
1261 end
1262
1263 redef private fun comment: String do
1264 var ret = ""
1265 if n_doc != null then
1266 for t in n_doc.n_comment do
1267 var txt = t.text.replace("# ", "")
1268 txt = txt.replace("#", "")
1269 ret += "{txt}"
1270 end
1271 end
1272 return ret
1273 end
1274 end
1275
1276 redef class MClassDef
1277 private fun namespace(mclass: MClass): String do
1278
1279 if mmodule.public_owner is null then
1280 return "{mmodule.full_name}::{mclass.name}"
1281 else if mclass is self.mclass then
1282 return "{mmodule.public_owner.name}::{mclass.name}"
1283 else
1284 return "{mmodule.public_owner.name}::<a href=\"{mclass.name}.html\">{mclass.name}</a>"
1285 end
1286 end
1287 end
1288
1289 redef class Set[E]
1290 fun last: E do
1291 return to_a[length-1]
1292 end
1293 end
1294
1295 # Create a tool context to handle options and paths
1296 var toolcontext = new ToolContext
1297
1298 # Here we launch the nit index
1299 var nitdoc = new Nitdoc(toolcontext)
1300 nitdoc.start