ni_nitdoc: Adding classes column 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 add_content
466 close("div")
467 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
468 end
469
470 # Insert all tags in content part
471 fun add_content do
472 open("div").add_class("content")
473 add("h1").text(modulename)
474 add("div").add_class("subtitle").text("module {modulename}")
475 module_comment
476 classes
477 close("div")
478 end
479
480 # Insert module comment in the content
481 fun module_comment do
482 var doc = amodule.comment
483 open("div").attr("id", "description")
484 add("pre").add_class("text_label").text(doc)
485 add("textarea").add_class("edit").attr("rows", "1").attr("cols", "76").attr("id", "fileContent").text(" ")
486 add("a").attr("id", "cancelBtn").text("Cancel")
487 add("a").attr("id", "commitBtn").text("Commit")
488 add("pre").add_class("text_label").attr("id", "preSave").attr("type", "2")
489 close("div")
490 end
491
492 fun classes do
493 open("div").add_class("module")
494 open("article").add_class("classes filterable")
495 add("h2").text("Classes")
496 open("ul")
497 for c, state in amodule.mmodule.mclasses do
498 var name = c.name
499 if state == c_is_intro or state == c_is_imported then
500 open("li").add_class("intro")
501 add("span").attr("title", "introduced in this module").text("I ")
502 else
503 open("li").add_class("redef")
504 add("span").attr("title", "refined in this module").text("R ")
505 end
506 add("a").attr("href", "{name}.html").text(name)
507 close("li")
508 end
509 close("ul")
510 close("article")
511 close("div")
512 end
513
514 end
515
516 class NitdocPage
517 super HTMLPage
518
519 var opt_nodot: Bool
520 var destinationdir : String
521
522 redef fun head do
523 add("meta").attr("charset", "utf-8")
524 add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
525 add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
526 add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
527 add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
528 end
529
530 redef fun body do header
531 fun header do end
532
533 # Generate a clickable graphviz image using a dot content
534 fun generate_dot(dot: String, name: String, alt: String) do
535 if opt_nodot then return
536 var file = new OFStream.open("{self.destinationdir}/{name}.dot")
537 file.write(dot)
538 file.close
539 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 ; \}")
540 open("article").add_class("graph")
541 add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
542 close("article")
543 var fmap = new IFStream.open("{self.destinationdir}/{name}.map")
544 add_html(fmap.read_all)
545 fmap.close
546 end
547
548 end
549
550 redef class AModule
551 private fun comment: String do
552 var ret = ""
553 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
554 if n_moduledecl.n_doc is null then return ""
555 for t in n_moduledecl.n_doc.n_comment do
556 ret += "{t.text.replace("# ", "")}"
557 end
558 return ret
559 end
560
561 private fun short_comment: String do
562 var ret = ""
563 if n_moduledecl != null and n_moduledecl.n_doc != null then
564 var txt = n_moduledecl.n_doc.n_comment.first.text
565 txt = txt.replace("# ", "")
566 txt = txt.replace("\n", "")
567 ret += txt
568 end
569 return ret
570 end
571 end
572
573 redef class MModule
574
575 var amodule: nullable AModule
576
577 # Get the list of all methods in a module
578 fun imported_methods: Set[MMethod] do
579 var methods = new HashSet[MMethod]
580 for mclass in imported_mclasses do
581 for method in mclass.intro_methods do
582 methods.add(method)
583 end
584 end
585 return methods
586 end
587
588 # Get the list aof all refined methods in a module
589 fun redef_methods: Set[MMethod] do
590 var methods = new HashSet[MMethod]
591 for mclass in redef_mclasses do
592 for method in mclass.intro_methods do
593 methods.add(method)
594 end
595 end
596 return methods
597 end
598 end
599
600 redef class MProperty
601
602 var is_redef: Bool
603 var apropdef: nullable APropdef
604
605 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
606 do
607 super
608 is_redef = false
609 end
610
611 fun local_class: MClass do
612 var classdef = self.intro_mclassdef
613 return classdef.mclass
614 end
615
616 end
617
618 # Create a tool context to handle options and paths
619 var toolcontext = new ToolContext
620 toolcontext.process_options
621
622 # Here we launch the nit index
623 var nitdoc = new Nitdoc(toolcontext)
624 nitdoc.start