nitdoc: Added Signed-Off management
[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 addGithubInformation
187 addCommitForm
188 add("<!DOCTYPE html>")
189 add("<html><head>{head}<title>Overview | {custom_title}</title></head><body>\n")
190 add(action_bar)
191 add("<div class=\"page\">")
192 add("<div class=\"content fullpage\">")
193 add("<h1>{custom_title}</h1>\n<article class='overview'>{overview_text}</article><article class='overview'><h2>Modules</h2><ul>")
194 var modss = mainmod.mhe.greaters_and_self.to_a
195 sort(modss)
196 for mod in modss do
197 if not mod.is_toplevel then continue
198 if not mod.require_doc(self) then continue
199 assert mod isa MMSrcModule
200 add("<li>{mod.html_link(self)} {mod.short_doc}</li>")
201
202 end
203 add("</ul>")
204
205 var op = new Buffer
206 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")
207 for mod in modss do
208 if not mod.is_toplevel then continue
209 if not mod.require_doc(self) then continue
210 op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n")
211 for mod2 in mod.tmhe.direct_greaters do
212 if not modss.has(mod2) then continue
213 op.append("\"{mod.name}\"->\"{mod2.name}\";\n")
214 end
215 end
216 op.append("\}\n")
217 self.gen_dot(op.to_s, "dep", "Modules hierarchy")
218 add("</article></div>")
219 add("</div>")
220 add("<footer>{footer_text}</footer>")
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 addGithubInformation
233 addCommitForm
234 add("<!DOCTYPE html>")
235 add("<html><head>{head}<title>{mod.name} module | {custom_title}</title></head><body>\n")
236 add(action_bar)
237 add("<div class=\"page\">")
238 mod.file_page_doc(self)
239 add("</div>")
240 add("<footer>{footer_text}</footer>")
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 addGithubInformation
252 addCommitForm
253 add("<!DOCTYPE html>")
254 add("<html><head>{head}<title>{c.name} class | {custom_title}</title></head><body>\n")
255 add(action_bar)
256 add("<div class=\"page\">")
257 c.file_page_doc(self)
258 add("</div>")
259 add("<footer>{footer_text}</footer>")
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 addGithubInformation
268 addCommitForm
269 add("<!DOCTYPE html>")
270 add("<html><head>{head}<title>Full Index | {custom_title}</title></head><body>\n")
271 add(action_bar)
272 add("<div class=\"page\">")
273 add("<div class=\"content fullpage\">")
274 mainmod.file_index_page_doc(self)
275 add("</div>")
276 add("</div>")
277 add("<footer>{footer_text}</footer>")
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
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("</div>\n")
647 end
648
649 var op = new Buffer
650 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")
651 var ms = new Array[nullable MMModule]
652 do
653 var m0: nullable MMModule = self
654 while m0 != null do
655 m0 = m0.direct_owner
656 ms.add(m0)
657 end
658 end
659 var cla = new HashSet[MMModule]
660 cla.add(self)
661 for m0 in self.mhe.greaters do
662 if not m0.require_doc(dctx) then continue
663 if self.visibility_for(m0) <= 1 then continue # private or hidden
664 if self.mnhe <= m0 then continue # do not want nested stuff
665 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
666 cla.add(m0)
667 end
668 for m0 in self.mhe.smallers do
669 if not m0.require_doc(dctx) then continue
670 if m0.visibility_for(self) <= 1 then continue # private or hidden
671 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
672 cla.add(m0)
673 end
674 for m0 in self.mnhe.smallers do
675 cla.add(m0)
676 end
677 ms = ms.reversed
678 for m0 in ms do
679 if m0 != null then
680 op.append("subgraph \"cluster_{m0.name}\"\{\n")
681 end
682 for c in cla do
683 if c.direct_owner != m0 then continue
684 if c == self then
685 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
686 else
687 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
688 end
689 end
690 if m0 != null then
691 op.append("\"{m0.name}\"[URL=\"{m0.html_name}.html\"];\n")
692 for c in m0.mhe.direct_greaters do
693 if not cla.has(c) then continue
694 op.append("\"{m0.name}\"->\"{c.name}\";\n")
695 end
696 end
697 end
698 for m0 in ms do
699 # Close the nesting subgraph
700 if m0 != null then
701 op.append("\}\n")
702 end
703 end
704 for c in cla do
705 for c2 in c.tmhe.direct_greaters do
706 if not cla.has(c2) then continue
707 op.append("\"{c.name}\"->\"{c2.name}\";\n")
708 end
709 end
710 op.append("\}\n")
711 dctx.gen_dot(op.to_s, name.to_s, "Dependency graph for module {name}")
712 dctx.add("</section>")
713
714 var clas = new Array[MMLocalClass]
715 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
716 var gprops = new Array[MMLocalProperty]
717 do
718 var m = self
719 for g in m.global_classes do
720 var lc = m[g]
721 if not lc.require_doc(dctx) then continue
722 var im = g.intro.mmmodule
723 if self.visibility_for(im) <= 1 then continue # private import or invisible import
724 var keep = false
725 for lc2 in lc.crhe.greaters_and_self do
726 if not lc2 isa MMSrcLocalClass then continue
727 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
728 keep = true
729 end
730 if not keep then continue
731 clas.add(self[g])
732 lc.compute_super_classes
733 for gp in lc.global_properties do
734 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
735 var lp = lc[gp]
736 var mp = lp.local_class.mmmodule
737 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
738 lp = self[g][gp]
739 if not lp.require_doc(dctx) then continue
740 if props.has_key(lp.global) then
741 if not props[lp.global].has(lp) then
742 props[lp.global].add(lp)
743 end
744 else
745 props[lp.global] = [lp]
746 gprops.add(lp.global.intro)
747 end
748 end
749 end
750 end
751 dctx.add("<section class=\"module\">\n")
752 dctx.open_stage
753 dctx.stage("<article class=\"classes filterable\">\n")
754 dctx.stage("<h2>Classes</h2>\n")
755 dctx.sort(clas)
756 dctx.stage("<ul>\n")
757 for lc in clas do
758 if self.mnhe <= lc.global.intro.mmmodule then
759 dctx.add("<li class='intro'><span title='introduced in this module'>I</span>&nbsp;")
760 else
761 dctx.add("<li class='redef'><span title='refined in this module'>R</span>&nbsp;")
762 end
763 dctx.add("{lc.html_link(dctx)}</li>\n")
764 end
765 dctx.stage("</ul></article>\n")
766 dctx.close_stage
767
768 dctx.open_stage
769 dctx.stage("<article class=\"properties filterable\">\n")
770 dctx.stage("<h2>Properties</h2>\n")
771 dctx.sort(gprops)
772 dctx.stage("<ul>\n")
773 for lgp in gprops do
774 var gp = lgp.global
775 var lps = props[gp]
776
777 if gp.intro isa MMAttribute then continue
778
779 var lpi = self[gp.intro.local_class.global][gp]
780
781 if lps.has(lpi) then
782 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")
783 lps.remove(lpi)
784 else
785 dctx.add("<li class='intro'><span title='introduction in this module'>I</span>&nbsp;{lpi.html_name}")
786 dctx.add("&nbsp;({lpi.local_class})</li>\n")
787 end
788 if lps.length >= 1 then
789 dctx.sort(lps)
790 for lp in lps do
791 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>")
792 end
793 end
794 end
795 dctx.stage("</ul></article>\n")
796 dctx.close_stage
797 dctx.add("</section>\n")
798 dctx.add("</div>\n")
799 end
800
801 # Fill the body for the page associated to the full index
802 fun file_index_page_doc(dctx: DocContext)
803 do
804
805 dctx.add("<h1>Full Index</h1>\n")
806
807 var clas = new Array[MMLocalClass]
808 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
809 var gprops = new Array[MMLocalProperty]
810 var mods = new Array[MMModule]
811 for m in mhe.greaters_and_self do
812 if not m.require_doc(dctx) then continue
813 mods.add(m)
814 end
815 for g in global_classes do
816 var lc = self[g]
817 if not lc.require_doc(dctx) then continue
818 clas.add(lc)
819 for gp in lc.global_properties do
820 var lp = lc[gp]
821 if not lp.require_doc(dctx) then continue
822 if props.has_key(lp.global) then
823 if not props[lp.global].has(lp) then
824 props[lp.global].add(lp)
825 end
826 else
827 props[lp.global] = [lp]
828 gprops.add(lp.global.intro)
829 end
830 end
831 end
832 dctx.open_stage
833 dctx.stage("<article class=\"modules filterable\">\n")
834 dctx.stage("<h2>Modules</h2>\n")
835 dctx.sort(mods)
836 dctx.stage("<ul>\n")
837 for m in mods do
838 dctx.add("<li>{m.html_link(dctx)}</li>")
839 end
840 dctx.stage("</ul></article>\n")
841 dctx.close_stage
842
843 dctx.open_stage
844 dctx.stage("<article class=\"classes filterable\">\n")
845 dctx.stage("<h2>Classes</h2>\n")
846 dctx.sort(clas)
847 dctx.stage("<ul>\n")
848 for lc in clas do
849 dctx.add("<li>{lc.html_link(dctx)}</li>")
850 end
851 dctx.stage("</ul></article>\n")
852 dctx.close_stage
853
854 dctx.open_stage
855 dctx.stage("<article class=\"properties filterable\">\n")
856 dctx.stage("<h2>Properties</h2>\n")
857 dctx.sort(gprops)
858 dctx.stage("<ul>\n")
859 for lgp in gprops do
860 var gp = lgp.global
861 var lps = props[gp]
862
863 if gp.intro isa MMAttribute then continue
864
865 var lpi = self[gp.intro.local_class.global][gp]
866
867 lps.remove(lpi)
868 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")
869 if lps.length >= 1 then
870 dctx.sort(lps)
871 for lp in lps do
872 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")
873 end
874 end
875 end
876 dctx.stage("</ul></article>\n")
877 dctx.close_stage
878 end
879
880 # Fill the quicksearch list JSON object
881 fun file_quicksearch_list_doc(dctx: DocContext)
882 do
883 var entities = new HashMap[String, Array[MMEntity]]
884 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
885 for m in mhe.greaters_and_self do
886 if not m.require_doc(dctx) then continue
887 var a = new Array[MMEntity]
888 a.add(m)
889 entities[m.html_name] = a
890 end
891 for g in global_classes do
892 var lc = self[g]
893 if not lc.require_doc(dctx) then continue
894 var a = new Array[MMEntity]
895 a.add(lc)
896 entities[lc.html_name] = a
897 for gp in lc.global_properties do
898 var lp = lc[gp]
899 if not lp.require_doc(dctx) then continue
900 if lp.kind == "var" then continue
901 if props.has_key(lp.global) then
902 if not props[lp.global].has(lp) then
903 props[lp.global].add(lp)
904 end
905 else
906 props[lp.global] = [lp]
907 end
908 end
909 end
910
911 for k, v in props do
912 entities[k.short_name] = v
913 end
914
915 var keys = entities.keys.to_a
916 var sorter = new AlphaSorter[String]
917 sorter.sort(keys)
918
919 dctx.open_stage
920 dctx.stage("var entries = \{")
921 for key in keys do
922 dctx.add("\"{key}\": [")
923 for entity in entities[key] do
924 dctx.add(entity.json_entry(dctx))
925 end
926 dctx.add("],")
927 end
928 dctx.stage("\};")
929 dctx.close_stage
930 end
931 end
932
933 redef class MMGlobalProperty
934 # Return the short name of the property
935 fun short_name: String do
936 return self.intro.html_name
937 end
938 end
939
940 redef class MMLocalProperty
941 super MMEntity
942 # Anchor of the property description in the module html file
943 fun html_anchor: String
944 do
945 return "PROP_{self.mmmodule.toplevel_owner}_{local_class}_{cmangle(name)}"
946 end
947
948 redef fun json_entry(dctx) do
949 return "\{txt:\"{qualified_name}\",url:\"{local_class.html_name}.html#{html_anchor}\"\},"
950 end
951
952 redef fun qualified_name do
953 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
954 end
955
956 fun html_open_link(dctx: DocContext): String
957 do
958 if not require_doc(dctx) then print "not required {self}"
959 var title = "{html_name}{signature.to_s}"
960 if short_doc != "&nbsp;" then
961 title += " #{short_doc}"
962 end
963 return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">"
964 end
965
966 fun html_name: String
967 do
968 return self.name.to_s.html_escape
969 end
970
971 redef fun html_link(dctx)
972 do
973 if not require_doc(dctx) then print "not required {self}"
974 var title = "{html_name}{signature.to_s}"
975 if short_doc != "&nbsp;" then
976 title += " #{short_doc}"
977 end
978 return "<a href=\"{local_class.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
979 end
980
981 fun html_link_special(dctx: DocContext, lc: MMLocalClass): String
982 do
983 if not require_doc(dctx) then print "not required {self}"
984 var title = "{html_name}{signature_for(lc.get_type)}"
985 if short_doc != "&nbsp;" then
986 title += " #{short_doc}"
987 end
988 return "<a href=\"{lc.html_name}.html#{html_anchor}\" title=\"{title}\">{html_name}</a>"
989 end
990
991 # Kind of property (fun, attr, etc.)
992 fun kind: String is abstract
993
994 redef fun short_doc
995 do
996 var d = doc
997 if d != null then
998 return d.short
999 else if global.intro == self then
1000 return "&nbsp;"
1001 else
1002 return global.intro.short_doc
1003 end
1004 end
1005
1006 redef fun doc
1007 do
1008 var n = node
1009 if n == null or not n isa APropdef then
1010 return null
1011 end
1012 var d = n.n_doc
1013 if d == null then
1014 return null
1015 end
1016 if d.n_comment.is_empty then
1017 return null
1018 else
1019 return d
1020 end
1021 end
1022
1023 # The most specific module in the nesting hierarchy that exports the intro of self
1024 fun intro_module: MMModule
1025 do
1026 var m = global.intro.mmmodule
1027 var mo = m.direct_owner
1028 while mo != null and mo.visibility_for(m) >= 2 do
1029 m = mo
1030 mo = m.direct_owner
1031 end
1032 return m
1033 end
1034
1035 # Is the intro of self exported by the top-level module ?
1036 fun is_toplevel: Bool
1037 do
1038 var m = intro_module
1039 return m == m.toplevel_owner
1040 end
1041
1042 # Return true if the global property must be documented according to the visibility configured
1043 fun require_doc(dctx: DocContext): Bool
1044 do
1045 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1046 if dctx.public_only then
1047 var m = intro_module
1048 if m != m.toplevel_owner then return false # Unexported
1049 end
1050 return true
1051 end
1052
1053 # Document the global property in the global class lc
1054 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1055 do
1056 var visibility: String
1057 if global.visibility_level == 1 then
1058 visibility = "public"
1059 else if global.visibility_level == 2 then
1060 visibility = "protected"
1061 else if global.visibility_level == 3 then
1062 visibility = "private"
1063 else
1064 abort
1065 end
1066
1067 var intro_class = global.intro.local_class
1068 var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner
1069
1070 dctx.add("<article id=\"{html_anchor}\" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\">\n")
1071 dctx.add("<h3 class=\"signature\">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1072 dctx.add("<div class=\"info\">\n")
1073
1074 if is_redef then
1075 dctx.add("redef ")
1076 end
1077 if not is_toplevel then
1078 dctx.add("(unexported) ")
1079 end
1080 if global.visibility_level == 2 then
1081 dctx.add("protected ")
1082 else if global.visibility_level == 3 then
1083 dctx.add("private ")
1084 end
1085 dctx.add(kind)
1086 dctx.add(" {intro_class.mmmodule.toplevel_owner.name}")
1087 if intro_class.global == lc.global then
1088 dctx.add("::{lc.name}")
1089 else
1090 dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}")
1091 end
1092 if is_redef then
1093 dctx.add("::{mmmodule[intro_class.global][global].global.intro.html_link(dctx)}")
1094 else
1095 dctx.add("::{html_name}")
1096 end
1097 dctx.add("</div>")
1098
1099 dctx.add("<div class=\"description\">")
1100
1101 # Collect all refinement of the global property in the same global property
1102 var lps = new Array[MMLocalProperty]
1103 for l in prhe.greaters_and_self do
1104 lps.add(l)
1105 end
1106
1107 var introdoc = false
1108 if global.intro.doc != null then
1109 for lp in lps do
1110 if lp.doc == null then introdoc = true
1111 end
1112 end
1113 if introdoc then
1114 dctx.add("<pre class=\"text_label\" name=\"{html_name}\" >{global.intro.doc.to_html}</pre>")
1115 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>")
1116 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
1117 end
1118
1119 var tlmods = new Array[MMModule]
1120 for lp in lps do
1121 var bm = lp.mmmodule.toplevel_owner
1122 var lcm = lc.global.intro.mmmodule
1123 if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner
1124 if not tlmods.has(bm) then tlmods.add(bm)
1125 end
1126
1127 for tm in tlmods do
1128 # Document the top level property for the current top level module
1129 var tlp
1130 if tm.global_classes.has(lc.global) then
1131 tlp = tm[lc.global][self.global]
1132 assert lps.has(tlp)
1133 else if tm.global_classes.has(self.local_class.global) then
1134 # Self is the inherited property. Process it
1135 tlp = tm[self.local_class.global][self.global]
1136 assert lps.has(tlp)
1137 else
1138 # We skip this module since the props defined by the module is
1139 continue
1140 end
1141
1142 var tlcm = lc.global.intro.mmmodule.toplevel_owner
1143 if not tlcm.mhe <= tm then
1144 dctx.add("<h4>In module {tm.html_link(dctx)} :</h4>")
1145 end
1146
1147 var doc = tlp.doc
1148 var n = tlp.node
1149 if doc != null and (not introdoc or global.intro.doc != doc) then
1150 if n != null then
1151 var l = n.location
1152 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>")
1153 end
1154 else if not is_redef then
1155 if n != null then
1156 var l = n.location
1157 dctx.add("<a class=\"newComment\" tag=\"{l.file.filename}\" title=\"{l.line_start.to_s}\">New comment</a>\n")
1158 end
1159 end
1160 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>")
1161 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
1162 dctx.add("<p>")
1163 if tlp.local_class.global != lc.global then
1164 dctx.add("inherited from {tlp.local_class.html_link(dctx)} ")
1165 end
1166 if tm != tlp.mmmodule then
1167 dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1168 end
1169 if n != null then
1170 var l = n.location
1171 dctx.show_source(l)
1172 end
1173
1174 dctx.open_stage
1175 dctx.stage(". previously defined by:")
1176 for lp in lps do
1177 var tl = lp.mmmodule.toplevel_owner
1178 if tl != tm then continue
1179 if lp == tlp then continue
1180 dctx.add(" {lp.mmmodule.html_link(dctx)}")
1181 if lp.local_class.global != lc.global then
1182 dctx.add(" for {lp.local_class.html_link(dctx)} ")
1183 end
1184
1185 n = lp.node
1186 if n != null then
1187 var l = n.location
1188 dctx.show_source(l)
1189 end
1190 end
1191 dctx.close_stage
1192 dctx.add("</p>")
1193 end
1194 dctx.add("</div>")
1195 dctx.add("</article>")
1196 end
1197 end
1198 redef class MMMethod
1199 redef fun kind do return if global.is_init then "init" else "fun"
1200 end
1201 redef class MMAttribute
1202 redef fun kind do return "var"
1203 end
1204 redef class MMTypeProperty
1205 redef fun kind do return "type"
1206 end
1207
1208 redef class MMSrcModule
1209 redef fun short_doc
1210 do
1211 var d = doc
1212 if d != null then
1213 return d.short
1214 else
1215 return "&nbsp;"
1216 end
1217 end
1218
1219 redef fun doc
1220 do
1221 var n = node
1222 if n.n_moduledecl == null then
1223 return null
1224 end
1225 var np = n.n_moduledecl
1226 var d = np.n_doc
1227 if d == null then
1228 return null
1229 end
1230 if d.n_comment.is_empty then
1231 return null
1232 else
1233 return d
1234 end
1235 end
1236 end
1237
1238 redef class ADoc
1239 # Html transcription of the doc
1240 fun to_html: String
1241 do
1242 var res = new Buffer
1243 for c in n_comment do
1244 res.append(c.text.substring_from(1))
1245 end
1246 return res.to_s.html_escape
1247 end
1248
1249 # Oneliner transcription of the doc
1250 fun short: String
1251 do
1252 return n_comment.first.text.substring_from(1).html_escape
1253 end
1254 end
1255
1256 redef class MMLocalClass
1257 super MMEntity
1258
1259 # Anchor of the class description in the module html file
1260 fun html_anchor: String do return "CLASS_{self}"
1261
1262 fun html_name: String do return "{self}"
1263
1264 redef fun html_link(dctx)
1265 do
1266 if not require_doc(dctx) then print "{dctx.filename}: not required {self}"
1267 if short_doc == "&nbsp;" then
1268 return "<a href=\"{html_name}.html\"\">{self}</a>"
1269 else
1270 return "<a href=\"{html_name}.html\" title=\"{short_doc}\">{self}</a>"
1271 end
1272 end
1273
1274 redef fun json_entry(dctx) do
1275 return "\{txt:\"{qualified_name}\",url:\"{html_name}.html\"\},"
1276 end
1277
1278 redef fun qualified_name do
1279 return "{intro_module.qualified_name}::{html_name}"
1280 end
1281
1282 redef fun short_doc do return global.intro.short_doc
1283
1284 redef fun doc do return global.intro.doc
1285
1286 fun kind: String
1287 do
1288 if global.is_interface then
1289 return "interface"
1290 else if global.is_abstract then
1291 return "abstract class"
1292 else if global.is_enum then
1293 return "enum"
1294 else
1295 return "class"
1296 end
1297 end
1298
1299 # The most specific module in the nesting hierarchy that exports the intro of self
1300 fun intro_module: MMModule
1301 do
1302 var m = global.intro.mmmodule
1303 var mo = m.direct_owner
1304 while mo != null and mo.visibility_for(m) >= 2 do
1305 m = mo
1306 mo = m.direct_owner
1307 end
1308 return m
1309 end
1310
1311 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1312 do
1313 if p.local_class.global != self.global then
1314 if p.global.intro.local_class.name == "Object".to_symbol then return
1315 if p.global.is_init or p isa MMTypeProperty then
1316 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1317 else
1318 dctx.add("<li class='inherit'><span title='Inherited'>H</span>&nbsp;{p.html_link(dctx)}</li>\n")
1319 end
1320 else if p.global.intro.local_class.global == self.global then
1321 dctx.add("<li class='intro'><span title='Introduced'>I</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1322 else
1323 dctx.add("<li class='redef'><span title='Redefined'>R</span>&nbsp;{p.html_link_special(dctx, self)}</li>\n")
1324 end
1325 end
1326
1327 # Return true if the global class must be documented according to the visibility configured
1328 fun require_doc(dctx: DocContext): Bool
1329 do
1330 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1331 if dctx.public_only then
1332 var m = intro_module
1333 if m != m.toplevel_owner then return false # Unexported
1334 end
1335 return true
1336 end
1337
1338 # Fill the body for the page associated to the global class
1339 fun file_page_doc(dctx: DocContext)
1340 do
1341 dctx.add("<div class=\"menu\">\n")
1342
1343 var props = new Array[MMLocalProperty]
1344 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1345 var inhs = new Array[MMLocalClass]
1346 for g in global_properties do
1347 var p = self[g]
1348 if not p.require_doc(dctx) then continue
1349 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1350 props.add(p)
1351 else
1352 var lc = mmmodule[p.local_class.global]
1353 if inh.has_key(lc) then
1354 inh[lc].add(p)
1355 else
1356 inh[lc] = [p]
1357 inhs.add(lc)
1358 end
1359 props.add(p)
1360 end
1361 end
1362 dctx.sort(props)
1363
1364 dctx.add("<nav class=\"properties filterable\">\n")
1365 dctx.add("<h3>Properties</h3>\n")
1366 dctx.open_stage
1367 dctx.stage("<h4>Virtual Types</h4>\n<ul>\n")
1368 for p in props do
1369 if p isa MMTypeProperty then
1370 menu_link(dctx, p)
1371 end
1372 end
1373 dctx.stage("</ul>\n")
1374 dctx.close_stage
1375 dctx.open_stage
1376 dctx.stage("<h4>Constructors</h4>\n<ul>\n")
1377 for p in props do
1378 if p.global.is_init_for(self) then
1379 menu_link(dctx, p)
1380 end
1381 end
1382 dctx.stage("</ul>\n")
1383 dctx.close_stage
1384 dctx.open_stage
1385 dctx.stage("<h4>Methods</h4>\n<ul>\n")
1386 for p in props do
1387 if not p.global.is_init and p isa MMMethod then
1388 menu_link(dctx, p)
1389 end
1390 end
1391 dctx.stage("</ul>\n")
1392 dctx.close_stage
1393 dctx.add("</nav>\n")
1394
1395 dctx.add("<nav class=\"inheritance filterable\">\n")
1396 dctx.add("<h3>Inheritance</h3>\n")
1397 dctx.add("<h4>Superclasses</h4>\n<ul>\n")
1398 for lc in cshe.linear_extension do
1399 if lc == self then continue
1400 if not lc.require_doc(dctx) then continue
1401 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1402 end
1403 dctx.add("</ul>\n")
1404 if cshe.smallers.length == 0 then
1405 dctx.add("<h4>No Known Subclasses</h4>\n")
1406 else if cshe.smallers.length <= 100 then
1407 dctx.add("<h4>Subclasses</h4>\n")
1408 dctx.add("<ul>\n")
1409 for lc in cshe.smallers do
1410 if not lc.require_doc(dctx) then continue
1411 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1412 end
1413 dctx.add("</ul>\n")
1414 else if cshe.direct_smallers.length <= 100 then
1415 dctx.add("<h4>Direct Subclasses Only</h4>\n<ul>\n")
1416 for lc in cshe.direct_smallers do
1417 if not lc.require_doc(dctx) then continue
1418 dctx.add("<li>{lc.html_link(dctx)}</li>\n")
1419 end
1420 dctx.add("</ul>\n")
1421 else
1422 dctx.add("<h4>Too much Subclasses to list</h4>\n")
1423 end
1424 dctx.add("</nav>\n")
1425
1426 dctx.add("</div>\n")
1427
1428
1429 dctx.add("<div class=\"content\">\n")
1430 dctx.add("<h1>{name}</h1>\n")
1431 dctx.add("<div class='subtitle'>")
1432 if global.visibility_level == 2 then
1433 abort
1434 else if global.visibility_level == 3 then
1435 dctx.add("private ")
1436 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1437 dctx.add("(unexported) ")
1438 end
1439 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div>")
1440
1441 dctx.add("<section class=\"description\">\n")
1442 var doc = doc
1443 if doc != null then
1444 var l = doc.location
1445 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")
1446 dctx.add("<textarea rows=\"1\" cols=\"76\" id=\"fileContent\" class=\"edit\"></textarea>")
1447 dctx.add("<a id=\"cancelBtn\">Cancel</a><a id=\"commitBtn\">Commit</a>")
1448 end
1449
1450 var cla = new HashSet[MMLocalClass]
1451 var sm = new HashSet[MMLocalClass]
1452 var sm2 = new HashSet[MMLocalClass]
1453 sm.add(self)
1454 while cla.length + sm.length < 10 and sm.length > 0 do
1455 cla.add_all(sm)
1456 sm2.clear
1457 for x in sm do
1458 sm2.add_all(x.cshe.direct_smallers)
1459 end
1460 var t = sm
1461 sm = sm2
1462 sm2 = t
1463 end
1464 cla.add_all(cshe.greaters_and_self)
1465
1466 var op = new Buffer
1467 var name = "class_{name}"
1468 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")
1469 for c in cla do
1470 if c == self then
1471 op.append("\"{c.name}\"[shape=box,margin=0.03];\n")
1472 else
1473 op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n")
1474 end
1475 for c2 in c.cshe.direct_greaters do
1476 if not cla.has(c2) then continue
1477 op.append("\"{c.name}\"->\"{c2.name}\";\n")
1478 end
1479 if not c.cshe.direct_smallers.is_empty then
1480 var others = true
1481 for c2 in c.cshe.direct_smallers do
1482 if cla.has(c2) then others = false
1483 end
1484 if others then
1485 op.append("\"{c.name}...\"[label=\"\"];\n")
1486 op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n")
1487 end
1488 end
1489 end
1490 op.append("\}\n")
1491 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph for class {name}")
1492 dctx.add("</section>\n")
1493
1494 # Concerns table
1495 dctx.open_stage
1496 dctx.stage("<section class=\"concerns\">\n")
1497 dctx.stage("<h2 class=\"section-header\">Concerns</h2>\n")
1498
1499 var mods = new Array[MMModule]
1500 mods.add(global.intro.mmmodule.toplevel_owner)
1501 for lc in crhe.greaters do
1502 if not lc isa MMSrcLocalClass then continue
1503 var m = lc.mmmodule.toplevel_owner
1504 if not mods.has(m) then mods.add(m)
1505 end
1506
1507 var intro = global.intro.mmmodule
1508 var short_doc
1509 dctx.add("<ul>\n")
1510 for m in mods do
1511 short_doc = ""
1512 if m.short_doc != "&nbsp;" then short_doc = ": {m.short_doc}"
1513 dctx.add("<li>{m.html_link_to_anchor}{short_doc}")
1514 dctx.add("<ul>\n")
1515 for lc in crhe.linear_extension.reversed do
1516 if lc.mmmodule.toplevel_owner != m then continue
1517 if lc.mmmodule == m then continue
1518 short_doc = ""
1519 if lc.mmmodule.short_doc != "&nbsp;" then short_doc = ": {lc.mmmodule.short_doc}"
1520 dctx.add("<li>{lc.mmmodule.html_link_to_anchor}{short_doc}</li>")
1521 end
1522 dctx.add("</ul>\n")
1523 dctx.add("</li>\n")
1524 end
1525 dctx.add("</ul>\n")
1526 dctx.stage("</section>\n")
1527 dctx.close_stage
1528
1529 dctx.open_stage
1530 dctx.stage("<section class=\"types\">\n")
1531 dctx.stage("<h2>Formal and Virtual Types</h2>\n")
1532 for i in [0..arity[ do
1533 var f = get_formal(i)
1534 f.full_documentation(dctx, self)
1535 end
1536 for p in props do
1537 if not p isa MMTypeProperty then continue
1538 p.full_documentation(dctx, self)
1539 end
1540 dctx.stage("</section>\n")
1541 dctx.close_stage
1542
1543 dctx.open_stage
1544 dctx.stage("<section class=\"constructors\">\n")
1545 dctx.stage("<h2 class=\"section-header\">Constructors</h2>\n")
1546 for p in props do
1547 if not p.global.is_init_for(self) then continue
1548 p.full_documentation(dctx, self)
1549 end
1550 dctx.stage("</section>\n")
1551 dctx.close_stage
1552
1553 dctx.open_stage
1554 dctx.stage("<section class=\"methods\">\n")
1555 dctx.stage("<h2 class=\"section-header\">Methods</h2>\n")
1556 var redefs = new HashMap[MMModule, HashMap[MMModule, Array[MMMethod]]]
1557 for p in props do
1558 if p.global.is_init then continue
1559 if p.local_class.global != self.global then continue
1560 if not p isa MMMethod then continue
1561 # Top level module
1562 var toplevel_module = p.mmmodule.toplevel_owner
1563 if not redefs.has_key(toplevel_module) then
1564 redefs[toplevel_module] = new HashMap[MMModule, Array[MMMethod]]
1565 end
1566 # Nested module
1567 var nested_module = p.mmmodule
1568 if not redefs[toplevel_module].has_key(nested_module) then
1569 redefs[toplevel_module][nested_module] = new Array[MMMethod]
1570 end
1571 # Props
1572 redefs[toplevel_module][nested_module].add(p)
1573
1574 # Redefs
1575 if p.mmmodule.toplevel_owner != p.intro_module then
1576 toplevel_module = p.intro_module
1577 nested_module = p.global.intro.mmmodule
1578
1579 if not redefs.has_key(toplevel_module) then
1580 redefs[toplevel_module] = new HashMap[MMModule, Array[MMMethod]]
1581 end
1582 if not redefs[toplevel_module].has_key(nested_module) then
1583 redefs[toplevel_module][nested_module] = new Array[MMMethod]
1584 end
1585
1586 redefs[toplevel_module][nested_module].add(p.global.intro.as(MMMethod))
1587 end
1588 end
1589
1590 # Display toplevel blocks
1591 for m in mods do
1592 if not redefs.has_key(m) then continue
1593 dctx.add(m.html_anchor)
1594 if m != global.intro.mmmodule.toplevel_owner then
1595 dctx.add("<h3 class=\"concern-toplevel\">Methods refined in {m.html_link(dctx)}</h3>")
1596 end
1597
1598 # Display nested module blocks
1599 for lc in crhe.linear_extension.reversed do
1600 if lc.mmmodule.toplevel_owner != m then continue
1601 var nm = lc.mmmodule
1602 if not redefs[m].has_key(nm) then continue
1603 dctx.add(nm.html_anchor)
1604 if nm != global.intro.mmmodule then
1605 short_doc = ""
1606 if nm.short_doc != "&nbsp;" then short_doc = ": {nm.short_doc}"
1607 dctx.add("<p class=\"concern-doc\">{nm.html_name}{short_doc}</p>\n")
1608 end
1609
1610 var pps = redefs[m][nm]
1611 dctx.sort(pps)
1612 for p in pps do
1613 p.full_documentation(dctx, self)
1614 end
1615 end
1616 end
1617
1618 if not inhs.is_empty then
1619 dctx.open_stage
1620 dctx.stage("<h3>Inherited Methods</h3>\n")
1621 for lc in inhs do
1622 dctx.open_stage
1623 dctx.stage("<p>Defined in {lc.html_link(dctx)}:")
1624
1625 var ims = new Array[MMMethod]
1626 for p in inh[lc] do
1627 if p.global.is_init then continue
1628 if not p isa MMMethod then continue
1629 ims.add(p)
1630 end
1631
1632 var i = 0
1633 for p in ims do
1634 dctx.add(" {p.html_link(dctx)}")
1635 if i < ims.length - 1 then dctx.add(",")
1636 i += 1
1637 end
1638
1639 dctx.stage("</p>")
1640 dctx.close_stage
1641 end
1642 dctx.close_stage
1643 end
1644 dctx.add("</section>\n")
1645 dctx.close_stage
1646 dctx.add("</div> <!-- end class {name} -->\n")
1647 end
1648 end
1649
1650 redef class MMSrcLocalClass
1651 redef fun short_doc
1652 do
1653 var d = doc
1654 if d != null then
1655 return d.short
1656 else if global.intro == self then
1657 return "&nbsp;"
1658 else
1659 var bc = global.intro
1660 return bc.short_doc
1661 end
1662 end
1663
1664 redef fun doc
1665 do
1666 var n = node
1667 if not n isa AStdClassdef then
1668 return null
1669 end
1670 var d = n.n_doc
1671 if d == null then
1672 return null
1673 end
1674 if d.n_comment.is_empty then
1675 return null
1676 else
1677 return d
1678 end
1679 end
1680 end
1681
1682 redef class MMSignature
1683 # Htlm transcription of the signature (with nested links)
1684 fun to_html(dctx: DocContext, with_closure: Bool): String
1685 do
1686 var res = new Buffer
1687 if arity > 0 then
1688 res.append("(")
1689 for i in [0..arity[ do
1690 if i > 0 then res.append(", ")
1691 res.append(self.params[i].name.to_s)
1692 res.append(": ")
1693 res.append(self[i].html_link(dctx))
1694 if self.vararg_rank == i then
1695 res.append("...")
1696 end
1697 end
1698 res.append(")")
1699 end
1700 if return_type != null then
1701 res.append(": ")
1702 res.append(return_type.html_link(dctx))
1703 end
1704 if with_closure then
1705 for c in closures do
1706 res.append(" ")
1707 if c.is_optional then res.append("[")
1708 if c.is_break then res.append("break ")
1709 res.append("!{c.name}")
1710 res.append(c.signature.to_html(dctx, false))
1711 if c.is_optional then res.append("]")
1712 end
1713 end
1714 return res.to_s
1715 end
1716 end
1717
1718 redef class MMType
1719 # Htlm transcription of the type (with nested links)
1720 fun html_link(dctx: DocContext): String do return to_s
1721 end
1722
1723 redef class MMTypeSimpleClass
1724 redef fun html_link(dctx) do return local_class.html_link(dctx)
1725 end
1726
1727 redef class MMTypeGeneric
1728 redef fun html_link(dctx)
1729 do
1730 var res = new Buffer
1731 res.append(local_class.html_link(dctx))
1732 res.append("[")
1733 res.append(params[0].html_link(dctx))
1734 for i in [1..params.length[ do
1735 res.append(", ")
1736 res.append(params[i].html_link(dctx))
1737 end
1738 res.append("]")
1739 return res.to_s
1740 end
1741 end
1742
1743 redef class MMTypeFormalParameter
1744 fun html_anchor: String
1745 do
1746 return "FT_{local_class}_{cmangle(name)}"
1747 end
1748 redef fun html_link(dctx)
1749 do
1750 return "<a href=\"#{html_anchor}\">{name}</a>"
1751 end
1752 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1753 do
1754 dctx.add("<article id=\"{html_anchor}\">\n")
1755 dctx.add("<h3 class=\"signature\">{name}: {bound.html_link(dctx)}</h3>\n")
1756 dctx.add("<div class=\"info\">")
1757 dctx.add("formal generic type")
1758 dctx.add("</div>")
1759 dctx.add("</article>")
1760 end
1761 end
1762
1763 redef class MMNullableType
1764 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1765 end
1766
1767 redef class MMVirtualType
1768 redef fun html_link(dctx) do return property.html_link(dctx)
1769 end
1770
1771 var c = new DocContext
1772 c.exec_cmd_line