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")
89 readable var _opt_overview_text
: OptionString = new OptionString("Text displayed as introduction of Overview page", "--overview-text")
90 readable var _opt_footer_text
: OptionString = new OptionString("Text displayed as footer of all pages", "--footer-text")
91 var sharedir
: nullable String
95 if self._opt_public
.value
== true then return true
99 fun with_private
: Bool
101 if self._opt_private
.value
== true then return true
105 # The current processed filename
108 # The main virtual module
109 var mainmod
: nullable MMVirtualModule
111 redef fun perform_work
(mods
)
113 mainmod
= new MMVirtualModule(self, mods
)
117 sys
.system
("cp -r '{sharedir.to_s}'/* {dir}/")
119 # Compute the set of direct owned nested modules
120 var owns
= new HashMap[MMModule, Array[MMModule]]
121 for mod
in modules
do
122 owns
[mod
] = new Array[MMModule]# [mod]
124 for mod
in modules
do
125 if mod
== mainmod
then continue
126 var d
= mod
.directory
129 if o
!= null and o
!= mod
then
133 if dp
== null or dp
== d
then break
138 # Builds the various module hierarchies
139 var mnh
= new PartialOrder[MMModule] # nested module hierarchy
140 var tmh
= new PartialOrder[MMModule] # top module import hierrchy
141 var ms
= mainmod
.mhe
.linear_extension
.reversed
143 if ms
== mainmod
then continue
144 m
.mnhe_
= mnh
.add
(m
, owns
[m
])
145 var pub
= new Array[MMModule]
146 for m2
in m
.mhe
.greaters
do
147 if m2
.toplevel_owner
!= m2
and m2
.toplevel_owner
!= m
.toplevel_owner
then continue
148 if m
.mnhe
<= m2
then continue
149 if m
.visibility_for
(m2
) <= 0 then
151 else if m
.visibility_for
(m2
) == 1 then
156 m
.tmhe_
= tmh
.add
(m
, pub
)
159 var head
= "<meta charset=\"utf-8\
">" +
160 "<script type=\"text
/javascript\
" src=\"scripts
/jquery-1
.7
.1.min
.js\
"></script>\n" +
161 "<script type=\"text
/javascript\
" src=\"quicksearch-list
.js\
"></script>\n" +
162 "<script type=\"text
/javascript\
" src=\"scripts
/js-facilities
.js\
"></script>\n" +
163 "<link rel=\"stylesheet\
" href=\"styles
/main
.css\
" type=\"text
/css\
" media=\"screen\
" />"
165 var action_bar
= "<header><nav class='main'><ul><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"
167 var overview_text
= ""
168 if self._opt_overview_text
.value
!= null then overview_text
= self._opt_overview_text
.value
.as(not null)
171 if self._opt_footer_text
.value
!= null then footer_text
= self._opt_footer_text
.value
.as(not null)
174 self.filename
= "index.html"
176 add
("<!DOCTYPE html>")
177 add
("<html><head>{head}<title>Index</title></head><body>\n")
179 add
("<div class=\"page\
">")
180 add
("<div class=\"content fullpage\
">")
181 add
("<h1>Modules</h1>\n<article class='overview'>{overview_text}<ul>")
182 var modss
= mainmod
.mhe
.greaters_and_self
.to_a
185 if not mod
.is_toplevel
then continue
186 if not mod
.require_doc
(self) then continue
187 assert mod
isa MMSrcModule
188 add
("<li>{mod.html_link(self)} {mod.short_doc}</li>")
194 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")
196 if not mod
.is_toplevel
then continue
197 if not mod
.require_doc
(self) then continue
198 op
.append
("\"{mod.name}\
"[URL=\"{mod.html_name}.html\
"];\n")
199 for mod2
in mod
.tmhe
.direct_greaters
do
200 if not modss
.has
(mod2
) then continue
201 op
.append
("\"{mod.name}\
"->\"{mod2.name}\
";\n")
205 self.gen_dot
(op
.to_s
, "dep", "Modules hierarchy")
206 add
("</article></div>")
207 add
("<div class='clear'></div>")
209 add
("<footer>{footer_text}</footer>")
210 add
("</body></html>\n")
211 write_to
("{dir}/index.html")
213 # Generate page for modules
214 for mod
in modules
do
215 if mod
== mainmod
then continue
216 assert mod
isa MMSrcModule
217 if not mod
.require_doc
(self) then continue
218 self.filename
= mod
.html_name
219 action_bar
= "<header><nav class='main'><ul><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"
221 add
("<!DOCTYPE html>")
222 add
("<html><head>{head}<title>Module {mod.name}</title></head><body>\n")
224 add
("<div class=\"page\
">")
225 mod
.file_page_doc
(self)
227 add
("<footer>{footer_text}</footer>")
228 add
("</body></html>\n")
229 write_to
("{dir}/{mod.html_name}.html")
232 # Generate pages for global classes
233 for c
in mainmod
.local_classes
do
234 if not c
.require_doc
(self) then continue
235 self.filename
= c
.html_name
236 action_bar
= "<header><nav class='main'><ul><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"
238 add
("<!DOCTYPE html>")
239 add
("<html><head>{head}<title>Class {c.name}</title></head><body>\n")
241 add
("<div class=\"page\
">")
242 c
.file_page_doc
(self)
244 add
("<footer>{footer_text}</footer>")
245 add
("</body></html>\n")
246 write_to
("{dir}/{c.html_name}.html")
249 self.filename
= "fullindex"
250 action_bar
= "<header><nav class='main'><ul><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"
252 add
("<!DOCTYPE html>")
253 add
("<html><head>{head}<title>Full Index</title></head><body>\n")
255 add
("<div class=\"page\
">")
256 add
("<div class=\"content fullpage\
">")
257 mainmod
.file_index_page_doc
(self)
260 add
("<footer>{footer_text}</footer>")
261 add
("</body></html>\n")
262 write_to
("{dir}/full-index.html")
264 self.filename
= "quicksearch-list"
266 mainmod
.file_quicksearch_list_doc
(self)
267 write_to
("{dir}/quicksearch-list.js")
271 # Add a (source) link fo a given location
272 fun show_source
(l
: Location)
274 var s
= opt_source
.value
276 add
("in {l.file.filename.simplify_path}")
278 # THIS IS JUST UGLY ! (but there is no replace yet)
279 var x
= s
.split_with
("%f")
280 s
= x
.join
(l
.file
.filename
.simplify_path
)
281 x
= s
.split_with
("%l")
282 s
= x
.join
(l
.line_start
.to_s
)
283 x
= s
.split_with
("%L")
284 s
= x
.join
(l
.line_end
.to_s
)
285 add
(" (<a href=\"{s}\
">show code</a>)")
289 # Generate a clicable graphiz image using a dot content.
290 # `name' refer to the filename (without extension) and the id name of the map.
291 # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...)
292 fun gen_dot
(dot
: String, name
: String, alt
: String)
294 if opt_nodot
.value
then return
295 var f
= new OFStream.open
("{self.dir}/{name}.dot")
298 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 ; \}")
299 self.add
("<article class=\"graph\
"><img src=\"{name}.png\
" usemap=\"#{name}\" style=\"margin:auto\" alt=\"{alt}\"/></article>")
300 var fmap
= new IFStream.open
("{self.dir}/{name}.map")
301 self.add
(fmap
.read_all
)
310 option_context
.add_option
(opt_public
)
311 option_context
.add_option
(opt_private
)
312 option_context
.add_option
(opt_dir
)
313 option_context
.add_option
(opt_source
)
314 option_context
.add_option
(opt_nodot
)
315 option_context
.add_option
(opt_sharedir
)
316 option_context
.add_option
(opt_overview_text
)
317 option_context
.add_option
(opt_footer_text
)
320 redef fun process_options
323 var d
= opt_dir
.value
324 if d
!= null then dir
= d
326 if not opt_nodot
.value
then
327 # Test if dot is runable
328 var res
= sys
.system
("sh -c dot </dev/null >/dev/null 2>&1")
330 stderr
.write
"--no-dot implied since `dot' is not available. Try to install graphviz.\n"
331 opt_nodot
.value
= true
335 sharedir
= opt_sharedir
.value
336 if sharedir
== null then
337 var dir
= "NIT_DIR".environ
339 dir
= "{sys.program_name.dirname}/../share/nitdoc"
340 if dir
.file_exists
then sharedir
= dir
342 dir
= "{dir}/share/nitdoc"
343 if dir
.file_exists
then sharedir
= dir
345 if sharedir
== null then
346 fatal_error
(null, "Error: Cannot locate nitdoc shared files. Uses --sharedir or envvar NIT_DIR.")
348 dir
= "{sharedir.to_s}/scripts/js-facilities.js"
349 if sharedir
== null then
350 fatal_error
(null, "Error: Invalid nitdoc shared files. Check --sharedir or envvar NIT_DIR.")
356 redef fun handle_property_conflict
(lc
, impls
)
358 # THIS IS SO UGLY! See MMVirtualModule
359 if lc
.mmmodule
== self.mainmod
then
360 return # We just accept, so one in impls is arbitrary inherited
366 # A virtual module is used to work as an implicit main module that combine unrelated modules
367 # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled
368 class MMVirtualModule
370 init(ctx
: MMContext, mods
: Array[MMModule])
372 # We need to compute the whole metamodel since there is no mmbuilder to do it
373 super(" main".to_symbol
, mods
.first
.directory
, ctx
, new Location(null,0,0,0,0))
374 ctx
.add_module
(self, mods
)
376 self.add_super_module
(m
, 1)
378 self.import_global_classes
379 self.import_local_classes
380 for c
in self.local_classes
do
381 c
.compute_super_classes
383 for c
in self.local_classes
do
388 redef fun require_doc
(dctx
) do return false
391 # Conditionnal part of the text content of a DocContext
393 # Content of the current stage
394 readable var _content
: Array[String] = new Array[String]
396 # Is a normal string already added?
397 readable writable var _validate
: Bool = false
399 # Parent stage is any
400 readable var _parent
: nullable StageContext = null
402 init(parent
: nullable StageContext) do _parent
= parent
406 # Efficiently sort object with their to_s method
407 class AlphaSorter[E
: Object]
408 super AbstractSorter[E
]
409 redef fun compare
(a
, b
)
429 # Keep track of to_s values
430 var _dico
: HashMap[Object, String] = new HashMap[Object, String]
435 # Generalization of metamodel entities
438 fun html_link
(dctx
: DocContext): String is abstract
440 # Return a one liner description
441 fun short_doc
: String do return " "
443 # The doc node from the AST
444 # Return null is none
445 fun doc
: nullable ADoc do return null
447 # Return a jason entry for quicksearch list JSON Object
448 fun json_entry
(dctx
: DocContext): String is abstract
450 # Return the qualified name as string
451 fun qualified_name
: String is abstract
457 redef fun html_link
(dctx
) do
458 return "<a href=\"{html_name}.html\
" title=\"{short_doc}\
">{self}</a>"
461 redef fun json_entry
(dctx
) do
462 return "\{txt:\"{self.qualified_name}\",url
:\
"{html_name}.html\"\
},"
465 redef fun qualified_name do
466 var buffer = new Buffer
467 for m in mnhe.smallers do
468 buffer.append("{m.html_name}::")
470 buffer.append("{self.name}")
474 fun require_doc(dctx: DocContext): Bool
476 if dctx.public_only and not is_toplevel then return false
480 # Return true if the module is a top-level owner or a top-level module
481 fun is_toplevel: Bool
483 var pd = directory.parent
484 return pd == null or (pd.owner == null and directory.owner == self)
487 # Element in the module nesting tree
488 fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null)
489 var mnhe_: nullable PartialOrderElement[MMModule] = null
491 # Element in the top level module importation hierarchy
492 fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null)
493 var tmhe_: nullable PartialOrderElement[MMModule] = null
495 fun toplevel_owner: MMModule
499 var ds = m.mnhe.direct_smallers
500 if ds.length == 0 then return m
501 if ds.length == 1 then m = ds.first else abort
505 fun html_name: String
510 fun direct_owner: nullable MMModule
513 while d.owner == self do d = d.parent.as(not null)
517 # Fill the body for the page associated to the module
518 fun file_page_doc(dctx: DocContext)
520 dctx.add("<div
class=\
"menu\">\n
")
522 var mods = new Array[MMModule]
523 mods = self.mhe.greaters.to_a
527 dctx.stage("<nav
>\n
")
528 dctx.stage("<h3
>Module Hierarchy</h3
>\n
")
529 dctx.stage("<h4
>All dependencies
</h4
>\n
")
532 if not mod.require_doc(dctx) then continue
533 if self.mnhe <= mod then continue # do not want nested stuff
534 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
535 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
537 dctx.stage("</ul
>\n
")
539 mods = self.mhe.smallers.to_a
541 dctx.stage("<h4
>All clients
</h4
>\n
")
544 if not mod.require_doc(dctx) then continue
545 if self.mnhe <= mod then continue # do not want nested stuff
546 if mod.direct_owner != null and not mod.direct_owner.mnhe <= self then continue # not in the right nesting
547 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
549 dctx.stage("</ul
>\n
")
550 dctx.stage("</nav
>\n
")
553 if not dctx.public_only then
554 mods = self.mnhe.direct_greaters.to_a
557 dctx.stage("<nav
>\n
")
558 dctx.stage("<h3
>Nested Modules</h3
><ul
>\n
")
560 if not mod.require_doc(dctx) then continue
561 dctx.add("<li
>{mod.html_link(dctx)}</li
>")
563 dctx.stage("</ul
></nav
>\n
")
567 dctx.add("</div
>") # metadata
569 dctx.add("<div
class=\
"content\">\n
")
570 dctx.add("<h1
>{name}</h1
>\n
")
571 dctx.add("<div
class='subtitle'>module ")
572 for m in mnhe.smallers do
573 dctx.add("{m.html_link(dctx)}::")
575 dctx.add("{self.name}</div
>\n
")
577 dctx.add("<section
class='description'>\n
")
581 dctx.add("<div id
=\
"description\">\n
")
582 dctx.add("<pre
>{doc.to_html}</pre
>\n
")
587 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
")
588 var ms = new Array[nullable MMModule]
590 var m0: nullable MMModule = self
596 var cla = new HashSet[MMModule]
598 for m0 in self.mhe.greaters do
599 if not m0.require_doc(dctx) then continue
600 if self.visibility_for(m0) <= 1 then continue # private or hidden
601 if self.mnhe <= m0 then continue # do not want nested stuff
602 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
605 for m0 in self.mhe.smallers do
606 if not m0.require_doc(dctx) then continue
607 if m0.visibility_for(self) <= 1 then continue # private or hidden
608 if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting
611 for m0 in self.mnhe.smallers do
617 op.append("subgraph \
"cluster_{m0.name}\"\
{\n
")
620 if c.direct_owner != m0 then continue
622 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
624 op.append("\
"{c.name}\"[URL=\
"{c.html_name}.html\"];\n
")
628 op.append("\
"{m0.name}\"[URL=\
"{m0.html_name}.html\"];\n
")
629 for c in m0.mhe.direct_greaters do
630 if not cla.has(c) then continue
631 op.append("\
"{m0.name}\"->\
"{c.name}\";\n
")
636 # Close the nesting subgraph
642 for c2 in c.tmhe.direct_greaters do
643 if not cla.has(c2) then continue
644 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
648 dctx.gen_dot(op.to_s, name.to_s, "Dependency graph
for module {name}")
649 dctx.add("</section
>")
651 var clas = new Array[MMLocalClass]
652 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
653 var gprops = new Array[MMLocalProperty]
656 for g in m.global_classes do
658 if not lc.require_doc(dctx) then continue
659 var im = g.intro.mmmodule
660 if self.visibility_for(im) <= 1 then continue # private import or invisible import
662 for lc2 in lc.crhe.greaters_and_self do
663 if not lc2 isa MMSrcLocalClass then continue
664 if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen
667 if not keep then continue
669 lc.compute_super_classes
670 for gp in lc.global_properties do
671 if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import
673 var mp = lp.local_class.mmmodule
674 if not self.mnhe <= mp then continue # not introduced/redefined here/stolen
676 if not lp.require_doc(dctx) then continue
677 if props.has_key(lp.global) then
678 if not props[lp.global].has(lp) then
679 props[lp.global].add(lp)
682 props[lp.global] = [lp]
683 gprops.add(lp.global.intro)
688 dctx.add("<section
class=\
"module\">\n
")
690 dctx.stage("<article
class=\
"classes filterable\">\n
")
691 dctx.stage("<h2
>Classes</h2
>\n
")
695 if self.mnhe <= lc.global.intro.mmmodule then
696 dctx.add("<li
class='intro'><span title
='introduced in this module'>I
</span
> 
;")
698 dctx.add("<li
class='redef'><span title
='refined in this module'>R
</span
> 
;")
700 dctx.add("{lc.html_link(dctx)}</li
>\n
")
702 dctx.stage("</ul
></article
>\n
")
706 dctx.stage("<article
class=\
"properties filterable\">\n
")
707 dctx.stage("<h2
>Properties</h2
>\n
")
714 if gp.intro isa MMAttribute then continue
716 var lpi = self[gp.intro.local_class.global][gp]
719 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
")
722 dctx.add("<li
class='intro'><span title
='introduction in this module'>I
</span
> 
;{lpi.html_name}")
723 dctx.add(" 
;({lpi.local_class})</li
>\n
")
725 if lps.length >= 1 then
728 dctx.add("<li
class='redef'><span title
='redefinition'>R
</span
> 
;{lp.html_open_link(dctx)}{lp.html_name} 
;({lp.local_class})</a
></li
>")
732 dctx.stage("</ul
></article
>\n
")
734 dctx.add("</section
>\n
")
738 # Fill the body for the page associated to the full index
739 fun file_index_page_doc(dctx: DocContext)
742 dctx.add("<h1
>Full Index</h1
>\n
")
744 var clas = new Array[MMLocalClass]
745 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
746 var gprops = new Array[MMLocalProperty]
747 var mods = new Array[MMModule]
748 for m in mhe.greaters_and_self do
749 if not m.require_doc(dctx) then continue
752 for g in global_classes do
754 if not lc.require_doc(dctx) then continue
756 for gp in lc.global_properties do
758 if not lp.require_doc(dctx) then continue
759 if props.has_key(lp.global) then
760 if not props[lp.global].has(lp) then
761 props[lp.global].add(lp)
764 props[lp.global] = [lp]
765 gprops.add(lp.global.intro)
770 dctx.stage("<article
class=\
"modules filterable\">\n
")
771 dctx.stage("<h2
>Modules</h2
>\n
")
775 dctx.add("<li
>{m.html_link(dctx)}</li
>")
777 dctx.stage("</ul
></article
>\n
")
781 dctx.stage("<article
class=\
"classes filterable\">\n
")
782 dctx.stage("<h2
>Classes</h2
>\n
")
786 dctx.add("<li
>{lc.html_link(dctx)}</li
>")
788 dctx.stage("</ul
></article
>\n
")
792 dctx.stage("<article
class=\
"properties filterable\">\n
")
793 dctx.stage("<h2
>Properties</h2
>\n
")
800 if gp.intro isa MMAttribute then continue
802 var lpi = self[gp.intro.local_class.global][gp]
805 dctx.add("<li
class='intro'><span title
='introduction'>I
</span
> 
;{lpi.html_open_link(dctx)}{lpi.html_name} 
;({lpi.local_class})</a
></li
>\n
")
806 if lps.length >= 1 then
809 dctx.add("<li
class='redef'><span title
='redefinition'>R
</span
> 
;{lp.html_open_link(dctx)}{lp.html_name} 
;({lp.local_class})</a
></li
>\n
")
813 dctx.stage("</ul
></article
>\n
")
817 # Fill the quicksearch list JSON object
818 fun file_quicksearch_list_doc(dctx: DocContext)
820 var entities = new HashMap[String, Array[MMEntity]]
821 var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]]
822 for m in mhe.greaters_and_self do
823 if not m.require_doc(dctx) then continue
824 var a = new Array[MMEntity]
826 entities[m.html_name] = a
828 for g in global_classes do
830 if not lc.require_doc(dctx) then continue
831 var a = new Array[MMEntity]
833 entities[lc.html_name] = a
834 for gp in lc.global_properties do
836 if not lp.require_doc(dctx) then continue
837 if props.has_key(lp.global) then
838 if not props[lp.global].has(lp) then
839 props[lp.global].add(lp)
842 props[lp.global] = [lp]
848 entities[k.short_name] = v
851 var keys = entities.keys.to_a
852 var sorter = new AlphaSorter[String]
856 dctx.stage("var entries
= \
{")
858 dctx.add("\
"{key}\": [")
859 for entity in entities[key] do
860 dctx.add(entity.json_entry(dctx))
869 redef class MMGlobalProperty
870 # Return the short name of the property
871 fun short_name: String do
872 return self.intro.html_name
876 redef class MMLocalProperty
878 # Anchor of the property description in the module html file
879 fun html_anchor: String
881 return "PROP_{local_class}_
{cmangle(name)}"
884 redef fun json_entry(dctx) do
885 return "\
{txt:\"{qualified_name}\
",url:\"{local_class.html_name}.html
#{html_anchor}\"\},"
888 redef fun qualified_name
do
889 return "{intro_module.qualified_name}::{local_class.html_name}::{html_name}"
892 fun html_open_link
(dctx
: DocContext): String
894 if not require_doc
(dctx
) then print
"not required {self}"
895 var title
= "{html_name}{signature.to_s}"
896 if short_doc
!= " " then
897 title
+= " #{short_doc}"
899 return "<a href=\"{local_class.html_name}.html
#{html_anchor}\" title=\"{title}\">"
902 fun html_name
: String
904 return self.name
.to_s
.html_escape
907 redef fun html_link
(dctx
)
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}\">{html_name}</a>"
917 fun html_link_special
(dctx
: DocContext, lc
: MMLocalClass): String
919 if not require_doc
(dctx
) then print
"not required {self}"
920 var title
= "{html_name}{signature_for(lc.get_type)}"
921 if short_doc
!= " " then
922 title
+= " #{short_doc}"
924 return "<a href=\"{lc.html_name}.html
#{html_anchor}\" title=\"{title}\">{html_name}</a>"
927 # Kind of property (fun, attr, etc.)
928 fun kind
: String is abstract
935 else if global
.intro
== self then
938 return global
.intro
.short_doc
945 if n
== null or not n
isa APropdef then
952 if d
.n_comment
.is_empty
then
959 # The most specific module in the nesting hierarchy that exports the intro of self
960 fun intro_module
: MMModule
962 var m
= global
.intro
.mmmodule
963 var mo
= m
.direct_owner
964 while mo
!= null and mo
.visibility_for
(m
) >= 2 do
971 # Is the intro of self exported by the top-level module ?
972 fun is_toplevel
: Bool
975 return m
== m
.toplevel_owner
978 # Return true if the global property must be documented according to the visibility configured
979 fun require_doc
(dctx
: DocContext): Bool
981 if global
.visibility_level
== 3 and not dctx
.with_private
then return false # Private
982 if dctx
.public_only
then
984 if m
!= m
.toplevel_owner
then return false # Unexported
989 # Document the global property in the global class lc
990 fun full_documentation
(dctx
: DocContext, lc
: MMLocalClass)
992 var visibility
: String
993 if global
.visibility_level
== 1 then
994 visibility
= "public"
995 else if global
.visibility_level
== 2 then
996 visibility
= "protected"
997 else if global
.visibility_level
== 3 then
998 visibility
= "private"
1003 var intro_class
= global
.intro
.local_class
1004 var is_redef
= local_class
.global
!= intro_class
.global
or local_class
.mmmodule
.toplevel_owner
!= intro_class
.mmmodule
.toplevel_owner
1006 dctx
.add
("<article id=\"{html_anchor}\
" class=\"{kind} {visibility} {if is_redef then "redef" else ""}\
">\n")
1007 dctx
.add
("<h3 class=\"signature\
">{html_name}{signature.to_html(dctx, true)}</h3>\n")
1008 dctx
.add
("<div class=\"info\
">\n")
1009 #dctx.add("<p>LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}</p>")
1014 if not is_toplevel
then
1015 dctx
.add
("(unexported) ")
1017 if global
.visibility_level
== 2 then
1018 dctx
.add
("protected ")
1019 else if global
.visibility_level
== 3 then
1020 dctx
.add
("private ")
1023 dctx
.add
(" {intro_class.mmmodule.toplevel_owner.name}")
1024 if intro_class
.global
== lc
.global
then
1025 dctx
.add
("::{lc.name}")
1027 dctx
.add
("::{mmmodule[intro_class.global].html_link(dctx)}")
1030 dctx
.add
("::{mmmodule[intro_class.global][global].html_link(dctx)}")
1032 dctx
.add
("::{html_name}")
1036 dctx
.add
("<div class=\"description\
">")
1038 # Collect all refinement of the global property in the same global property
1039 var lps
= new Array[MMLocalProperty]
1040 for l
in prhe
.greaters_and_self
do
1044 var introdoc
= false
1045 if global
.intro
.doc
!= null then
1047 if lp
.doc
== null then introdoc
= true
1051 dctx
.add
("<pre>{global.intro.doc.to_html}</pre>")
1054 var tlmods
= new Array[MMModule]
1056 var bm
= lp
.mmmodule
.toplevel_owner
1057 var lcm
= lc
.global
.intro
.mmmodule
1058 if lcm
.mhe
< lp
.mmmodule
then bm
= lcm
.toplevel_owner
1059 if not tlmods
.has
(bm
) then tlmods
.add
(bm
)
1063 # Document the top level property for the current top level module
1065 if tm
.global_classes
.has
(lc
.global
) then
1066 tlp
= tm
[lc
.global
][self.global
]
1068 else if tm
.global_classes
.has
(self.local_class
.global
) then
1069 # Self is the inherited property. Process it
1070 tlp
= tm
[self.local_class
.global
][self.global
]
1073 # We skip this module since the props defined by the module is
1077 var tlcm
= lc
.global
.intro
.mmmodule
.toplevel_owner
1078 if not tlcm
.mhe
<= tm
then
1079 dctx
.add
("<h4>In module {tm.html_link(dctx)} :</h4>")
1082 #dctx.add("<p>TLP: {tm} x {lc} : {tlp.full_name}</p>")
1085 if doc
!= null and (not introdoc
or global
.intro
.doc
!= doc
) then
1086 dctx
.add
("<pre>{doc.to_html}</pre>")
1089 if tlp
.local_class
.global
!= lc
.global
then
1090 dctx
.add
("inherited from {tlp.local_class.html_link(dctx)} ")
1092 if tm
!= tlp
.mmmodule
then
1093 dctx
.add
("defined by the module {tlp.mmmodule.html_link(dctx)} ")
1102 dctx
.stage
(". previously defined by:")
1104 var tl
= lp
.mmmodule
.toplevel_owner
1105 if tl
!= tm
then continue
1106 if lp
== tlp
then continue
1107 dctx
.add
(" {lp.mmmodule.html_link(dctx)}")
1108 if lp
.local_class
.global
!= lc
.global
then
1109 dctx
.add
(" for {lp.local_class.html_link(dctx)} ")
1119 #if doc != null and (not introdoc or global.intro.doc != doc) then
1120 # dctx.add("<pre>{doc.to_html}</pre>")
1127 dctx
.add
("</article>")
1130 redef class MMMethod
1131 redef fun kind
do return if global
.is_init
then "init" else "fun"
1133 redef class MMAttribute
1134 redef fun kind
do return "var"
1136 redef class MMTypeProperty
1137 redef fun kind
do return "type"
1140 redef class MMSrcModule
1154 if n
.n_moduledecl
== null then
1157 var np
= n
.n_moduledecl
1162 if d
.n_comment
.is_empty
then
1171 # Html transcription of the doc
1174 var res
= new Buffer
1175 for c
in n_comment
do
1176 res
.append
(c
.text
.substring_from
(1))
1178 return res
.to_s
.html_escape
1181 # Oneliner transcription of the doc
1184 return n_comment
.first
.text
.substring_from
(1).html_escape
1188 redef class MMLocalClass
1191 # Anchor of the class description in the module html file
1192 fun html_anchor
: String do return "CLASS_{self}"
1194 fun html_name
: String do return "{self}"
1196 redef fun html_link
(dctx
)
1198 if not require_doc
(dctx
) then print
"{dctx.filename}: not required {self}"
1199 return "<a href=\"{html_name}.html\
" title=\"{short_doc}\
">{self}</a>"
1202 redef fun json_entry
(dctx
) do
1203 return "\{txt:\"{qualified_name}\",url
:\
"{html_name}.html\"\
},"
1206 redef fun qualified_name do
1207 return "{intro_module.qualified_name}::{html_name}"
1210 redef fun short_doc do return global.intro.short_doc
1212 redef fun doc do return global.intro.doc
1216 if global.is_interface then
1218 else if global.is_abstract then
1219 return "abstract class"
1220 else if global.is_enum then
1227 # The most specific module in the nesting hierarchy that exports the intro of self
1228 fun intro_module: MMModule
1230 var m = global.intro.mmmodule
1231 var mo = m.direct_owner
1232 while mo != null and mo.visibility_for(m) >= 2 do
1239 fun menu_link(dctx: DocContext, p: MMLocalProperty)
1241 if p.local_class.global != self.global then
1242 if p.global.intro.local_class.name == "Object".to_symbol then return
1243 if p.global.is_init or p isa MMTypeProperty then
1244 dctx.add("<li
class='inherit'><span title
='Inherited'>H
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1246 dctx.add("<li
class='inherit'><span title
='Inherited'>H
</span
> 
;{p.html_link(dctx)}</li
>\n
")
1248 else if p.global.intro.local_class.global == self.global then
1249 dctx.add("<li
class='intro'><span title
='Introduced'>I
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1251 dctx.add("<li
class='redef'><span title
='Redefined'>R
</span
> 
;{p.html_link_special(dctx, self)}</li
>\n
")
1255 # Return true if the global class must be documented according to the visibility configured
1256 fun require_doc(dctx: DocContext): Bool
1258 if global.visibility_level == 3 and not dctx.with_private then return false # Private
1259 if dctx.public_only then
1260 var m = intro_module
1261 if m != m.toplevel_owner then return false # Unexported
1266 # Fill the body for the page associated to the global class
1267 fun file_page_doc(dctx: DocContext)
1269 dctx.add("<div
class=\
"menu\">\n
")
1271 var props = new Array[MMLocalProperty]
1272 var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]]
1273 var inhs = new Array[MMLocalClass]
1274 for g in global_properties do
1276 if not p.require_doc(dctx) then continue
1277 if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then
1280 var lc = mmmodule[p.local_class.global]
1281 if inh.has_key(lc) then
1292 dctx.add("<nav
class=\
"properties filterable\">\n
")
1293 dctx.add("<h3
>Properties</h3
>\n
")
1295 dctx.stage("<h4
>Virtual Types</h4
>\n
<ul
>\n
")
1297 if p isa MMTypeProperty then
1301 dctx.stage("</ul
>\n
")
1304 dctx.stage("<h4
>Constructors</h4
>\n
<ul
>\n
")
1306 if p.global.is_init_for(self) then
1310 dctx.stage("</ul
>\n
")
1313 dctx.stage("<h4
>Methods</h4
>\n
<ul
>\n
")
1315 if not p.global.is_init and p isa MMMethod then
1319 dctx.stage("</ul
>\n
")
1321 dctx.add("</nav
>\n
")
1323 dctx.add("<nav
class=\
"inheritance filterable\">\n
")
1324 dctx.add("<h3
>Inheritance</h3
>\n
")
1325 dctx.add("<h4
>Superclasses</h4
>\n
<ul
>\n
")
1326 for lc in cshe.linear_extension do
1327 if lc == self then continue
1328 if not lc.require_doc(dctx) then continue
1329 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1332 if cshe.smallers.length == 0 then
1333 dctx.add("<h4
>No Known Subclasses</h4
>\n
")
1334 else if cshe.smallers.length <= 100 then
1335 dctx.add("<h4
>Subclasses</h4
>\n
")
1337 for lc in cshe.smallers do
1338 if not lc.require_doc(dctx) then continue
1339 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1342 else if cshe.direct_smallers.length <= 100 then
1343 dctx.add("<h4
>Direct Subclasses Only</h4
>\n
<ul
>\n
")
1344 for lc in cshe.direct_smallers do
1345 if not lc.require_doc(dctx) then continue
1346 dctx.add("<li
>{lc.html_link(dctx)}</li
>\n
")
1350 dctx.add("<h4
>Too much
Subclasses to list
</h4
>\n
")
1352 dctx.add("</nav
>\n
")
1354 dctx.add("</div
>\n
")
1357 dctx.add("<div
class=\
"content\">\n
")
1358 dctx.add("<h1
>{name}</h1
>\n
")
1359 dctx.add("<div
class='subtitle'>")
1360 if global.visibility_level == 2 then
1362 else if global.visibility_level == 3 then
1363 dctx.add("private ")
1364 else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then
1365 dctx.add("(unexported
) ")
1367 dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}</div
>")
1369 dctx.add("<section
class=\
"description\">\n
")
1372 dctx.add("<pre
>{doc.to_html}</pre
>\n
")
1375 var cla = new HashSet[MMLocalClass]
1376 var sm = new HashSet[MMLocalClass]
1377 var sm2 = new HashSet[MMLocalClass]
1379 while cla.length + sm.length < 10 and sm.length > 0 do
1383 sm2.add_all(x.cshe.direct_smallers)
1389 cla.add_all(cshe.greaters_and_self)
1392 var name = "class_
{name}"
1393 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
")
1396 op.append("\
"{c.name}\"[shape
=box
,margin
=0.03];\n
")
1398 op.append("\
"{c.name}\"[URL=\
"{c.html_name}.html\"];\n
")
1400 for c2 in c.cshe.direct_greaters do
1401 if not cla.has(c2) then continue
1402 op.append("\
"{c.name}\"->\
"{c2.name}\";\n
")
1404 if not c.cshe.direct_smallers.is_empty then
1406 for c2 in c.cshe.direct_smallers do
1407 if cla.has(c2) then others = false
1410 op.append("\
"{c.name}...\"[label=\
"\"];\n
")
1411 op.append("\
"{c.name}...\"->\
"{c.name}\"[style
=dotted
];\n
")
1416 dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph
for class {name}")
1419 var mods = new Array[MMModule]
1420 mods.add(global.intro.mmmodule.toplevel_owner)
1421 for lc in crhe.greaters do
1422 if not lc isa MMSrcLocalClass then continue
1423 var m = lc.mmmodule.toplevel_owner
1424 if not mods.has(m) then mods.add(m)
1428 if m == global.intro.mmmodule.toplevel_owner then
1429 dctx.add("<p
>Introduced by
{m.html_link(dctx)}")
1431 dctx.add("<p
>Refined by
{m.html_link(dctx)}")
1434 dctx.stage(". Definition in:")
1435 for lc in crhe.greaters do
1436 if lc.mmmodule.toplevel_owner != m then continue
1437 dctx.add(" {lc.mmmodule.html_link(dctx)} ")
1438 assert lc isa MMSrcLocalClass
1441 dctx.show_source(n.location)
1447 dctx.add("</section
>\n
")
1450 dctx.stage("<section
class=\
"types\">\n
")
1451 dctx.stage("<h2
>Formal and Virtual Types</h2
>\n
")
1452 for i in [0..arity[ do
1453 var f = get_formal(i)
1454 f.full_documentation(dctx, self)
1457 if not p isa MMTypeProperty then continue
1458 p.full_documentation(dctx, self)
1460 dctx.stage("</section
>\n
")
1464 dctx.stage("<section
class=\
"constructors\">\n
")
1465 dctx.stage("<h2
class=\
"section-header\">Constructors</h2
>\n
")
1467 if not p.global.is_init_for(self) then continue
1468 p.full_documentation(dctx, self)
1470 dctx.stage("</section
>\n
")
1474 dctx.stage("<section
class=\
"methods\">\n
")
1475 dctx.stage("<h2
class=\
"section-header\">Methods</h2
>\n
")
1477 if p.global.is_init then continue
1478 if p.local_class.global != self.global then continue
1479 if not p isa MMMethod then continue
1480 p.full_documentation(dctx, self)
1482 if not inhs.is_empty then
1484 dctx.stage("<h3
>Inherited Methods</h3
>\n
")
1487 dctx.stage("<p
>Defined in {lc.html_link(dctx)}:")
1489 if p.global.is_init then continue
1490 if not p isa MMMethod then continue
1491 dctx.add(" {p.html_link(dctx)}")
1498 dctx.add("</section
>\n
")
1500 dctx.add("</div
> <!-- end class {name} -->\n
")
1504 redef class MMSrcLocalClass
1510 else if global.intro == self then
1513 var bc = global.intro
1521 if not n isa AStdClassdef then
1528 if d.n_comment.is_empty then
1536 redef class MMSignature
1537 # Htlm transcription of the signature (with nested links)
1538 fun to_html(dctx: DocContext, with_closure: Bool): String
1540 var res = new Buffer
1543 for i in [0..arity[ do
1544 if i > 0 then res.append(", ")
1545 res.append(self.params[i].name.to_s)
1547 res.append(self[i].html_link(dctx))
1548 if self.vararg_rank == i then
1554 if return_type != null then
1556 res.append(return_type.html_link(dctx))
1558 if with_closure then
1559 for c in closures do
1561 if c.is_optional then res.append("[")
1562 if c.is_break then res.append("break ")
1563 res.append("!{c.name}")
1564 res.append(c.signature.to_html(dctx, false))
1565 if c.is_optional then res.append("]")
1573 # Htlm transcription of the type (with nested links)
1574 fun html_link(dctx: DocContext): String do return to_s
1577 redef class MMTypeSimpleClass
1578 redef fun html_link(dctx) do return local_class.html_link(dctx)
1581 redef class MMTypeGeneric
1582 redef fun html_link(dctx)
1584 var res = new Buffer
1585 res.append(local_class.html_link(dctx))
1587 res.append(params[0].html_link(dctx))
1588 for i in [1..params.length[ do
1590 res.append(params[i].html_link(dctx))
1597 redef class MMTypeFormalParameter
1598 fun html_anchor: String
1600 return "FT_{local_class}_
{cmangle(name)}"
1602 redef fun html_link(dctx)
1604 return "<a href
=\
"#{html_anchor}\">{name}</a
>"
1606 fun full_documentation(dctx: DocContext, lc: MMLocalClass)
1608 dctx.add("<article id
=\
"{html_anchor}\">\n
")
1609 dctx.add("<h3
class=\
"signature\">{name}: {bound.html_link(dctx)}</h3
>\n
")
1610 dctx.add("<div
class=\
"info\">")
1611 dctx.add("formal generic
type")
1613 dctx.add("</article
>")
1617 redef class MMNullableType
1618 redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx)
1621 redef class MMVirtualType
1622 redef fun html_link(dctx) do return property.html_link(dctx)
1625 var c = new DocContext