c59f300e5714062ea3e688b2c25f4807bd2f9d9a
[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 end
99 end
100
101 fun overview do
102 var overviewpage = new NitdocOverview.with(modelbuilder.nmodules, self.opt_nodot.value, destinationdir.to_s)
103 overviewpage.save("{destinationdir.to_s}/index.html")
104 end
105
106 fun fullindex do
107 var fullindex = new NitdocFullindex.with(model.mmodules)
108 fullindex.save("{destinationdir.to_s}/full-index.html")
109 end
110
111 end
112
113 class NitdocOverview
114 super NitdocPage
115
116 var amodules: Array[AModule]
117
118 # Init with Array[AModule] to get all ifnormations about each MModule containt in a program
119 # opt_nodot to inform about the graph gen
120 # destination: to know where will be saved dot files
121 init with(modules: Array[AModule], opt_nodot: Bool, destination: String) do
122 self.amodules = modules
123 self.opt_nodot = opt_nodot
124 self.destinationdir = destination
125 end
126
127 redef fun head do
128 super
129 add("title").text("Overview | Nit Standard Library")
130 end
131
132 redef fun header do
133 open("header")
134 open("nav").add_class("main")
135 open("ul")
136 add("li").add_class("current").text("Overview")
137 open("li")
138 add_html("<a href=\"full-index.html\">Full Index</a>")
139 close("li")
140 open("li").attr("id", "liGitHub")
141 open("a").add_class("btn").attr("id", "logGitHub")
142 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
143 close("a")
144 open("div").add_class("popover bottom")
145 add("div").add_class("arrow").text(" ")
146 open("div").add_class("githubTitle")
147 add("h3").text("Github Sign In")
148 close("div")
149 open("div")
150 add("label").attr("id", "lbloginGit").text("Username")
151 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
152 open("label").attr("id", "logginMessage").text("Hello ")
153 open("a").attr("id", "githubAccount")
154 add("strong").attr("id", "nickName").text(" ")
155 close("a")
156 close("label")
157 close("div")
158 open("div")
159 add("label").attr("id", "lbpasswordGit").text("Password")
160 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
161 open("div").attr("id", "listBranches")
162 add("label").attr("id", "lbBranches").text("Branch")
163 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
164 close("div")
165 close("div")
166 open("div")
167 add("label").attr("id", "lbrepositoryGit").text("Repository")
168 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
169 close("div")
170 open("div")
171 add("label").attr("id", "lbbranchGit").text("Branch")
172 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
173 close("div")
174 open("div")
175 add("a").attr("id", "signIn").text("Sign In")
176 close("div")
177 close("div")
178 close("li")
179 close("ul")
180 close("nav")
181 close("header")
182 end
183
184 redef fun body do
185 super
186 open("div").add_class("page")
187 open("div").add_class("content fullpage")
188 add("h1").text("Nit Standard Library")
189 open("article").add_class("overview")
190 add_html("<p>Documentation for the standard library of Nit<br />Version jenkins-component=stdlib-19<br />Date: TODAY</p>")
191 close("article")
192 open("article").add_class("overview")
193 add("h2").text("Modules")
194 open("ul")
195 add_modules
196 close("ul")
197 process_generate_dot
198 close("article")
199 close("div")
200 close("div")
201 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
202 end
203
204 fun add_modules do
205 var ls = new List[nullable MModule]
206 for amodule in amodules do
207 var mmodule = amodule.mmodule.public_owner
208 if mmodule != null and not ls.has(mmodule) then
209 open("li")
210 add("a").attr("href", "{mmodule.name}.html").text("{mmodule.to_s} ")
211 add_html(amodule.comment)
212 close("li")
213 ls.add(mmodule)
214 end
215 end
216 end
217
218 fun process_generate_dot do
219 var op = new Buffer
220 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")
221 for amodule in amodules do
222 op.append("\"{amodule.mmodule.name}\"[URL=\"{amodule.mmodule.name}.html\"];\n")
223 for mmodule2 in amodule.mmodule.in_importation.direct_greaters do
224 op.append("\"{amodule.mmodule.name}\"->\"{mmodule2.name}\";\n")
225 end
226 end
227 op.append("\}\n")
228 generate_dot(op.to_s, "dep", "Modules hierarchy")
229 end
230
231 end
232
233 class NitdocFullindex
234 super NitdocPage
235
236 var mmodules: Array[MModule]
237
238 init with(mmodules: Array[MModule]) do
239 self.mmodules = mmodules
240 opt_nodot = false
241 destinationdir = ""
242 end
243
244 redef fun head do
245 super
246 add("title").text("Full Index | Nit Standard Library")
247 end
248
249 redef fun header do
250 open("header")
251 open("nav").add_class("main")
252 open("ul")
253 open("li")
254 add_html("<a href=\"index.html\">Overview</a>")
255 close("li")
256 add("li").add_class("current").text("Full Index")
257 open("li").attr("id", "liGitHub")
258 open("a").add_class("btn").attr("id", "logGitHub")
259 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
260 close("a")
261 open("div").add_class("popover bottom")
262 add("div").add_class("arrow").text(" ")
263 open("div").add_class("githubTitle")
264 add("h3").text("Github Sign In")
265 close("div")
266 open("div")
267 add("label").attr("id", "lbloginGit").text("Username")
268 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
269 open("label").attr("id", "logginMessage").text("Hello ")
270 open("a").attr("id", "githubAccount")
271 add("strong").attr("id", "nickName").text(" ")
272 close("a")
273 close("label")
274 close("div")
275 open("div")
276 add("label").attr("id", "lbpasswordGit").text("Password")
277 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
278 open("div").attr("id", "listBranches")
279 add("label").attr("id", "lbBranches").text("Branch")
280 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
281 close("div")
282 close("div")
283 open("div")
284 add("label").attr("id", "lbrepositoryGit").text("Repository")
285 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
286 close("div")
287 open("div")
288 add("label").attr("id", "lbbranchGit").text("Branch")
289 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
290 close("div")
291 open("div")
292 add("a").attr("id", "signIn").text("Sign In")
293 close("div")
294 close("div")
295 close("li")
296 close("ul")
297 close("nav")
298 close("header")
299 end
300
301 redef fun body do
302 super
303 open("div").add_class("page")
304 open("div").add_class("content fullpage")
305 add("h1").text("Full Index")
306 add_content
307 close("div")
308 close("div")
309 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
310 end
311
312 fun add_content do
313 module_column
314 classes_column
315 properties_column
316 end
317
318 # Add to content modules column
319 fun module_column do
320 var ls = new List[nullable MModule]
321 open("article").add_class("modules filterable")
322 add("h2").text("Modules")
323 open("ul")
324 for mmodule in mmodules do
325 if mmodule.public_owner != null and not ls.has(mmodule.public_owner) then
326 ls.add(mmodule.public_owner)
327 open("li")
328 add("a").attr("href", "{mmodule.public_owner.name}.html").text(mmodule.public_owner.name)
329 close("li")
330 end
331 end
332 close("ul")
333 close("article")
334 end
335
336 # Add to content classes modules
337 fun classes_column do
338 open("article").add_class("classes filterable")
339 add("h2").text("Classes")
340 open("ul")
341
342 for mclass in mmodules.first.imported_mclasses do
343 open("li")
344 add("a").attr("href", "{mclass.name}.html").text(mclass.name)
345 close("li")
346 end
347
348 close("ul")
349 close("article")
350 end
351
352 # Insert the properties column of fullindex page
353 fun properties_column do
354 open("article").add_class("properties filterable")
355 add("h2").text("Properties")
356 open("ul")
357
358 for method in mmodules.first.imported_methods do
359 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
360 open("li").add_class("intro")
361 add("span").attr("title", "introduction").text("I")
362 add_html("&nbsp;")
363 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
364 close("li")
365 end
366
367 for method in mmodules.first.redef_methods do
368 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
369 open("li").add_class("redef")
370 add("span").attr("title", "redefinition").text("R")
371 add_html("&nbsp;")
372 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
373 close("li")
374 end
375
376 close("ul")
377 close("article")
378 end
379
380 end
381
382 class NitdocPage
383 super HTMLPage
384
385 var opt_nodot: Bool
386 var destinationdir : String
387
388 redef fun head do
389 add("meta").attr("charset", "utf-8")
390 add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
391 add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
392 add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
393 add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
394 end
395
396 redef fun body do header
397 fun header do end
398
399 # Generate a clickable graphviz image using a dot content
400 fun generate_dot(dot: String, name: String, alt: String) do
401 if opt_nodot then return
402 var file = new OFStream.open("{self.destinationdir}/{name}.dot")
403 file.write(dot)
404 file.close
405 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 ; \}")
406 open("article").add_class("graph")
407 add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
408 close("article")
409 var fmap = new IFStream.open("{self.destinationdir}/{name}.map")
410 add_html(fmap.read_all)
411 fmap.close
412 end
413
414 end
415
416 redef class AModule
417 private fun comment: String do
418 var ret = ""
419 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
420 if n_moduledecl.n_doc is null then return ""
421 for t in n_moduledecl.n_doc.n_comment do
422 ret += "{t.text.replace("# ", "")}"
423 end
424 return ret
425 end
426
427 private fun short_comment: String do
428 var ret = ""
429 if n_moduledecl != null and n_moduledecl.n_doc != null then
430 var txt = n_moduledecl.n_doc.n_comment.first.text
431 txt = txt.replace("# ", "")
432 txt = txt.replace("\n", "")
433 ret += txt
434 end
435 return ret
436 end
437 end
438
439 redef class MModule
440
441 var amodule: nullable AModule
442
443 # Get the list of all methods in a module
444 fun imported_methods: Set[MMethod] do
445 var methods = new HashSet[MMethod]
446 for mclass in imported_mclasses do
447 for method in mclass.intro_methods do
448 methods.add(method)
449 end
450 end
451 return methods
452 end
453
454 # Get the list aof all refined methods in a module
455 fun redef_methods: Set[MMethod] do
456 var methods = new HashSet[MMethod]
457 for mclass in redef_mclasses do
458 for method in mclass.intro_methods do
459 methods.add(method)
460 end
461 end
462 return methods
463 end
464 end
465
466 redef class MProperty
467
468 var is_redef: Bool
469 var apropdef: nullable APropdef
470
471 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
472 do
473 super
474 is_redef = false
475 end
476
477 fun local_class: MClass do
478 var classdef = self.intro_mclassdef
479 return classdef.mclass
480 end
481
482 end
483
484 # Create a tool context to handle options and paths
485 var toolcontext = new ToolContext
486 toolcontext.process_options
487
488 # Here we launch the nit index
489 var nitdoc = new Nitdoc(toolcontext)
490 nitdoc.start