ni_nitdoc: added fast copy past utility to signatures.
[nit.git] / src / 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 # The main module of the nitdoc program
18 package nitdoc
19
20 import syntax
21 private import utils
22 import abstracttool
23
24
25 # Store knowledge and facilities to generate files
26 class DocContext
27 super AbstractCompiler
28 # Destination directory
29 readable writable var _dir: String = "doc"
30 # GitHub Repo name
31 var github_repo: nullable String = null
32 # Content of a generated file
33 var _stage_context: StageContext = new StageContext(null)
34
35 # Add a string in the content
36 fun add(s: String) do
37 _stage_context.content.add(s)
38 _stage_context.validate = true
39 end
40
41 # Add a string in the content iff some other string are added
42 fun stage(s: String) do _stage_context.content.add(s)
43
44 # Create a new stage in the content
45 fun open_stage do _stage_context = new StageContext(_stage_context)
46
47 # Close the current stage in the content
48 fun close_stage
49 do
50 var s = _stage_context.parent
51 if _stage_context.validate then
52 s.content.add_all(_stage_context.content)
53 s.validate = true
54 end
55 assert s != null
56 _stage_context = s
57 end
58
59 # Write the content to a new file
60 fun write_to(filename: String)
61 do
62 var f = new OFStream.open(filename)
63 for s in _stage_context.content do
64 f.write(s)
65 end
66 f.close
67 end
68
69 # Start a new file
70 fun clear
71 do
72 _stage_context = new StageContext(null)
73 end
74
75 # Sorter of entities in alphabetical order
76 var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity]
77
78 # Sort entities in the alphabetical order
79 fun sort(array: Array[MMEntity])
80 do
81 _sorter.sort(array)
82 end
83
84 readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
85 readable var _opt_source: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
86 readable var _opt_public: OptionBool = new OptionBool("Generate only the public API", "--public")
87 readable var _opt_private: OptionBool = new OptionBool("Generate the private API", "--private")
88 readable var _opt_nodot: OptionBool = new OptionBool("Do not generate graphes with graphviz", "--no-dot")
89 readable var _opt_sharedir: OptionString = new OptionString("Directory containing the nitdoc files", "--sharedir")
90
91 readable var _opt_custom_menu_items: OptionString = new OptionString("Items displayed in menu before the 'Overview' item (Each item must be enclosed in 'li' tags)", "--custom-menu-items")
92 readable var _opt_custom_title: OptionString = new OptionString("Title displayed in the top of the Overview page and as suffix of all page names", "--custom-title")
93 readable var _opt_custom_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text")
94 readable var _opt_custom_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text")
95 readable var _opt_github_repo_name: OptionString = new OptionString("GitHub repo name, example: --github MyRepoName", "--github")
96 var sharedir: nullable String
97
98 fun public_only: Bool
99 do
100 if self._opt_public.value == true then return true
101 return false
102 end
103
104 fun with_private: Bool
105 do
106 if self._opt_private.value == true then return true
107 return false
108 end
109
110 # The current processed filename
111 var filename: String
112
113 # The main virtual module
114 var mainmod: nullable MMVirtualModule
115
116 redef fun perform_work(mods)
117 do
118 mainmod = new MMVirtualModule(self, mods)
119
120 dir.mkdir
121
122 sys.system("cp -r '{sharedir.to_s}'/* {dir}/")
123
124 # Compute the set of direct owned nested modules
125 var owns = new HashMap[MMModule, Array[MMModule]]
126 for mod in modules do
127 owns[mod] = new Array[MMModule]# [mod]
128 end
129 for mod in modules do
130 if mod == mainmod then continue
131 var d = mod.directory
132 loop
133 var o = d.owner
134 if o != null and o != mod then
135 owns[o].add(mod)
136 end
137 var dp = d.parent
138 if dp == null or dp == d then break
139 d = dp
140 end
141 end
142
143 # Builds the various module hierarchies
144 var mnh = new PartialOrder[MMModule] # nested module hierarchy
145 var tmh = new PartialOrder[MMModule] # top module import hierrchy
146 var ms = mainmod.mhe.linear_extension.reversed
147 for m in ms do
148 if ms == mainmod then continue
149 m.mnhe_ = mnh.add(m, owns[m])
150 var pub = new Array[MMModule]
151 for m2 in m.mhe.greaters do
152 if m2.toplevel_owner != m2 and m2.toplevel_owner != m.toplevel_owner then continue
153 if m.mnhe <= m2 then continue
154 if m.visibility_for(m2) <= 0 then
155 # nothing
156 else if m.visibility_for(m2) == 1 then
157 else
158 pub.add(m2)
159 end
160 end
161 m.tmhe_ = tmh.add(m, pub)
162 end
163
164 var head = "<meta charset=\"utf-8\">" +
165 "<script type=\"text/javascript\" src=\"scripts/jquery-1.7.1.min.js\"></script>\n" +
166 "<script type=\"text/javascript\" src=\"quicksearch-list.js\"></script>\n" +
167 "<script type=\"text/javascript\" src=\"scripts/js-facilities.js\"></script>\n" +
168 "<link rel=\"stylesheet\" href=\"styles/main.css\" type=\"text/css\" media=\"screen\" />"
169
170 var custom_items = ""
171 if self._opt_custom_menu_items.value != null then custom_items = self._opt_custom_menu_items.value.as(not null)
172
173 var action_bar = "<header><nav class='main'><ul>{custom_items}<li class=\"current\">Overview</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li><li id=\"liGitHub\" class=\"\"><a id=\"logGitHub\" class=\"btn\" ><img id=\"imgGitHub\" src=\"resources/icons/github-icon.png\" /></a><div class=\"popover bottom\"><div class=\"arrow\"></div><div class=\"githubTitle\"><h3>Github Sign In</h3></div><div><label id=\"lbloginGit\" for=\"login\">Username</label><input id=\"loginGit\" type=\"text\" name=\"login\"><label id=\"logginMessage\" >Hello <a id=\"githubAccount\" ><strong id=\"nickName\"></strong></a></label></div><div><label id=\"lbpasswordGit\" for=\"password\">Password</label><input id=\"passwordGit\" type=\"password\" name=\"password\"><div id=\"divGitHubRepoDisplay\"><label id=\"lbRepoDisplay\">Repository: </label><strong><label id=\"githubRepoDisplay\"></label></strong></div></div><div><label id=\"lbrepositoryGit\" for=\"repository\">Repository</label><input id=\"repositoryGit\" type=\"text\" name=\"repository\"><div id=\"listBranches\"><label id=\"lbBranches\">Branch </label><select id=\"dropBranches\" name=\"dropBranches\" tabindex=\"1\" class=\"dropdown\"></select></div></div><div><label id=\"lbbranchGit\" for=\"branch\">Branch</label><input id=\"branchGit\" type=\"text\" name=\"branch\"></div><div id=\"divlogIn\"><a id=\"signIn\" >Sign In</a></div></div></li></ul></nav></header>\n"
174 var custom_title = "Nitdoc"
175 if self._opt_custom_title.value != null then custom_title = self._opt_custom_title.value.as(not null)
176
177 var overview_text = ""
178 if self._opt_custom_overview_text.value != null then overview_text = self._opt_custom_overview_text.value.as(not null)
179
180 var footer_text = ""
181 if self._opt_custom_footer_text.value != null then footer_text = self._opt_custom_footer_text.value.as(not null)
182
183 # generate the index
184 self.filename = "index.html"
185 clear
186 add("<!DOCTYPE html>")
187 add("<html><head>{head}<title>Overview | {custom_title}</title></head><body>\n")
188 add(action_bar)
189 add("<div class=\"page\">")
190 add("<div class=\"content fullpage\">")
191 add("<h1>{custom_title}</h1>\n<article class='overview'>{overview_text}</article><article class='overview'><h2>Modules</h2><ul>")
192 var modss = mainmod.mhe.greaters_and_self.to_a
193 sort(modss)
194 for mod in modss do
195 if not mod.is_toplevel then continue
196 if not mod.require_doc(self) then continue
197 assert mod isa MMSrcModule
198 add("<li>{mod.html_link(self)} {mod.short_doc}</li>")
199
200 end
201 add("</ul>")
202
203 var op = new Buffer
204 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")
205 for mod in modss do
206 if not mod.is_toplevel then continue
207 if not mod.require_doc(self) then continue
208 op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n")
209 for mod2 in mod.tmhe.direct_greaters do
210 if not modss.has(mod2) then continue
211 op.append("\"{mod.name}\"->\"{mod2.name}\";\n")
212 end
213 end
214 op.append("\}\n")
215 self.gen_dot(op.to_s, "dep", "Modules hierarchy")
216 add("</article></div>")
217 add("</div>")
218 add("<footer>{footer_text}</footer>")
219 addGithubInformation
220 addCommitForm
221 add("</body></html>\n")
222 write_to("{dir}/index.html")
223
224 # Generate page for modules
225 for mod in modules do
226 if mod == mainmod then continue
227 assert mod isa MMSrcModule
228 if not mod.require_doc(self) then continue
229 self.filename = mod.html_name
230 action_bar = "<header><nav class='main'><ul>{custom_items}<li><a href='./index.html'>Overview</a></li><li class=\"current\">{mod.name}</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li><li id=\"liGitHub\" class=\"\"><a id=\"logGitHub\" class=\"btn\" ><img id=\"imgGitHub\" src=\"resources/icons/github-icon.png\" /></a><div class=\"popover bottom\"><div class=\"arrow\"></div><div class=\"githubTitle\"><h3>Github Sign In</h3></div><div><label id=\"lbloginGit\" for=\"login\">Username</label><input id=\"loginGit\" type=\"text\" name=\"login\"><label id=\"logginMessage\" >Hello <a id=\"githubAccount\" ><strong id=\"nickName\"></strong></a></label></div><div><label id=\"lbpasswordGit\" for=\"password\">Password</label><input id=\"passwordGit\" type=\"password\" name=\"password\"><div id=\"divGitHubRepoDisplay\"><label id=\"lbRepoDisplay\">Repository: </label><strong><label id=\"githubRepoDisplay\"></label></strong></div></div><div><label id=\"lbrepositoryGit\" for=\"repository\">Repository</label><input id=\"repositoryGit\" type=\"text\" name=\"repository\"><div id=\"listBranches\"><label id=\"lbBranches\">Branch </label><select id=\"dropBranches\" name=\"dropBranches\" tabindex=\"1\" class=\"dropdown\"></select></div></div><div><label id=\"lbbranchGit\" for=\"branch\">Branch</label><input id=\"branchGit\" type=\"text\" name=\"branch\"></div><div id=\"divlogIn\"><a id=\"signIn\" >Sign In</a></div></div></li></ul></nav></header>\n"
231 clear
232 add("<!DOCTYPE html>")
233 add("<html><head>{head}<title>{mod.name} module | {custom_title}</title></head><body>\n")
234 add(action_bar)
235 add("<div class=\"page\">")
236 mod.file_page_doc(self)
237 add("</div>")
238 add("<footer>{footer_text}</footer>")
239 addGithubInformation
240 addCommitForm
241 add("</body></html>\n")
242 write_to("{dir}/{mod.html_name}.html")
243 end
244
245 # Generate pages for global classes
246 for c in mainmod.local_classes do
247 if not c.require_doc(self) then continue
248 self.filename = c.html_name
249 action_bar = "<header><nav class='main'><ul>{custom_items}<li><a href='./index.html'>Overview</a></li><li>{c.global.intro.mmmodule.toplevel_owner.html_link(self)}</li><li class=\"current\">{c.name}</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li><li id=\"liGitHub\" class=\"\"><a id=\"logGitHub\" class=\"btn\" ><img id=\"imgGitHub\" src=\"resources/icons/github-icon.png\" /></a><div class=\"popover bottom\"><div class=\"arrow\"></div><div class=\"githubTitle\"><h3>Github Sign In</h3></div><div><label id=\"lbloginGit\" for=\"login\">Username</label><input id=\"loginGit\" type=\"text\" name=\"login\"><label id=\"logginMessage\" >Hello <a id=\"githubAccount\" ><strong id=\"nickName\"></strong></a></label></div><div><label id=\"lbpasswordGit\" for=\"password\">Password</label><input id=\"passwordGit\" type=\"password\" name=\"password\"><div id=\"divGitHubRepoDisplay\"><label id=\"lbRepoDisplay\">Repository: </label><strong><label id=\"githubRepoDisplay\"></label></strong></div></div><div><label id=\"lbrepositoryGit\" for=\"repository\">Repository</label><input id=\"repositoryGit\" type=\"text\" name=\"repository\"><div id=\"listBranches\"><label id=\"lbBranches\">Branch </label><select id=\"dropBranches\" name=\"dropBranches\" tabindex=\"1\" class=\"dropdown\"></select></div></div><div><label id=\"lbbranchGit\" for=\"branch\">Branch</label><input id=\"branchGit\" type=\"text\" name=\"branch\"></div><div id=\"divlogIn\"><a id=\"signIn\" >Sign In</a></div></div></li></ul></nav></header>\n"
250 clear
251 add("<!DOCTYPE html>")
252 add("<html><head>{head}<title>{c.name} class | {custom_title}</title></head><body>\n")
253 add(action_bar)
254 add("<div class=\"page\">")
255 c.file_page_doc(self)
256 add("</div>")
257 add("<footer>{footer_text}</footer>")
258 addGithubInformation
259 addCommitForm
260 add("</body></html>\n")
261 write_to("{dir}/{c.html_name}.html")
262 end
263
264 self.filename = "fullindex"
265 action_bar = "<header><nav class='main'><ul>{custom_items}<li><a href='./index.html'>Overview</a></li><li class=\"current\">Full Index</li><li><a href=\"help.html\">Help</a></li><li id=\"liGitHub\" class=\"\"><a id=\"logGitHub\" class=\"btn\" ><img id=\"imgGitHub\" src=\"resources/icons/github-icon.png\" /></a><div class=\"popover bottom\"><div class=\"arrow\"></div><div class=\"githubTitle\"><h3>Github Sign In</h3></div><div><label id=\"lbloginGit\" for=\"login\">Username</label><input id=\"loginGit\" type=\"text\" name=\"login\"><label id=\"logginMessage\" >Hello <a id=\"githubAccount\" ><strong id=\"nickName\"></strong></a></label></div><div><label id=\"lbpasswordGit\" for=\"password\">Password</label><input id=\"passwordGit\" type=\"password\" name=\"password\"><div id=\"divGitHubRepoDisplay\"><label id=\"lbRepoDisplay\">Repository: </label><strong><label id=\"githubRepoDisplay\"></label></strong></div></div><div><label id=\"lbrepositoryGit\" for=\"repository\">Repository</label><input id=\"repositoryGit\" type=\"text\" name=\"repository\"><div id=\"listBranches\"><label id=\"lbBranches\">Branch </label><select id=\"dropBranches\" name=\"dropBranches\" tabindex=\"1\" class=\"dropdown\"></select></div></div><div><label id=\"lbbranchGit\" for=\"branch\">Branch</label><input id=\"branchGit\" type=\"text\" name=\"branch\"></div></div> <div id=\"divlogIn\"><a id=\"signIn\" >Sign In</a></div></div></li></ul></nav></header>\n"
266 clear
267 add("<!DOCTYPE html>")
268 add("<html><head>{head}<title>Full Index | {custom_title}</title></head><body>\n")
269 add(action_bar)
270 add("<div class=\"page\">")
271 add("<div class=\"content fullpage\">")
272 mainmod.file_index_page_doc(self)
273 add("</div>")
274 add("</div>")
275 add("<footer>{footer_text}</footer>")
276 addGithubInformation
277 addCommitForm
278 add("</body></html>\n")
279 write_to("{dir}/full-index.html")
280
281 self.filename = "quicksearch-list"
282 clear
283 mainmod.file_quicksearch_list_doc(self)
284 write_to("{dir}/quicksearch-list.js")
285 end
286
287 # Add or not a tag for the github repository
288 fun addGithubInformation do
289 if not github_repo == null then add("<div id=\"repoName\" name=\"{github_repo.to_s}\"></div>")
290 end
291
292 # Add all tags for the commit form
293 fun addCommitForm do
294 add("<div id=\"modal\"><form class=\"clearfix\"><div><label for=\"commitMessage\">Commit message</label><textarea id=\"commitMessage\" rows=\"1\" cols=\"76\" name=\"commitMessage\" ></textarea><input id=\"chkSignedOff\" type=\"checkbox\" name=\"chkSignedOff\">Signed-Off </div><div class=\"social-signup login\"><form ></form></div><form id=\"github-connect-form\" class=\"connect-button\" name=\"login\"><a id=\"loginAction\" title=\"Commit on GitHub\"><img src=\"resources/icons/github-icon.png\"><span id=\"btnGitHub\"><strong>Commit</strong></span></a></form></form></div><div id=\"modalQuestion\"><label id=\"txtQuestion\"></label><br><a id=\"btnCreateBranch\">Yes</a><a id=\"btnCancelBranch\">No</a></div><div id=\"waitCommit\"></div>\n\n")
295 end
296
297 # Add a (source) link fo a given location
298 fun show_source(l: Location)
299 do
300 var s = opt_source.value
301 if s == null then
302 add("({l.file.filename.simplify_path})")
303 else
304 # THIS IS JUST UGLY ! (but there is no replace yet)
305 var x = s.split_with("%f")
306 s = x.join(l.file.filename.simplify_path)
307 x = s.split_with("%l")
308 s = x.join(l.line_start.to_s)
309 x = s.split_with("%L")
310 s = x.join(l.line_end.to_s)
311 add(" (<a href=\"{s}\">show code</a>)")
312 end
313 end
314
315 # Return source link for a given location
316 fun get_source(l: Location): String
317 do
318 var s = opt_source.value
319 if s == null then
320 return l.file.filename.simplify_path
321 else
322 # THIS IS JUST UGLY ! (but there is no replace yet)
323 var x = s.split_with("%f")
324 s = x.join(l.file.filename.simplify_path)
325 x = s.split_with("%l")
326 s = x.join(l.line_start.to_s)
327 x = s.split_with("%L")
328 s = x.join(l.line_end.to_s)
329 return s
330 end
331 end
332
333 # Generate a clicable graphiz image using a dot content.
334 # `name' refer to the filename (without extension) and the id name of the map.
335 # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...)
336 fun gen_dot(dot: String, name: String, alt: String)
337 do
338 if opt_nodot.value then return
339 var f = new OFStream.open("{self.dir}/{name}.dot")
340 f.write(dot)
341 f.close
342 sys.system("\{ test -f {self.dir}/{name}.png && test -f {self.dir}/{name}.s.dot && diff {self.dir}/{name}.dot {self.dir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {self.dir}/{name}.dot {self.dir}/{name}.s.dot && dot -Tpng -o{self.dir}/{name}.png -Tcmapx -o{self.dir}/{name}.map {self.dir}/{name}.s.dot ; \}")
343 self.add("<article class=\"graph\"><img src=\"{name}.png\" usemap=\"#{name}\" style=\"margin:auto\" alt=\"{alt}\"/></article>")
344 var fmap = new IFStream.open("{self.dir}/{name}.map")
345 self.add(fmap.read_all)
346 fmap.close
347 end
348
349 init
350 do
351 keep_ast = true
352 super("nitdoc")
353 filename = "-unset-"
354 option_context.add_option(opt_public)
355 option_context.add_option(opt_private)
356 option_context.add_option(opt_dir)
357 option_context.add_option(opt_source)
358 option_context.add_option(opt_nodot)
359 option_context.add_option(opt_sharedir)
360 option_context.add_option(opt_custom_title)
361 option_context.add_option(opt_custom_menu_items)
362 option_context.add_option(opt_custom_overview_text)
363 option_context.add_option(opt_custom_footer_text)
364 option_context.add_option(opt_github_repo_name)
365 end
366
367 redef fun process_options
368 do
369 super
370 var d = opt_dir.value
371 if d != null then dir = d
372
373 if not opt_nodot.value then
374 # Test if dot is runable
375 var res = sys.system("sh -c dot </dev/null >/dev/null 2>&1")
376 if res != 0 then
377 stderr.write "--no-dot implied since `dot' is not available. Try to install graphviz.\n"
378 opt_nodot.value = true
379 end
380 end
381
382 sharedir = opt_sharedir.value
383 if sharedir == null then
384 var dir = "NIT_DIR".environ
385 if dir.is_empty then
386 dir = "{sys.program_name.dirname}/../share/nitdoc"
387 if dir.file_exists then sharedir = dir
388 else
389 dir = "{dir}/share/nitdoc"
390 if dir.file_exists then sharedir = dir
391 end
392 if sharedir == null then
393 fatal_error(null, "Error: Cannot locate nitdoc shared files. Uses --sharedir or envvar NIT_DIR.")
394 end
395 dir = "{sharedir.to_s}/scripts/js-facilities.js"
396 if sharedir == null then
397 fatal_error(null, "Error: Invalid nitdoc shared files. Check --sharedir or envvar NIT_DIR.")
398 end
399
400 end
401 var git = opt_github_repo_name.value
402 if not git == null then github_repo = git
403 end
404
405 redef fun handle_property_conflict(lc, impls)
406 do
407 # THIS IS SO UGLY! See MMVirtualModule
408 if lc.mmmodule == self.mainmod then
409 return # We just accept, so one in impls is arbitrary inherited
410 end
411 super
412 end
413 end
414
415 # A virtual module is used to work as an implicit main module that combine unrelated modules
416 # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled
417 class MMVirtualModule
418 super MMModule
419 init(ctx: MMContext, mods: Array[MMModule])
420 do
421 # We need to compute the whole metamodel since there is no mmbuilder to do it
422 super(" main".to_symbol, mods.first.directory, ctx, new Location(null,0,0,0,0))
423 ctx.add_module(self, mods)
424 for m in mods do
425 self.add_super_module(m, 1)
426 end
427 self.import_global_classes
428 self.import_local_classes
429 for c in self.local_classes do
430 c.compute_super_classes
431 end
432 for c in self.local_classes do
433 c.compute_ancestors
434 end
435
436 end
437 redef fun require_doc(dctx) do return false
438 end
439
440 # Conditionnal part of the text content of a DocContext
441 class StageContext
442 # Content of the current stage
443 readable var _content: Array[String] = new Array[String]
444
445 # Is a normal string already added?
446 readable writable var _validate: Bool = false
447
448 # Parent stage is any
449 readable var _parent: nullable StageContext = null
450
451 init(parent: nullable StageContext) do _parent = parent
452 end
453
454
455 # Efficiently sort object with their to_s method
456 class AlphaSorter[E: Object]
457 super AbstractSorter[E]
458 redef fun compare(a, b)
459 do
460 var sa: String
461 var sb: String
462 var d = _dico
463 if d.has_key(a) then
464 sa = d[a]
465 else
466 sa = a.to_s
467 d[a] = sa
468 end
469 if d.has_key(b) then
470 sb = d[b]
471 else
472 sb = b.to_s
473 d[b] = sb
474 end
475 return sa <=> sb
476 end
477
478 # Keep track of to_s values
479 var _dico: HashMap[Object, String] = new HashMap[Object, String]
480
481 init do end
482 end
483
484 # Generalization of metamodel entities
485 interface MMEntity
486 # Return a link to
487 fun html_link(dctx: DocContext): String is abstract
488
489 # Return a one liner description
490 fun short_doc: String do return "&nbsp;"
491
492 # The doc node from the AST
493 # Return null is none
494 fun doc: nullable ADoc do return null
495
496 # Return a JSON entry for quicksearch list
497 fun json_entry(dctx: DocContext): String is abstract
498
499 # Return the qualified name as string
500 fun qualified_name: String is abstract
501
502 end
503
504 redef class MMModule
505 super MMEntity
506 redef fun html_link(dctx) do
507 if short_doc == "&nbsp;" then
508 return "<a href=\"{html_name}.html\"\">{self}</a>"
509 else
510 return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
511 end
512 end
513
514 fun html_anchor: String do
515 return "<a id=\"MOD_{html_name}\"></a>"
516 end
517
518 fun html_link_to_anchor: String do
519 return "<a href=\"#MOD_{html_name}\" title=\"Jump to definitions from module {html_name}\">{self}</a>"
520 end
521
522 redef fun json_entry(dctx) do
523 return "\{txt:\"{self.qualified_name}\",url:\"{html_name}.html\"\},"
524 end
525
526 redef fun qualified_name do
527 var buffer = new Buffer
528 for m in mnhe.smallers do
529 buffer.append("{m.html_name}::")
530 end
531 buffer.append("{self.name}")
532 return buffer.to_s
533 end
534
535 fun require_doc(dctx: DocContext): Bool
536 do
537 if dctx.public_only and not is_toplevel then return false
538 return true
539 end
540
541 # Return true if the module is a top-level owner or a top-level module
542 fun is_toplevel: Bool
543 do
544 var pd = directory.parent
545 return pd == null or (pd.owner == null and directory.owner == self)
546 end
547
548 # Element in the module nesting tree
549 fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null)
550 var mnhe_: nullable PartialOrderElement[MMModule] = null
551
552 # Element in the top level module importation hierarchy
553 fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null)
554 var tmhe_: nullable PartialOrderElement[MMModule] = null
555
556 fun toplevel_owner: MMModule
557 do
558 var m = self
559 loop
560 var ds = m.mnhe.direct_smallers
561 if ds.length == 0 then return m
562 if ds.length == 1 then m = ds.first else abort
563 end
564 end
565
566 fun html_name: String
567 do
568 return "{name}"
569 end
570
571 fun direct_owner: nullable MMModule
572 do
573 var d = directory
574 while d.owner == self do d = d.parent.as(not null)
575 return d.owner
576 end
577
578 # Fill the body for the page associated to the module
579 fun file_page_doc(dctx: DocContext)
580 do
581 dctx.add("<div class=\"menu\">\n")
582
583 var mods = new Array[MMModule]
584 mods = self.mhe.greaters.to_a
585 dctx.sort(mods)
586
587 dctx.open_stage
588 dctx.stage("<nav>\n")
589 dctx.stage("<h3>Module Hierarchy</h3>\n")
590 dctx.stage("<h4>All dependencies</h4>\n")
591 dctx.stage("<ul>\n")
592 for mod in mods do
593 if not mod.require_doc(dctx) then continue
594 if self.mnhe <= mod then continue # do not want nested stuff
595 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
596 dctx.add("<li>{mod.html_link(dctx)}</li>")
597 end
598 dctx.stage("</ul>\n")
599
600 mods = self.mhe.smallers.to_a
601 dctx.sort(mods)
602 dctx.stage("<h4>All clients</h4>\n")
603 dctx.stage("<ul>\n")
604 for mod in mods do
605 if not mod.require_doc(dctx) then continue
606 if self.mnhe <= mod then continue # do not want nested stuff
607 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
608 dctx.add("<li>{mod.html_link(dctx)}</li>")
609 end
610 dctx.stage("</ul>\n")
611 dctx.stage("</nav>\n")
612 dctx.close_stage
613
614 if not dctx.public_only then
615 mods = self.mnhe.direct_greaters.to_a
616 dctx.sort(mods)
617 dctx.open_stage
618 dctx.stage("<nav>\n")
619 dctx.stage("<h3>Nested Modules</h3><ul>\n")
620 for mod in mods do
621 if not mod.require_doc(dctx) then continue
622 dctx.add("<li>{mod.html_link(dctx)}</li>")
623 end
624 dctx.stage("</ul></nav>\n")
625 dctx.close_stage
626 end
627
628 dctx.add("</div>") # metadata
629
630 dctx.add("<div class=\"content\">\n")
631 dctx.add("<h1>{name}</h1>\n")
632 dctx.add("<div class='subtitle'>module ")
633 for m in mnhe.smallers do
634 dctx.add("{m.html_link(dctx)}::")
635 end
636 dctx.add("{self.name}</div>\n")
637 dctx.add("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
638 dctx.add("<section class='description'>\n")
639
640 var doc = doc
641 if doc != null then
642 dctx.add("<div id=\"description\">\n")
643 dctx.add("<pre class=\"text_label\">{doc.to_html}</pre>\n")
644 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>\n")
645 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
646 dctx.add("<pre id=\"preSave\" type=\"2\" class=\"text_label\" ></pre>")
647 dctx.add("</div>\n")
648 end
649
650 var op = new Buffer
651 op.append("digraph {name} \{ 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")
652 var ms = new Array[nullable MMModule]
653 do
654 var m0: nullable MMModule = self
655 while m0 != null do
656 m0 = m0.direct_owner
657 ms.add(m0)
658 end
659 end
660 var cla = new HashSet[MMModule]
661 cla.add(self)
662 for m0 in self.mhe.greaters do
663 if not m0.require_doc(dctx) then continue
664 if self.visibility_for(m0) <= 1 then continue # private or hidden
665 if self.mnhe <= m0 then continue # do not want nested stuff
666 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
667 cla.add(m0)
668 end
669 for m0 in self.mhe.smallers do
670 if not m0.require_doc(dctx) then continue
671 if m0.visibility_for(self) <= 1 then continue # private or hidden
672 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
673 cla.add(m0)
674 end
675 for m0 in self.mnhe.smallers do
676 cla.add(m0)
677 end
678 ms = ms.reversed
679 for m0 in ms do
680 if m0 != null then
681 op.append("subgraph \"cluster_{m0.name}\"\{\n")
682 end
683 for c in cla do
684 if c.direct_owner != m0 then continue
685 if c == self then
686 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
687 else
688 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
689 end
690 end
691 if m0 != null then
692 op.append("\"{m0.name}\"[URL=\"{m0.html_name}.html\"];\n")
693 for c in m0.mhe.direct_greaters do
694 if not cla.has(c) then continue
695 op.append("\"{m0.name}\"->\"{c.name}\";\n")
696 end
697 end
698 end
699 for m0 in ms do
700 # Close the nesting subgraph
701 if m0 != null then
702 op.append("\}\n")
703 end
704 end
705 for c in cla do
706 for c2 in c.tmhe.direct_greaters do
707 if not cla.has(c2) then continue
708 op.append("\"{c.name}\"->\"{c2.name}\";\n")
709 end
710 end
711 op.append("\}\n")
712 dctx.gen_dot(op.to_s, name.to_s, "Dependency graph for module {name}")
713 dctx.add("</section>")
714
715 var clas = new Array[MMLocalClass]
716 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
717 var gprops = new Array[MMLocalProperty]
718 do
719 var m = self
720 for g in m.global_classes do
721 var lc = m[g]
722 if not lc.require_doc(dctx) then continue
723 var im = g.intro.mmmodule
724 if self.visibility_for(im) <= 1 then continue # private import or invisible import
725 var keep = false
726 for lc2 in lc.crhe.greaters_and_self do
727 if not lc2 isa MMSrcLocalClass then continue
728 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
729 keep = true
730 end
731 if not keep then continue
732 clas.add(self[g])
733 lc.compute_super_classes
734 for gp in lc.global_properties do
735 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
736 var lp = lc[gp]
737 var mp = lp.local_class.mmmodule
738 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
739 lp = self[g][gp]
740 if not lp.require_doc(dctx) then continue
741 if props.has_key(lp.global) then
742 if not props[lp.global].has(lp) then
743 props[lp.global].add(lp)
744 end
745 else
746 props[lp.global] = [lp]
747 gprops.add(lp.global.intro)
748 end
749 end
750 end
751 end
752 dctx.add("<section class=\"module\">\n")
753 dctx.open_stage
754 dctx.stage("<article class=\"classes filterable\">\n")
755 dctx.stage("<h2>Classes</h2>\n")
756 dctx.sort(clas)
757 dctx.stage("<ul>\n")
758 for lc in clas do
759 if self.mnhe <= lc.global.intro.mmmodule then
760 dctx.add("<li class='intro'><span title='introduced in this module'>I</span>&nbsp;")
761 else
762 dctx.add("<li class='redef'><span title='refined in this module'>R</span>&nbsp;")
763 end
764 dctx.add("{lc.html_link(dctx)}</li>\n")
765 end
766 dctx.stage("</ul></article>\n")
767 dctx.close_stage
768
769 dctx.open_stage
770 dctx.stage("<article class=\"properties filterable\">\n")
771 dctx.stage("<h2>Properties</h2>\n")
772 dctx.sort(gprops)
773 dctx.stage("<ul>\n")
774 for lgp in gprops do
775 var gp = lgp.global
776 var lps = props[gp]
777
778 if gp.intro isa MMAttribute then continue
779
780 var lpi = self[gp.intro.local_class.global][gp]
781
782 if lps.has(lpi) then
783 dctx.add("<li class='intro'><span title='introduction in an other module'>I</span>&nbsp;{lpi.html_open_link(dctx)}{lpi.html_name}&nbsp;({lpi.local_class})</a></li>\n")
784 lps.remove(lpi)
785 else
786 dctx.add("<li class='intro'><span title='introduction in this module'>I</span>&nbsp;{lpi.html_name}")
787 dctx.add("&nbsp;({lpi.local_class})</li>\n")
788 end
789 if lps.length >= 1 then
790 dctx.sort(lps)
791 for lp in lps do
792 dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;{lp.html_open_link(dctx)}{lp.html_name}&nbsp;({lp.local_class})</a></li>")
793 end
794 end
795 end
796 dctx.stage("</ul></article>\n")
797 dctx.close_stage
798 dctx.add("</section>\n")
799 dctx.add("</div>\n")
800 end
801
802 # Fill the body for the page associated to the full index
803 fun file_index_page_doc(dctx: DocContext)
804 do
805
806 dctx.add("<h1>Full Index</h1>\n")
807
808 var clas = new Array[MMLocalClass]
809 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
810 var gprops = new Array[MMLocalProperty]
811 var mods = new Array[MMModule]
812 for m in mhe.greaters_and_self do
813 if not m.require_doc(dctx) then continue
814 mods.add(m)
815 end
816 for g in global_classes do
817 var lc = self[g]
818 if not lc.require_doc(dctx) then continue
819 clas.add(lc)
820 for gp in lc.global_properties do
821 var lp = lc[gp]
822 if not lp.require_doc(dctx) then continue
823 if props.has_key(lp.global) then
824 if not props[lp.global].has(lp) then
825 props[lp.global].add(lp)
826 end
827 else
828 props[lp.global] = [lp]
829 gprops.add(lp.global.intro)
830 end
831 end
832 end
833 dctx.open_stage
834 dctx.stage("<article class=\"modules filterable\">\n")
835 dctx.stage("<h2>Modules</h2>\n")
836 dctx.sort(mods)
837 dctx.stage("<ul>\n")
838 for m in mods do
839 dctx.add("<li>{m.html_link(dctx)}</li>")
840 end
841 dctx.stage("</ul></article>\n")
842 dctx.close_stage
843
844 dctx.open_stage
845 dctx.stage("<article class=\"classes filterable\">\n")
846 dctx.stage("<h2>Classes</h2>\n")
847 dctx.sort(clas)
848 dctx.stage("<ul>\n")
849 for lc in clas do
850 dctx.add("<li>{lc.html_link(dctx)}</li>")
851 end
852 dctx.stage("</ul></article>\n")
853 dctx.close_stage
854
855 dctx.open_stage
856 dctx.stage("<article class=\"properties filterable\">\n")
857 dctx.stage("<h2>Properties</h2>\n")
858 dctx.sort(gprops)
859 dctx.stage("<ul>\n")
860 for lgp in gprops do
861 var gp = lgp.global
862 var lps = props[gp]
863
864 if gp.intro isa MMAttribute then continue
865
866 var lpi = self[gp.intro.local_class.global][gp]
867
868 lps.remove(lpi)
869 dctx.add("<li class='intro'><span title='introduction'>I</span>&nbsp;{lpi.html_open_link(dctx)}{lpi.html_name}&nbsp;({lpi.local_class})</a></li>\n")
870 if lps.length >= 1 then
871 dctx.sort(lps)
872 for lp in lps do
873 dctx.add("<li class='redef'><span title='redefinition'>R</span>&nbsp;{lp.html_open_link(dctx)}{lp.html_name}&nbsp;({lp.local_class})</a></li>\n")
874 end
875 end
876 end
877 dctx.stage("</ul></article>\n")
878 dctx.close_stage
879 end
880
881 # Fill the quicksearch list JSON object
882 fun file_quicksearch_list_doc(dctx: DocContext)
883 do
884 var entities = new HashMap[String, Array[MMEntity]]
885 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
886 for m in mhe.greaters_and_self do
887 if not m.require_doc(dctx) then continue
888 var a = new Array[MMEntity]
889 a.add(m)
890 entities[m.html_name] = a
891 end
892 for g in global_classes do
893 var lc = self[g]
894 if not lc.require_doc(dctx) then continue
895 var a = new Array[MMEntity]
896 a.add(lc)
897 entities[lc.html_name] = a
898 for gp in lc.global_properties do
899 var lp = lc[gp]
900 if not lp.require_doc(dctx) then continue
901 if lp.kind == "var" then continue
902 if props.has_key(lp.global) then
903 if not props[lp.global].has(lp) then
904 props[lp.global].add(lp)
905 end
906 else
907 props[lp.global] = [lp]
908 end
909 end
910 end
911
912 for k, v in props do
913 entities[k.short_name] = v
914 end
915
916 var keys = entities.keys.to_a
917 var sorter = new AlphaSorter[String]
918 sorter.sort(keys)
919
920 dctx.open_stage
921 dctx.stage("var entries = \{")
922 for key in keys do
923 dctx.add("\"{key}\": [")
924 for entity in entities[key] do
925 dctx.add(entity.json_entry(dctx))
926 end
927 dctx.add("],")
928 end
929 dctx.stage("\};")
930 dctx.close_stage
931 end
932 end
933
934 redef class MMGlobalProperty
935 # Return the short name of the property
936 fun short_name: String do
937 return self.intro.html_name
938 end
939 end
940
941 redef class MMLocalProperty
942 super MMEntity
943 # Anchor of the property description in the module html file
944 fun html_anchor: String
945 do
946 return "PROP_{self.mmmodule.toplevel_owner}_{local_class}_{cmangle(name)}"
947 end
948
949 redef fun json_entry(dctx) do
950 return "\{txt:\"{qualified_name}\",url:\"{local_class.html_name}.html#{html_anchor}\"\},"
951 end
952
953 redef fun qualified_name do
954 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
955 end
956
957 fun html_open_link(dctx: DocContext): String
958 do
959 if not require_doc(dctx) then print "not required {self}"
960 var title = "{html_name}{signature.to_s}"
961 if short_doc != "&nbsp;" then
962 title += " #{short_doc}"
963 end
964 return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">"
965 end
966
967 fun html_name: String
968 do
969 return self.name.to_s.html_escape
970 end
971
972 redef fun html_link(dctx)
973 do
974 if not require_doc(dctx) then print "not required {self}"
975 var title = "{html_name}{signature.to_s}"
976 if short_doc != "&nbsp;" then
977 title += " #{short_doc}"
978 end
979 return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
980 end
981
982 fun html_link_special(dctx: DocContext, lc: MMLocalClass): String
983 do
984 if not require_doc(dctx) then print "not required {self}"
985 var title = "{html_name}{signature_for(lc.get_type)}"
986 if short_doc != "&nbsp;" then
987 title += " #{short_doc}"
988 end
989 return "<a href=\"{lc.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
990 end
991
992 # Kind of property (fun, attr, etc.)
993 fun kind: String is abstract
994
995 redef fun short_doc
996 do
997 var d = doc
998 if d != null then
999 return d.short
1000 else if global.intro == self then
1001 return "&nbsp;"
1002 else
1003 return global.intro.short_doc
1004 end
1005 end
1006
1007 redef fun doc
1008 do
1009 var n = node
1010 if n == null or not n isa APropdef then
1011 return null
1012 end
1013 var d = n.n_doc
1014 if d == null then
1015 return null
1016 end
1017 if d.n_comment.is_empty then
1018 return null
1019 else
1020 return d
1021 end
1022 end
1023
1024 # The most specific module in the nesting hierarchy that exports the intro of self
1025 fun intro_module: MMModule
1026 do
1027 var m = global.intro.mmmodule
1028 var mo = m.direct_owner
1029 while mo != null and mo.visibility_for(m) >= 2 do
1030 m = mo
1031 mo = m.direct_owner
1032 end
1033 return m
1034 end
1035
1036 # Is the intro of self exported by the top-level module ?
1037 fun is_toplevel: Bool
1038 do
1039 var m = intro_module
1040 return m == m.toplevel_owner
1041 end
1042
1043 # Return true if the global property must be documented according to the visibility configured
1044 fun require_doc(dctx: DocContext): Bool
1045 do
1046 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1047 if dctx.public_only then
1048 var m = intro_module
1049 if m != m.toplevel_owner then return false # Unexported
1050 end
1051 return true
1052 end
1053
1054 # Document the global property in the global class lc
1055 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1056 do
1057 var visibility: String
1058 if global.visibility_level == 1 then
1059 visibility = "public"
1060 else if global.visibility_level == 2 then
1061 visibility = "protected"
1062 else if global.visibility_level == 3 then
1063 visibility = "private"
1064 else
1065 abort
1066 end
1067
1068 var intro_class = global.intro.local_class
1069 var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner
1070
1071 dctx.add("<article id=\"{html_anchor}\" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\">\n")
1072 dctx.add("<h3 class=\"signature\">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1073 dctx.add("<div class=\"info\">\n")
1074
1075 if is_redef then
1076 dctx.add("redef ")
1077 end
1078 if not is_toplevel then
1079 dctx.add("(unexported) ")
1080 end
1081 if global.visibility_level == 2 then
1082 dctx.add("protected ")
1083 else if global.visibility_level == 3 then
1084 dctx.add("private ")
1085 end
1086 dctx.add(kind)
1087 dctx.add(" {intro_class.mmmodule.toplevel_owner.name}")
1088 if intro_class.global == lc.global then
1089 dctx.add("::{lc.name}")
1090 else
1091 dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}")
1092 end
1093 if is_redef then
1094 dctx.add("::{mmmodule[intro_class.global][global].global.intro.html_link(dctx)}")
1095 else
1096 dctx.add("::{html_name}")
1097 end
1098 dctx.add("</div>")
1099 dctx.add("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
1100 dctx.add("<div class=\"description\">")
1101
1102 # Collect all refinement of the global property in the same global property
1103 var lps = new Array[MMLocalProperty]
1104 for l in prhe.greaters_and_self do
1105 lps.add(l)
1106 end
1107
1108 var introdoc = false
1109 if global.intro.doc != null then
1110 for lp in lps do
1111 if lp.doc == null then introdoc = true
1112 end
1113 end
1114 if introdoc then
1115 dctx.add("<pre class=\"text_label\" name=\"{html_name}\" >{global.intro.doc.to_html}</pre>")
1116 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>")
1117 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
1118 dctx.add("<pre id=\"preSave\" type=\"2\" class=\"text_label\" ></pre>")
1119 end
1120
1121 var tlmods = new Array[MMModule]
1122 for lp in lps do
1123 var bm = lp.mmmodule.toplevel_owner
1124 var lcm = lc.global.intro.mmmodule
1125 if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner
1126 if not tlmods.has(bm) then tlmods.add(bm)
1127 end
1128
1129 for tm in tlmods do
1130 # Document the top level property for the current top level module
1131 var tlp
1132 if tm.global_classes.has(lc.global) then
1133 tlp = tm[lc.global][self.global]
1134 #assert lps.has(tlp) # FIXME What this line is used for?
1135 else if tm.global_classes.has(self.local_class.global) then
1136 # Self is the inherited property. Process it
1137 tlp = tm[self.local_class.global][self.global]
1138 assert lps.has(tlp)
1139 else
1140 # We skip this module since the props defined by the module is
1141 continue
1142 end
1143
1144 var tlcm = lc.global.intro.mmmodule.toplevel_owner
1145 if not tlcm.mhe <= tm then
1146 dctx.add("<h4>In module {tm.html_link(dctx)} :</h4>")
1147 end
1148
1149 var doc = tlp.doc
1150 var n = tlp.node
1151 if doc != null and (not introdoc or global.intro.doc != doc) then
1152 if n != null then
1153 var l = n.location
1154 dctx.add("<pre type=\"1\" class=\"text_label\" tag=\"{l.file.filename}\" name=\"{dctx.get_source(l)}\" title=\"{l.line_start.to_s}\" >{doc.to_html}</pre>")
1155 end
1156 else if not is_redef then
1157 if n != null then
1158 var l = n.location
1159 dctx.add("<a class=\"newComment\" tag=\"{l.file.filename}\" title=\"{l.line_start.to_s}\">New comment</a>\n")
1160 end
1161 end
1162 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>")
1163 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
1164 dctx.add("<pre id=\"preSave\" type=\"2\" class=\"text_label\" ></pre>")
1165 dctx.add("<p>")
1166 if tlp.local_class.global != lc.global then
1167 dctx.add("inherited from {tlp.local_class.html_link(dctx)} ")
1168 end
1169 if tm != tlp.mmmodule then
1170 dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1171 end
1172 if n != null then
1173 var l = n.location
1174 dctx.show_source(l)
1175 end
1176
1177 dctx.open_stage
1178 dctx.stage(". previously defined by:")
1179 for lp in lps do
1180 var tl = lp.mmmodule.toplevel_owner
1181 if tl != tm then continue
1182 if lp == tlp then continue
1183 dctx.add(" {lp.mmmodule.html_link(dctx)}")
1184 if lp.local_class.global != lc.global then
1185 dctx.add(" for {lp.local_class.html_link(dctx)} ")
1186 end
1187
1188 n = lp.node
1189 if n != null then
1190 var l = n.location
1191 dctx.show_source(l)
1192 end
1193 end
1194 dctx.close_stage
1195 dctx.add("</p>")
1196 end
1197 dctx.add("</div>")
1198 dctx.add("</article>")
1199 end
1200 end
1201 redef class MMMethod
1202 redef fun kind do return if global.is_init then "init" else "fun"
1203 end
1204 redef class MMAttribute
1205 redef fun kind do return "var"
1206 end
1207 redef class MMTypeProperty
1208 redef fun kind do return "type"
1209 end
1210
1211 redef class MMSrcModule
1212 redef fun short_doc
1213 do
1214 var d = doc
1215 if d != null then
1216 return d.short
1217 else
1218 return "&nbsp;"
1219 end
1220 end
1221
1222 redef fun doc
1223 do
1224 var n = node
1225 if n.n_moduledecl == null then
1226 return null
1227 end
1228 var np = n.n_moduledecl
1229 var d = np.n_doc
1230 if d == null then
1231 return null
1232 end
1233 if d.n_comment.is_empty then
1234 return null
1235 else
1236 return d
1237 end
1238 end
1239 end
1240
1241 redef class ADoc
1242 # Html transcription of the doc
1243 fun to_html: String
1244 do
1245 var res = new Buffer
1246 for c in n_comment do
1247 res.append(c.text.substring_from(1))
1248 end
1249 return res.to_s.html_escape
1250 end
1251
1252 # Oneliner transcription of the doc
1253 fun short: String
1254 do
1255 return n_comment.first.text.substring_from(1).html_escape
1256 end
1257 end
1258
1259 redef class MMLocalClass
1260 super MMEntity
1261
1262 # Anchor of the class description in the module html file
1263 fun html_anchor: String do return "CLASS_{self}"
1264
1265 fun html_name: String do return "{self}"
1266
1267 redef fun html_link(dctx)
1268 do
1269 if not require_doc(dctx) then print "{dctx.filename}: not required {self}"
1270 if short_doc == "&nbsp;" then
1271 return "<a href=\"{html_name}.html\"\">{self}</a>"
1272 else
1273 return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
1274 end
1275 end
1276
1277 redef fun json_entry(dctx) do
1278 return "\{txt:\"{qualified_name}\",url:\"{html_name}.html\"\},"
1279 end
1280
1281 redef fun qualified_name do
1282 return "{intro_module.qualified_name}::{html_name}"
1283 end
1284
1285 redef fun short_doc do return global.intro.short_doc
1286
1287 redef fun doc do return global.intro.doc
1288
1289 fun kind: String
1290 do
1291 if global.is_interface then
1292 return "interface"
1293 else if global.is_abstract then
1294 return "abstract class"
1295 else if global.is_enum then
1296 return "enum"
1297 else
1298 return "class"
1299 end
1300 end
1301
1302 # The most specific module in the nesting hierarchy that exports the intro of self
1303 fun intro_module: MMModule
1304 do
1305 var m = global.intro.mmmodule
1306 var mo = m.direct_owner
1307 while mo != null and mo.visibility_for(m) >= 2 do
1308 m = mo
1309 mo = m.direct_owner
1310 end
1311 return m
1312 end
1313
1314 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1315 do
1316 if p.local_class.global != self.global then
1317 if p.global.intro.local_class.name == "Object".to_symbol then return
1318 if p.global.is_init or p isa MMTypeProperty then
1319 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1320 else
1321 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link(dctx)}</li>\n")
1322 end
1323 else if p.global.intro.local_class.global == self.global then
1324 dctx.add("<li class='intro'><span title='Introduced'>I</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1325 else
1326 dctx.add("<li class='redef'><span title='Redefined'>R</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1327 end
1328 end
1329
1330 # Return true if the global class must be documented according to the visibility configured
1331 fun require_doc(dctx: DocContext): Bool
1332 do
1333 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1334 if dctx.public_only then
1335 var m = intro_module
1336 if m != m.toplevel_owner then return false # Unexported
1337 end
1338 return true
1339 end
1340
1341 # Fill the body for the page associated to the global class
1342 fun file_page_doc(dctx: DocContext)
1343 do
1344 dctx.add("<div class=\"menu\">\n")
1345
1346 var props = new Array[MMLocalProperty]
1347 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1348 var inhs = new Array[MMLocalClass]
1349 for g in global_properties do
1350 var p = self[g]
1351 if not p.require_doc(dctx) then continue
1352 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1353 props.add(p)
1354 else
1355 var lc = mmmodule[p.local_class.global]
1356 if inh.has_key(lc) then
1357 inh[lc].add(p)
1358 else
1359 inh[lc] = [p]
1360 inhs.add(lc)
1361 end
1362 props.add(p)
1363 end
1364 end
1365 dctx.sort(props)
1366
1367 dctx.add("<nav class=\"properties filterable\">\n")
1368 dctx.add("<h3>Properties</h3>\n")
1369 dctx.open_stage
1370 dctx.stage("<h4>Virtual Types</h4>\n<ul>\n")
1371 for p in props do
1372 if p isa MMTypeProperty then
1373 menu_link(dctx, p)
1374 end
1375 end
1376 dctx.stage("</ul>\n")
1377 dctx.close_stage
1378 dctx.open_stage
1379 dctx.stage("<h4>Constructors</h4>\n<ul>\n")
1380 for p in props do
1381 if p.global.is_init_for(self) then
1382 menu_link(dctx, p)
1383 end
1384 end
1385 dctx.stage("</ul>\n")
1386 dctx.close_stage
1387 dctx.open_stage
1388 dctx.stage("<h4>Methods</h4>\n<ul>\n")
1389 for p in props do
1390 if not p.global.is_init and p isa MMMethod then
1391 menu_link(dctx, p)
1392 end
1393 end
1394 dctx.stage("</ul>\n")
1395 dctx.close_stage
1396 dctx.add("</nav>\n")
1397
1398 dctx.add("<nav class=\"inheritance filterable\">\n")
1399 dctx.add("<h3>Inheritance</h3>\n")
1400 dctx.add("<h4>Superclasses</h4>\n<ul>\n")
1401 for lc in cshe.linear_extension do
1402 if lc == self then continue
1403 if not lc.require_doc(dctx) then continue
1404 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1405 end
1406 dctx.add("</ul>\n")
1407 if cshe.smallers.length == 0 then
1408 dctx.add("<h4>No Known Subclasses</h4>\n")
1409 else if cshe.smallers.length <= 100 then
1410 dctx.add("<h4>Subclasses</h4>\n")
1411 dctx.add("<ul>\n")
1412 for lc in cshe.smallers do
1413 if not lc.require_doc(dctx) then continue
1414 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1415 end
1416 dctx.add("</ul>\n")
1417 else if cshe.direct_smallers.length <= 100 then
1418 dctx.add("<h4>Direct Subclasses Only</h4>\n<ul>\n")
1419 for lc in cshe.direct_smallers do
1420 if not lc.require_doc(dctx) then continue
1421 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1422 end
1423 dctx.add("</ul>\n")
1424 else
1425 dctx.add("<h4>Too much Subclasses to list</h4>\n")
1426 end
1427 dctx.add("</nav>\n")
1428
1429 dctx.add("</div>\n")
1430
1431
1432 dctx.add("<div class=\"content\">\n")
1433 dctx.add("<h1>{name}</h1>\n")
1434 dctx.add("<div class='subtitle'>")
1435 if global.visibility_level == 2 then
1436 abort
1437 else if global.visibility_level == 3 then
1438 dctx.add("private ")
1439 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1440 dctx.add("(unexported) ")
1441 end
1442 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div>")
1443 dctx.add("<div style=\"float: right;\"><a id=\"lblDiffCommit\"></a></div>")
1444 dctx.add("<section class=\"description\">\n")
1445 var doc = doc
1446 if doc != null then
1447 var l = doc.location
1448 dctx.add("<pre type=\"2\" class=\"text_label\" tag=\"{l.file.filename}\" name=\"{dctx.get_source(l)}\" title=\"{l.line_start.to_s}\">{doc.to_html}</pre>\n")
1449 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>")
1450 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
1451 dctx.add("<pre id=\"preSave\" type=\"2\" class=\"text_label\" ></pre>")
1452 end
1453
1454 var cla = new HashSet[MMLocalClass]
1455 var sm = new HashSet[MMLocalClass]
1456 var sm2 = new HashSet[MMLocalClass]
1457 sm.add(self)
1458 while cla.length + sm.length < 10 and sm.length > 0 do
1459 cla.add_all(sm)
1460 sm2.clear
1461 for x in sm do
1462 sm2.add_all(x.cshe.direct_smallers)
1463 end
1464 var t = sm
1465 sm = sm2
1466 sm2 = t
1467 end
1468 cla.add_all(cshe.greaters_and_self)
1469
1470 var op = new Buffer
1471 var name = "class_{name}"
1472 op.append("digraph {name} \{ 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")
1473 for c in cla do
1474 if c == self then
1475 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1476 else
1477 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
1478 end
1479 for c2 in c.cshe.direct_greaters do
1480 if not cla.has(c2) then continue
1481 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1482 end
1483 if not c.cshe.direct_smallers.is_empty then
1484 var others = true
1485 for c2 in c.cshe.direct_smallers do
1486 if cla.has(c2) then others = false
1487 end
1488 if others then
1489 op.append("\"{c.name}...\"[label=\"\"];\n")
1490 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1491 end
1492 end
1493 end
1494 op.append("\}\n")
1495 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph for class {name}")
1496 dctx.add("</section>\n")
1497
1498 # Concerns table
1499 dctx.open_stage
1500 dctx.stage("<section class=\"concerns\">\n")
1501 dctx.stage("<h2 class=\"section-header\">Concerns</h2>\n")
1502
1503 var mods = new Array[MMModule]
1504 mods.add(global.intro.mmmodule.toplevel_owner)
1505 for lc in crhe.greaters do
1506 if not lc isa MMSrcLocalClass then continue
1507 var m = lc.mmmodule.toplevel_owner
1508 if not mods.has(m) then mods.add(m)
1509 end
1510
1511 var intro = global.intro.mmmodule
1512 var short_doc
1513 dctx.add("<ul>\n")
1514 for m in mods do
1515 short_doc = ""
1516 if m.short_doc != "&nbsp;" then short_doc = ": {m.short_doc}"
1517 dctx.add("<li>{m.html_link_to_anchor}{short_doc}")
1518 dctx.add("<ul>\n")
1519 for lc in crhe.linear_extension.reversed do
1520 if lc.mmmodule.toplevel_owner != m then continue
1521 if lc.mmmodule == m then continue
1522 short_doc = ""
1523 if lc.mmmodule.short_doc != "&nbsp;" then short_doc = ": {lc.mmmodule.short_doc}"
1524 dctx.add("<li>{lc.mmmodule.html_link_to_anchor}{short_doc}</li>")
1525 end
1526 dctx.add("</ul>\n")
1527 dctx.add("</li>\n")
1528 end
1529 dctx.add("</ul>\n")
1530 dctx.stage("</section>\n")
1531 dctx.close_stage
1532
1533 dctx.open_stage
1534 dctx.stage("<section class=\"types\">\n")
1535 dctx.stage("<h2>Formal and Virtual Types</h2>\n")
1536 for i in [0..arity[ do
1537 var f = get_formal(i)
1538 f.full_documentation(dctx, self)
1539 end
1540 for p in props do
1541 if not p isa MMTypeProperty then continue
1542 p.full_documentation(dctx, self)
1543 end
1544 dctx.stage("</section>\n")
1545 dctx.close_stage
1546
1547 dctx.open_stage
1548 dctx.stage("<section class=\"constructors\">\n")
1549 dctx.stage("<h2 class=\"section-header\">Constructors</h2>\n")
1550 for p in props do
1551 if not p.global.is_init_for(self) then continue
1552 p.full_documentation(dctx, self)
1553 end
1554 dctx.stage("</section>\n")
1555 dctx.close_stage
1556
1557 dctx.open_stage
1558 dctx.stage("<section class=\"methods\">\n")
1559 dctx.stage("<h2 class=\"section-header\">Methods</h2>\n")
1560 var redefs = new HashMap[MMModule, HashMap[MMModule, Array[MMMethod]]]
1561 for p in props do
1562 if p.global.is_init then continue
1563 if p.local_class.global != self.global then continue
1564 if not p isa MMMethod then continue
1565 # Top level module
1566 var toplevel_module = p.mmmodule.toplevel_owner
1567 if not redefs.has_key(toplevel_module) then
1568 redefs[toplevel_module] = new HashMap[MMModule, Array[MMMethod]]
1569 end
1570 # Nested module
1571 var nested_module = p.mmmodule
1572 if not redefs[toplevel_module].has_key(nested_module) then
1573 redefs[toplevel_module][nested_module] = new Array[MMMethod]
1574 end
1575 # Props
1576 redefs[toplevel_module][nested_module].add(p)
1577
1578 # Redefs
1579 if p.mmmodule.toplevel_owner != p.intro_module then
1580 toplevel_module = p.intro_module
1581 nested_module = p.global.intro.mmmodule
1582
1583 if not redefs.has_key(toplevel_module) then
1584 redefs[toplevel_module] = new HashMap[MMModule, Array[MMMethod]]
1585 end
1586 if not redefs[toplevel_module].has_key(nested_module) then
1587 redefs[toplevel_module][nested_module] = new Array[MMMethod]
1588 end
1589
1590 redefs[toplevel_module][nested_module].add(p.global.intro.as(MMMethod))
1591 end
1592 end
1593
1594 # Display toplevel blocks
1595 for m in mods do
1596 if not redefs.has_key(m) then continue
1597 dctx.add(m.html_anchor)
1598 if m != global.intro.mmmodule.toplevel_owner then
1599 dctx.add("<h3 class=\"concern-toplevel\">Methods refined in {m.html_link(dctx)}</h3>")
1600 end
1601
1602 # Display nested module blocks
1603 for lc in crhe.linear_extension.reversed do
1604 if lc.mmmodule.toplevel_owner != m then continue
1605 var nm = lc.mmmodule
1606 if not redefs[m].has_key(nm) then continue
1607 dctx.add(nm.html_anchor)
1608 if nm != global.intro.mmmodule then
1609 short_doc = ""
1610 if nm.short_doc != "&nbsp;" then short_doc = ": {nm.short_doc}"
1611 dctx.add("<p class=\"concern-doc\">{nm.html_name}{short_doc}</p>\n")
1612 end
1613
1614 var pps = redefs[m][nm]
1615 dctx.sort(pps)
1616 for p in pps do
1617 p.full_documentation(dctx, self)
1618 end
1619 end
1620 end
1621
1622 if not inhs.is_empty then
1623 dctx.open_stage
1624 dctx.stage("<h3>Inherited Methods</h3>\n")
1625 for lc in inhs do
1626 dctx.open_stage
1627 dctx.stage("<p>Defined in {lc.html_link(dctx)}:")
1628
1629 var ims = new Array[MMMethod]
1630 for p in inh[lc] do
1631 if p.global.is_init then continue
1632 if not p isa MMMethod then continue
1633 ims.add(p)
1634 end
1635
1636 var i = 0
1637 for p in ims do
1638 dctx.add(" {p.html_link(dctx)}")
1639 if i < ims.length - 1 then dctx.add(",")
1640 i += 1
1641 end
1642
1643 dctx.stage("</p>")
1644 dctx.close_stage
1645 end
1646 dctx.close_stage
1647 end
1648 dctx.add("</section>\n")
1649 dctx.close_stage
1650 dctx.add("</div> <!-- end class {name} -->\n")
1651 end
1652 end
1653
1654 redef class MMSrcLocalClass
1655 redef fun short_doc
1656 do
1657 var d = doc
1658 if d != null then
1659 return d.short
1660 else if global.intro == self then
1661 return "&nbsp;"
1662 else
1663 var bc = global.intro
1664 return bc.short_doc
1665 end
1666 end
1667
1668 redef fun doc
1669 do
1670 var n = node
1671 if not n isa AStdClassdef then
1672 return null
1673 end
1674 var d = n.n_doc
1675 if d == null then
1676 return null
1677 end
1678 if d.n_comment.is_empty then
1679 return null
1680 else
1681 return d
1682 end
1683 end
1684 end
1685
1686 redef class MMSignature
1687 # Htlm transcription of the signature (with nested links)
1688 fun to_html(dctx: DocContext, with_closure: Bool): String
1689 do
1690 var res = new Buffer
1691 if arity > 0 then
1692 res.append("(")
1693 for i in [0..arity[ do
1694 if i > 0 then res.append(", ")
1695 res.append(self.params[i].name.to_s)
1696 res.append(": ")
1697 res.append(self[i].html_link(dctx))
1698 if self.vararg_rank == i then
1699 res.append("...")
1700 end
1701 end
1702 res.append(")")
1703 end
1704 if return_type != null then
1705 res.append(": ")
1706 res.append(return_type.html_link(dctx))
1707 end
1708 if with_closure then
1709 for c in closures do
1710 res.append(" ")
1711 if c.is_optional then res.append("[")
1712 if c.is_break then res.append("break ")
1713 res.append("!{c.name}")
1714 res.append(c.signature.to_html(dctx, false))
1715 if c.is_optional then res.append("]")
1716 end
1717 end
1718 return res.to_s
1719 end
1720 end
1721
1722 redef class MMType
1723 # Htlm transcription of the type (with nested links)
1724 fun html_link(dctx: DocContext): String do return to_s
1725 end
1726
1727 redef class MMTypeSimpleClass
1728 redef fun html_link(dctx) do return local_class.html_link(dctx)
1729 end
1730
1731 redef class MMTypeGeneric
1732 redef fun html_link(dctx)
1733 do
1734 var res = new Buffer
1735 res.append(local_class.html_link(dctx))
1736 res.append("[")
1737 res.append(params[0].html_link(dctx))
1738 for i in [1..params.length[ do
1739 res.append(", ")
1740 res.append(params[i].html_link(dctx))
1741 end
1742 res.append("]")
1743 return res.to_s
1744 end
1745 end
1746
1747 redef class MMTypeFormalParameter
1748 fun html_anchor: String
1749 do
1750 return "FT_{local_class}_{cmangle(name)}"
1751 end
1752 redef fun html_link(dctx)
1753 do
1754 return "<a href=\"#{html_anchor}\">{name}</a>"
1755 end
1756 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1757 do
1758 dctx.add("<article id=\"{html_anchor}\">\n")
1759 dctx.add("<h3 class=\"signature\">{name}: {bound.html_link(dctx)}</h3>\n")
1760 dctx.add("<div class=\"info\">")
1761 dctx.add("formal generic type")
1762 dctx.add("</div>")
1763 dctx.add("</article>")
1764 end
1765 end
1766
1767 redef class MMNullableType
1768 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1769 end
1770
1771 redef class MMVirtualType
1772 redef fun html_link(dctx) do return property.html_link(dctx)
1773 end
1774
1775 var c = new DocContext
1776 c.exec_cmd_line