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