# This file is part of NIT ( http://www.nitlanguage.org ). # # 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. # Nitdoc page generation module doc_pages import toolcontext import doc_model private import json::static redef class ToolContext private var opt_dir = new OptionString("output directory", "-d", "--dir") private var opt_source = new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source") private var opt_sharedir = new OptionString("directory containing nitdoc assets", "--sharedir") private var opt_shareurl = new OptionString("use shareurl instead of copy shared files", "--shareurl") private var opt_nodot = new OptionBool("do not generate graphes with graphviz", "--no-dot") private var opt_custom_title = new OptionString("custom title for homepage", "--custom-title") private var opt_custom_brand = new OptionString("custom link to external site", "--custom-brand") private var opt_custom_intro = new OptionString("custom intro text for homepage", "--custom-overview-text") private var opt_custom_footer = new OptionString("custom footer text", "--custom-footer-text") private var opt_github_upstream = new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream") private var opt_github_base_sha1 = new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1") private var opt_github_gitdir = new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir") private var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker") private var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id") private var output_dir: String private var min_visibility: MVisibility redef init do super var opts = option_context opts.add_option(opt_dir, opt_source, opt_sharedir, opt_shareurl, opt_nodot) opts.add_option(opt_custom_title, opt_custom_footer, opt_custom_intro, opt_custom_brand) opts.add_option(opt_github_upstream, opt_github_base_sha1, opt_github_gitdir) opts.add_option(opt_piwik_tracker, opt_piwik_site_id) end redef fun process_options(args) do super # output dir var output_dir = opt_dir.value if output_dir == null then output_dir = "doc" end self.output_dir = output_dir # github urls var gh_upstream = opt_github_upstream.value var gh_base_sha = opt_github_base_sha1.value var gh_gitdir = opt_github_gitdir.value if not gh_upstream == null or not gh_base_sha == null or not gh_gitdir == null then if gh_upstream == null or gh_base_sha == null or gh_gitdir == null then print "Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin" abort end end end end # The Nitdoc class explores the model and generate pages for each mentities found class Nitdoc var ctx: ToolContext var model: Model var mainmodule: MModule private fun init_output_dir do # create destination dir if it's necessary var output_dir = ctx.output_dir if not output_dir.file_exists then output_dir.mkdir # locate share dir var sharedir = ctx.opt_sharedir.value if sharedir == null then var dir = ctx.nit_dir sharedir = dir/"share/nitdoc" if not sharedir.file_exists then print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR" abort end end # copy shared files if ctx.opt_shareurl.value == null then sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/") else sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/resources/ {output_dir.to_s.escape_to_sh}/resources/") end end end # Nitdoc QuickSearch list generator # # Create a JSON object containing links to: # * modules # * mclasses # * mpropdefs # All entities are grouped by name to make the research easier. class QuickSearch private var table = new QuickSearchTable var ctx: ToolContext var model: Model init do for mmodule in model.mmodules do if mmodule.is_fictive or mmodule.is_test_suite then continue add_result_for(mmodule.name, mmodule.full_name, mmodule.nitdoc_url) end for mclass in model.mclasses do if not ctx.filter_mclass(mclass) then continue add_result_for(mclass.name, mclass.full_name, mclass.nitdoc_url) end for mproperty in model.mproperties do if not ctx.filter_mproperty(mproperty) then continue for mpropdef in mproperty.mpropdefs do var full_name = mpropdef.mclassdef.mclass.full_name var cls_url = mpropdef.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}" add_result_for(mproperty.name, full_name, def_url) end end end private fun add_result_for(query: String, txt: String, url: String) do table[query].add new QuickSearchResult(txt, url) end fun render: Template do var tpl = new Template var buffer = new RopeBuffer tpl.add buffer buffer.append "var nitdocQuickSearchRawList=" table.append_json buffer buffer.append ";" return tpl end end # The result map for QuickSearch. private class QuickSearchTable super JsonMapRead[String, QuickSearchResultList] super HashMap[String, QuickSearchResultList] redef fun provide_default_value(key) do var v = new QuickSearchResultList self[key] = v return v end end # A QuickSearch result list. private class QuickSearchResultList super JsonSequenceRead[QuickSearchResult] super Array[QuickSearchResult] end # A QuickSearch result. private class QuickSearchResult super Jsonable # The text of the link. var txt: String # The destination of the link. var url: String redef fun to_json do return "\{\"txt\":{txt.to_json},\"url\":{url.to_json}\}" end end # Nitdoc base page # Define page structure and properties abstract class NitdocPage private var ctx: ToolContext private var model: Model private var mainmodule: MModule private var name_sorter = new MEntityNameSorter # Render the page as a html template fun render: Template do var shareurl = "." if ctx.opt_shareurl.value != null then shareurl = ctx.opt_shareurl.value.as(not null) end # build page var tpl = tpl_page tpl.title = tpl_title tpl.url = page_url tpl.shareurl = shareurl tpl.topmenu = tpl_topmenu tpl_content tpl.footer = ctx.opt_custom_footer.value tpl.body_attrs.add(new TagAttribute("data-bootstrap-share", shareurl)) tpl.sidebar = tpl_sidebar # piwik tracking var tracker_url = ctx.opt_piwik_tracker.value var site_id = ctx.opt_piwik_site_id.value if tracker_url != null and site_id != null then tpl.scripts.add new TplPiwikScript(tracker_url, site_id) end return tpl end # URL to this page. fun page_url: String is abstract # Build page template fun tpl_page: TplPage is abstract # Build page sidebar if any fun tpl_sidebar: nullable TplSidebar do return null # Build page title string fun tpl_title: String do if ctx.opt_custom_title.value != null then return ctx.opt_custom_title.value.to_s end return "Nitdoc" end # Build top menu template fun tpl_topmenu: TplTopMenu do var topmenu = new TplTopMenu(page_url) var brand = ctx.opt_custom_brand.value if brand != null then var tpl = new Template tpl.add "" tpl.add brand tpl.add "" topmenu.brand = tpl end topmenu.add_link new TplLink("index.html", "Overview") topmenu.add_link new TplLink("search.html", "Index") return topmenu end # Build page content template fun tpl_content is abstract # Clickable graphviz image using dot format # return null if no graph for this page fun tpl_graph(dot: Buffer, name: String, title: nullable String): nullable TplArticle do if ctx.opt_nodot.value then return null var output_dir = ctx.output_dir var path = output_dir / name var path_sh = path.escape_to_sh var file = new OFStream.open("{path}.dot") file.write(dot) file.close sys.system("\{ test -f {path_sh}.png && test -f {path_sh}.s.dot && diff -- {path_sh}.dot {path_sh}.s.dot >/dev/null 2>&1 ; \} || \{ cp -- {path_sh}.dot {path_sh}.s.dot && dot -Tpng -o{path_sh}.png -Tcmapx -o{path_sh}.map {path_sh}.s.dot ; \}") var fmap = new IFStream.open("{path}.map") var map = fmap.read_all fmap.close var article = new TplArticle("graph") var alt = "" if title != null then article.title = title alt = "alt='{title.html_escape}'" end article.css_classes.add "text-center" var content = new Template var name_html = name.html_escape content.add "" content.add map article.content = content return article end # A (source) link template for a given location fun tpl_showsource(location: nullable Location): nullable String do if location == null then return null var source = ctx.opt_source.value if source == null then var url = location.file.filename.simplify_path return "View Source" end # THIS IS JUST UGLY ! (but there is no replace yet) var x = source.split_with("%f") source = x.join(location.file.filename.simplify_path) x = source.split_with("%l") source = x.join(location.line_start.to_s) x = source.split_with("%L") source = x.join(location.line_end.to_s) source = source.simplify_path return "View Source" end # MProject description template fun tpl_mproject_article(mproject: MProject): TplArticle do var article = mproject.tpl_article article.subtitle = mproject.tpl_declaration article.content = mproject.tpl_definition var mdoc = mproject.mdoc_or_fallback if mdoc != null then article.content = mdoc.tpl_short_comment end return article end # MGroup description template fun tpl_mgroup_article(mgroup: MGroup): TplArticle do var article = mgroup.tpl_article article.subtitle = mgroup.tpl_declaration article.content = mgroup.tpl_definition return article end # MModule description template fun tpl_mmodule_article(mmodule: MModule): TplArticle do var article = mmodule.tpl_article article.subtitle = mmodule.tpl_declaration article.content = mmodule.tpl_definition # mclassdefs list var intros = mmodule.intro_mclassdefs(ctx.min_visibility).to_a if not intros.is_empty then mainmodule.linearize_mclassdefs(intros) var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}.intros", "Introduces") var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mclassdef in intros do intros_lst.add_li mclassdef.tpl_list_item end if not intros_lst.is_empty then intros_art.content = intros_lst article.add_child intros_art end end var redefs = mmodule.redef_mclassdefs(ctx.min_visibility).to_a if not redefs.is_empty then mainmodule.linearize_mclassdefs(redefs) var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}.redefs", "Redefines") var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mclassdef in redefs do redefs_lst.add_li mclassdef.tpl_list_item end if not redefs_lst.is_empty then redefs_art.content = redefs_lst article.add_child redefs_art end end return article end # MClassDef description template fun tpl_mclass_article(mclass: MClass, mclassdefs: Array[MClassDef]): TplArticle do var article = mclass.tpl_article if not mclassdefs.has(mclass.intro) then # add intro synopsys var intro_article = mclass.intro.tpl_short_article intro_article.source_link = tpl_showsource(mclass.intro.location) article.add_child intro_article end mainmodule.linearize_mclassdefs(mclassdefs) for mclassdef in mclassdefs do # add mclassdef full description var redef_article = mclassdef.tpl_article redef_article.source_link = tpl_showsource(mclassdef.location) article.add_child redef_article # mpropdefs list var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}.intros", "Introduces") var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in mclassdef.collect_intro_mpropdefs(ctx.min_visibility) do intros_lst.add_li mpropdef.tpl_list_item end if not intros_lst.is_empty then intros.content = intros_lst redef_article.add_child intros end var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}.redefs", "Redefines") var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in mclassdef.collect_redef_mpropdefs(ctx.min_visibility) do redefs_lst.add_li mpropdef.tpl_list_item end if not redefs_lst.is_empty then redefs.content = redefs_lst redef_article.add_child redefs end end return article end # MClassDef description template fun tpl_mclassdef_article(mclassdef: MClassDef): TplArticle do var article = mclassdef.tpl_article if mclassdef.is_intro then article.content = null article.source_link = tpl_showsource(mclassdef.location) return article end # MProp description template # # `main_mpropdef`: The most important mpropdef to display # `local_mpropdefs`: List of other locally defined mpropdefs to display # `lin`: full linearization from local_mpropdefs to intro (displayed in redef tree) fun tpl_mprop_article(main_mpropdef: MPropDef, local_mpropdefs: Array[MPropDef], lin: Array[MPropDef]): TplArticle do var mprop = main_mpropdef.mproperty var article = new TplArticle(mprop.nitdoc_id) var title = new Template title.add mprop.tpl_icon title.add "" if main_mpropdef.is_intro then title.add mprop.tpl_link title.add mprop.intro.tpl_signature else var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mprop.nitdoc_id}" var lnk = new TplLink.with_title(def_url, mprop.nitdoc_name, "Go to introduction") title.add "redef " title.add lnk end article.title = title article.title_classes.add "signature" article.summary_title = "{mprop.nitdoc_name}" article.subtitle = main_mpropdef.tpl_namespace if main_mpropdef.mdoc_or_fallback != null then article.content = main_mpropdef.mdoc_or_fallback.tpl_comment end var subarticle = new TplArticle("{main_mpropdef.nitdoc_id}.redefs") # Add redef in same `MClass` if local_mpropdefs.length > 1 then for mpropdef in local_mpropdefs do if mpropdef == main_mpropdef then continue var redef_article = new TplArticle("{mpropdef.nitdoc_id}") var redef_title = new Template redef_title.add "also redef in " redef_title.add mpropdef.tpl_namespace redef_article.title = redef_title redef_article.title_classes.add "signature info" redef_article.css_classes.add "nospace" var redef_content = new Template if mpropdef.mdoc != null then redef_content.add mpropdef.mdoc.tpl_comment end redef_article.content = redef_content subarticle.add_child redef_article end end # Add linearization if lin.length > 1 then var lin_article = new TplArticle("{main_mpropdef.nitdoc_id}.lin") lin_article.title = "Inheritance" var lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in lin do lst.add_li mpropdef.tpl_inheritance_item end lin_article.content = lst subarticle.add_child lin_article end article.add_child subarticle return article end # MProperty description template fun tpl_mpropdef_article(mpropdef: MPropDef): TplArticle do var article = mpropdef.tpl_article article.source_link = tpl_showsource(mpropdef.location) return article end end # The overview page # Display a list of modules contained in program class NitdocOverview super NitdocPage private var page = new TplPage redef fun tpl_page do return page private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar redef fun tpl_title do if ctx.opt_custom_title.value != null then return ctx.opt_custom_title.value.to_s else return "Overview" end end redef fun page_url do return "index.html" end # The search page # Display a list of modules, classes and properties class NitdocSearch super NitdocPage private var page = new TplPage redef fun tpl_page do return page redef fun tpl_title do return "Index" redef fun page_url do return "search.html" end # A group page # Display a flattened view of the group class NitdocGroup super NitdocPage private var mgroup: MGroup private var page = new TplPage redef fun tpl_page do return page private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar redef fun tpl_title do return mgroup.nitdoc_name redef fun page_url do return mgroup.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mproject = mgroup.mproject if not mgroup.is_root then topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name) end topmenu.add_link new TplLink(page_url, mproject.nitdoc_name) return topmenu end # Class list to display in sidebar fun tpl_sidebar_mclasses do var mclasses = new HashSet[MClass] mclasses.add_all intros mclasses.add_all redefs if mclasses.is_empty then return var list = new TplList.with_classes(["list-unstyled", "list-labeled"]) var sorted = mclasses.to_a name_sorter.sort(sorted) for mclass in sorted do list.add_li tpl_sidebar_item(mclass) end tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list) end private fun tpl_sidebar_item(def: MClass): TplListItem do var classes = def.intro.tpl_css_classes.to_a if intros.has(def) then classes.add "intro" else classes.add "redef" end var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add def.tpl_link return new TplListItem.with_content(lnk) end end # A module page # Display the list of introduced and redefined classes in module class NitdocModule super NitdocPage private var mmodule: MModule private var page = new TplPage redef fun tpl_page do return page private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar redef fun tpl_title do return mmodule.nitdoc_name redef fun page_url do return mmodule.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mproject = mmodule.mgroup.mproject topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name) topmenu.add_link new TplLink(page_url, mmodule.nitdoc_name) return topmenu end # Class list to display in sidebar fun tpl_sidebar_mclasses do var mclasses = new HashSet[MClass] mclasses.add_all mmodule.filter_intro_mclasses(ctx.min_visibility) mclasses.add_all mmodule.filter_redef_mclasses(ctx.min_visibility) if mclasses.is_empty then return var list = new TplList.with_classes(["list-unstyled", "list-labeled"]) var sorted = mclasses.to_a name_sorter.sort(sorted) for mclass in sorted do list.add_li tpl_sidebar_item(mclass) end tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list) end private fun tpl_sidebar_item(def: MClass): TplListItem do var classes = def.intro.tpl_css_classes.to_a if def.intro_mmodule == mmodule then classes.add "intro" else classes.add "redef" end var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add def.tpl_link return new TplListItem.with_content(lnk) end # inheritance section private fun tpl_inheritance(parent: TplSection) do # Display lists var section = new TplSection.with_title("dependencies", "Dependencies") # Graph var mmodules = new HashSet[MModule] mmodules.add_all mmodule.nested_mmodules mmodules.add_all imports if clients.length < 10 then mmodules.add_all clients mmodules.add mmodule var graph = tpl_dot(mmodules) if graph != null then section.add_child graph # Imports var lst = new Array[MModule] if not lst.is_empty then name_sorter.sort lst section.add_child tpl_list("imports", "Imports", lst) end # Clients lst = new Array[MModule] if not lst.is_empty then name_sorter.sort lst section.add_child tpl_list("clients", "Clients", lst) end parent.add_child section end private fun tpl_list(id: String, title: String, mmodules: Array[MModule]): TplArticle do var article = new TplArticle.with_title(id, title) var list = new TplList.with_classes(["list-unstyled", "list-definition"]) for mmodule in mmodules do list.elts.add mmodule.tpl_list_item article.content = list return article end # Genrate dot hierarchy for class inheritance fun tpl_dot(mmodules: Collection[MModule]): nullable TplArticle do # build graph var op = new RopeBuffer var name = "dep_module_{mmodule.nitdoc_id}" op.append("digraph \"{name.escape_to_dot}\" \{ 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 mmodule in poset do if mmodule == self.mmodule then op.append("\"{mmodule.name.escape_to_dot}\"[shape=box,margin=0.03];\n") else op.append("\"{mmodule.name.escape_to_dot}\"[URL=\"{mmodule.nitdoc_url.escape_to_dot}\"];\n") end for omodule in poset[mmodule].direct_greaters do op.append("\"{mmodule.name.escape_to_dot}\"->\"{omodule.name.escape_to_dot}\";\n") end end op.append("\}\n") return tpl_graph(op, name, null) end end # A class page # Display a list properties defined or redefined for this class class NitdocClass super NitdocPage private var mclass: MClass private var page = new TplPage redef fun tpl_page do return page private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar redef fun tpl_title do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}" redef fun page_url do return mclass.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mproject = mclass.intro_mmodule.mgroup.mproject topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}") topmenu.add_link new TplLink(page_url, mclass.nitdoc_name) return topmenu end # Property list to display in sidebar fun tpl_sidebar_properties do var by_kind = new PropertiesByKind.with_elements(mclass_inherited_mprops) var summary = new TplList.with_classes(["list-unstyled"]) by_kind.sort_groups(name_sorter) for g in by_kind.groups do tpl_sidebar_list(g, summary) tpl_sidebar.boxes.add new TplSideBox.with_content("All properties", summary) end private fun tpl_sidebar_list(mprops: PropertyGroup[MProperty], summary: TplList) do if mprops.is_empty then return var entry = new TplListItem.with_content(mprops.title) var list = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mprop in mprops do list.add_li tpl_sidebar_item(mprop) end entry.append list summary.elts.add entry end private fun tpl_sidebar_item(mprop: MProperty): TplListItem do var classes = mprop.intro.tpl_css_classes.to_a if not mprops2mdefs.has_key(mprop) then classes.add "inherit" var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mprop.nitdoc_id}" var lnk = new TplLink(def_url, mprop.nitdoc_name) var mdoc = mprop.intro.mdoc_or_fallback if mdoc != null then lnk.title = mdoc.short_comment var item = new Template item.add new TplLabel.with_classes(classes) item.add lnk return new TplListItem.with_content(item) end var defs = mprops2mdefs[mprop] if defs.has(mprop.intro) then classes.add "intro" else classes.add "redef" end var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add mprop.tpl_anchor return new TplListItem.with_content(lnk) end private fun tpl_inheritance(parent: TplSection) do # Display lists var section = new TplSection.with_title("inheritance", "Inheritance") # Graph var mclasses = new HashSet[MClass] mclasses.add_all hancestors mclasses.add_all hparents mclasses.add_all hchildren mclasses.add_all hdescendants mclasses.add mclass var graph = tpl_dot(mclasses) if graph != null then section.add_child graph # parents if not hparents.is_empty then var lst = hparents.to_a name_sorter.sort lst section.add_child tpl_list("parents", "Parents", lst) end # ancestors if not hancestors.is_empty then var lst = hancestors.to_a name_sorter.sort lst section.add_child tpl_list("ancestors", "Ancestors", lst) end # children if not hchildren.is_empty then var lst = hchildren.to_a name_sorter.sort lst section.add_child tpl_list("children", "Children", lst) end # descendants if not hdescendants.is_empty then var lst = hdescendants.to_a name_sorter.sort lst section.add_child tpl_list("descendants", "Descendants", lst) end parent.add_child section end private fun tpl_list(id: String, title: String, elts: Array[MClass]): TplArticle do var article = new TplArticle.with_title(id, title) if elts.length > 20 then var tpl = new Template for e in elts do tpl.add e.tpl_link if e != elts.last then tpl.add ", " end article.content = tpl else var list = new TplList.with_classes(["list-unstyled", "list-definition"]) for elt in elts do list.elts.add elt.tpl_list_item article.content = list end return article end # Generate dot hierarchy for classes fun tpl_dot(mclasses: Collection[MClass]): nullable TplArticle do var op = new RopeBuffer var name = "dep_class_{mclass.nitdoc_id}" op.append("digraph \"{name.escape_to_dot}\" \{ 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 classes = poset.to_a var todo = new Array[MClass] var done = new HashSet[MClass] mainmodule.linearize_mclasses(classes) if not classes.is_empty then todo.add classes.first while not todo.is_empty do var c = todo.shift if done.has(c) then continue done.add c if c == mclass then op.append("\"{c.name.escape_to_dot}\"[shape=box,margin=0.03];\n") else op.append("\"{c.name.escape_to_dot}\"[URL=\"{c.nitdoc_url.escape_to_dot}\"];\n") end var smallers = poset[c].direct_smallers if smallers.length < 10 then for c2 in smallers do op.append("\"{c2.name.escape_to_dot}\"->\"{c.name.escape_to_dot}\";\n") end todo.add_all smallers else op.append("\"...\"->\"{c.name.escape_to_dot}\";\n") end end op.append("\}\n") return tpl_graph(op, name, null) end end # A MProperty page class NitdocProperty super NitdocPage private var mproperty: MProperty private var page = new TplPage redef fun tpl_page do return page private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar redef fun tpl_title do return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}" end redef fun page_url do return mproperty.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mmodule = mproperty.intro_mclassdef.mmodule var mproject = mmodule.mgroup.mproject var mclass = mproperty.intro_mclassdef.mclass topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}") topmenu.add_link new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}") topmenu.add_link new TplLink(page_url, mproperty.nitdoc_name) return topmenu end end