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>")
217 add
("<footer>{footer_text}</footer>")
218 add
("</body></html>\n")
219 write_to
("{dir}/index.html")
221 # Generate page for modules
222 for mod
in modules
do
223 if mod
== mainmod
then continue
224 assert mod
isa MMSrcModule
225 if not mod
.require_doc
(self) then continue
226 self.filename
= mod
.html_name
227 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"
229 add
("<!DOCTYPE html>")
230 add
("<html><head>{head}<title>{mod.name} module | {custom_title}</title></head><body>\n")
232 add
("<div class=\"page\
">")
233 mod
.file_page_doc
(self)
235 add
("<footer>{footer_text}</footer>")
236 add
("</body></html>\n")
237 write_to
("{dir}/{mod.html_name}.html")
240 # Generate pages for global classes
241 for c
in mainmod
.local_classes
do
242 if not c
.require_doc
(self) then continue
243 self.filename
= c
.html_name
244 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"
246 add
("<!DOCTYPE html>")
247 add
("<html><head>{head}<title>{c.name} class | {custom_title}</title></head><body>\n")
249 add
("<div class=\"page\
">")
250 c
.file_page_doc
(self)
252 add
("<footer>{footer_text}</footer>")
253 add
("</body></html>\n")
254 write_to
("{dir}/{c.html_name}.html")
257 self.filename
= "fullindex"
258 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"
260 add
("<!DOCTYPE html>")
261 add
("<html><head>{head}<title>Full Index | {custom_title}</title></head><body>\n")
263 add
("<div class=\"page\
">")
264 add
("<div class=\"content fullpage\
">")
265 mainmod
.file_index_page_doc
(self)
268 add
("<footer>{footer_text}</footer>")
269 add
("</body></html>\n")
270 write_to
("{dir}/full-index.html")
272 self.filename
= "quicksearch-list"
274 mainmod
.file_quicksearch_list_doc
(self)
275 write_to
("{dir}/quicksearch-list.js")
279 # Add a (source) link fo a given location
280 fun show_source
(l
: Location)
282 var s
= opt_source
.value
284 add
("in {l.file.filename.simplify_path}")
286 # THIS IS JUST UGLY ! (but there is no replace yet)
287 var x
= s
.split_with
("%f")
288 s
= x
.join
(l
.file
.filename
.simplify_path
)
289 x
= s
.split_with
("%l")
290 s
= x
.join
(l
.line_start
.to_s
)
291 x
= s
.split_with
("%L")
292 s
= x
.join
(l
.line_end
.to_s
)
293 add
(" (<a href=\"{s}\
">show code</a>)")
297 # Generate a clicable graphiz image using a dot content.
298 # `name' refer to the filename (without extension) and the id name of the map.
299 # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...)
300 fun gen_dot
(dot
: String, name
: String, alt
: String)
302 if opt_nodot
.value
then return
303 var f
= new OFStream.open
("{self.dir}/{name}.dot")
306 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 ; \}")
307 self.add
("<article class=\"graph\
"><img src=\"{name}.png\
" usemap=\"#{name}\" style=\"margin:auto\" alt=\"{alt}\"/></article>")
308 var fmap
= new IFStream.open
("{self.dir}/{name}.map")
309 self.add
(fmap
.read_all
)
318 option_context
.add_option
(opt_public
)
319 option_context
.add_option
(opt_private
)
320 option_context
.add_option
(opt_dir
)
321 option_context
.add_option
(opt_source
)
322 option_context
.add_option
(opt_nodot
)
323 option_context
.add_option
(opt_sharedir
)
324 option_context
.add_option
(opt_custom_title
)
325 option_context
.add_option
(opt_custom_menu_items
)
326 option_context
.add_option
(opt_custom_overview_text
)
327 option_context
.add_option
(opt_custom_footer_text
)
330 redef fun process_options
333 var d
= opt_dir
.value
334 if d
!= null then dir
= d
336 if not opt_nodot
.value
then
337 # Test if dot is runable
338 var res
= sys
.system
("sh -c dot </dev/null >/dev/null 2>&1")
340 stderr
.write
"--no-dot implied since `dot' is not available. Try to install graphviz.\n"
341 opt_nodot
.value
= true
345 sharedir
= opt_sharedir
.value
346 if sharedir
== null then
347 var dir
= once
("NIT_DIR".to_symbol
).environ
349 dir
= "{sys.program_name.dirname}/../share/nitdoc"
350 if dir
.file_exists
then sharedir
= dir
352 dir
= "{dir}/share/nitdoc"
353 if dir
.file_exists
then sharedir
= dir
355 if sharedir
== null then
356 fatal_error
(null, "Error: Cannot locate nitdoc shared files. Uses --sharedir or envvar NIT_DIR.")
358 dir
= "{sharedir.to_s}/scripts/js-facilities.js"
359 if sharedir
== null then
360 fatal_error
(null, "Error: Invalid nitdoc shared files. Check --sharedir or envvar NIT_DIR.")
366 redef fun handle_property_conflict
(lc
, impls
)
368 # THIS IS SO UGLY! See MMVirtualModule
369 if lc
.mmmodule
== self.mainmod
then
370 return # We just accept, so one in impls is arbitrary inherited
376 # A virtual module is used to work as an implicit main module that combine unrelated modules
377 # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled
378 class MMVirtualModule
380 init(ctx
: MMContext, mods
: Array[MMModule])
382 # We need to compute the whole metamodel since there is no mmbuilder to do it
383 super(" main".to_symbol
, mods
.first
.directory
, ctx
, new Location(null,0,0,0,0))
384 ctx
.add_module
(self, mods
)
386 self.add_super_module
(m
, 1)
388 self.import_global_classes
389 self.import_local_classes
390 for c
in self.local_classes
do
391 c
.compute_super_classes
393 for c
in self.local_classes
do
398 redef fun require_doc
(dctx
) do return false
401 # Conditionnal part of the text content of a DocContext
403 # Content of the current stage
404 readable var _content
: Array[String] = new Array[String]
406 # Is a normal string already added?
407 readable writable var _validate
: Bool = false
409 # Parent stage is any
410 readable var _parent
: nullable StageContext = null
412 init(parent
: nullable StageContext) do _parent
= parent
416 # Efficiently sort object with their to_s method
417 class AlphaSorter[E
: Object]
418 super AbstractSorter[E
]
419 redef fun compare
(a
, b
)
439 # Keep track of to_s values
440 var _dico
: HashMap[Object, String] = new HashMap[Object, String]
445 # Generalization of metamodel entities
448 fun html_link
(dctx
: DocContext): String is abstract
450 # Return a one liner description
451 fun short_doc
: String do return " "
453 # The doc node from the AST
454 # Return null is none
455 fun doc
: nullable ADoc do return null
457 # Return a JSON entry for quicksearch list
458 fun json_entry
(dctx
: DocContext): String is abstract
460 # Return the qualified name as string
461 fun qualified_name
: String is abstract
467 redef fun html_link
(dctx
) do
468 return "<a href=\"{html_name}.html\
" title=\"{short_doc}\
">{self}</a>"
471 redef fun json_entry
(dctx
) do
472 return "\{txt:\"{self.qualified_name}\",url
:\
"{html_name}.html\"\
},"
475 redef fun qualified_name do
476 var buffer = new Buffer
477 for m in mnhe.smallers do
478 buffer.append("{m.html_name}::")
480 buffer.append("{self.name}")
484 fun require_doc(dctx: DocContext): Bool
486 if dctx.public_only and not is_toplevel then return false
490 # Return true if the module is a top-level owner or a top-level module
491 fun is_toplevel: Bool
493 var pd = directory.parent
494 return pd == null or (pd.owner == null and directory.owner == self)
497 # Element in the module nesting tree
498 fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null)
499 var mnhe_: nullable PartialOrderElement[MMModule] = null
501 # Element in the top level module importation hierarchy
502 fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null)
503 var tmhe_: nullable PartialOrderElement[MMModule] = null
505 fun toplevel_owner: MMModule
509 var ds = m.mnhe.direct_smallers
510 if ds.length == 0 then return m
511 if ds.length == 1 then m = ds.first else abort
515 fun html_name: String
520 fun direct_owner: nullable MMModule
523 while d.owner == self do d = d.parent.as(not null)
527 # Fill the body for the page associated to the module
528 fun file_page_doc(dctx: DocContext)
530 dctx.add("<div
class=\
"menu\">\n
")
532 var mods = new Array[MMModule]
533 mods = self.mhe.greaters.to_a
537 dctx.stage("<nav
>\n
")
538 dctx.stage("<h3
>Module Hierarchy</h3
>\n
")
539 dctx.stage("<h4
>All dependencies
</h4
>\n
")
542 if not mod.require_doc(dctx) then continue
543 if self.mnhe <= mod then continue # do not want nested stuff
544 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
545 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
547 dctx.stage("</ul
>\n
")
549 mods = self.mhe.smallers.to_a
551 dctx.stage("<h4
>All clients
</h4
>\n
")
554 if not mod.require_doc(dctx) then continue
555 if self.mnhe <= mod then continue # do not want nested stuff
556 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
557 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
559 dctx.stage("</ul
>\n
")
560 dctx.stage("</nav
>\n
")
563 if not dctx.public_only then
564 mods = self.mnhe.direct_greaters.to_a
567 dctx.stage("<nav
>\n
")
568 dctx.stage("<h3
>Nested Modules</h3
><ul
>\n
")
570 if not mod.require_doc(dctx) then continue
571 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
573 dctx.stage("</ul
></nav
>\n
")
577 dctx.add("</div
>") # metadata
579 dctx.add("<div
class=\
"content\">\n
")
580 dctx.add("<h1
>{name}</h1
>\n
")
581 dctx.add("<div
class='subtitle'>module ")
582 for m in mnhe.smallers do
583 dctx.add("{m.html_link(dctx)}::")
585 dctx.add("{self.name}</div
>\n
")
587 dctx.add("<section
class='description'>\n
")
591 dctx.add("<div id
=\
"description\">\n
")
592 dctx.add("<pre
>{doc.to_html}</pre
>\n
")
597 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
")
598 var ms = new Array[nullable MMModule]
600 var m0: nullable MMModule = self
606 var cla = new HashSet[MMModule]
608 for m0 in self.mhe.greaters do
609 if not m0.require_doc(dctx) then continue
610 if self.visibility_for(m0) <= 1 then continue # private or hidden
611 if self.mnhe <= m0 then continue # do not want nested stuff
612 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
615 for m0 in self.mhe.smallers do
616 if not m0.require_doc(dctx) then continue
617 if m0.visibility_for(self) <= 1 then continue # private or hidden
618 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
621 for m0 in self.mnhe.smallers do
627 op.append("subgraph \
"cluster_{m0.name}\"\
{\n
")
630 if c.direct_owner != m0 then continue
632 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
634 op.append("\
"{c.name}\"[URL=\
"{c.html_name}.html\"];\n
")
638 op.append("\
"{m0.name}\"[URL=\
"{m0.html_name}.html\"];\n
")
639 for c in m0.mhe.direct_greaters do
640 if not cla.has(c) then continue
641 op.append("\
"{m0.name}\"->\
"{c.name}\";\n
")
646 # Close the nesting subgraph
652 for c2 in c.tmhe.direct_greaters do
653 if not cla.has(c2) then continue
654 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
658 dctx.gen_dot(op.to_s, name.to_s, "Dependency graph
for module {name}")
659 dctx.add("</section
>")
661 var clas = new Array[MMLocalClass]
662 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
663 var gprops = new Array[MMLocalProperty]
666 for g in m.global_classes do
668 if not lc.require_doc(dctx) then continue
669 var im = g.intro.mmmodule
670 if self.visibility_for(im) <= 1 then continue # private import or invisible import
672 for lc2 in lc.crhe.greaters_and_self do
673 if not lc2 isa MMSrcLocalClass then continue
674 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
677 if not keep then continue
679 lc.compute_super_classes
680 for gp in lc.global_properties do
681 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
683 var mp = lp.local_class.mmmodule
684 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
686 if not lp.require_doc(dctx) then continue
687 if props.has_key(lp.global) then
688 if not props[lp.global].has(lp) then
689 props[lp.global].add(lp)
692 props[lp.global] = [lp]
693 gprops.add(lp.global.intro)
698 dctx.add("<section
class=\
"module\">\n
")
700 dctx.stage("<article
class=\
"classes filterable\">\n
")
701 dctx.stage("<h2
>Classes</h2
>\n
")
705 if self.mnhe <= lc.global.intro.mmmodule then
706 dctx.add("<li
class='intro'><span title
='introduced in this module'>I
</span
> 
;")
708 dctx.add("<li
class='redef'><span title
='refined in this module'>R
</span
> 
;")
710 dctx.add("{lc.html_link(dctx)}</li
>\n
")
712 dctx.stage("</ul
></article
>\n
")
716 dctx.stage("<article
class=\
"properties filterable\">\n
")
717 dctx.stage("<h2
>Properties</h2
>\n
")
724 if gp.intro isa MMAttribute then continue
726 var lpi = self[gp.intro.local_class.global][gp]
729 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
")
732 dctx.add("<li
class='intro'><span title
='introduction in this module'>I
</span
> 
;{lpi.html_name}")
733 dctx.add(" 
;({lpi.local_class})</li
>\n
")
735 if lps.length >= 1 then
738 dctx.add("<li
class='redef'><span title
='redefinition'>R
</span
> 
;{lp.html_open_link(dctx)}{lp.html_name} 
;({lp.local_class})</a
></li
>")
742 dctx.stage("</ul
></article
>\n
")
744 dctx.add("</section
>\n
")
748 # Fill the body for the page associated to the full index
749 fun file_index_page_doc(dctx: DocContext)
752 dctx.add("<h1
>Full Index</h1
>\n
")
754 var clas = new Array[MMLocalClass]
755 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
756 var gprops = new Array[MMLocalProperty]
757 var mods = new Array[MMModule]
758 for m in mhe.greaters_and_self do
759 if not m.require_doc(dctx) then continue
762 for g in global_classes do
764 if not lc.require_doc(dctx) then continue
766 for gp in lc.global_properties do
768 if not lp.require_doc(dctx) then continue
769 if props.has_key(lp.global) then
770 if not props[lp.global].has(lp) then
771 props[lp.global].add(lp)
774 props[lp.global] = [lp]
775 gprops.add(lp.global.intro)
780 dctx.stage("<article
class=\
"modules filterable\">\n
")
781 dctx.stage("<h2
>Modules</h2
>\n
")
785 dctx.add("<li
>{m.html_link(dctx)}</li
>")
787 dctx.stage("</ul
></article
>\n
")
791 dctx.stage("<article
class=\
"classes filterable\">\n
")
792 dctx.stage("<h2
>Classes</h2
>\n
")
796 dctx.add("<li
>{lc.html_link(dctx)}</li
>")
798 dctx.stage("</ul
></article
>\n
")
802 dctx.stage("<article
class=\
"properties filterable\">\n
")
803 dctx.stage("<h2
>Properties</h2
>\n
")
810 if gp.intro isa MMAttribute then continue
812 var lpi = self[gp.intro.local_class.global][gp]
815 dctx.add("<li
class='intro'><span title
='introduction'>I
</span
> 
;{lpi.html_open_link(dctx)}{lpi.html_name} 
;({lpi.local_class})</a
></li
>\n
")
816 if lps.length >= 1 then
819 dctx.add("<li
class='redef'><span title
='redefinition'>R
</span
> 
;{lp.html_open_link(dctx)}{lp.html_name} 
;({lp.local_class})</a
></li
>\n
")
823 dctx.stage("</ul
></article
>\n
")
827 # Fill the quicksearch list JSON object
828 fun file_quicksearch_list_doc(dctx: DocContext)
830 var entities = new HashMap[String, Array[MMEntity]]
831 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
832 for m in mhe.greaters_and_self do
833 if not m.require_doc(dctx) then continue
834 var a = new Array[MMEntity]
836 entities[m.html_name] = a
838 for g in global_classes do
840 if not lc.require_doc(dctx) then continue
841 var a = new Array[MMEntity]
843 entities[lc.html_name] = a
844 for gp in lc.global_properties do
846 if not lp.require_doc(dctx) then continue
847 if lp.kind == "var" then continue
848 if props.has_key(lp.global) then
849 if not props[lp.global].has(lp) then
850 props[lp.global].add(lp)
853 props[lp.global] = [lp]
859 entities[k.short_name] = v
862 var keys = entities.keys.to_a
863 var sorter = new AlphaSorter[String]
867 dctx.stage("var entries
= \
{")
869 dctx.add("\
"{key}\": [")
870 for entity in entities[key] do
871 dctx.add(entity.json_entry(dctx))
880 redef class MMGlobalProperty
881 # Return the short name of the property
882 fun short_name: String do
883 return self.intro.html_name
887 redef class MMLocalProperty
889 # Anchor of the property description in the module html file
890 fun html_anchor: String
892 return "PROP_{local_class}_
{cmangle(name)}"
895 redef fun json_entry(dctx) do
896 return "\
{txt:\"{qualified_name}\
",url:\"{local_class.html_name}.html
#{html_anchor}\"\},"
899 redef fun qualified_name
do
900 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
903 fun html_open_link
(dctx
: DocContext): String
905 if not require_doc
(dctx
) then print
"not required {self}"
906 var title
= "{html_name}{signature.to_s}"
907 if short_doc
!= " " then
908 title
+= " #{short_doc}"
910 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">"
913 fun html_name
: String
915 return self.name
.to_s
.html_escape
918 redef fun html_link
(dctx
)
920 if not require_doc
(dctx
) then print
"not required {self}"
921 var title
= "{html_name}{signature.to_s}"
922 if short_doc
!= " " then
923 title
+= " #{short_doc}"
925 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
928 fun html_link_special
(dctx
: DocContext, lc
: MMLocalClass): String
930 if not require_doc
(dctx
) then print
"not required {self}"
931 var title
= "{html_name}{signature_for(lc.get_type)}"
932 if short_doc
!= " " then
933 title
+= " #{short_doc}"
935 return "<a href=\"{lc.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
938 # Kind of property (fun, attr, etc.)
939 fun kind
: String is abstract
946 else if global
.intro
== self then
949 return global
.intro
.short_doc
956 if n
== null or not n
isa APropdef then
963 if d
.n_comment
.is_empty
then
970 # The most specific module in the nesting hierarchy that exports the intro of self
971 fun intro_module
: MMModule
973 var m
= global
.intro
.mmmodule
974 var mo
= m
.direct_owner
975 while mo
!= null and mo
.visibility_for
(m
) >= 2 do
982 # Is the intro of self exported by the top-level module ?
983 fun is_toplevel
: Bool
986 return m
== m
.toplevel_owner
989 # Return true if the global property must be documented according to the visibility configured
990 fun require_doc
(dctx
: DocContext): Bool
992 if global
.visibility_level
== 3 and not dctx
.with_private
then return false # Private
993 if dctx
.public_only
then
995 if m
!= m
.toplevel_owner
then return false # Unexported
1000 # Document the global property in the global class lc
1001 fun full_documentation
(dctx
: DocContext, lc
: MMLocalClass)
1003 var visibility
: String
1004 if global
.visibility_level
== 1 then
1005 visibility
= "public"
1006 else if global
.visibility_level
== 2 then
1007 visibility
= "protected"
1008 else if global
.visibility_level
== 3 then
1009 visibility
= "private"
1014 var intro_class
= global
.intro
.local_class
1015 var is_redef
= local_class
.global
!= intro_class
.global
or local_class
.mmmodule
.toplevel_owner
!= intro_class
.mmmodule
.toplevel_owner
1017 dctx
.add
("<article id=\"{html_anchor}\
" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\
">\n")
1018 dctx
.add
("<h3 class=\"signature\
">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1019 dctx
.add
("<div class=\"info\
">\n")
1020 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
1025 if not is_toplevel
then
1026 dctx
.add
("(unexported) ")
1028 if global
.visibility_level
== 2 then
1029 dctx
.add
("protected ")
1030 else if global
.visibility_level
== 3 then
1031 dctx
.add
("private ")
1034 dctx
.add
(" {intro_class.mmmodule.toplevel_owner.name}")
1035 if intro_class
.global
== lc
.global
then
1036 dctx
.add
("::{lc.name}")
1038 dctx
.add
("::{mmmodule[intro_class.global].html_link(dctx)}")
1041 dctx
.add
("::{mmmodule[intro_class.global][global].html_link(dctx)}")
1043 dctx
.add
("::{html_name}")
1047 dctx
.add
("<div class=\"description\
">")
1049 # Collect all refinement of the global property in the same global property
1050 var lps
= new Array[MMLocalProperty]
1051 for l
in prhe
.greaters_and_self
do
1055 var introdoc
= false
1056 if global
.intro
.doc
!= null then
1058 if lp
.doc
== null then introdoc
= true
1062 dctx
.add
("<pre>{global.intro.doc.to_html}</pre>")
1065 var tlmods
= new Array[MMModule]
1067 var bm
= lp
.mmmodule
.toplevel_owner
1068 var lcm
= lc
.global
.intro
.mmmodule
1069 if lcm
.mhe
< lp
.mmmodule
then bm
= lcm
.toplevel_owner
1070 if not tlmods
.has
(bm
) then tlmods
.add
(bm
)
1074 # Document the top level property for the current top level module
1076 if tm
.global_classes
.has
(lc
.global
) then
1077 tlp
= tm
[lc
.global
][self.global
]
1079 else if tm
.global_classes
.has
(self.local_class
.global
) then
1080 # Self is the inherited property. Process it
1081 tlp
= tm
[self.local_class
.global
][self.global
]
1084 # We skip this module since the props defined by the module is
1088 var tlcm
= lc
.global
.intro
.mmmodule
.toplevel_owner
1089 if not tlcm
.mhe
<= tm
then
1090 dctx
.add
("<h4>In module {tm.html_link(dctx)} :</h4>")
1093 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
1096 if doc
!= null and (not introdoc
or global
.intro
.doc
!= doc
) then
1097 dctx
.add
("<pre>{doc.to_html}</pre>")
1100 if tlp
.local_class
.global
!= lc
.global
then
1101 dctx
.add
("inherited from {tlp.local_class.html_link(dctx)} ")
1103 if tm
!= tlp
.mmmodule
then
1104 dctx
.add
("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1113 dctx
.stage
(". previously defined by:")
1115 var tl
= lp
.mmmodule
.toplevel_owner
1116 if tl
!= tm
then continue
1117 if lp
== tlp
then continue
1118 dctx
.add
(" {lp.mmmodule.html_link(dctx)}")
1119 if lp
.local_class
.global
!= lc
.global
then
1120 dctx
.add
(" for {lp.local_class.html_link(dctx)} ")
1130 #if doc != null and (not introdoc or global.intro.doc != doc) then
1131 # dctx.add("<pre>{doc.to_html}</pre>")
1138 dctx
.add
("</article>")
1141 redef class MMMethod
1142 redef fun kind
do return if global
.is_init
then "init" else "fun"
1144 redef class MMAttribute
1145 redef fun kind
do return "var"
1147 redef class MMTypeProperty
1148 redef fun kind
do return "type"
1151 redef class MMSrcModule
1165 if n
.n_moduledecl
== null then
1168 var np
= n
.n_moduledecl
1173 if d
.n_comment
.is_empty
then
1182 # Html transcription of the doc
1185 var res
= new Buffer
1186 for c
in n_comment
do
1187 res
.append
(c
.text
.substring_from
(1))
1189 return res
.to_s
.html_escape
1192 # Oneliner transcription of the doc
1195 return n_comment
.first
.text
.substring_from
(1).html_escape
1199 redef class MMLocalClass
1202 # Anchor of the class description in the module html file
1203 fun html_anchor
: String do return "CLASS_{self}"
1205 fun html_name
: String do return "{self}"
1207 redef fun html_link
(dctx
)
1209 if not require_doc
(dctx
) then print
"{dctx.filename}: not required {self}"
1210 return "<a href=\"{html_name}.html\
" title=\"{short_doc}\
">{self}</a>"
1213 redef fun json_entry
(dctx
) do
1214 return "\{txt:\"{qualified_name}\",url
:\
"{html_name}.html\"\
},"
1217 redef fun qualified_name do
1218 return "{intro_module.qualified_name}::{html_name}"
1221 redef fun short_doc do return global.intro.short_doc
1223 redef fun doc do return global.intro.doc
1227 if global.is_interface then
1229 else if global.is_abstract then
1230 return "abstract class"
1231 else if global.is_enum then
1238 # The most specific module in the nesting hierarchy that exports the intro of self
1239 fun intro_module: MMModule
1241 var m = global.intro.mmmodule
1242 var mo = m.direct_owner
1243 while mo != null and mo.visibility_for(m) >= 2 do
1250 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1252 if p.local_class.global != self.global then
1253 if p.global.intro.local_class.name == "Object".to_symbol then return
1254 if p.global.is_init or p isa MMTypeProperty then
1255 dctx.add("<li
class='inherit'><span title
='Inherited'>H
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1257 dctx.add("<li
class='inherit'><span title
='Inherited'>H
</span
> 
;{p.html_link(dctx)}</li
>\n
")
1259 else if p.global.intro.local_class.global == self.global then
1260 dctx.add("<li
class='intro'><span title
='Introduced'>I
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1262 dctx.add("<li
class='redef'><span title
='Redefined'>R
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1266 # Return true if the global class must be documented according to the visibility configured
1267 fun require_doc(dctx: DocContext): Bool
1269 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1270 if dctx.public_only then
1271 var m = intro_module
1272 if m != m.toplevel_owner then return false # Unexported
1277 # Fill the body for the page associated to the global class
1278 fun file_page_doc(dctx: DocContext)
1280 dctx.add("<div
class=\
"menu\">\n
")
1282 var props = new Array[MMLocalProperty]
1283 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1284 var inhs = new Array[MMLocalClass]
1285 for g in global_properties do
1287 if not p.require_doc(dctx) then continue
1288 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1291 var lc = mmmodule[p.local_class.global]
1292 if inh.has_key(lc) then
1303 dctx.add("<nav
class=\
"properties filterable\">\n
")
1304 dctx.add("<h3
>Properties</h3
>\n
")
1306 dctx.stage("<h4
>Virtual Types</h4
>\n
<ul
>\n
")
1308 if p isa MMTypeProperty then
1312 dctx.stage("</ul
>\n
")
1315 dctx.stage("<h4
>Constructors</h4
>\n
<ul
>\n
")
1317 if p.global.is_init_for(self) then
1321 dctx.stage("</ul
>\n
")
1324 dctx.stage("<h4
>Methods</h4
>\n
<ul
>\n
")
1326 if not p.global.is_init and p isa MMMethod then
1330 dctx.stage("</ul
>\n
")
1332 dctx.add("</nav
>\n
")
1334 dctx.add("<nav
class=\
"inheritance filterable\">\n
")
1335 dctx.add("<h3
>Inheritance</h3
>\n
")
1336 dctx.add("<h4
>Superclasses</h4
>\n
<ul
>\n
")
1337 for lc in cshe.linear_extension do
1338 if lc == self then continue
1339 if not lc.require_doc(dctx) then continue
1340 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1343 if cshe.smallers.length == 0 then
1344 dctx.add("<h4
>No Known Subclasses</h4
>\n
")
1345 else if cshe.smallers.length <= 100 then
1346 dctx.add("<h4
>Subclasses</h4
>\n
")
1348 for lc in cshe.smallers do
1349 if not lc.require_doc(dctx) then continue
1350 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1353 else if cshe.direct_smallers.length <= 100 then
1354 dctx.add("<h4
>Direct Subclasses Only</h4
>\n
<ul
>\n
")
1355 for lc in cshe.direct_smallers do
1356 if not lc.require_doc(dctx) then continue
1357 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1361 dctx.add("<h4
>Too much
Subclasses to list
</h4
>\n
")
1363 dctx.add("</nav
>\n
")
1365 dctx.add("</div
>\n
")
1368 dctx.add("<div
class=\
"content\">\n
")
1369 dctx.add("<h1
>{name}</h1
>\n
")
1370 dctx.add("<div
class='subtitle'>")
1371 if global.visibility_level == 2 then
1373 else if global.visibility_level == 3 then
1374 dctx.add("private ")
1375 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1376 dctx.add("(unexported
) ")
1378 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div
>")
1380 dctx.add("<section
class=\
"description\">\n
")
1383 dctx.add("<pre
>{doc.to_html}</pre
>\n
")
1386 var cla = new HashSet[MMLocalClass]
1387 var sm = new HashSet[MMLocalClass]
1388 var sm2 = new HashSet[MMLocalClass]
1390 while cla.length + sm.length < 10 and sm.length > 0 do
1394 sm2.add_all(x.cshe.direct_smallers)
1400 cla.add_all(cshe.greaters_and_self)
1403 var name = "class_
{name}"
1404 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
")
1407 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
1409 op.append("\
"{c.name}\"[URL=\
"{c.html_name}.html\"];\n
")
1411 for c2 in c.cshe.direct_greaters do
1412 if not cla.has(c2) then continue
1413 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
1415 if not c.cshe.direct_smallers.is_empty then
1417 for c2 in c.cshe.direct_smallers do
1418 if cla.has(c2) then others = false
1421 op.append("\
"{c.name}...\"[label=\
"\"];\n
")
1422 op.append("\
"{c.name}...\"->\
"{c.name}\"[style
=dotted
];\n
")
1427 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph
for class {name}")
1430 var mods = new Array[MMModule]
1431 mods.add(global.intro.mmmodule.toplevel_owner)
1432 for lc in crhe.greaters do
1433 if not lc isa MMSrcLocalClass then continue
1434 var m = lc.mmmodule.toplevel_owner
1435 if not mods.has(m) then mods.add(m)
1439 if m == global.intro.mmmodule.toplevel_owner then
1440 dctx.add("<p
>Introduced by
{m.html_link(dctx)}")
1442 dctx.add("<p
>Refined by
{m.html_link(dctx)}")
1445 dctx.stage(". Definition in:")
1446 for lc in crhe.greaters do
1447 if lc.mmmodule.toplevel_owner != m then continue
1448 dctx.add(" {lc.mmmodule.html_link(dctx)} ")
1449 assert lc isa MMSrcLocalClass
1452 dctx.show_source(n.location)
1458 dctx.add("</section
>\n
")
1461 dctx.stage("<section
class=\
"types\">\n
")
1462 dctx.stage("<h2
>Formal and Virtual Types</h2
>\n
")
1463 for i in [0..arity[ do
1464 var f = get_formal(i)
1465 f.full_documentation(dctx, self)
1468 if not p isa MMTypeProperty then continue
1469 p.full_documentation(dctx, self)
1471 dctx.stage("</section
>\n
")
1475 dctx.stage("<section
class=\
"constructors\">\n
")
1476 dctx.stage("<h2
class=\
"section-header\">Constructors</h2
>\n
")
1478 if not p.global.is_init_for(self) then continue
1479 p.full_documentation(dctx, self)
1481 dctx.stage("</section
>\n
")
1485 dctx.stage("<section
class=\
"methods\">\n
")
1486 dctx.stage("<h2
class=\
"section-header\">Methods</h2
>\n
")
1488 if p.global.is_init then continue
1489 if p.local_class.global != self.global then continue
1490 if not p isa MMMethod then continue
1491 p.full_documentation(dctx, self)
1493 if not inhs.is_empty then
1495 dctx.stage("<h3
>Inherited Methods</h3
>\n
")
1498 dctx.stage("<p
>Defined in {lc.html_link(dctx)}:")
1500 if p.global.is_init then continue
1501 if not p isa MMMethod then continue
1502 dctx.add(" {p.html_link(dctx)}")
1509 dctx.add("</section
>\n
")
1511 dctx.add("</div
> <!-- end class {name} -->\n
")
1515 redef class MMSrcLocalClass
1521 else if global.intro == self then
1524 var bc = global.intro
1532 if not n isa AStdClassdef then
1539 if d.n_comment.is_empty then
1547 redef class MMSignature
1548 # Htlm transcription of the signature (with nested links)
1549 fun to_html(dctx: DocContext, with_closure: Bool): String
1551 var res = new Buffer
1554 for i in [0..arity[ do
1555 if i > 0 then res.append(", ")
1556 res.append(self.params[i].name.to_s)
1558 res.append(self[i].html_link(dctx))
1559 if self.vararg_rank == i then
1565 if return_type != null then
1567 res.append(return_type.html_link(dctx))
1569 if with_closure then
1570 for c in closures do
1572 if c.is_optional then res.append("[")
1573 if c.is_break then res.append("break ")
1574 res.append("!{c.name}")
1575 res.append(c.signature.to_html(dctx, false))
1576 if c.is_optional then res.append("]")
1584 # Htlm transcription of the type (with nested links)
1585 fun html_link(dctx: DocContext): String do return to_s
1588 redef class MMTypeSimpleClass
1589 redef fun html_link(dctx) do return local_class.html_link(dctx)
1592 redef class MMTypeGeneric
1593 redef fun html_link(dctx)
1595 var res = new Buffer
1596 res.append(local_class.html_link(dctx))
1598 res.append(params[0].html_link(dctx))
1599 for i in [1..params.length[ do
1601 res.append(params[i].html_link(dctx))
1608 redef class MMTypeFormalParameter
1609 fun html_anchor: String
1611 return "FT_{local_class}_
{cmangle(name)}"
1613 redef fun html_link(dctx)
1615 return "<a href
=\
"#{html_anchor}\">{name}</a
>"
1617 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1619 dctx.add("<article id
=\
"{html_anchor}\">\n
")
1620 dctx.add("<h3
class=\
"signature\">{name}: {bound.html_link(dctx)}</h3
>\n
")
1621 dctx.add("<div
class=\
"info\">")
1622 dctx.add("formal generic
type")
1624 dctx.add("</article
>")
1628 redef class MMNullableType
1629 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1632 redef class MMVirtualType
1633 redef fun html_link(dctx) do return property.html_link(dctx)
1636 var c = new DocContext