1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2008 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # The main module of the nitdoc program
25 # Store knowledge and facilities to generate files
27 super AbstractCompiler
28 # Destination directory
29 readable writable var _dir
: String = "doc"
31 # Content of a generated file
32 var _stage_context
: StageContext = new StageContext(null)
34 # Add a string in the content
36 _stage_context
.content
.add
(s
)
37 _stage_context
.validate
= true
40 # Add a string in the content iff some other string are added
41 fun stage
(s
: String) do _stage_context
.content
.add
(s
)
43 # Create a new stage in the content
44 fun open_stage
do _stage_context
= new StageContext(_stage_context
)
46 # Close the current stage in the content
49 var s
= _stage_context
.parent
50 if _stage_context
.validate
then
51 s
.content
.add_all
(_stage_context
.content
)
58 # Write the content to a new file
59 fun write_to
(filename
: String)
61 var f
= new OFStream.open
(filename
)
62 for s
in _stage_context
.content
do
71 _stage_context
= new StageContext(null)
74 # Sorter of entities in alphabetical order
75 var _sorter
: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity]
77 # Sort entities in the alphabetical order
78 fun sort
(array
: Array[MMEntity])
83 readable var _opt_dir
: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
84 readable var _opt_source
: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
85 readable var _opt_public
: OptionBool = new OptionBool("Generate only the public API", "--public")
86 readable var _opt_private
: OptionBool = new OptionBool("Generate the private API", "--private")
87 readable var _opt_nodot
: OptionBool = new OptionBool("Do not generate graphes with graphviz", "--no-dot")
88 readable var _opt_sharedir
: OptionString = new OptionString("Directory containing the nitdoc files", "--sharedir")
90 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")
91 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")
92 readable var _opt_custom_overview_text
: OptionString = new OptionString("Text displayed as introduction of Overview page before the modules list", "--custom-overview-text")
93 readable var _opt_custom_footer_text
: OptionString = new OptionString("Text displayed as footer of all pages", "--custom-footer-text")
94 var sharedir
: nullable String
98 if self._opt_public
.value
== true then return true
102 fun with_private
: Bool
104 if self._opt_private
.value
== true then return true
108 # The current processed filename
111 # The main virtual module
112 var mainmod
: nullable MMVirtualModule
114 redef fun perform_work
(mods
)
116 mainmod
= new MMVirtualModule(self, mods
)
120 sys
.system
("cp -r '{sharedir.to_s}'/* {dir}/")
122 # Compute the set of direct owned nested modules
123 var owns
= new HashMap[MMModule, Array[MMModule]]
124 for mod
in modules
do
125 owns
[mod
] = new Array[MMModule]# [mod]
127 for mod
in modules
do
128 if mod
== mainmod
then continue
129 var d
= mod
.directory
132 if o
!= null and o
!= mod
then
136 if dp
== null or dp
== d
then break
141 # Builds the various module hierarchies
142 var mnh
= new PartialOrder[MMModule] # nested module hierarchy
143 var tmh
= new PartialOrder[MMModule] # top module import hierrchy
144 var ms
= mainmod
.mhe
.linear_extension
.reversed
146 if ms
== mainmod
then continue
147 m
.mnhe_
= mnh
.add
(m
, owns
[m
])
148 var pub
= new Array[MMModule]
149 for m2
in m
.mhe
.greaters
do
150 if m2
.toplevel_owner
!= m2
and m2
.toplevel_owner
!= m
.toplevel_owner
then continue
151 if m
.mnhe
<= m2
then continue
152 if m
.visibility_for
(m2
) <= 0 then
154 else if m
.visibility_for
(m2
) == 1 then
159 m
.tmhe_
= tmh
.add
(m
, pub
)
162 var head
= "<meta charset=\"utf-8\
">" +
163 "<script type=\"text
/javascript\
" src=\"scripts
/jquery-1
.7
.1.min
.js\
"></script>\n" +
164 "<script type=\"text
/javascript\
" src=\"quicksearch-list
.js\
"></script>\n" +
165 "<script type=\"text
/javascript\
" src=\"scripts
/js-facilities
.js\
"></script>\n" +
166 "<link rel=\"stylesheet\
" href=\"styles
/main
.css\
" type=\"text
/css\
" media=\"screen\
" />"
168 var custom_items
= ""
169 if self._opt_custom_menu_items
.value
!= null then custom_items
= self._opt_custom_menu_items
.value
.as(not null)
171 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></ul></nav></header>\n"
173 var custom_title
= "Nitdoc"
174 if self._opt_custom_title
.value
!= null then custom_title
= self._opt_custom_title
.value
.as(not null)
176 var overview_text
= ""
177 if self._opt_custom_overview_text
.value
!= null then overview_text
= self._opt_custom_overview_text
.value
.as(not null)
180 if self._opt_custom_footer_text
.value
!= null then footer_text
= self._opt_custom_footer_text
.value
.as(not null)
183 self.filename
= "index.html"
185 add
("<!DOCTYPE html>")
186 add
("<html><head>{head}<title>Overview | {custom_title}</title></head><body>\n")
188 add
("<div class=\"page\
">")
189 add
("<div class=\"content fullpage\
">")
190 add
("<h1>{custom_title}</h1>\n<article class='overview'>{overview_text}</article><article class='overview'><h2>Modules</h2><ul>")
191 var modss
= mainmod
.mhe
.greaters_and_self
.to_a
194 if not mod
.is_toplevel
then continue
195 if not mod
.require_doc
(self) then continue
196 assert mod
isa MMSrcModule
197 add
("<li>{mod.html_link(self)} {mod.short_doc}</li>")
203 op
.append
("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
205 if not mod
.is_toplevel
then continue
206 if not mod
.require_doc
(self) then continue
207 op
.append
("\"{mod.name}\
"[URL=\"{mod.html_name}.html\
"];\n")
208 for mod2
in mod
.tmhe
.direct_greaters
do
209 if not modss
.has
(mod2
) then continue
210 op
.append
("\"{mod.name}\
"->\"{mod2.name}\
";\n")
214 self.gen_dot
(op
.to_s
, "dep", "Modules hierarchy")
215 add
("</article></div>")
216 add
("<div class='clear'></div>")
218 add
("<footer>{footer_text}</footer>")
219 add
("</body></html>\n")
220 write_to
("{dir}/index.html")
222 # Generate page for modules
223 for mod
in modules
do
224 if mod
== mainmod
then continue
225 assert mod
isa MMSrcModule
226 if not mod
.require_doc
(self) then continue
227 self.filename
= mod
.html_name
228 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></ul></nav></header>\n"
230 add
("<!DOCTYPE html>")
231 add
("<html><head>{head}<title>{mod.name} module | {custom_title}</title></head><body>\n")
233 add
("<div class=\"page\
">")
234 mod
.file_page_doc
(self)
236 add
("<footer>{footer_text}</footer>")
237 add
("</body></html>\n")
238 write_to
("{dir}/{mod.html_name}.html")
241 # Generate pages for global classes
242 for c
in mainmod
.local_classes
do
243 if not c
.require_doc
(self) then continue
244 self.filename
= c
.html_name
245 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></ul></nav></header>\n"
247 add
("<!DOCTYPE html>")
248 add
("<html><head>{head}<title>{c.name} class | {custom_title}</title></head><body>\n")
250 add
("<div class=\"page\
">")
251 c
.file_page_doc
(self)
253 add
("<footer>{footer_text}</footer>")
254 add
("</body></html>\n")
255 write_to
("{dir}/{c.html_name}.html")
258 self.filename
= "fullindex"
259 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></ul></nav></header>\n"
261 add
("<!DOCTYPE html>")
262 add
("<html><head>{head}<title>Full Index | {custom_title}</title></head><body>\n")
264 add
("<div class=\"page\
">")
265 add
("<div class=\"content fullpage\
">")
266 mainmod
.file_index_page_doc
(self)
269 add
("<footer>{footer_text}</footer>")
270 add
("</body></html>\n")
271 write_to
("{dir}/full-index.html")
273 self.filename
= "quicksearch-list"
275 mainmod
.file_quicksearch_list_doc
(self)
276 write_to
("{dir}/quicksearch-list.js")
280 # Add a (source) link fo a given location
281 fun show_source
(l
: Location)
283 var s
= opt_source
.value
285 add
("in {l.file.filename.simplify_path}")
287 # THIS IS JUST UGLY ! (but there is no replace yet)
288 var x
= s
.split_with
("%f")
289 s
= x
.join
(l
.file
.filename
.simplify_path
)
290 x
= s
.split_with
("%l")
291 s
= x
.join
(l
.line_start
.to_s
)
292 x
= s
.split_with
("%L")
293 s
= x
.join
(l
.line_end
.to_s
)
294 add
(" (<a href=\"{s}\
">show code</a>)")
298 # Generate a clicable graphiz image using a dot content.
299 # `name' refer to the filename (without extension) and the id name of the map.
300 # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...)
301 fun gen_dot
(dot
: String, name
: String, alt
: String)
303 if opt_nodot
.value
then return
304 var f
= new OFStream.open
("{self.dir}/{name}.dot")
307 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 ; \}")
308 self.add
("<article class=\"graph\
"><img src=\"{name}.png\
" usemap=\"#{name}\" style=\"margin:auto\" alt=\"{alt}\"/></article>")
309 var fmap
= new IFStream.open
("{self.dir}/{name}.map")
310 self.add
(fmap
.read_all
)
319 option_context
.add_option
(opt_public
)
320 option_context
.add_option
(opt_private
)
321 option_context
.add_option
(opt_dir
)
322 option_context
.add_option
(opt_source
)
323 option_context
.add_option
(opt_nodot
)
324 option_context
.add_option
(opt_sharedir
)
325 option_context
.add_option
(opt_custom_title
)
326 option_context
.add_option
(opt_custom_menu_items
)
327 option_context
.add_option
(opt_custom_overview_text
)
328 option_context
.add_option
(opt_custom_footer_text
)
331 redef fun process_options
334 var d
= opt_dir
.value
335 if d
!= null then dir
= d
337 if not opt_nodot
.value
then
338 # Test if dot is runable
339 var res
= sys
.system
("sh -c dot </dev/null >/dev/null 2>&1")
341 stderr
.write
"--no-dot implied since `dot' is not available. Try to install graphviz.\n"
342 opt_nodot
.value
= true
346 sharedir
= opt_sharedir
.value
347 if sharedir
== null then
348 var dir
= once
("NIT_DIR".to_symbol
).environ
350 dir
= "{sys.program_name.dirname}/../share/nitdoc"
351 if dir
.file_exists
then sharedir
= dir
353 dir
= "{dir}/share/nitdoc"
354 if dir
.file_exists
then sharedir
= dir
356 if sharedir
== null then
357 fatal_error
(null, "Error: Cannot locate nitdoc shared files. Uses --sharedir or envvar NIT_DIR.")
359 dir
= "{sharedir.to_s}/scripts/js-facilities.js"
360 if sharedir
== null then
361 fatal_error
(null, "Error: Invalid nitdoc shared files. Check --sharedir or envvar NIT_DIR.")
367 redef fun handle_property_conflict
(lc
, impls
)
369 # THIS IS SO UGLY! See MMVirtualModule
370 if lc
.mmmodule
== self.mainmod
then
371 return # We just accept, so one in impls is arbitrary inherited
377 # A virtual module is used to work as an implicit main module that combine unrelated modules
378 # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled
379 class MMVirtualModule
381 init(ctx
: MMContext, mods
: Array[MMModule])
383 # We need to compute the whole metamodel since there is no mmbuilder to do it
384 super(" main".to_symbol
, mods
.first
.directory
, ctx
, new Location(null,0,0,0,0))
385 ctx
.add_module
(self, mods
)
387 self.add_super_module
(m
, 1)
389 self.import_global_classes
390 self.import_local_classes
391 for c
in self.local_classes
do
392 c
.compute_super_classes
394 for c
in self.local_classes
do
399 redef fun require_doc
(dctx
) do return false
402 # Conditionnal part of the text content of a DocContext
404 # Content of the current stage
405 readable var _content
: Array[String] = new Array[String]
407 # Is a normal string already added?
408 readable writable var _validate
: Bool = false
410 # Parent stage is any
411 readable var _parent
: nullable StageContext = null
413 init(parent
: nullable StageContext) do _parent
= parent
417 # Efficiently sort object with their to_s method
418 class AlphaSorter[E
: Object]
419 super AbstractSorter[E
]
420 redef fun compare
(a
, b
)
440 # Keep track of to_s values
441 var _dico
: HashMap[Object, String] = new HashMap[Object, String]
446 # Generalization of metamodel entities
449 fun html_link
(dctx
: DocContext): String is abstract
451 # Return a one liner description
452 fun short_doc
: String do return " "
454 # The doc node from the AST
455 # Return null is none
456 fun doc
: nullable ADoc do return null
458 # Return a JSON entry for quicksearch list
459 fun json_entry
(dctx
: DocContext): String is abstract
461 # Return the qualified name as string
462 fun qualified_name
: String is abstract
468 redef fun html_link
(dctx
) do
469 return "<a href=\"{html_name}.html\
" title=\"{short_doc}\
">{self}</a>"
472 redef fun json_entry
(dctx
) do
473 return "\{txt:\"{self.qualified_name}\",url
:\
"{html_name}.html\"\
},"
476 redef fun qualified_name do
477 var buffer = new Buffer
478 for m in mnhe.smallers do
479 buffer.append("{m.html_name}::")
481 buffer.append("{self.name}")
485 fun require_doc(dctx: DocContext): Bool
487 if dctx.public_only and not is_toplevel then return false
491 # Return true if the module is a top-level owner or a top-level module
492 fun is_toplevel: Bool
494 var pd = directory.parent
495 return pd == null or (pd.owner == null and directory.owner == self)
498 # Element in the module nesting tree
499 fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null)
500 var mnhe_: nullable PartialOrderElement[MMModule] = null
502 # Element in the top level module importation hierarchy
503 fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null)
504 var tmhe_: nullable PartialOrderElement[MMModule] = null
506 fun toplevel_owner: MMModule
510 var ds = m.mnhe.direct_smallers
511 if ds.length == 0 then return m
512 if ds.length == 1 then m = ds.first else abort
516 fun html_name: String
521 fun direct_owner: nullable MMModule
524 while d.owner == self do d = d.parent.as(not null)
528 # Fill the body for the page associated to the module
529 fun file_page_doc(dctx: DocContext)
531 dctx.add("<div
class=\
"menu\">\n
")
533 var mods = new Array[MMModule]
534 mods = self.mhe.greaters.to_a
538 dctx.stage("<nav
>\n
")
539 dctx.stage("<h3
>Module Hierarchy</h3
>\n
")
540 dctx.stage("<h4
>All dependencies
</h4
>\n
")
543 if not mod.require_doc(dctx) then continue
544 if self.mnhe <= mod then continue # do not want nested stuff
545 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
546 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
548 dctx.stage("</ul
>\n
")
550 mods = self.mhe.smallers.to_a
552 dctx.stage("<h4
>All clients
</h4
>\n
")
555 if not mod.require_doc(dctx) then continue
556 if self.mnhe <= mod then continue # do not want nested stuff
557 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
558 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
560 dctx.stage("</ul
>\n
")
561 dctx.stage("</nav
>\n
")
564 if not dctx.public_only then
565 mods = self.mnhe.direct_greaters.to_a
568 dctx.stage("<nav
>\n
")
569 dctx.stage("<h3
>Nested Modules</h3
><ul
>\n
")
571 if not mod.require_doc(dctx) then continue
572 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
574 dctx.stage("</ul
></nav
>\n
")
578 dctx.add("</div
>") # metadata
580 dctx.add("<div
class=\
"content\">\n
")
581 dctx.add("<h1
>{name}</h1
>\n
")
582 dctx.add("<div
class='subtitle'>module ")
583 for m in mnhe.smallers do
584 dctx.add("{m.html_link(dctx)}::")
586 dctx.add("{self.name}</div
>\n
")
588 dctx.add("<section
class='description'>\n
")
592 dctx.add("<div id
=\
"description\">\n
")
593 dctx.add("<pre
>{doc.to_html}</pre
>\n
")
598 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
")
599 var ms = new Array[nullable MMModule]
601 var m0: nullable MMModule = self
607 var cla = new HashSet[MMModule]
609 for m0 in self.mhe.greaters do
610 if not m0.require_doc(dctx) then continue
611 if self.visibility_for(m0) <= 1 then continue # private or hidden
612 if self.mnhe <= m0 then continue # do not want nested stuff
613 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
616 for m0 in self.mhe.smallers do
617 if not m0.require_doc(dctx) then continue
618 if m0.visibility_for(self) <= 1 then continue # private or hidden
619 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
622 for m0 in self.mnhe.smallers do
628 op.append("subgraph \
"cluster_{m0.name}\"\
{\n
")
631 if c.direct_owner != m0 then continue
633 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
635 op.append("\
"{c.name}\"[URL=\
"{c.html_name}.html\"];\n
")
639 op.append("\
"{m0.name}\"[URL=\
"{m0.html_name}.html\"];\n
")
640 for c in m0.mhe.direct_greaters do
641 if not cla.has(c) then continue
642 op.append("\
"{m0.name}\"->\
"{c.name}\";\n
")
647 # Close the nesting subgraph
653 for c2 in c.tmhe.direct_greaters do
654 if not cla.has(c2) then continue
655 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
659 dctx.gen_dot(op.to_s, name.to_s, "Dependency graph
for module {name}")
660 dctx.add("</section
>")
662 var clas = new Array[MMLocalClass]
663 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
664 var gprops = new Array[MMLocalProperty]
667 for g in m.global_classes do
669 if not lc.require_doc(dctx) then continue
670 var im = g.intro.mmmodule
671 if self.visibility_for(im) <= 1 then continue # private import or invisible import
673 for lc2 in lc.crhe.greaters_and_self do
674 if not lc2 isa MMSrcLocalClass then continue
675 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
678 if not keep then continue
680 lc.compute_super_classes
681 for gp in lc.global_properties do
682 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
684 var mp = lp.local_class.mmmodule
685 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
687 if not lp.require_doc(dctx) then continue
688 if props.has_key(lp.global) then
689 if not props[lp.global].has(lp) then
690 props[lp.global].add(lp)
693 props[lp.global] = [lp]
694 gprops.add(lp.global.intro)
699 dctx.add("<section
class=\
"module\">\n
")
701 dctx.stage("<article
class=\
"classes filterable\">\n
")
702 dctx.stage("<h2
>Classes</h2
>\n
")
706 if self.mnhe <= lc.global.intro.mmmodule then
707 dctx.add("<li
class='intro'><span title
='introduced in this module'>I
</span
> 
;")
709 dctx.add("<li
class='redef'><span title
='refined in this module'>R
</span
> 
;")
711 dctx.add("{lc.html_link(dctx)}</li
>\n
")
713 dctx.stage("</ul
></article
>\n
")
717 dctx.stage("<article
class=\
"properties filterable\">\n
")
718 dctx.stage("<h2
>Properties</h2
>\n
")
725 if gp.intro isa MMAttribute then continue
727 var lpi = self[gp.intro.local_class.global][gp]
730 dctx.add("<li
class='intro'><span title
='introduction in an other module'>I
</span
> 
;{lpi.html_open_link(dctx)}{lpi.html_name} 
;({lpi.local_class})</a
></li
>\n
")
733 dctx.add("<li
class='intro'><span title
='introduction in this module'>I
</span
> 
;{lpi.html_name}")
734 dctx.add(" 
;({lpi.local_class})</li
>\n
")
736 if lps.length >= 1 then
739 dctx.add("<li
class='redef'><span title
='redefinition'>R
</span
> 
;{lp.html_open_link(dctx)}{lp.html_name} 
;({lp.local_class})</a
></li
>")
743 dctx.stage("</ul
></article
>\n
")
745 dctx.add("</section
>\n
")
749 # Fill the body for the page associated to the full index
750 fun file_index_page_doc(dctx: DocContext)
753 dctx.add("<h1
>Full Index</h1
>\n
")
755 var clas = new Array[MMLocalClass]
756 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
757 var gprops = new Array[MMLocalProperty]
758 var mods = new Array[MMModule]
759 for m in mhe.greaters_and_self do
760 if not m.require_doc(dctx) then continue
763 for g in global_classes do
765 if not lc.require_doc(dctx) then continue
767 for gp in lc.global_properties do
769 if not lp.require_doc(dctx) then continue
770 if props.has_key(lp.global) then
771 if not props[lp.global].has(lp) then
772 props[lp.global].add(lp)
775 props[lp.global] = [lp]
776 gprops.add(lp.global.intro)
781 dctx.stage("<article
class=\
"modules filterable\">\n
")
782 dctx.stage("<h2
>Modules</h2
>\n
")
786 dctx.add("<li
>{m.html_link(dctx)}</li
>")
788 dctx.stage("</ul
></article
>\n
")
792 dctx.stage("<article
class=\
"classes filterable\">\n
")
793 dctx.stage("<h2
>Classes</h2
>\n
")
797 dctx.add("<li
>{lc.html_link(dctx)}</li
>")
799 dctx.stage("</ul
></article
>\n
")
803 dctx.stage("<article
class=\
"properties filterable\">\n
")
804 dctx.stage("<h2
>Properties</h2
>\n
")
811 if gp.intro isa MMAttribute then continue
813 var lpi = self[gp.intro.local_class.global][gp]
816 dctx.add("<li
class='intro'><span title
='introduction'>I
</span
> 
;{lpi.html_open_link(dctx)}{lpi.html_name} 
;({lpi.local_class})</a
></li
>\n
")
817 if lps.length >= 1 then
820 dctx.add("<li
class='redef'><span title
='redefinition'>R
</span
> 
;{lp.html_open_link(dctx)}{lp.html_name} 
;({lp.local_class})</a
></li
>\n
")
824 dctx.stage("</ul
></article
>\n
")
828 # Fill the quicksearch list JSON object
829 fun file_quicksearch_list_doc(dctx: DocContext)
831 var entities = new HashMap[String, Array[MMEntity]]
832 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
833 for m in mhe.greaters_and_self do
834 if not m.require_doc(dctx) then continue
835 var a = new Array[MMEntity]
837 entities[m.html_name] = a
839 for g in global_classes do
841 if not lc.require_doc(dctx) then continue
842 var a = new Array[MMEntity]
844 entities[lc.html_name] = a
845 for gp in lc.global_properties do
847 if not lp.require_doc(dctx) then continue
848 if lp.kind == "var" then continue
849 if props.has_key(lp.global) then
850 if not props[lp.global].has(lp) then
851 props[lp.global].add(lp)
854 props[lp.global] = [lp]
860 entities[k.short_name] = v
863 var keys = entities.keys.to_a
864 var sorter = new AlphaSorter[String]
868 dctx.stage("var entries
= \
{")
870 dctx.add("\
"{key}\": [")
871 for entity in entities[key] do
872 dctx.add(entity.json_entry(dctx))
881 redef class MMGlobalProperty
882 # Return the short name of the property
883 fun short_name: String do
884 return self.intro.html_name
888 redef class MMLocalProperty
890 # Anchor of the property description in the module html file
891 fun html_anchor: String
893 return "PROP_{local_class}_
{cmangle(name)}"
896 redef fun json_entry(dctx) do
897 return "\
{txt:\"{qualified_name}\
",url:\"{local_class.html_name}.html
#{html_anchor}\"\},"
900 redef fun qualified_name
do
901 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
904 fun html_open_link
(dctx
: DocContext): String
906 if not require_doc
(dctx
) then print
"not required {self}"
907 var title
= "{html_name}{signature.to_s}"
908 if short_doc
!= " " then
909 title
+= " #{short_doc}"
911 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">"
914 fun html_name
: String
916 return self.name
.to_s
.html_escape
919 redef fun html_link
(dctx
)
921 if not require_doc
(dctx
) then print
"not required {self}"
922 var title
= "{html_name}{signature.to_s}"
923 if short_doc
!= " " then
924 title
+= " #{short_doc}"
926 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
929 fun html_link_special
(dctx
: DocContext, lc
: MMLocalClass): String
931 if not require_doc
(dctx
) then print
"not required {self}"
932 var title
= "{html_name}{signature_for(lc.get_type)}"
933 if short_doc
!= " " then
934 title
+= " #{short_doc}"
936 return "<a href=\"{lc.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
939 # Kind of property (fun, attr, etc.)
940 fun kind
: String is abstract
947 else if global
.intro
== self then
950 return global
.intro
.short_doc
957 if n
== null or not n
isa APropdef then
964 if d
.n_comment
.is_empty
then
971 # The most specific module in the nesting hierarchy that exports the intro of self
972 fun intro_module
: MMModule
974 var m
= global
.intro
.mmmodule
975 var mo
= m
.direct_owner
976 while mo
!= null and mo
.visibility_for
(m
) >= 2 do
983 # Is the intro of self exported by the top-level module ?
984 fun is_toplevel
: Bool
987 return m
== m
.toplevel_owner
990 # Return true if the global property must be documented according to the visibility configured
991 fun require_doc
(dctx
: DocContext): Bool
993 if global
.visibility_level
== 3 and not dctx
.with_private
then return false # Private
994 if dctx
.public_only
then
996 if m
!= m
.toplevel_owner
then return false # Unexported
1001 # Document the global property in the global class lc
1002 fun full_documentation
(dctx
: DocContext, lc
: MMLocalClass)
1004 var visibility
: String
1005 if global
.visibility_level
== 1 then
1006 visibility
= "public"
1007 else if global
.visibility_level
== 2 then
1008 visibility
= "protected"
1009 else if global
.visibility_level
== 3 then
1010 visibility
= "private"
1015 var intro_class
= global
.intro
.local_class
1016 var is_redef
= local_class
.global
!= intro_class
.global
or local_class
.mmmodule
.toplevel_owner
!= intro_class
.mmmodule
.toplevel_owner
1018 dctx
.add
("<article id=\"{html_anchor}\
" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\
">\n")
1019 dctx
.add
("<h3 class=\"signature\
">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1020 dctx
.add
("<div class=\"info\
">\n")
1021 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
1026 if not is_toplevel
then
1027 dctx
.add
("(unexported) ")
1029 if global
.visibility_level
== 2 then
1030 dctx
.add
("protected ")
1031 else if global
.visibility_level
== 3 then
1032 dctx
.add
("private ")
1035 dctx
.add
(" {intro_class.mmmodule.toplevel_owner.name}")
1036 if intro_class
.global
== lc
.global
then
1037 dctx
.add
("::{lc.name}")
1039 dctx
.add
("::{mmmodule[intro_class.global].html_link(dctx)}")
1042 dctx
.add
("::{mmmodule[intro_class.global][global].html_link(dctx)}")
1044 dctx
.add
("::{html_name}")
1048 dctx
.add
("<div class=\"description\
">")
1050 # Collect all refinement of the global property in the same global property
1051 var lps
= new Array[MMLocalProperty]
1052 for l
in prhe
.greaters_and_self
do
1056 var introdoc
= false
1057 if global
.intro
.doc
!= null then
1059 if lp
.doc
== null then introdoc
= true
1063 dctx
.add
("<pre>{global.intro.doc.to_html}</pre>")
1066 var tlmods
= new Array[MMModule]
1068 var bm
= lp
.mmmodule
.toplevel_owner
1069 var lcm
= lc
.global
.intro
.mmmodule
1070 if lcm
.mhe
< lp
.mmmodule
then bm
= lcm
.toplevel_owner
1071 if not tlmods
.has
(bm
) then tlmods
.add
(bm
)
1075 # Document the top level property for the current top level module
1077 if tm
.global_classes
.has
(lc
.global
) then
1078 tlp
= tm
[lc
.global
][self.global
]
1080 else if tm
.global_classes
.has
(self.local_class
.global
) then
1081 # Self is the inherited property. Process it
1082 tlp
= tm
[self.local_class
.global
][self.global
]
1085 # We skip this module since the props defined by the module is
1089 var tlcm
= lc
.global
.intro
.mmmodule
.toplevel_owner
1090 if not tlcm
.mhe
<= tm
then
1091 dctx
.add
("<h4>In module {tm.html_link(dctx)} :</h4>")
1094 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
1097 if doc
!= null and (not introdoc
or global
.intro
.doc
!= doc
) then
1098 dctx
.add
("<pre>{doc.to_html}</pre>")
1101 if tlp
.local_class
.global
!= lc
.global
then
1102 dctx
.add
("inherited from {tlp.local_class.html_link(dctx)} ")
1104 if tm
!= tlp
.mmmodule
then
1105 dctx
.add
("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1114 dctx
.stage
(". previously defined by:")
1116 var tl
= lp
.mmmodule
.toplevel_owner
1117 if tl
!= tm
then continue
1118 if lp
== tlp
then continue
1119 dctx
.add
(" {lp.mmmodule.html_link(dctx)}")
1120 if lp
.local_class
.global
!= lc
.global
then
1121 dctx
.add
(" for {lp.local_class.html_link(dctx)} ")
1131 #if doc != null and (not introdoc or global.intro.doc != doc) then
1132 # dctx.add("<pre>{doc.to_html}</pre>")
1139 dctx
.add
("</article>")
1142 redef class MMMethod
1143 redef fun kind
do return if global
.is_init
then "init" else "fun"
1145 redef class MMAttribute
1146 redef fun kind
do return "var"
1148 redef class MMTypeProperty
1149 redef fun kind
do return "type"
1152 redef class MMSrcModule
1166 if n
.n_moduledecl
== null then
1169 var np
= n
.n_moduledecl
1174 if d
.n_comment
.is_empty
then
1183 # Html transcription of the doc
1186 var res
= new Buffer
1187 for c
in n_comment
do
1188 res
.append
(c
.text
.substring_from
(1))
1190 return res
.to_s
.html_escape
1193 # Oneliner transcription of the doc
1196 return n_comment
.first
.text
.substring_from
(1).html_escape
1200 redef class MMLocalClass
1203 # Anchor of the class description in the module html file
1204 fun html_anchor
: String do return "CLASS_{self}"
1206 fun html_name
: String do return "{self}"
1208 redef fun html_link
(dctx
)
1210 if not require_doc
(dctx
) then print
"{dctx.filename}: not required {self}"
1211 return "<a href=\"{html_name}.html\
" title=\"{short_doc}\
">{self}</a>"
1214 redef fun json_entry
(dctx
) do
1215 return "\{txt:\"{qualified_name}\",url
:\
"{html_name}.html\"\
},"
1218 redef fun qualified_name do
1219 return "{intro_module.qualified_name}::{html_name}"
1222 redef fun short_doc do return global.intro.short_doc
1224 redef fun doc do return global.intro.doc
1228 if global.is_interface then
1230 else if global.is_abstract then
1231 return "abstract class"
1232 else if global.is_enum then
1239 # The most specific module in the nesting hierarchy that exports the intro of self
1240 fun intro_module: MMModule
1242 var m = global.intro.mmmodule
1243 var mo = m.direct_owner
1244 while mo != null and mo.visibility_for(m) >= 2 do
1251 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1253 if p.local_class.global != self.global then
1254 if p.global.intro.local_class.name == "Object".to_symbol then return
1255 if p.global.is_init or p isa MMTypeProperty then
1256 dctx.add("<li
class='inherit'><span title
='Inherited'>H
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1258 dctx.add("<li
class='inherit'><span title
='Inherited'>H
</span
> 
;{p.html_link(dctx)}</li
>\n
")
1260 else if p.global.intro.local_class.global == self.global then
1261 dctx.add("<li
class='intro'><span title
='Introduced'>I
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1263 dctx.add("<li
class='redef'><span title
='Redefined'>R
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1267 # Return true if the global class must be documented according to the visibility configured
1268 fun require_doc(dctx: DocContext): Bool
1270 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1271 if dctx.public_only then
1272 var m = intro_module
1273 if m != m.toplevel_owner then return false # Unexported
1278 # Fill the body for the page associated to the global class
1279 fun file_page_doc(dctx: DocContext)
1281 dctx.add("<div
class=\
"menu\">\n
")
1283 var props = new Array[MMLocalProperty]
1284 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1285 var inhs = new Array[MMLocalClass]
1286 for g in global_properties do
1288 if not p.require_doc(dctx) then continue
1289 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1292 var lc = mmmodule[p.local_class.global]
1293 if inh.has_key(lc) then
1304 dctx.add("<nav
class=\
"properties filterable\">\n
")
1305 dctx.add("<h3
>Properties</h3
>\n
")
1307 dctx.stage("<h4
>Virtual Types</h4
>\n
<ul
>\n
")
1309 if p isa MMTypeProperty then
1313 dctx.stage("</ul
>\n
")
1316 dctx.stage("<h4
>Constructors</h4
>\n
<ul
>\n
")
1318 if p.global.is_init_for(self) then
1322 dctx.stage("</ul
>\n
")
1325 dctx.stage("<h4
>Methods</h4
>\n
<ul
>\n
")
1327 if not p.global.is_init and p isa MMMethod then
1331 dctx.stage("</ul
>\n
")
1333 dctx.add("</nav
>\n
")
1335 dctx.add("<nav
class=\
"inheritance filterable\">\n
")
1336 dctx.add("<h3
>Inheritance</h3
>\n
")
1337 dctx.add("<h4
>Superclasses</h4
>\n
<ul
>\n
")
1338 for lc in cshe.linear_extension do
1339 if lc == self then continue
1340 if not lc.require_doc(dctx) then continue
1341 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1344 if cshe.smallers.length == 0 then
1345 dctx.add("<h4
>No Known Subclasses</h4
>\n
")
1346 else if cshe.smallers.length <= 100 then
1347 dctx.add("<h4
>Subclasses</h4
>\n
")
1349 for lc in cshe.smallers do
1350 if not lc.require_doc(dctx) then continue
1351 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1354 else if cshe.direct_smallers.length <= 100 then
1355 dctx.add("<h4
>Direct Subclasses Only</h4
>\n
<ul
>\n
")
1356 for lc in cshe.direct_smallers do
1357 if not lc.require_doc(dctx) then continue
1358 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1362 dctx.add("<h4
>Too much
Subclasses to list
</h4
>\n
")
1364 dctx.add("</nav
>\n
")
1366 dctx.add("</div
>\n
")
1369 dctx.add("<div
class=\
"content\">\n
")
1370 dctx.add("<h1
>{name}</h1
>\n
")
1371 dctx.add("<div
class='subtitle'>")
1372 if global.visibility_level == 2 then
1374 else if global.visibility_level == 3 then
1375 dctx.add("private ")
1376 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1377 dctx.add("(unexported
) ")
1379 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div
>")
1381 dctx.add("<section
class=\
"description\">\n
")
1384 dctx.add("<pre
>{doc.to_html}</pre
>\n
")
1387 var cla = new HashSet[MMLocalClass]
1388 var sm = new HashSet[MMLocalClass]
1389 var sm2 = new HashSet[MMLocalClass]
1391 while cla.length + sm.length < 10 and sm.length > 0 do
1395 sm2.add_all(x.cshe.direct_smallers)
1401 cla.add_all(cshe.greaters_and_self)
1404 var name = "class_
{name}"
1405 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
")
1408 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
1410 op.append("\
"{c.name}\"[URL=\
"{c.html_name}.html\"];\n
")
1412 for c2 in c.cshe.direct_greaters do
1413 if not cla.has(c2) then continue
1414 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
1416 if not c.cshe.direct_smallers.is_empty then
1418 for c2 in c.cshe.direct_smallers do
1419 if cla.has(c2) then others = false
1422 op.append("\
"{c.name}...\"[label=\
"\"];\n
")
1423 op.append("\
"{c.name}...\"->\
"{c.name}\"[style
=dotted
];\n
")
1428 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph
for class {name}")
1431 var mods = new Array[MMModule]
1432 mods.add(global.intro.mmmodule.toplevel_owner)
1433 for lc in crhe.greaters do
1434 if not lc isa MMSrcLocalClass then continue
1435 var m = lc.mmmodule.toplevel_owner
1436 if not mods.has(m) then mods.add(m)
1440 if m == global.intro.mmmodule.toplevel_owner then
1441 dctx.add("<p
>Introduced by
{m.html_link(dctx)}")
1443 dctx.add("<p
>Refined by
{m.html_link(dctx)}")
1446 dctx.stage(". Definition in:")
1447 for lc in crhe.greaters do
1448 if lc.mmmodule.toplevel_owner != m then continue
1449 dctx.add(" {lc.mmmodule.html_link(dctx)} ")
1450 assert lc isa MMSrcLocalClass
1453 dctx.show_source(n.location)
1459 dctx.add("</section
>\n
")
1462 dctx.stage("<section
class=\
"types\">\n
")
1463 dctx.stage("<h2
>Formal and Virtual Types</h2
>\n
")
1464 for i in [0..arity[ do
1465 var f = get_formal(i)
1466 f.full_documentation(dctx, self)
1469 if not p isa MMTypeProperty then continue
1470 p.full_documentation(dctx, self)
1472 dctx.stage("</section
>\n
")
1476 dctx.stage("<section
class=\
"constructors\">\n
")
1477 dctx.stage("<h2
class=\
"section-header\">Constructors</h2
>\n
")
1479 if not p.global.is_init_for(self) then continue
1480 p.full_documentation(dctx, self)
1482 dctx.stage("</section
>\n
")
1486 dctx.stage("<section
class=\
"methods\">\n
")
1487 dctx.stage("<h2
class=\
"section-header\">Methods</h2
>\n
")
1489 if p.global.is_init then continue
1490 if p.local_class.global != self.global then continue
1491 if not p isa MMMethod then continue
1492 p.full_documentation(dctx, self)
1494 if not inhs.is_empty then
1496 dctx.stage("<h3
>Inherited Methods</h3
>\n
")
1499 dctx.stage("<p
>Defined in {lc.html_link(dctx)}:")
1501 if p.global.is_init then continue
1502 if not p isa MMMethod then continue
1503 dctx.add(" {p.html_link(dctx)}")
1510 dctx.add("</section
>\n
")
1512 dctx.add("</div
> <!-- end class {name} -->\n
")
1516 redef class MMSrcLocalClass
1522 else if global.intro == self then
1525 var bc = global.intro
1533 if not n isa AStdClassdef then
1540 if d.n_comment.is_empty then
1548 redef class MMSignature
1549 # Htlm transcription of the signature (with nested links)
1550 fun to_html(dctx: DocContext, with_closure: Bool): String
1552 var res = new Buffer
1555 for i in [0..arity[ do
1556 if i > 0 then res.append(", ")
1557 res.append(self.params[i].name.to_s)
1559 res.append(self[i].html_link(dctx))
1560 if self.vararg_rank == i then
1566 if return_type != null then
1568 res.append(return_type.html_link(dctx))
1570 if with_closure then
1571 for c in closures do
1573 if c.is_optional then res.append("[")
1574 if c.is_break then res.append("break ")
1575 res.append("!{c.name}")
1576 res.append(c.signature.to_html(dctx, false))
1577 if c.is_optional then res.append("]")
1585 # Htlm transcription of the type (with nested links)
1586 fun html_link(dctx: DocContext): String do return to_s
1589 redef class MMTypeSimpleClass
1590 redef fun html_link(dctx) do return local_class.html_link(dctx)
1593 redef class MMTypeGeneric
1594 redef fun html_link(dctx)
1596 var res = new Buffer
1597 res.append(local_class.html_link(dctx))
1599 res.append(params[0].html_link(dctx))
1600 for i in [1..params.length[ do
1602 res.append(params[i].html_link(dctx))
1609 redef class MMTypeFormalParameter
1610 fun html_anchor: String
1612 return "FT_{local_class}_
{cmangle(name)}"
1614 redef fun html_link(dctx)
1616 return "<a href
=\
"#{html_anchor}\">{name}</a
>"
1618 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1620 dctx.add("<article id
=\
"{html_anchor}\">\n
")
1621 dctx.add("<h3
class=\
"signature\">{name}: {bound.html_link(dctx)}</h3
>\n
")
1622 dctx.add("<div
class=\
"info\">")
1623 dctx.add("formal generic
type")
1625 dctx.add("</article
>")
1629 redef class MMNullableType
1630 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1633 redef class MMVirtualType
1634 redef fun html_link(dctx) do return property.html_link(dctx)
1637 var c = new DocContext