ni_nitdoc: Adding menu in modules page
[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
32 private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir")
33 private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
34 private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir")
35 private var opt_nodot = new OptionBool("Do not generate graphes with graphiviz", "--no-dot")
36
37 init(toolcontext: ToolContext) do
38 # We need a model to collect stufs
39 self.toolcontext = toolcontext
40 self.arguments = toolcontext.option_context.rest
41 toolcontext.option_context.add_option(opt_dir)
42 toolcontext.option_context.add_option(opt_source)
43 toolcontext.option_context.add_option(opt_sharedir)
44 toolcontext.option_context.add_option(opt_nodot)
45 process_options
46
47 if arguments.length < 1 then
48 toolcontext.option_context.usage
49 exit(1)
50 end
51
52 model = new Model
53 modelbuilder = new ModelBuilder(model, toolcontext)
54
55 # Here we load an process std modules
56 var mmodules = modelbuilder.parse_and_build([arguments.first])
57 if mmodules.is_empty then return
58 modelbuilder.full_propdef_semantic_analysis
59 assert mmodules.length == 1
60 self.mainmodule = mmodules.first
61 end
62
63 private fun process_options do
64 if not opt_dir.value is null then
65 destinationdir = opt_dir.value
66 else
67 destinationdir = "nitdoc_directory"
68 end
69 if not opt_sharedir.value is null then
70 sharedir = opt_sharedir.value
71 else
72 var dir = "NIT_DIR".environ
73 if dir.is_empty then
74 dir = "{sys.program_name.dirname}/../share/nitdoc"
75 else
76 dir = "{dir}/share/nitdoc"
77 end
78 sharedir = dir
79 if sharedir is null then
80 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
81 abort
82 end
83 dir = "{sharedir.to_s}/scripts/js-facilities.js"
84 if sharedir is null then
85 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
86 abort
87 end
88 end
89 end
90
91 fun start do
92 if arguments.length == 1 then
93 # Create destination dir if it's necessary
94 if not destinationdir.file_exists then destinationdir.mkdir
95 sys.system("cp -r {sharedir.to_s}/* {destinationdir.to_s}/")
96 overview
97 fullindex
98 modules
99 end
100 end
101
102 fun overview do
103 var overviewpage = new NitdocOverview.with(modelbuilder.nmodules, self.opt_nodot.value, destinationdir.to_s)
104 overviewpage.save("{destinationdir.to_s}/index.html")
105 end
106
107 fun fullindex do
108 var fullindex = new NitdocFullindex.with(model.mmodules)
109 fullindex.save("{destinationdir.to_s}/full-index.html")
110 end
111
112 fun modules do
113 for mod in modelbuilder.nmodules do
114 var modulepage = new NitdocModules.with(mod)
115 modulepage.save("{destinationdir.to_s}/{mod.mmodule.name}.html")
116 end
117 end
118
119 end
120
121 class NitdocOverview
122 super NitdocPage
123
124 var amodules: Array[AModule]
125
126 # Init with Array[AModule] to get all ifnormations about each MModule containt in a program
127 # opt_nodot to inform about the graph gen
128 # destination: to know where will be saved dot files
129 init with(modules: Array[AModule], opt_nodot: Bool, destination: String) do
130 self.amodules = modules
131 self.opt_nodot = opt_nodot
132 self.destinationdir = destination
133 end
134
135 redef fun head do
136 super
137 add("title").text("Overview | Nit Standard Library")
138 end
139
140 redef fun header do
141 open("header")
142 open("nav").add_class("main")
143 open("ul")
144 add("li").add_class("current").text("Overview")
145 open("li")
146 add_html("<a href=\"full-index.html\">Full Index</a>")
147 close("li")
148 open("li").attr("id", "liGitHub")
149 open("a").add_class("btn").attr("id", "logGitHub")
150 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
151 close("a")
152 open("div").add_class("popover bottom")
153 add("div").add_class("arrow").text(" ")
154 open("div").add_class("githubTitle")
155 add("h3").text("Github Sign In")
156 close("div")
157 open("div")
158 add("label").attr("id", "lbloginGit").text("Username")
159 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
160 open("label").attr("id", "logginMessage").text("Hello ")
161 open("a").attr("id", "githubAccount")
162 add("strong").attr("id", "nickName").text(" ")
163 close("a")
164 close("label")
165 close("div")
166 open("div")
167 add("label").attr("id", "lbpasswordGit").text("Password")
168 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
169 open("div").attr("id", "listBranches")
170 add("label").attr("id", "lbBranches").text("Branch")
171 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
172 close("div")
173 close("div")
174 open("div")
175 add("label").attr("id", "lbrepositoryGit").text("Repository")
176 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
177 close("div")
178 open("div")
179 add("label").attr("id", "lbbranchGit").text("Branch")
180 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
181 close("div")
182 open("div")
183 add("a").attr("id", "signIn").text("Sign In")
184 close("div")
185 close("div")
186 close("li")
187 close("ul")
188 close("nav")
189 close("header")
190 end
191
192 redef fun body do
193 super
194 open("div").add_class("page")
195 open("div").add_class("content fullpage")
196 add("h1").text("Nit Standard Library")
197 open("article").add_class("overview")
198 add_html("<p>Documentation for the standard library of Nit<br />Version jenkins-component=stdlib-19<br />Date: TODAY</p>")
199 close("article")
200 open("article").add_class("overview")
201 add("h2").text("Modules")
202 open("ul")
203 add_modules
204 close("ul")
205 process_generate_dot
206 close("article")
207 close("div")
208 close("div")
209 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
210 end
211
212 fun add_modules do
213 var ls = new List[nullable MModule]
214 for amodule in amodules do
215 var mmodule = amodule.mmodule.public_owner
216 if mmodule != null and not ls.has(mmodule) then
217 open("li")
218 add("a").attr("href", "{mmodule.name}.html").text("{mmodule.to_s} ")
219 add_html(amodule.comment)
220 close("li")
221 ls.add(mmodule)
222 end
223 end
224 end
225
226 fun process_generate_dot do
227 var op = new Buffer
228 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")
229 for amodule in amodules do
230 op.append("\"{amodule.mmodule.name}\"[URL=\"{amodule.mmodule.name}.html\"];\n")
231 for mmodule2 in amodule.mmodule.in_importation.direct_greaters do
232 op.append("\"{amodule.mmodule.name}\"->\"{mmodule2.name}\";\n")
233 end
234 end
235 op.append("\}\n")
236 generate_dot(op.to_s, "dep", "Modules hierarchy")
237 end
238
239 end
240
241 class NitdocFullindex
242 super NitdocPage
243
244 var mmodules: Array[MModule]
245
246 init with(mmodules: Array[MModule]) do
247 self.mmodules = mmodules
248 opt_nodot = false
249 destinationdir = ""
250 end
251
252 redef fun head do
253 super
254 add("title").text("Full Index | Nit Standard Library")
255 end
256
257 redef fun header do
258 open("header")
259 open("nav").add_class("main")
260 open("ul")
261 open("li")
262 add_html("<a href=\"index.html\">Overview</a>")
263 close("li")
264 add("li").add_class("current").text("Full Index")
265 open("li").attr("id", "liGitHub")
266 open("a").add_class("btn").attr("id", "logGitHub")
267 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
268 close("a")
269 open("div").add_class("popover bottom")
270 add("div").add_class("arrow").text(" ")
271 open("div").add_class("githubTitle")
272 add("h3").text("Github Sign In")
273 close("div")
274 open("div")
275 add("label").attr("id", "lbloginGit").text("Username")
276 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
277 open("label").attr("id", "logginMessage").text("Hello ")
278 open("a").attr("id", "githubAccount")
279 add("strong").attr("id", "nickName").text(" ")
280 close("a")
281 close("label")
282 close("div")
283 open("div")
284 add("label").attr("id", "lbpasswordGit").text("Password")
285 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
286 open("div").attr("id", "listBranches")
287 add("label").attr("id", "lbBranches").text("Branch")
288 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
289 close("div")
290 close("div")
291 open("div")
292 add("label").attr("id", "lbrepositoryGit").text("Repository")
293 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
294 close("div")
295 open("div")
296 add("label").attr("id", "lbbranchGit").text("Branch")
297 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
298 close("div")
299 open("div")
300 add("a").attr("id", "signIn").text("Sign In")
301 close("div")
302 close("div")
303 close("li")
304 close("ul")
305 close("nav")
306 close("header")
307 end
308
309 redef fun body do
310 super
311 open("div").add_class("page")
312 open("div").add_class("content fullpage")
313 add("h1").text("Full Index")
314 add_content
315 close("div")
316 close("div")
317 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
318 end
319
320 fun add_content do
321 module_column
322 classes_column
323 properties_column
324 end
325
326 # Add to content modules column
327 fun module_column do
328 var ls = new List[nullable MModule]
329 open("article").add_class("modules filterable")
330 add("h2").text("Modules")
331 open("ul")
332 for mmodule in mmodules do
333 if mmodule.public_owner != null and not ls.has(mmodule.public_owner) then
334 ls.add(mmodule.public_owner)
335 open("li")
336 add("a").attr("href", "{mmodule.public_owner.name}.html").text(mmodule.public_owner.name)
337 close("li")
338 end
339 end
340 close("ul")
341 close("article")
342 end
343
344 # Add to content classes modules
345 fun classes_column do
346 open("article").add_class("classes filterable")
347 add("h2").text("Classes")
348 open("ul")
349
350 for mclass in mmodules.first.imported_mclasses do
351 open("li")
352 add("a").attr("href", "{mclass.name}.html").text(mclass.name)
353 close("li")
354 end
355
356 close("ul")
357 close("article")
358 end
359
360 # Insert the properties column of fullindex page
361 fun properties_column do
362 open("article").add_class("properties filterable")
363 add("h2").text("Properties")
364 open("ul")
365
366 for method in mmodules.first.imported_methods do
367 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
368 open("li").add_class("intro")
369 add("span").attr("title", "introduction").text("I")
370 add_html("&nbsp;")
371 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
372 close("li")
373 end
374
375 for method in mmodules.first.redef_methods do
376 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
377 open("li").add_class("redef")
378 add("span").attr("title", "redefinition").text("R")
379 add_html("&nbsp;")
380 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
381 close("li")
382 end
383
384 close("ul")
385 close("article")
386 end
387
388 end
389
390 class NitdocModules
391 super NitdocPage
392
393 var amodule: AModule
394 var modulename: String
395 init with(amodule: AModule) do
396 self.amodule = amodule
397 self.modulename = self.amodule.mmodule.name
398 opt_nodot = false
399 destinationdir = ""
400 end
401
402 redef fun head do
403 super
404 add("title").text("{modulename} module | {amodule.short_comment}")
405 end
406
407 redef fun header do
408 open("header")
409 open("nav").add_class("main")
410 open("ul")
411 open("li")
412 add_html("<a href=\"index.html\">Overview</a>")
413 close("li")
414 add("li").add_class("current").text(modulename)
415 open("li")
416 add_html("<a href=\"full-index.html\" >Full Index</a>")
417 close("li")
418 open("li").attr("id", "liGitHub")
419 open("a").add_class("btn").attr("id", "logGitHub")
420 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
421 close("a")
422 open("div").add_class("popover bottom")
423 add("div").add_class("arrow").text(" ")
424 open("div").add_class("githubTitle")
425 add("h3").text("Github Sign In")
426 close("div")
427 open("div")
428 add("label").attr("id", "lbloginGit").text("Username")
429 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
430 open("label").attr("id", "logginMessage").text("Hello ")
431 open("a").attr("id", "githubAccount")
432 add("strong").attr("id", "nickName").text(" ")
433 close("a")
434 close("label")
435 close("div")
436 open("div")
437 add("label").attr("id", "lbpasswordGit").text("Password")
438 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
439 open("div").attr("id", "listBranches")
440 add("label").attr("id", "lbBranches").text("Branch")
441 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
442 close("div")
443 close("div")
444 open("div")
445 add("label").attr("id", "lbrepositoryGit").text("Repository")
446 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
447 close("div")
448 open("div")
449 add("label").attr("id", "lbbranchGit").text("Branch")
450 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
451 close("div")
452 open("div")
453 add("a").attr("id", "signIn").text("Sign In")
454 close("div")
455 close("div")
456 close("li")
457 close("ul")
458 close("nav")
459 close("header")
460 end
461
462 redef fun body do
463 super
464 open("div").add_class("page")
465 menu
466 add_content
467 close("div")
468 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
469 end
470
471 # Insert all tags in content part
472 fun add_content do
473 open("div").add_class("content")
474 add("h1").text(modulename)
475 add("div").add_class("subtitle").text("module {modulename}")
476 module_comment
477 classes
478 close("div")
479 end
480
481 # Insert module comment in the content
482 fun module_comment do
483 var doc = amodule.comment
484 open("div").attr("id", "description")
485 add("pre").add_class("text_label").text(doc)
486 add("textarea").add_class("edit").attr("rows", "1").attr("cols", "76").attr("id", "fileContent").text(" ")
487 add("a").attr("id", "cancelBtn").text("Cancel")
488 add("a").attr("id", "commitBtn").text("Commit")
489 add("pre").add_class("text_label").attr("id", "preSave").attr("type", "2")
490 close("div")
491 end
492
493 fun menu do
494 var mmodule = amodule.mmodule
495 open("div").add_class("menu")
496 open("nav")
497 add("h3").text("Module Hierarchy").attr("style","cursor: pointer;")
498 if mmodule.in_importation.direct_greaters.length > 0 then
499 add_html("<h4>All dependencies</h4><ul>")
500 for m in mmodule.in_importation.direct_greaters do
501 if m == mmodule or mmodule == m.public_owner then continue
502 open("li")
503 add("a").attr("href", "{m.name}.html").text(m.name)
504 close("li")
505 end
506 add_html("</ul>")
507 end
508 if mmodule.in_importation.greaters.length > 0 then
509 add_html("<h4>All clients</h4><ul>")
510 for m in mmodule.in_importation.greaters do
511 if m == mmodule then continue
512 open("li")
513 add("a").attr("href", "{m.name}.html").text(m.name)
514 close("li")
515 end
516 add_html("</ul>")
517 end
518 close("nav")
519 if mmodule.in_nesting.direct_greaters.length > 0 then
520 open("nav")
521 add("h3").text("Nested Modules").attr("style","cursor: pointer;")
522 open("ul")
523 for m in mmodule.in_nesting.direct_greaters do
524 open("li")
525 add("a").attr("href", "{m.name}.html").text(m.name)
526 close("li")
527 end
528 close("ul")
529
530 close("nav")
531 end
532 close("div")
533 end
534
535 fun classes do
536 open("div").add_class("module")
537 open("article").add_class("classes filterable")
538 add("h2").text("Classes")
539 open("ul")
540 for c, state in amodule.mmodule.mclasses do
541 var name = c.name
542 if state == c_is_intro or state == c_is_imported then
543 open("li").add_class("intro")
544 add("span").attr("title", "introduced in this module").text("I ")
545 else
546 open("li").add_class("redef")
547 add("span").attr("title", "refined in this module").text("R ")
548 end
549 add("a").attr("href", "{name}.html").text(name)
550 close("li")
551 end
552 close("ul")
553 close("article")
554 close("div")
555 end
556
557 end
558
559 class NitdocPage
560 super HTMLPage
561
562 var opt_nodot: Bool
563 var destinationdir : String
564
565 redef fun head do
566 add("meta").attr("charset", "utf-8")
567 add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
568 add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
569 add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
570 add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
571 end
572
573 redef fun body do header
574 fun header do end
575
576 # Generate a clickable graphviz image using a dot content
577 fun generate_dot(dot: String, name: String, alt: String) do
578 if opt_nodot then return
579 var file = new OFStream.open("{self.destinationdir}/{name}.dot")
580 file.write(dot)
581 file.close
582 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 ; \}")
583 open("article").add_class("graph")
584 add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
585 close("article")
586 var fmap = new IFStream.open("{self.destinationdir}/{name}.map")
587 add_html(fmap.read_all)
588 fmap.close
589 end
590
591 end
592
593 redef class AModule
594 private fun comment: String do
595 var ret = ""
596 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
597 if n_moduledecl.n_doc is null then return ""
598 for t in n_moduledecl.n_doc.n_comment do
599 ret += "{t.text.replace("# ", "")}"
600 end
601 return ret
602 end
603
604 private fun short_comment: String do
605 var ret = ""
606 if n_moduledecl != null and n_moduledecl.n_doc != null then
607 var txt = n_moduledecl.n_doc.n_comment.first.text
608 txt = txt.replace("# ", "")
609 txt = txt.replace("\n", "")
610 ret += txt
611 end
612 return ret
613 end
614 end
615
616 redef class MModule
617
618 var amodule: nullable AModule
619
620 # Get the list of all methods in a module
621 fun imported_methods: Set[MMethod] do
622 var methods = new HashSet[MMethod]
623 for mclass in imported_mclasses do
624 for method in mclass.intro_methods do
625 methods.add(method)
626 end
627 end
628 return methods
629 end
630
631 # Get the list aof all refined methods in a module
632 fun redef_methods: Set[MMethod] do
633 var methods = new HashSet[MMethod]
634 for mclass in redef_mclasses do
635 for method in mclass.intro_methods do
636 methods.add(method)
637 end
638 end
639 return methods
640 end
641 end
642
643 redef class MProperty
644
645 var is_redef: Bool
646 var apropdef: nullable APropdef
647
648 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
649 do
650 super
651 is_redef = false
652 end
653
654 fun local_class: MClass do
655 var classdef = self.intro_mclassdef
656 return classdef.mclass
657 end
658
659 end
660
661 # Create a tool context to handle options and paths
662 var toolcontext = new ToolContext
663 toolcontext.process_options
664
665 # Here we launch the nit index
666 var nitdoc = new Nitdoc(toolcontext)
667 nitdoc.start