# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2008 Jean Privat # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # The main module of the nitdoc program package nitdoc import syntax private import utils import abstracttool # Store knowledge and facilities to generate files class DocContext super AbstractCompiler # Destination directory readable writable var _dir: String = "doc" # Content of a generated file var _stage_context: StageContext = new StageContext(null) # Add a string in the content fun add(s: String) do _stage_context.content.add(s) _stage_context.validate = true end # Add a string in the content iff some other string are added fun stage(s: String) do _stage_context.content.add(s) # Create a new stage in the content fun open_stage do _stage_context = new StageContext(_stage_context) # Close the current stage in the content fun close_stage do var s = _stage_context.parent if _stage_context.validate then s.content.add_all(_stage_context.content) s.validate = true end assert s != null _stage_context = s end # Write the content to a new file fun write_to(filename: String) do var f = new OFStream.open(filename) for s in _stage_context.content do f.write(s) end f.close end # Start a new file fun clear do _stage_context = new StageContext(null) end # Sorter of entities in alphabetical order var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity] # Sort entities in the alphabetical order fun sort(array: Array[MMEntity]) do _sorter.sort(array) end readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir") readable var _opt_source: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source") readable var _opt_public: OptionBool = new OptionBool("Generate only the public API", "--public") fun public_only: Bool do if self._opt_public.value == true then return true return false end # The current processed filename var filename: String # The main virtual module var mainmod: nullable MMVirtualModule redef fun perform_work(mods) do mainmod = new MMVirtualModule(self, mods) dir.mkdir # Compute the set of direct owned nested modules var owns = new HashMap[MMModule, Array[MMModule]] for mod in modules do owns[mod] = new Array[MMModule]# [mod] end for mod in modules do if mod == mainmod then continue var d = mod.directory loop var o = d.owner if o != null and o != mod then owns[o].add(mod) end var dp = d.parent if dp == null or dp == d then break d = dp end end # Builds the various module hierarchies var mnh = new PartialOrder[MMModule] # nested module hierarchy var tmh = new PartialOrder[MMModule] # top module import hierrchy var ms = mainmod.mhe.linear_extension.reversed for m in ms do if ms == mainmod then continue m.mnhe_ = mnh.add(m, owns[m]) var pub = new Array[MMModule] for m2 in m.mhe.greaters do if m2.toplevel_owner != m2 and m2.toplevel_owner != m.toplevel_owner then continue if m.mnhe <= m2 then continue if m.visibility_for(m2) <= 0 then # nothing else if m.visibility_for(m2) == 1 then else pub.add(m2) end end m.tmhe_ = tmh.add(m, pub) end var head = "" + "\n" + "\n" + "" var action_bar = "
\n" # generate the index self.filename = "index.html" clear add("") add("{head}Index\n") add(action_bar) add("
") add("
") add("

Modules

\n
    ") var modss = mainmod.mhe.greaters_and_self.to_a sort(modss) for mod in modss do if not mod.is_toplevel then continue if not mod.require_doc(self) then continue assert mod isa MMSrcModule add("
  • {mod.html_link(self)} {mod.short_doc}
  • ") end add("
") var op = new Buffer 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") for mod in modss do if not mod.is_toplevel then continue if not mod.require_doc(self) then continue op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n") for mod2 in mod.tmhe.direct_greaters do if not modss.has(mod2) then continue op.append("\"{mod.name}\"->\"{mod2.name}\";\n") end end op.append("\}\n") self.gen_dot(op.to_s, "dep", "Modules hierarchy") add("
") add("
") add("
") add("\n") write_to("{dir}/index.html") # Generate page for modules for mod in modules do if mod == mainmod then continue assert mod isa MMSrcModule if not mod.require_doc(self) then continue self.filename = mod.html_name action_bar = "
\n" clear add("") add("{head}Module {mod.name}\n") add(action_bar) add("
") mod.file_page_doc(self) add("
") add("\n") write_to("{dir}/{mod.html_name}.html") end # Generate pages for global classes for c in mainmod.local_classes do if not c.require_doc(self) then continue self.filename = c.html_name action_bar = "
\n" clear add("") add("{head}Class {c.name}\n") add(action_bar) add("
") c.file_page_doc(self) add("
") add("\n") write_to("{dir}/{c.html_name}.html") end self.filename = "fullindex" action_bar = "
\n" clear add("") add("{head}Full Index\n") add(action_bar) add("
") add("
") mainmod.file_index_page_doc(self) add("
") add("
") add("\n") write_to("{dir}/full-index.html") end # Add a (source) link fo a given location fun show_source(l: Location) do var s = opt_source.value if s == null then add("in #{l.file.filename.simplify_path}") else # THIS IS JUST UGLY ! (but there is no replace yet) var x = s.split_with("%f") s = x.join(l.file.filename.simplify_path) x = s.split_with("%l") s = x.join(l.line_start.to_s) x = s.split_with("%L") s = x.join(l.line_end.to_s) add(" (show code)") end end # Generate a clicable graphiz image using a dot content. # `name' refer to the filename (without extension) and the id name of the map. # `name' must also match the name of the graph in the dot content (eg. digraph NAME {...) fun gen_dot(dot: String, name: String, alt: String) do var f = new OFStream.open("{self.dir}/{name}.dot") f.write(dot) f.close 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 ; \}") self.add("
\"{alt}\"/
") var fmap = new IFStream.open("{self.dir}/{name}.map") self.add(fmap.read_all) fmap.close end init do keep_ast = true super("nitdoc") filename = "-unset-" option_context.add_option(opt_public) option_context.add_option(opt_dir) option_context.add_option(opt_source) end redef fun process_options do super var d = opt_dir.value if d != null then dir = d end redef fun handle_property_conflict(lc, impls) do # THIS IS SO UGLY! See MMVirtualModule if lc.mmmodule == self.mainmod then return # We just accept, so one in impls is arbitrary inherited end super end end redef class String # Replace all occurence of pattern ith string fun replace(p: Pattern, string: String): String do return self.split_with(p).join(string) end # Escape the following characters < > & and " with their html counterpart fun html_escape: String do var ret = self if ret.has('&') then ret = ret.replace('&', "&") if ret.has('<') then ret = ret.replace('<', "<") if ret.has('>') then ret = ret.replace('>', ">") if ret.has('"') then ret = ret.replace('"', """) return ret end # Remove "/./", "//" and "bla/../" fun simplify_path: String do var a = self.split_with("/") var a2 = new Array[String] for x in a do if x == "." then continue if x == "" and not a2.is_empty then continue if x == ".." and not a2.is_empty then a2.pop continue end a2.push(x) end return a2.join("/") end end # A virtual module is used to work as an implicit main module that combine unrelated modules # Since conflict may arrise in a virtual module (the main method for instance) conflicts are disabled class MMVirtualModule super MMModule init(ctx: MMContext, mods: Array[MMModule]) do # We need to compute the whole metamodel since there is no mmbuilder to do it super(" main".to_symbol, mods.first.directory, ctx, new Location(null,0,0,0,0)) ctx.add_module(self, mods) for m in mods do self.add_super_module(m, 1) end self.import_global_classes self.import_local_classes for c in self.local_classes do c.compute_super_classes end for c in self.local_classes do c.compute_ancestors end end redef fun require_doc(dctx) do return false end # Conditionnal part of the text content of a DocContext class StageContext # Content of the current stage readable var _content: Array[String] = new Array[String] # Is a normal string already added? readable writable var _validate: Bool = false # Parent stage is any readable var _parent: nullable StageContext = null init(parent: nullable StageContext) do _parent = parent end # Efficiently sort object with their to_s method class AlphaSorter[E: Object] super AbstractSorter[E] redef fun compare(a, b) do var sa: String var sb: String var d = _dico if d.has_key(a) then sa = d[a] else sa = a.to_s d[a] = sa end if d.has_key(b) then sb = d[b] else sb = b.to_s d[b] = sb end return sa <=> sb end # Keep track of to_s values var _dico: HashMap[Object, String] = new HashMap[Object, String] init do end end # Generalization of metamodel entities class MMEntity # Return a link to fun html_link(dctx: DocContext): String is abstract # Return a one liner description fun short_doc: String do return " " # The doc node from the AST # Return null is none fun doc: nullable ADoc do return null end redef class MMModule super MMEntity redef fun html_link(dctx) do return "{self}" end fun require_doc(dctx: DocContext): Bool do if dctx.public_only and not is_toplevel then return false return true end # Return true if the module is a top-level owner or a top-level module fun is_toplevel: Bool do var pd = directory.parent return pd == null or (pd.owner == null and directory.owner == self) end # Element in the module nesting tree fun mnhe: PartialOrderElement[MMModule] do return mnhe_.as(not null) var mnhe_: nullable PartialOrderElement[MMModule] = null # Element in the top level module importation hierarchy fun tmhe: PartialOrderElement[MMModule] do return tmhe_.as(not null) var tmhe_: nullable PartialOrderElement[MMModule] = null fun toplevel_owner: MMModule do var m = self loop var ds = m.mnhe.direct_smallers if ds.length == 0 then return m if ds.length == 1 then m = ds.first else abort end end fun html_name: String do return "{name}" end fun direct_owner: nullable MMModule do var d = directory while d.owner == self do d = d.parent.as(not null) return d.owner end # Fill the body for the page associated to the module fun file_page_doc(dctx: DocContext) do dctx.add("
\n") var mods = new Array[MMModule] mods = self.mhe.greaters.to_a dctx.sort(mods) dctx.open_stage dctx.stage("\n") dctx.close_stage if not dctx.public_only then mods = self.mnhe.direct_greaters.to_a dctx.sort(mods) dctx.open_stage dctx.stage("\n") dctx.close_stage end dctx.add("
") # metadata dctx.add("
\n") dctx.add("

{name}

\n") dctx.add("
module ") for m in mnhe.smallers do dctx.add("{m.html_link(dctx)}::") end dctx.add("{self.name}
\n") dctx.add("
\n") var doc = doc if doc != null then dctx.add("
\n") dctx.add("
{doc.to_html}
\n") dctx.add("
\n") end var op = new Buffer 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") var ms = new Array[nullable MMModule] do var m0: nullable MMModule = self while m0 != null do m0 = m0.direct_owner ms.add(m0) end end var cla = new HashSet[MMModule] cla.add(self) for m0 in self.mhe.greaters do if not m0.require_doc(dctx) then continue if self.visibility_for(m0) <= 1 then continue # private or hidden if self.mnhe <= m0 then continue # do not want nested stuff if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting cla.add(m0) end for m0 in self.mhe.smallers do if not m0.require_doc(dctx) then continue if m0.visibility_for(self) <= 1 then continue # private or hidden if m0.direct_owner != null and not m0.direct_owner.mnhe <= self then continue # not in the right nesting cla.add(m0) end for m0 in self.mnhe.smallers do cla.add(m0) end ms = ms.reversed for m0 in ms do if m0 != null then op.append("subgraph \"cluster_{m0.name}\"\{\n") end for c in cla do if c.direct_owner != m0 then continue if c == self then op.append("\"{c.name}\"[shape=box,margin=0.03];\n") else op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n") end end if m0 != null then op.append("\"{m0.name}\"[URL=\"{m0.html_name}.html\"];\n") for c in m0.mhe.direct_greaters do if not cla.has(c) then continue op.append("\"{m0.name}\"->\"{c.name}\";\n") end end end for m0 in ms do # Close the nesting subgraph if m0 != null then op.append("\}\n") end end for c in cla do for c2 in c.tmhe.direct_greaters do if not cla.has(c2) then continue op.append("\"{c.name}\"->\"{c2.name}\";\n") end end op.append("\}\n") dctx.gen_dot(op.to_s, name.to_s, "Dependency graph for module {name}") dctx.add("
") var clas = new Array[MMLocalClass] var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]] var gprops = new Array[MMLocalProperty] do var m = self for g in m.global_classes do var lc = m[g] if not lc.require_doc(dctx) then continue var im = g.intro.mmmodule if self.visibility_for(im) <= 1 then continue # private import or invisible import var keep = false for lc2 in lc.crhe.greaters_and_self do if not lc2 isa MMSrcLocalClass then continue if not self.mnhe <= lc2.mmmodule then continue # not introduced/redefined here/stolen keep = true end if not keep then continue clas.add(self[g]) for gp in lc.global_properties do if self.visibility_for(gp.intro.local_class.mmmodule) <= 1 then continue # private import or invisible import var lp = lc[gp] var mp = lp.local_class.mmmodule if not self.mnhe <= mp then continue # not introduced/redefined here/stolen lp = self[g][gp] if not lp.require_doc(dctx) then continue if props.has_key(lp.global) then if not props[lp.global].has(lp) then props[lp.global].add(lp) end else props[lp.global] = [lp] gprops.add(lp.global.intro) end end end end dctx.add("
\n") dctx.open_stage dctx.stage("
\n") dctx.stage("

Classes

\n") dctx.sort(clas) dctx.stage("
    \n") for lc in clas do if self.mnhe <= lc.global.intro.mmmodule then dctx.add("
  • I ") else dctx.add("
  • R ") end dctx.add("{lc.html_link(dctx)}
  • \n") end dctx.stage("
\n") dctx.close_stage dctx.open_stage dctx.stage("
\n") dctx.stage("

Properties

\n") dctx.sort(gprops) dctx.stage("
    \n") for lgp in gprops do var gp = lgp.global var lps = props[gp] if gp.intro isa MMAttribute then continue var lpi = self[gp.intro.local_class.global][gp] if lps.has(lpi) then dctx.add("
  • I {lpi.html_open_link(dctx)}{lpi} ({lpi.local_class})
  • \n") lps.remove(lpi) else dctx.add("
  • I {lpi}") dctx.add(" ({lpi.local_class})
  • \n") end if lps.length >= 1 then dctx.sort(lps) for lp in lps do dctx.add("
  • R {lp.html_open_link(dctx)}{lp} ({lp.local_class})
  • ") end end end dctx.stage("
\n") dctx.close_stage dctx.add("
\n") dctx.add("
\n") end # Fill the body for the page associated to the full index fun file_index_page_doc(dctx: DocContext) do dctx.add("

Full Index

\n") var clas = new Array[MMLocalClass] var props = new HashMap[MMGlobalProperty, Array[MMLocalProperty]] var gprops = new Array[MMLocalProperty] var mods = new Array[MMModule] for m in mhe.greaters_and_self do if not m.require_doc(dctx) then continue mods.add(m) end for g in global_classes do var lc = self[g] if not lc.require_doc(dctx) then continue clas.add(lc) for gp in lc.global_properties do var lp = lc[gp] if not lp.require_doc(dctx) then continue if props.has_key(lp.global) then if not props[lp.global].has(lp) then props[lp.global].add(lp) end else props[lp.global] = [lp] gprops.add(lp.global.intro) end end end dctx.open_stage dctx.stage("
\n") dctx.stage("

Modules

\n") dctx.sort(mods) dctx.stage("
\n") dctx.close_stage dctx.open_stage dctx.stage("
\n") dctx.stage("

Classes

\n") dctx.sort(clas) dctx.stage("
\n") dctx.close_stage dctx.open_stage dctx.stage("
\n") dctx.stage("

Properties

\n") dctx.sort(gprops) dctx.stage("
\n") dctx.close_stage end end redef class MMLocalProperty super MMEntity # Anchor of the property description in the module html file fun html_anchor: String do return "PROP_{local_class}_{cmangle(name)}" end fun html_open_link(dctx: DocContext): String do if not require_doc(dctx) then print "not required {self}" var title = "{name}{signature.to_s}" if short_doc != " " then title += " #{short_doc}" end return "" end redef fun html_link(dctx) do if not require_doc(dctx) then print "not required {self}" var title = "{name}{signature.to_s}" if short_doc != " " then title += " #{short_doc}" end return "{self}" end fun html_link_special(dctx: DocContext, lc: MMLocalClass): String do if not require_doc(dctx) then print "not required {self}" var title = "{name}{signature_for(lc.get_type)}" if short_doc != " " then title += " #{short_doc}" end return "{self}" end # Kind of property (fun, attr, etc.) fun kind: String is abstract redef fun short_doc do var d = doc if d != null then return d.short else if global.intro == self then return " " else return global.intro.short_doc end end redef fun doc do var n = node if n == null or not n isa APropdef then return null end var d = n.n_doc if d == null then return null end if d.n_comment.is_empty then return null else return d end end # The most specific module in the nesting hierarchy that exports the intro of self fun intro_module: MMModule do var m = global.intro.mmmodule var mo = m.direct_owner while mo != null and mo.visibility_for(m) >= 2 do m = mo mo = m.direct_owner end return m end # Is the intro of self exported by the top-level module ? fun is_toplevel: Bool do var m = intro_module return m == m.toplevel_owner end # Return true if the global class must be documented according to the visibility configured fun require_doc(dctx: DocContext): Bool do if global.visibility_level == 3 then return false # Private if dctx.public_only then var m = intro_module if m != m.toplevel_owner then return false # Unexported end return true end # Document the global property in the global class lc fun full_documentation(dctx: DocContext, lc: MMLocalClass) do var visibility: String if global.visibility_level == 1 then visibility = "public" else if global.visibility_level == 2 then visibility = "protected" else if global.visibility_level == 3 then visibility = "private" else abort end var intro_class = global.intro.local_class var is_redef = local_class.global != intro_class.global or local_class.mmmodule.toplevel_owner != intro_class.mmmodule.toplevel_owner dctx.add("
\n") dctx.add("

{name}{signature.to_html(dctx, true)}

\n") dctx.add("
\n") #dctx.add("

LP: {self.mmmodule.html_link(dctx)}::{self.local_class.html_link(dctx)}::{self.html_link(dctx)}

") if is_redef then dctx.add("redef ") end if not is_toplevel then dctx.add("(unexported) ") end if global.visibility_level == 2 then dctx.add("protected ") else if global.visibility_level == 3 then dctx.add("private ") end dctx.add(kind) dctx.add(" {intro_class.mmmodule.toplevel_owner.name}") if intro_class.global == lc.global then dctx.add("::{lc.name}") else dctx.add("::{mmmodule[intro_class.global].html_link(dctx)}") end if is_redef then dctx.add("::{mmmodule[intro_class.global][global].html_link(dctx)}") else dctx.add("::{name}") end dctx.add("
") dctx.add("
") # Collect all refinement of the global property in the same global property var lps = new Array[MMLocalProperty] for l in prhe.greaters_and_self do lps.add(l) end var introdoc = false if global.intro.doc != null then for lp in lps do if lp.doc == null then introdoc = true end end if introdoc then dctx.add("
{global.intro.doc.to_html}
") end var tlmods = new Array[MMModule] for lp in lps do var bm = lp.mmmodule.toplevel_owner var lcm = lc.global.intro.mmmodule if lcm.mhe < lp.mmmodule then bm = lcm.toplevel_owner if not tlmods.has(bm) then tlmods.add(bm) end for tm in tlmods do # Document the top level property for the current top level module var tlp if tm.global_classes.has(lc.global) then tlp = tm[lc.global][self.global] assert lps.has(tlp) else if tm.global_classes.has(self.local_class.global) then # Self is the inherited property. Process it tlp = tm[self.local_class.global][self.global] assert lps.has(tlp) else # We skip this module since the props defined by the module is continue end var tlcm = lc.global.intro.mmmodule.toplevel_owner if not tlcm.mhe <= tm then dctx.add("

In module {tm.html_link(dctx)} :

") end #dctx.add("

TLP: {tm} x {lc} : {tlp.full_name}

") var doc = tlp.doc if doc != null and (not introdoc or global.intro.doc != doc) then dctx.add("
{doc.to_html}
") end dctx.add("

") if tlp.local_class.global != lc.global then dctx.add("inherited from {tlp.local_class.html_link(dctx)} ") end if tm != tlp.mmmodule then dctx.add("defined by the module {tlp.mmmodule.html_link(dctx)} ") end var n = tlp.node if n != null then var l = n.location dctx.show_source(l) end dctx.open_stage dctx.stage(". previously defined by:") for lp in lps do var tl = lp.mmmodule.toplevel_owner if tl != tm then continue if lp == tlp then continue dctx.add(" {lp.mmmodule.html_link(dctx)}") if lp.local_class.global != lc.global then dctx.add(" for {lp.local_class.html_link(dctx)}") end n = lp.node if n != null then var l = n.location dctx.show_source(l) end #var doc = lp.doc #if doc != null and (not introdoc or global.intro.doc != doc) then # dctx.add("

{doc.to_html}
") #end end dctx.close_stage dctx.add("

") end dctx.add("
") dctx.add("
") end end redef class MMMethod redef fun kind do return if global.is_init then "init" else "fun" end redef class MMAttribute redef fun kind do return "var" end redef class MMTypeProperty redef fun kind do return "type" end redef class Symbol # Replace < and > with html entities redef fun to_s do var ret = super.to_s if(ret.has('<')) then var parts = ret.split_with("<") ret = "" for i in [0..parts.length[ do ret += parts[i] if(i < parts.length - 1) then ret += "<" end end end if(ret.has('>')) then var parts = ret.split_with(">") ret = "" for i in [0..parts.length[ do ret += parts[i] if(i < parts.length - 1) then ret += ">" end end end return ret end end redef class MMSrcModule redef fun short_doc do var d = doc if d != null then return d.short else return " " end end redef fun doc do var n = node if n.n_moduledecl == null then return null end var np = n.n_moduledecl var d = np.n_doc if d == null then return null end if d.n_comment.is_empty then return null else return d end end end redef class ADoc # Html transcription of the doc fun to_html: String do var res = new Buffer for c in n_comment do res.append(c.text.substring_from(1)) end return res.to_s.html_escape end # Oneliner transcription of the doc fun short: String do return n_comment.first.text.substring_from(1).html_escape end end redef class MMLocalClass super MMEntity # Anchor of the class description in the module html file fun html_anchor: String do return "CLASS_{self}" fun html_name: String do return "{self}" redef fun html_link(dctx) do if not require_doc(dctx) then print "{dctx.filename}: not required {self}" return "{self}" end redef fun short_doc do return global.intro.short_doc redef fun doc do return global.intro.doc fun kind: String do if global.is_interface then return "interface" else if global.is_abstract then return "abstract class" else if global.is_enum then return "enum" else return "class" end end # The most specific module in the nesting hierarchy that exports the intro of self fun intro_module: MMModule do var m = global.intro.mmmodule var mo = m.direct_owner while mo != null and mo.visibility_for(m) >= 2 do m = mo mo = m.direct_owner end return m end fun menu_link(dctx: DocContext, p: MMLocalProperty) do if p.local_class.global != self.global then if p.global.intro.local_class.name == "Object".to_symbol then return if p.global.is_init or p isa MMTypeProperty then dctx.add("
  • H {p.html_link_special(dctx, self)}
  • \n") else dctx.add("
  • H {p.html_link(dctx)}
  • \n") end else if p.global.intro.local_class.global == self.global then dctx.add("
  • I {p.html_link_special(dctx, self)}
  • \n") else dctx.add("
  • R {p.html_link_special(dctx, self)}
  • \n") end end # Return true if the global class must be documented according to the visibility configured fun require_doc(dctx: DocContext): Bool do if global.visibility_level == 3 then return false # Private if dctx.public_only then var m = intro_module if m != m.toplevel_owner then return false # Unexported end return true end # Fill the body for the page associated to the global class fun file_page_doc(dctx: DocContext) do dctx.add("
    \n") var props = new Array[MMLocalProperty] var inh = new HashMap[MMLocalClass, Array[MMLocalProperty]] var inhs = new Array[MMLocalClass] for g in global_properties do var p = self[g] if not p.require_doc(dctx) then continue if p.local_class.global == global or g.is_init_for(self) or p isa MMTypeProperty then props.add(p) else var lc = mmmodule[p.local_class.global] if inh.has_key(lc) then inh[lc].add(p) else inh[lc] = [p] inhs.add(lc) end props.add(p) end end dctx.sort(props) dctx.add("\n") dctx.add("\n") dctx.add("
    \n") dctx.add("
    \n") dctx.add("

    {name}

    \n") dctx.add("
    ") if global.visibility_level == 2 then abort else if global.visibility_level == 3 then dctx.add("private ") else if self.global.intro.mmmodule.toplevel_owner.visibility_for(self.global.intro.mmmodule) <= 1 then dctx.add("(unexported) ") end dctx.add("{kind} {global.intro.mmmodule.toplevel_owner.html_link(dctx)}::{name}
    ") dctx.add("
    \n") var doc = doc if doc != null then dctx.add("
    {doc.to_html}
    \n") end var cla = new HashSet[MMLocalClass] var sm = new HashSet[MMLocalClass] var sm2 = new HashSet[MMLocalClass] sm.add(self) while cla.length + sm.length < 10 and sm.length > 0 do cla.add_all(sm) sm2.clear for x in sm do sm2.add_all(x.cshe.direct_smallers) end var t = sm sm = sm2 sm2 = t end cla.add_all(cshe.greaters_and_self) var op = new Buffer 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") for c in cla do if c == self then op.append("\"{c.name}\"[shape=box,margin=0.03];\n") else op.append("\"{c.name}\"[URL=\"{c.html_name}.html\"];\n") end for c2 in c.cshe.direct_greaters do if not cla.has(c2) then continue op.append("\"{c.name}\"->\"{c2.name}\";\n") end if not c.cshe.direct_smallers.is_empty then var others = true for c2 in c.cshe.direct_smallers do if cla.has(c2) then others = false end if others then op.append("\"{c.name}...\"[label=\"\"];\n") op.append("\"{c.name}...\"->\"{c.name}\"[style=dotted];\n") end end end op.append("\}\n") dctx.gen_dot(op.to_s, name.to_s, "Inheritance graph for class {name}") var mods = new Array[MMModule] mods.add(global.intro.mmmodule.toplevel_owner) for lc in crhe.greaters do if not lc isa MMSrcLocalClass then continue var m = lc.mmmodule.toplevel_owner if not mods.has(m) then mods.add(m) end dctx.sort(mods) for m in mods do if m == global.intro.mmmodule.toplevel_owner then dctx.add("

    Introduced by {m.html_link(dctx)}") else dctx.add("

    Refined by {m.html_link(dctx)}") end dctx.open_stage dctx.stage(". Definition in:") for lc in crhe.greaters do if lc.mmmodule.toplevel_owner != m then continue dctx.add(" {lc.mmmodule.html_link(dctx)}") assert lc isa MMSrcLocalClass var n = lc.node if n != null then dctx.show_source(n.location) end end dctx.close_stage dctx.add("

    \n") end dctx.add("
    \n") dctx.open_stage dctx.stage("
    \n") dctx.stage("

    Formal and Virtual Types

    \n") for i in [0..arity[ do var f = get_formal(i) f.full_documentation(dctx, self) end for p in props do if not p isa MMTypeProperty then continue p.full_documentation(dctx, self) end dctx.stage("
    \n") dctx.close_stage dctx.open_stage dctx.stage("
    \n") dctx.stage("

    Constructors

    \n") for p in props do if not p.global.is_init_for(self) then continue p.full_documentation(dctx, self) end dctx.stage("
    \n") dctx.close_stage dctx.open_stage dctx.stage("
    \n") dctx.stage("

    Methods

    \n") for p in props do if p.global.is_init then continue if p.local_class.global != self.global then continue if not p isa MMMethod then continue p.full_documentation(dctx, self) end if not inhs.is_empty then dctx.open_stage dctx.stage("

    Inherited Methods

    \n") for lc in inhs do dctx.open_stage dctx.stage("

    Defined in {lc.html_link(dctx)}:") for p in inh[lc] do if p.global.is_init then continue if not p isa MMMethod then continue dctx.add(" {p.html_link(dctx)}") end dctx.stage("

    ") dctx.close_stage end dctx.close_stage end dctx.add("
    \n") dctx.close_stage dctx.add("
    \n") end end redef class MMSrcLocalClass redef fun short_doc do var d = doc if d != null then return d.short else if global.intro == self then return " " else var bc = global.intro return bc.short_doc end end redef fun doc do var n = node if not n isa AStdClassdef then return null end var d = n.n_doc if d == null then return null end if d.n_comment.is_empty then return null else return d end end end redef class MMSignature # Htlm transcription of the signature (with nested links) fun to_html(dctx: DocContext, with_closure: Bool): String do var res = new Buffer if arity > 0 then res.append("(") res.append(self.params[0].name.to_s) res.append(": ") res.append(self[0].html_link(dctx)) for i in [1..arity[ do res.append(", ") res.append(self.params[i].name.to_s) res.append(": ") res.append(self[i].html_link(dctx)) end res.append(")") end if return_type != null then res.append(": ") res.append(return_type.html_link(dctx)) end if with_closure then for c in closures do res.append(" ") if c.is_optional then res.append("[") if c.is_break then res.append("break ") res.append("!{c.name}") res.append(c.signature.to_html(dctx, false)) if c.is_optional then res.append("]") end end return res.to_s end end redef class MMType # Htlm transcription of the type (with nested links) fun html_link(dctx: DocContext): String do return to_s end redef class MMTypeSimpleClass redef fun html_link(dctx) do return local_class.html_link(dctx) end redef class MMTypeGeneric redef fun html_link(dctx) do var res = new Buffer res.append(local_class.html_link(dctx)) res.append("[") res.append(params[0].html_link(dctx)) for i in [1..params.length[ do res.append(", ") res.append(params[i].html_link(dctx)) end res.append("]") return res.to_s end end redef class MMTypeFormalParameter fun html_anchor: String do return "FT_{local_class}_{cmangle(name)}" end redef fun html_link(dctx) do return "{name}" end fun full_documentation(dctx: DocContext, lc: MMLocalClass) do dctx.add("
    \n") dctx.add("

    {name}: {bound.html_link(dctx)}

    \n") dctx.add("
    ") dctx.add("formal generic type") dctx.add("
    ") dctx.add("
    ") end end redef class MMNullableType redef fun html_link(dctx) do return "nullable " + as_notnull.html_link(dctx) end redef class MMVirtualType redef fun html_link(dctx) do return property.html_link(dctx) end var c = new DocContext c.exec_cmd_line