ni_nitdoc: Adding header/body for 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 close("div")
476 end
477
478 end
479
480 class NitdocPage
481 super HTMLPage
482
483 var opt_nodot: Bool
484 var destinationdir : String
485
486 redef fun head do
487 add("meta").attr("charset", "utf-8")
488 add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
489 add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
490 add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
491 add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
492 end
493
494 redef fun body do header
495 fun header do end
496
497 # Generate a clickable graphviz image using a dot content
498 fun generate_dot(dot: String, name: String, alt: String) do
499 if opt_nodot then return
500 var file = new OFStream.open("{self.destinationdir}/{name}.dot")
501 file.write(dot)
502 file.close
503 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 ; \}")
504 open("article").add_class("graph")
505 add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
506 close("article")
507 var fmap = new IFStream.open("{self.destinationdir}/{name}.map")
508 add_html(fmap.read_all)
509 fmap.close
510 end
511
512 end
513
514 redef class AModule
515 private fun comment: String do
516 var ret = ""
517 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
518 if n_moduledecl.n_doc is null then return ""
519 for t in n_moduledecl.n_doc.n_comment do
520 ret += "{t.text.replace("# ", "")}"
521 end
522 return ret
523 end
524
525 private fun short_comment: String do
526 var ret = ""
527 if n_moduledecl != null and n_moduledecl.n_doc != null then
528 var txt = n_moduledecl.n_doc.n_comment.first.text
529 txt = txt.replace("# ", "")
530 txt = txt.replace("\n", "")
531 ret += txt
532 end
533 return ret
534 end
535 end
536
537 redef class MModule
538
539 var amodule: nullable AModule
540
541 # Get the list of all methods in a module
542 fun imported_methods: Set[MMethod] do
543 var methods = new HashSet[MMethod]
544 for mclass in imported_mclasses do
545 for method in mclass.intro_methods do
546 methods.add(method)
547 end
548 end
549 return methods
550 end
551
552 # Get the list aof all refined methods in a module
553 fun redef_methods: Set[MMethod] do
554 var methods = new HashSet[MMethod]
555 for mclass in redef_mclasses do
556 for method in mclass.intro_methods do
557 methods.add(method)
558 end
559 end
560 return methods
561 end
562 end
563
564 redef class MProperty
565
566 var is_redef: Bool
567 var apropdef: nullable APropdef
568
569 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
570 do
571 super
572 is_redef = false
573 end
574
575 fun local_class: MClass do
576 var classdef = self.intro_mclassdef
577 return classdef.mclass
578 end
579
580 end
581
582 # Create a tool context to handle options and paths
583 var toolcontext = new ToolContext
584 toolcontext.process_options
585
586 # Here we launch the nit index
587 var nitdoc = new Nitdoc(toolcontext)
588 nitdoc.start