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 if short_doc
== " " then
469 return "<a href=\"{html_name}.html\
"\">{self}</a
>"
471 return "<a href
=\
"{html_name}.html\" title
=\
"{short_doc}\">{self}</a
>"
475 redef fun json_entry(dctx) do
476 return "\
{txt:\"{self.qualified_name}\
",url:\"{html_name}.html\
"\},"
479 redef fun qualified_name
do
480 var buffer
= new Buffer
481 for m
in mnhe
.smallers
do
482 buffer
.append
("{m.html_name}::")
484 buffer
.append
("{self.name}")
488 fun require_doc
(dctx
: DocContext): Bool
490 if dctx
.public_only
and not is_toplevel
then return false
494 # Return true if the module is a top-level owner or a top-level module
495 fun is_toplevel
: Bool
497 var pd
= directory
.parent
498 return pd
== null or (pd
.owner
== null and directory
.owner
== self)
501 # Element in the module nesting tree
502 fun mnhe
: PartialOrderElement[MMModule] do return mnhe_
.as(not null)
503 var mnhe_
: nullable PartialOrderElement[MMModule] = null
505 # Element in the top level module importation hierarchy
506 fun tmhe
: PartialOrderElement[MMModule] do return tmhe_
.as(not null)
507 var tmhe_
: nullable PartialOrderElement[MMModule] = null
509 fun toplevel_owner
: MMModule
513 var ds
= m
.mnhe
.direct_smallers
514 if ds
.length
== 0 then return m
515 if ds
.length
== 1 then m
= ds
.first
else abort
519 fun html_name
: String
524 fun direct_owner
: nullable MMModule
527 while d
.owner
== self do d
= d
.parent
.as(not null)
531 # Fill the body for the page associated to the module
532 fun file_page_doc
(dctx
: DocContext)
534 dctx
.add
("<div class=\"menu\
">\n")
536 var mods
= new Array[MMModule]
537 mods
= self.mhe
.greaters
.to_a
541 dctx
.stage
("<nav>\n")
542 dctx
.stage
("<h3>Module Hierarchy</h3>\n")
543 dctx
.stage
("<h4>All dependencies</h4>\n")
546 if not mod
.require_doc
(dctx
) then continue
547 if self.mnhe
<= mod
then continue # do not want nested stuff
548 if mod
.direct_owner
!= null and not mod
.direct_owner
.mnhe
<= self then continue # not in the right nesting
549 dctx
.add
("<li>{mod.html_link(dctx)}</li>")
551 dctx
.stage
("</ul>\n")
553 mods
= self.mhe
.smallers
.to_a
555 dctx
.stage
("<h4>All clients</h4>\n")
558 if not mod
.require_doc
(dctx
) then continue
559 if self.mnhe
<= mod
then continue # do not want nested stuff
560 if mod
.direct_owner
!= null and not mod
.direct_owner
.mnhe
<= self then continue # not in the right nesting
561 dctx
.add
("<li>{mod.html_link(dctx)}</li>")
563 dctx
.stage
("</ul>\n")
564 dctx
.stage
("</nav>\n")
567 if not dctx
.public_only
then
568 mods
= self.mnhe
.direct_greaters
.to_a
571 dctx
.stage
("<nav>\n")
572 dctx
.stage
("<h3>Nested Modules</h3><ul>\n")
574 if not mod
.require_doc
(dctx
) then continue
575 dctx
.add
("<li>{mod.html_link(dctx)}</li>")
577 dctx
.stage
("</ul></nav>\n")
581 dctx
.add
("</div>") # metadata
583 dctx
.add
("<div class=\"content\
">\n")
584 dctx
.add
("<h1>{name}</h1>\n")
585 dctx
.add
("<div class='subtitle'>module ")
586 for m
in mnhe
.smallers
do
587 dctx
.add
("{m.html_link(dctx)}::")
589 dctx
.add
("{self.name}</div>\n")
591 dctx
.add
("<section class='description'>\n")
595 dctx
.add
("<div id=\"description\
">\n")
596 dctx
.add
("<pre>{doc.to_html}</pre>\n")
601 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")
602 var ms
= new Array[nullable MMModule]
604 var m0
: nullable MMModule = self
610 var cla
= new HashSet[MMModule]
612 for m0
in self.mhe
.greaters
do
613 if not m0
.require_doc
(dctx
) then continue
614 if self.visibility_for
(m0
) <= 1 then continue # private or hidden
615 if self.mnhe
<= m0
then continue # do not want nested stuff
616 if m0
.direct_owner
!= null and not m0
.direct_owner
.mnhe
<= self then continue # not in the right nesting
619 for m0
in self.mhe
.smallers
do
620 if not m0
.require_doc
(dctx
) then continue
621 if m0
.visibility_for
(self) <= 1 then continue # private or hidden
622 if m0
.direct_owner
!= null and not m0
.direct_owner
.mnhe
<= self then continue # not in the right nesting
625 for m0
in self.mnhe
.smallers
do
631 op
.append
("subgraph \"cluster_
{m0.name}\
"\{\n")
634 if c
.direct_owner
!= m0
then continue
636 op
.append
("\"{c.name}\
"[shape=box,margin=0.03];\n")
638 op
.append
("\"{c.name}\
"[URL=\"{c.html_name}.html\
"];\n")
642 op
.append
("\"{m0.name}\
"[URL=\"{m0.html_name}.html\
"];\n")
643 for c
in m0
.mhe
.direct_greaters
do
644 if not cla
.has
(c
) then continue
645 op
.append
("\"{m0.name}\
"->\"{c.name}\
";\n")
650 # Close the nesting subgraph
656 for c2
in c
.tmhe
.direct_greaters
do
657 if not cla
.has
(c2
) then continue
658 op
.append
("\"{c.name}\
"->\"{c2.name}\
";\n")
662 dctx
.gen_dot
(op
.to_s
, name
.to_s
, "Dependency graph for module {name}")
663 dctx
.add
("</section>")
665 var clas
= new Array[MMLocalClass]
666 var props
= new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
667 var gprops
= new Array[MMLocalProperty]
670 for g
in m
.global_classes
do
672 if not lc
.require_doc
(dctx
) then continue
673 var im
= g
.intro
.mmmodule
674 if self.visibility_for
(im
) <= 1 then continue # private import or invisible import
676 for lc2
in lc
.crhe
.greaters_and_self
do
677 if not lc2
isa MMSrcLocalClass then continue
678 if not self.mnhe
<= lc2
.mmmodule
then continue # not introduced/redefined here/stolen
681 if not keep
then continue
683 lc
.compute_super_classes
684 for gp
in lc
.global_properties
do
685 if self.visibility_for
(gp
.intro
.local_class
.mmmodule
) <= 1 then continue # private import or invisible import
687 var mp
= lp
.local_class
.mmmodule
688 if not self.mnhe
<= mp
then continue # not introduced/redefined here/stolen
690 if not lp
.require_doc
(dctx
) then continue
691 if props
.has_key
(lp
.global
) then
692 if not props
[lp
.global
].has
(lp
) then
693 props
[lp
.global
].add
(lp
)
696 props
[lp
.global
] = [lp
]
697 gprops
.add
(lp
.global
.intro
)
702 dctx
.add
("<section class=\"module\
">\n")
704 dctx
.stage
("<article class=\"classes filterable\
">\n")
705 dctx
.stage
("<h2>Classes</h2>\n")
709 if self.mnhe
<= lc
.global
.intro
.mmmodule
then
710 dctx
.add
("<li class='intro'><span title='introduced in this module'>I</span> ")
712 dctx
.add
("<li class='redef'><span title='refined in this module'>R</span> ")
714 dctx
.add
("{lc.html_link(dctx)}</li>\n")
716 dctx
.stage
("</ul></article>\n")
720 dctx
.stage
("<article class=\"properties filterable\
">\n")
721 dctx
.stage
("<h2>Properties</h2>\n")
728 if gp
.intro
isa MMAttribute then continue
730 var lpi
= self[gp
.intro
.local_class
.global
][gp
]
733 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")
736 dctx
.add
("<li class='intro'><span title='introduction in this module'>I</span> {lpi.html_name}")
737 dctx
.add
(" ({lpi.local_class})</li>\n")
739 if lps
.length
>= 1 then
742 dctx
.add
("<li class='redef'><span title='redefinition'>R</span> {lp.html_open_link(dctx)}{lp.html_name} ({lp.local_class})</a></li>")
746 dctx
.stage
("</ul></article>\n")
748 dctx
.add
("</section>\n")
752 # Fill the body for the page associated to the full index
753 fun file_index_page_doc
(dctx
: DocContext)
756 dctx
.add
("<h1>Full Index</h1>\n")
758 var clas
= new Array[MMLocalClass]
759 var props
= new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
760 var gprops
= new Array[MMLocalProperty]
761 var mods
= new Array[MMModule]
762 for m
in mhe
.greaters_and_self
do
763 if not m
.require_doc
(dctx
) then continue
766 for g
in global_classes
do
768 if not lc
.require_doc
(dctx
) then continue
770 for gp
in lc
.global_properties
do
772 if not lp
.require_doc
(dctx
) then continue
773 if props
.has_key
(lp
.global
) then
774 if not props
[lp
.global
].has
(lp
) then
775 props
[lp
.global
].add
(lp
)
778 props
[lp
.global
] = [lp
]
779 gprops
.add
(lp
.global
.intro
)
784 dctx
.stage
("<article class=\"modules filterable\
">\n")
785 dctx
.stage
("<h2>Modules</h2>\n")
789 dctx
.add
("<li>{m.html_link(dctx)}</li>")
791 dctx
.stage
("</ul></article>\n")
795 dctx
.stage
("<article class=\"classes filterable\
">\n")
796 dctx
.stage
("<h2>Classes</h2>\n")
800 dctx
.add
("<li>{lc.html_link(dctx)}</li>")
802 dctx
.stage
("</ul></article>\n")
806 dctx
.stage
("<article class=\"properties filterable\
">\n")
807 dctx
.stage
("<h2>Properties</h2>\n")
814 if gp
.intro
isa MMAttribute then continue
816 var lpi
= self[gp
.intro
.local_class
.global
][gp
]
819 dctx
.add
("<li class='intro'><span title='introduction'>I</span> {lpi.html_open_link(dctx)}{lpi.html_name} ({lpi.local_class})</a></li>\n")
820 if lps
.length
>= 1 then
823 dctx
.add
("<li class='redef'><span title='redefinition'>R</span> {lp.html_open_link(dctx)}{lp.html_name} ({lp.local_class})</a></li>\n")
827 dctx
.stage
("</ul></article>\n")
831 # Fill the quicksearch list JSON object
832 fun file_quicksearch_list_doc
(dctx
: DocContext)
834 var entities
= new HashMap[String, Array[MMEntity]]
835 var props
= new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
836 for m
in mhe
.greaters_and_self
do
837 if not m
.require_doc
(dctx
) then continue
838 var a
= new Array[MMEntity]
840 entities
[m
.html_name
] = a
842 for g
in global_classes
do
844 if not lc
.require_doc
(dctx
) then continue
845 var a
= new Array[MMEntity]
847 entities
[lc
.html_name
] = a
848 for gp
in lc
.global_properties
do
850 if not lp
.require_doc
(dctx
) then continue
851 if lp
.kind
== "var" then continue
852 if props
.has_key
(lp
.global
) then
853 if not props
[lp
.global
].has
(lp
) then
854 props
[lp
.global
].add
(lp
)
857 props
[lp
.global
] = [lp
]
863 entities
[k
.short_name
] = v
866 var keys
= entities
.keys
.to_a
867 var sorter
= new AlphaSorter[String]
871 dctx
.stage
("var entries = \{")
873 dctx
.add
("\"{key}\
": [")
874 for entity
in entities
[key
] do
875 dctx
.add
(entity
.json_entry
(dctx
))
884 redef class MMGlobalProperty
885 # Return the short name of the property
886 fun short_name
: String do
887 return self.intro
.html_name
891 redef class MMLocalProperty
893 # Anchor of the property description in the module html file
894 fun html_anchor
: String
896 return "PROP_{local_class}_{cmangle(name)}"
899 redef fun json_entry
(dctx
) do
900 return "\{txt:\"{qualified_name}\",url
:\
"{local_class.html_name}.html#{html_anchor}\"\
},"
903 redef fun qualified_name do
904 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
907 fun html_open_link(dctx: DocContext): String
909 if not require_doc(dctx) then print "not required
{self}"
910 var title = "{html_name}{signature.to_s}"
911 if short_doc != " 
;" then
912 title += " #{short_doc}"
914 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">"
917 fun html_name
: String
919 return self.name
.to_s
.html_escape
922 redef fun html_link
(dctx
)
924 if not require_doc
(dctx
) then print
"not required {self}"
925 var title
= "{html_name}{signature.to_s}"
926 if short_doc
!= " " then
927 title
+= " #{short_doc}"
929 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
932 fun html_link_special
(dctx
: DocContext, lc
: MMLocalClass): String
934 if not require_doc
(dctx
) then print
"not required {self}"
935 var title
= "{html_name}{signature_for(lc.get_type)}"
936 if short_doc
!= " " then
937 title
+= " #{short_doc}"
939 return "<a href=\"{lc.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
942 # Kind of property (fun, attr, etc.)
943 fun kind
: String is abstract
950 else if global
.intro
== self then
953 return global
.intro
.short_doc
960 if n
== null or not n
isa APropdef then
967 if d
.n_comment
.is_empty
then
974 # The most specific module in the nesting hierarchy that exports the intro of self
975 fun intro_module
: MMModule
977 var m
= global
.intro
.mmmodule
978 var mo
= m
.direct_owner
979 while mo
!= null and mo
.visibility_for
(m
) >= 2 do
986 # Is the intro of self exported by the top-level module ?
987 fun is_toplevel
: Bool
990 return m
== m
.toplevel_owner
993 # Return true if the global property must be documented according to the visibility configured
994 fun require_doc
(dctx
: DocContext): Bool
996 if global
.visibility_level
== 3 and not dctx
.with_private
then return false # Private
997 if dctx
.public_only
then
999 if m
!= m
.toplevel_owner
then return false # Unexported
1004 # Document the global property in the global class lc
1005 fun full_documentation
(dctx
: DocContext, lc
: MMLocalClass)
1007 var visibility
: String
1008 if global
.visibility_level
== 1 then
1009 visibility
= "public"
1010 else if global
.visibility_level
== 2 then
1011 visibility
= "protected"
1012 else if global
.visibility_level
== 3 then
1013 visibility
= "private"
1018 var intro_class
= global
.intro
.local_class
1019 var is_redef
= local_class
.global
!= intro_class
.global
or local_class
.mmmodule
.toplevel_owner
!= intro_class
.mmmodule
.toplevel_owner
1021 dctx
.add
("<article id=\"{html_anchor}\
" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\
">\n")
1022 dctx
.add
("<h3 class=\"signature\
">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1023 dctx
.add
("<div class=\"info\
">\n")
1024 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
1029 if not is_toplevel
then
1030 dctx
.add
("(unexported) ")
1032 if global
.visibility_level
== 2 then
1033 dctx
.add
("protected ")
1034 else if global
.visibility_level
== 3 then
1035 dctx
.add
("private ")
1038 dctx
.add
(" {intro_class.mmmodule.toplevel_owner.name}")
1039 if intro_class
.global
== lc
.global
then
1040 dctx
.add
("::{lc.name}")
1042 dctx
.add
("::{mmmodule[intro_class.global].html_link(dctx)}")
1045 dctx
.add
("::{mmmodule[intro_class.global][global].html_link(dctx)}")
1047 dctx
.add
("::{html_name}")
1051 dctx
.add
("<div class=\"description\
">")
1053 # Collect all refinement of the global property in the same global property
1054 var lps
= new Array[MMLocalProperty]
1055 for l
in prhe
.greaters_and_self
do
1059 var introdoc
= false
1060 if global
.intro
.doc
!= null then
1062 if lp
.doc
== null then introdoc
= true
1066 dctx
.add
("<pre>{global.intro.doc.to_html}</pre>")
1069 var tlmods
= new Array[MMModule]
1071 var bm
= lp
.mmmodule
.toplevel_owner
1072 var lcm
= lc
.global
.intro
.mmmodule
1073 if lcm
.mhe
< lp
.mmmodule
then bm
= lcm
.toplevel_owner
1074 if not tlmods
.has
(bm
) then tlmods
.add
(bm
)
1078 # Document the top level property for the current top level module
1080 if tm
.global_classes
.has
(lc
.global
) then
1081 tlp
= tm
[lc
.global
][self.global
]
1083 else if tm
.global_classes
.has
(self.local_class
.global
) then
1084 # Self is the inherited property. Process it
1085 tlp
= tm
[self.local_class
.global
][self.global
]
1088 # We skip this module since the props defined by the module is
1092 var tlcm
= lc
.global
.intro
.mmmodule
.toplevel_owner
1093 if not tlcm
.mhe
<= tm
then
1094 dctx
.add
("<h4>In module {tm.html_link(dctx)} :</h4>")
1097 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
1100 if doc
!= null and (not introdoc
or global
.intro
.doc
!= doc
) then
1101 dctx
.add
("<pre>{doc.to_html}</pre>")
1104 if tlp
.local_class
.global
!= lc
.global
then
1105 dctx
.add
("inherited from {tlp.local_class.html_link(dctx)} ")
1107 if tm
!= tlp
.mmmodule
then
1108 dctx
.add
("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1117 dctx
.stage
(". previously defined by:")
1119 var tl
= lp
.mmmodule
.toplevel_owner
1120 if tl
!= tm
then continue
1121 if lp
== tlp
then continue
1122 dctx
.add
(" {lp.mmmodule.html_link(dctx)}")
1123 if lp
.local_class
.global
!= lc
.global
then
1124 dctx
.add
(" for {lp.local_class.html_link(dctx)} ")
1134 #if doc != null and (not introdoc or global.intro.doc != doc) then
1135 # dctx.add("<pre>{doc.to_html}</pre>")
1142 dctx
.add
("</article>")
1145 redef class MMMethod
1146 redef fun kind
do return if global
.is_init
then "init" else "fun"
1148 redef class MMAttribute
1149 redef fun kind
do return "var"
1151 redef class MMTypeProperty
1152 redef fun kind
do return "type"
1155 redef class MMSrcModule
1169 if n
.n_moduledecl
== null then
1172 var np
= n
.n_moduledecl
1177 if d
.n_comment
.is_empty
then
1186 # Html transcription of the doc
1189 var res
= new Buffer
1190 for c
in n_comment
do
1191 res
.append
(c
.text
.substring_from
(1))
1193 return res
.to_s
.html_escape
1196 # Oneliner transcription of the doc
1199 return n_comment
.first
.text
.substring_from
(1).html_escape
1203 redef class MMLocalClass
1206 # Anchor of the class description in the module html file
1207 fun html_anchor
: String do return "CLASS_{self}"
1209 fun html_name
: String do return "{self}"
1211 redef fun html_link
(dctx
)
1213 if not require_doc
(dctx
) then print
"{dctx.filename}: not required {self}"
1214 if short_doc
== " " then
1215 return "<a href=\"{html_name}.html\
"\">{self}</a
>"
1217 return "<a href
=\
"{html_name}.html\" title
=\
"{short_doc}\">{self}</a
>"
1221 redef fun json_entry(dctx) do
1222 return "\
{txt:\"{qualified_name}\
",url:\"{html_name}.html\
"\},"
1225 redef fun qualified_name
do
1226 return "{intro_module.qualified_name}::{html_name}"
1229 redef fun short_doc
do return global
.intro
.short_doc
1231 redef fun doc
do return global
.intro
.doc
1235 if global
.is_interface
then
1237 else if global
.is_abstract
then
1238 return "abstract class"
1239 else if global
.is_enum
then
1246 # The most specific module in the nesting hierarchy that exports the intro of self
1247 fun intro_module
: MMModule
1249 var m
= global
.intro
.mmmodule
1250 var mo
= m
.direct_owner
1251 while mo
!= null and mo
.visibility_for
(m
) >= 2 do
1258 fun menu_link
(dctx
: DocContext, p
: MMLocalProperty)
1260 if p
.local_class
.global
!= self.global
then
1261 if p
.global
.intro
.local_class
.name
== "Object".to_symbol
then return
1262 if p
.global
.is_init
or p
isa MMTypeProperty then
1263 dctx
.add
("<li class='inherit'><span title='Inherited'>H</span> {p.html_link_special(dctx, self)}</li>\n")
1265 dctx
.add
("<li class='inherit'><span title='Inherited'>H</span> {p.html_link(dctx)}</li>\n")
1267 else if p
.global
.intro
.local_class
.global
== self.global
then
1268 dctx
.add
("<li class='intro'><span title='Introduced'>I</span> {p.html_link_special(dctx, self)}</li>\n")
1270 dctx
.add
("<li class='redef'><span title='Redefined'>R</span> {p.html_link_special(dctx, self)}</li>\n")
1274 # Return true if the global class must be documented according to the visibility configured
1275 fun require_doc
(dctx
: DocContext): Bool
1277 if global
.visibility_level
== 3 and not dctx
.with_private
then return false # Private
1278 if dctx
.public_only
then
1279 var m
= intro_module
1280 if m
!= m
.toplevel_owner
then return false # Unexported
1285 # Fill the body for the page associated to the global class
1286 fun file_page_doc
(dctx
: DocContext)
1288 dctx
.add
("<div class=\"menu\
">\n")
1290 var props
= new Array[MMLocalProperty]
1291 var inh
= new HashMap[MMLocalClass, Array[MMLocalProperty]]
1292 var inhs
= new Array[MMLocalClass]
1293 for g
in global_properties
do
1295 if not p
.require_doc
(dctx
) then continue
1296 if p
.local_class
.global
== global
or g
.is_init_for
(self) or p
isa MMTypeProperty then
1299 var lc
= mmmodule
[p
.local_class
.global
]
1300 if inh
.has_key
(lc
) then
1311 dctx
.add
("<nav class=\"properties filterable\
">\n")
1312 dctx
.add
("<h3>Properties</h3>\n")
1314 dctx
.stage
("<h4>Virtual Types</h4>\n<ul>\n")
1316 if p
isa MMTypeProperty then
1320 dctx
.stage
("</ul>\n")
1323 dctx
.stage
("<h4>Constructors</h4>\n<ul>\n")
1325 if p
.global
.is_init_for
(self) then
1329 dctx
.stage
("</ul>\n")
1332 dctx
.stage
("<h4>Methods</h4>\n<ul>\n")
1334 if not p
.global
.is_init
and p
isa MMMethod then
1338 dctx
.stage
("</ul>\n")
1340 dctx
.add
("</nav>\n")
1342 dctx
.add
("<nav class=\"inheritance filterable\
">\n")
1343 dctx
.add
("<h3>Inheritance</h3>\n")
1344 dctx
.add
("<h4>Superclasses</h4>\n<ul>\n")
1345 for lc
in cshe
.linear_extension
do
1346 if lc
== self then continue
1347 if not lc
.require_doc
(dctx
) then continue
1348 dctx
.add
("<li>{lc.html_link(dctx)}</li>\n")
1351 if cshe
.smallers
.length
== 0 then
1352 dctx
.add
("<h4>No Known Subclasses</h4>\n")
1353 else if cshe
.smallers
.length
<= 100 then
1354 dctx
.add
("<h4>Subclasses</h4>\n")
1356 for lc
in cshe
.smallers
do
1357 if not lc
.require_doc
(dctx
) then continue
1358 dctx
.add
("<li>{lc.html_link(dctx)}</li>\n")
1361 else if cshe
.direct_smallers
.length
<= 100 then
1362 dctx
.add
("<h4>Direct Subclasses Only</h4>\n<ul>\n")
1363 for lc
in cshe
.direct_smallers
do
1364 if not lc
.require_doc
(dctx
) then continue
1365 dctx
.add
("<li>{lc.html_link(dctx)}</li>\n")
1369 dctx
.add
("<h4>Too much Subclasses to list</h4>\n")
1371 dctx
.add
("</nav>\n")
1373 dctx
.add
("</div>\n")
1376 dctx
.add
("<div class=\"content\
">\n")
1377 dctx
.add
("<h1>{name}</h1>\n")
1378 dctx
.add
("<div class='subtitle'>")
1379 if global
.visibility_level
== 2 then
1381 else if global
.visibility_level
== 3 then
1382 dctx
.add
("private ")
1383 else if self.global
.intro
.mmmodule
.toplevel_owner
.visibility_for
(self.global
.intro
.mmmodule
) <= 1 then
1384 dctx
.add
("(unexported) ")
1386 dctx
.add
("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div>")
1388 dctx
.add
("<section class=\"description\
">\n")
1391 dctx
.add
("<pre>{doc.to_html}</pre>\n")
1394 var cla
= new HashSet[MMLocalClass]
1395 var sm
= new HashSet[MMLocalClass]
1396 var sm2
= new HashSet[MMLocalClass]
1398 while cla
.length
+ sm
.length
< 10 and sm
.length
> 0 do
1402 sm2
.add_all
(x
.cshe
.direct_smallers
)
1408 cla
.add_all
(cshe
.greaters_and_self
)
1411 var name
= "class_{name}"
1412 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")
1415 op
.append
("\"{c.name}\
"[shape=box,margin=0.03];\n")
1417 op
.append
("\"{c.name}\
"[URL=\"{c.html_name}.html\
"];\n")
1419 for c2
in c
.cshe
.direct_greaters
do
1420 if not cla
.has
(c2
) then continue
1421 op
.append
("\"{c.name}\
"->\"{c2.name}\
";\n")
1423 if not c
.cshe
.direct_smallers
.is_empty
then
1425 for c2
in c
.cshe
.direct_smallers
do
1426 if cla
.has
(c2
) then others
= false
1429 op
.append
("\"{c.name}...\
"[label=\"\
"];\n")
1430 op
.append
("\"{c.name}...\
"->\"{c.name}\
"[style=dotted];\n")
1435 dctx
.gen_dot
(op
.to_s
, name
.to_s
, "Inheritance graph for class {name}")
1438 var mods
= new Array[MMModule]
1439 mods
.add
(global
.intro
.mmmodule
.toplevel_owner
)
1440 for lc
in crhe
.greaters
do
1441 if not lc
isa MMSrcLocalClass then continue
1442 var m
= lc
.mmmodule
.toplevel_owner
1443 if not mods
.has
(m
) then mods
.add
(m
)
1447 if m
== global
.intro
.mmmodule
.toplevel_owner
then
1448 dctx
.add
("<p>Introduced by {m.html_link(dctx)}")
1450 dctx
.add
("<p>Refined by {m.html_link(dctx)}")
1453 dctx
.stage
(". Definition in:")
1454 for lc
in crhe
.greaters
do
1455 if lc
.mmmodule
.toplevel_owner
!= m
then continue
1456 dctx
.add
(" {lc.mmmodule.html_link(dctx)} ")
1457 assert lc
isa MMSrcLocalClass
1460 dctx
.show_source
(n
.location
)
1466 dctx
.add
("</section>\n")
1469 dctx
.stage
("<section class=\"types\
">\n")
1470 dctx
.stage
("<h2>Formal and Virtual Types</h2>\n")
1471 for i
in [0..arity
[ do
1472 var f
= get_formal
(i
)
1473 f
.full_documentation
(dctx
, self)
1476 if not p
isa MMTypeProperty then continue
1477 p
.full_documentation
(dctx
, self)
1479 dctx
.stage
("</section>\n")
1483 dctx
.stage
("<section class=\"constructors\
">\n")
1484 dctx
.stage
("<h2 class=\"section-header\
">Constructors</h2>\n")
1486 if not p
.global
.is_init_for
(self) then continue
1487 p
.full_documentation
(dctx
, self)
1489 dctx
.stage
("</section>\n")
1493 dctx
.stage
("<section class=\"methods\
">\n")
1494 dctx
.stage
("<h2 class=\"section-header\
">Methods</h2>\n")
1496 if p
.global
.is_init
then continue
1497 if p
.local_class
.global
!= self.global
then continue
1498 if not p
isa MMMethod then continue
1499 p
.full_documentation
(dctx
, self)
1501 if not inhs
.is_empty
then
1503 dctx
.stage
("<h3>Inherited Methods</h3>\n")
1506 dctx
.stage
("<p>Defined in {lc.html_link(dctx)}:")
1508 if p
.global
.is_init
then continue
1509 if not p
isa MMMethod then continue
1510 dctx
.add
(" {p.html_link(dctx)}")
1517 dctx
.add
("</section>\n")
1519 dctx
.add
("</div> <!-- end class {name} -->\n")
1523 redef class MMSrcLocalClass
1529 else if global
.intro
== self then
1532 var bc
= global
.intro
1540 if not n
isa AStdClassdef then
1547 if d
.n_comment
.is_empty
then
1555 redef class MMSignature
1556 # Htlm transcription of the signature (with nested links)
1557 fun to_html
(dctx
: DocContext, with_closure
: Bool): String
1559 var res
= new Buffer
1562 for i
in [0..arity
[ do
1563 if i
> 0 then res
.append
(", ")
1564 res
.append
(self.params
[i
].name
.to_s
)
1566 res
.append
(self[i
].html_link
(dctx
))
1567 if self.vararg_rank
== i
then
1573 if return_type
!= null then
1575 res
.append
(return_type
.html_link
(dctx
))
1577 if with_closure
then
1578 for c
in closures
do
1580 if c
.is_optional
then res
.append
("[")
1581 if c
.is_break
then res
.append
("break ")
1582 res
.append
("!{c.name}")
1583 res
.append
(c
.signature
.to_html
(dctx
, false))
1584 if c
.is_optional
then res
.append
("]")
1592 # Htlm transcription of the type (with nested links)
1593 fun html_link
(dctx
: DocContext): String do return to_s
1596 redef class MMTypeSimpleClass
1597 redef fun html_link
(dctx
) do return local_class
.html_link
(dctx
)
1600 redef class MMTypeGeneric
1601 redef fun html_link
(dctx
)
1603 var res
= new Buffer
1604 res
.append
(local_class
.html_link
(dctx
))
1606 res
.append
(params
[0].html_link
(dctx
))
1607 for i
in [1..params
.length
[ do
1609 res
.append
(params
[i
].html_link
(dctx
))
1616 redef class MMTypeFormalParameter
1617 fun html_anchor
: String
1619 return "FT_{local_class}_{cmangle(name)}"
1621 redef fun html_link
(dctx
)
1623 return "<a href=\"#{html_anchor}\">{name}</a>"
1625 fun full_documentation
(dctx
: DocContext, lc
: MMLocalClass)
1627 dctx
.add
("<article id=\"{html_anchor}\
">\n")
1628 dctx
.add
("<h3 class=\"signature\
">{name}: {bound.html_link(dctx)}</h3>\n")
1629 dctx
.add
("<div class=\"info\
">")
1630 dctx
.add
("formal generic type")
1632 dctx
.add
("</article>")
1636 redef class MMNullableType
1637 redef fun html_link
(dctx
) do return "nullable " + as_notnull
.html_link
(dctx
)
1640 redef class MMVirtualType
1641 redef fun html_link
(dctx
) do return property
.html_link
(dctx
)
1644 var c
= new DocContext