From: Alexandre Terrasa Date: Fri, 30 Aug 2013 19:51:34 +0000 (-0400) Subject: nitx: rethinked architecture for further developments X-Git-Tag: v0.6.2~41^2 X-Git-Url: http://nitlanguage.org nitx: rethinked architecture for further developments Signed-off-by: Alexandre Terrasa --- diff --git a/src/nitx.nit b/src/nitx.nit index a5f022c..1931b5c 100644 --- a/src/nitx.nit +++ b/src/nitx.nit @@ -19,30 +19,6 @@ import model_utils import modelize_property import frontend -private class Pager - var content = new Buffer - fun add(text: String) do addn("{text}\n") - fun addn(text: String) do content.append(text.escape) - fun add_rule do add("\n---\n") - fun render do sys.system("echo \"{content}\" | pager -r") -end - -private class Query - var keyword: String - init(keyword: String) do - self.keyword = keyword - end -end - -private class QueryPair - super Query - var category: String - init(keyword: String, category: String) do - super(keyword) - self.category = category - end -end - # Main class of the nit index tool # NitIndex build the model using the toolcontext argument # then wait for query on std in to display documentation @@ -52,6 +28,7 @@ class NitIndex private var mbuilder: ModelBuilder private var mainmodule: MModule private var arguments: Array[String] + private var renderer: PagerMatchesRenderer init(toolcontext: ToolContext) do # We need a model to collect stufs @@ -76,6 +53,8 @@ class NitIndex mbuilder.run_phases assert mmodules.length == 1 self.mainmodule = mmodules.first + + renderer = new PagerMatchesRenderer(self) end fun start do @@ -83,7 +62,7 @@ class NitIndex welcome prompt else - seek(arguments[1]) + search(arguments[1]) end end @@ -115,10 +94,10 @@ class NitIndex fun prompt do printn ">> " - seek(stdin.read_line) + search(stdin.read_line) end - fun seek(entry: String) do + fun search(entry: String) do if entry.is_empty then prompt return @@ -129,351 +108,115 @@ class NitIndex return end if entry == ":q" then exit(0) - var pager = new Pager + + # Parse query string var query = parse_query(entry) - if query isa QueryPair then - # seek return types + + # search in index + var matches = new HashSet[IndexMatch] + if query isa IndexQueryPair then if query.category == "return" then - var matches = seek_returns(query.keyword) - props_fulldoc(pager, matches) - # seek param types + # seek return types + matches.add_all(search_returns(query)) else if query.category == "param" then - var matches = seek_params(query.keyword) - props_fulldoc(pager, matches) - # seek type inits + # seek param types + matches.add_all(search_params(query)) else if query.category == "new" then - var matches = seek_inits(query.keyword) - props_fulldoc(pager, matches) + # seek type inits + matches.add_all(search_inits(query)) end else - # seek for modules - var mmatches = new List[MModule] - for m in model.mmodules do - if m.name == entry then mmatches.add(m) - end - if not mmatches.is_empty then modules_fulldoc(pager, mmatches) - # seek for classes - var cmatches = new List[MClass] - for c in model.mclasses do - if c.name == entry then cmatches.add(c) - end - if not cmatches.is_empty then classes_fulldoc(pager, cmatches) - # seek for properties - var matches = new List[MProperty] - for p in model.mproperties do - if p.name == entry then matches.add(p) - end - if not matches.is_empty then props_fulldoc(pager, matches) + matches.add_all(search_modules(query)) + matches.add_all(search_classes(query)) + matches.add_all(search_properties(query)) end # no matches - if pager.content.is_empty then - print "Nothing known about '{entry}', type ':h' for help" + if matches.is_empty then + print "Nothing known about '{query.string}', type ':h' for help" else - pager.render + renderer.render_matches(query, matches) end if arguments.length == 1 then prompt end - private fun parse_query(str: String): Query do + private fun parse_query(str: String): IndexQuery do var parts = str.split_with(":") if parts.length == 1 then - return new Query(parts[0]) + return new IndexQuery(str, parts[0]) else var category = parts[0] var keyword = parts[1] if keyword.first == ' ' then keyword = keyword.substring_from(1) - return new QueryPair(keyword, category) + return new IndexQueryPair(str, keyword, category) end end - private fun modules_fulldoc(pager: Pager, mmodules: List[MModule]) do - for mmodule in mmodules do - # name and prototype - pager.add("# {mmodule.namespace}\n".bold) - # comment - if mbuilder.mmodule2nmodule.has_key(mmodule) then - var nmodule = mbuilder.mmodule2nmodule[mmodule] - if not nmodule.n_moduledecl.n_doc == null then - for comment in nmodule.n_moduledecl.n_doc.comment do pager.add(comment.green) - end - end - pager.add("{mmodule.prototype}\n") - # imports - var msorter = new MModuleNameSorter - var ms = mmodule.in_importation.greaters.to_a - if ms.length > 1 then - msorter.sort(ms) - pager.add("## imported modules".bold) - pager.addn("\t") - for i in [0..ms.length[ do - if ms[i] == mmodule then continue - pager.addn(ms[i].name) - if i < ms.length - 1 then pager.addn(", ") - end - pager.add("\n") - end - # clients - ms = mmodule.in_importation.smallers.to_a - if ms.length > 1 then - msorter.sort(ms) - pager.add("## known modules".bold) - pager.addn("\t") - for i in [0..ms.length[ do - if ms[i] == mmodule then continue - pager.addn(ms[i].name) - if i < ms.length - 1 then pager.addn(", ") - end - pager.add("\n") - end - # local classes and interfaces - var sorter = new MClassDefNameSorter - var intro_mclassdefs = new Array[MClassDef] - var redef_mclassdefs = new Array[MClassDef] - for mclassdef in mmodule.mclassdefs do - if mclassdef.is_intro then - intro_mclassdefs.add(mclassdef) - else - redef_mclassdefs.add(mclassdef) - end - end - # intro - if not intro_mclassdefs.is_empty then - sorter.sort(intro_mclassdefs) - pager.add("\n## introduced classes".bold) - for mclassdef in intro_mclassdefs do - pager.add("") - var nclass = mbuilder.mclassdef2nclassdef[mclassdef] - if nclass isa AStdClassdef and not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then - pager.add("\t{nclass.n_doc.short_comment.green}") - end - pager.add("\t{mclassdef.mclass.prototype}") - #TODO add redefs? - end - end - # redefs - if not redef_mclassdefs.is_empty then - sorter.sort(redef_mclassdefs) - pager.add("\n## refined classes".bold) - for mclassdef in redef_mclassdefs do - pager.add("") - #TODO intro comment? - var nclass = mbuilder.mclassdef2nclassdef[mclassdef] - if nclass isa AStdClassdef and not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then - pager.add("\t# {nclass.n_doc.short_comment.green}") - end - pager.add("\t{mclassdef.mclass.prototype}") - pager.add("\t\t" + "introduced in {mclassdef.mclass.intro.mmodule.namespace.bold}".gray) - for odef in mclassdef.mclass.mclassdefs do - if odef.is_intro or odef == mclassdef or mclassdef.mmodule == mmodule then continue - pager.add("\t\t" + "refined in {mclassdef.mmodule.namespace.bold}".gray) - end - end - end - #TODO add inherited classes? - pager.add_rule + # search for modules + private fun search_modules(query: IndexQuery): Set[MModule] do + var matches = new HashSet[MModule] + for mmodule in model.mmodules do + if mmodule.name == query.keyword then matches.add(mmodule) end + return matches end - private fun classes_fulldoc(pager: Pager, mclasses: List[MClass]) do - for mclass in mclasses do - # title - pager.add("# {mclass.namespace}\n".bold) - # comment - if mbuilder.mclassdef2nclassdef.has_key(mclass.intro) then - var nclass = mbuilder.mclassdef2nclassdef[mclass.intro] - if nclass isa AStdClassdef and not nclass.n_doc == null then - for comment in nclass.n_doc.comment do pager.add(comment.green) - end - end - pager.addn("{mclass.prototype}") - if not mclass.in_hierarchy(mainmodule).direct_greaters.is_empty then - var supers = mclass.in_hierarchy(mainmodule).direct_greaters.to_a - pager.addn(" super ") - for i in [0..supers.length[ do - if supers[i] == mclass then continue - pager.addn("{supers[i].name}{supers[i].signature}") - if i < mclass.in_hierarchy(mainmodule).direct_greaters.length -1 then pager.addn(", ") - end - end - pager.add("\n") - # formal types - if not mclass.parameter_types.is_empty then - pager.add("## formal types".bold) - for ft, bound in mclass.parameter_types do - pager.add("") - pager.add("\t{ft.to_s.green}: {bound.to_console}") - end - pager.add("") - end - # get properties - var cats = new ArrayMap[String, Set[MPropDef]] - cats["virtual types"] = new HashSet[MPropDef] - cats["constructors"] = new HashSet[MPropDef] - cats["introduced methods"] = new HashSet[MPropDef] - cats["refined methods"] = new HashSet[MPropDef] - - for mclassdef in mclass.mclassdefs do - for mpropdef in mclassdef.mpropdefs do - if mpropdef isa MAttributeDef then continue - if mpropdef isa MVirtualTypeDef then cats["virtual types"].add(mpropdef) - if mpropdef isa MMethodDef then - if mpropdef.mproperty.is_init then - cats["constructors"].add(mpropdef) - else if mpropdef.is_intro then - cats["introduced methods"].add(mpropdef) - else - cats["refined methods"].add(mpropdef) - end - end - end - end - # local mproperties - for cat, list in cats do - if not list.is_empty then - #sort list - var sorted = new Array[MPropDef] - sorted.add_all(list) - var sorter = new MPropDefNameSorter - sorter.sort(sorted) - pager.add("## {cat}".bold) - for mpropdef in sorted do - pager.add("") - if mbuilder.mpropdef2npropdef.has_key(mpropdef) then - var nprop = mbuilder.mpropdef2npropdef[mpropdef] - if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then - for comment in nprop.n_doc.comment do pager.add("\t{comment.green}") - else - nprop = mbuilder.mpropdef2npropdef[mpropdef.mproperty.intro] - if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then - for comment in nprop.n_doc.comment do pager.add("\t{comment.green}") - end - end - end - pager.add("\t{mpropdef.to_console}") - mainmodule.linearize_mpropdefs(mpropdef.mproperty.mpropdefs) - var previous_defs = new Array[MPropDef] - for def in mpropdef.mproperty.mpropdefs do - if def == mpropdef then continue - if def.is_intro then continue - if mclass.in_hierarchy(mainmodule) < def.mclassdef.mclass then - previous_defs.add(def) - end - end - if not mpropdef.is_intro then - pager.add("\t\t" + "introduced by {mpropdef.mproperty.intro.mclassdef.namespace.bold}".gray) - end - if not previous_defs.is_empty then - for def in previous_defs do pager.add("\t\t" + "inherited from {def.mclassdef.namespace.bold}".gray) - end - end - pager.add("") - end - end - # inherited mproperties - var inhs = new ArrayMap[MClass, Array[MProperty]] - var ancestors = mclass.in_hierarchy(mainmodule).greaters.to_a - mainmodule.linearize_mclasses(ancestors) - for a in ancestors do - if a == mclass then continue - for c in a.mclassdefs do - for p in c.intro_mproperties do - if p.intro_mclassdef == c then - if not inhs.has_key(a) then inhs[a] = new Array[MProperty] - inhs[a].add(p) - end - end - end - end - if not inhs.is_empty then - pager.add("## inherited properties".bold) - for a, ps in inhs do - pager.add("\n\tfrom {a.namespace.bold}: {ps.join(", ")}") - end - end - pager.add_rule + # search for classes + private fun search_classes(query: IndexQuery): Set[MClass] do + var matches = new HashSet[MClass] + for mclass in model.mclasses do + if mclass.name == query.keyword then matches.add(mclass) end + return matches end - private fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do - # group by module - var cats = new HashMap[MModule, Array[MProperty]] - for mprop in raw_mprops do - if mprop isa MAttribute then continue - var key = mprop.intro.mclassdef.mmodule - if not cats.has_key(key) then cats[key] = new Array[MProperty] - cats[key].add(mprop) - end - #sort groups - var sorter = new MModuleNameSorter - var sorted = new Array[MModule] - sorted.add_all(cats.keys) - sorter.sort(sorted) - # display - for mmodule in sorted do - var mprops = cats[mmodule] - pager.add("# matches in module {mmodule.namespace.bold}") - var sorterp = new MPropertyNameSorter - sorterp.sort(mprops) - for mprop in mprops do - pager.add("") - if mbuilder.mpropdef2npropdef.has_key(mprop.intro) then - var nprop = mbuilder.mpropdef2npropdef[mprop.intro] - if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then - for comment in nprop.n_doc.comment do pager.add("\t{comment.green}") - end - end - pager.add("\t{mprop.intro.to_console}") - pager.add("\t\t" + "introduced in {mprop.intro_mclassdef.namespace.bold}".gray) - var mpropdefs = mprop.mpropdefs - mainmodule.linearize_mpropdefs(mpropdefs) - for mpdef in mpropdefs do - if not mpdef.is_intro then - pager.add("\t\t" + "refined in {mpdef.mclassdef.namespace.bold}".gray) - end - end - end - pager.add_rule + # search for properties + private fun search_properties(query: IndexQuery): Set[MProperty] do + var matches = new HashSet[MProperty] + for mproperty in model.mproperties do + if mproperty.name == query.keyword then matches.add(mproperty) end + return matches end - private fun seek_returns(entry: String): List[MProperty] do - var matches = new List[MProperty] - for mprop in model.mproperties do - var intro = mprop.intro + # search for mpropdef returning keyword + private fun search_returns(query: IndexQuery): Set[MProperty] do + var matches = new HashSet[MProperty] + for mproperty in model.mproperties do + var intro = mproperty.intro if intro isa MMethodDef then - if intro.msignature.return_mtype != null and intro.msignature.return_mtype.to_console.has_prefix(entry) then matches.add(mprop) + if intro.msignature.return_mtype != null and intro.msignature.return_mtype.to_console.has_prefix(query.keyword) then matches.add(mproperty) else if intro isa MAttributeDef then - if intro.static_mtype.to_console.has_prefix(entry) then matches.add(mprop) + if intro.static_mtype.to_console.has_prefix(query.keyword) then matches.add(mproperty) end end return matches end - private fun seek_params(entry: String): List[MProperty] do - var matches = new List[MProperty] - for mprop in model.mproperties do - var intro = mprop.intro + # search for mpropdef taking keyword as parameter + private fun search_params(query: IndexQuery): Set[MProperty] do + var matches = new HashSet[MProperty] + for mproperty in model.mproperties do + var intro = mproperty.intro if intro isa MMethodDef then var mparameters = intro.msignature.mparameters for mparameter in mparameters do - print mparameter.mtype.to_console - if mparameter.mtype.to_console.has_prefix(entry) then matches.add(mprop) + if mparameter.mtype.to_console.has_prefix(query.keyword) then matches.add(mproperty) end else if intro isa MAttributeDef then - if intro.static_mtype.to_console.has_prefix(entry) then matches.add(mprop) + if intro.static_mtype.to_console.has_prefix(query.keyword) then matches.add(mproperty) end end return matches end - #TODO should be returning a List[MPropDef] - private fun seek_inits(entry: String): List[MProperty] do + # search for mpropdef creating new instance of keyword + private fun search_inits(query: IndexQuery): Set[MPropDef] do var mtype2mpropdefs = toolcontext.nitx_phase.mtype2mpropdefs - var matches = new List[MProperty] + var matches = new HashSet[MPropDef] for mtype in mtype2mpropdefs.keys do - if mtype.to_console.has_prefix(entry) then + if mtype.to_console.has_prefix(query.keyword) then for mpropdef in mtype2mpropdefs[mtype] do - matches.add(mpropdef.mproperty) + matches.add(mpropdef) end end end @@ -481,6 +224,31 @@ class NitIndex end end +private class IndexQuery + var string: String + var keyword: String + init(string: String, keyword: String) do + self.string = string + self.keyword = keyword + end +end + +private class IndexQueryPair + super IndexQuery + var category: String + init(string: String, keyword: String, category: String) do + super(string, keyword) + self.category = category + end +end + +# A match to a query in the nit index +private interface IndexMatch + # Short preview of the result for result list display + fun preview(index: NitIndex, output: Pager) is abstract + fun content(index: NitIndex, output: Pager) is abstract +end + # Code Analysis redef class ToolContext @@ -519,12 +287,78 @@ private class TypeInitVisitor end end -# Printing facilities +# Pager output for console + +private class PagerMatchesRenderer + var index: NitIndex + init(index: NitIndex) do self.index = index + + fun render_matches(query: IndexQuery, matches: Collection[IndexMatch]) do + var pager = new Pager + if matches.length == 1 then + pager.add("= result for '{query.string}'".bold) + pager.add("") + pager.indent = pager.indent + 1 + matches.first.content(index, pager) + pager.indent = pager.indent - 1 + else + pager.add("= multiple results for '{query.string}'".bold) + pager.indent = pager.indent + 1 + for match in matches do + pager.add("") + match.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + pager.render + end + + private fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do + # group by module + var cats = new HashMap[MModule, Array[MProperty]] + for mprop in raw_mprops do + if mprop isa MAttribute then continue + var key = mprop.intro.mclassdef.mmodule + if not cats.has_key(key) then cats[key] = new Array[MProperty] + cats[key].add(mprop) + end + #sort groups + var sorter = new MModuleNameSorter + var sorted = new Array[MModule] + sorted.add_all(cats.keys) + sorter.sort(sorted) + # display + for mmodule in sorted do + var mprops = cats[mmodule] + pager.add("# matches in module {mmodule.namespace.bold}") + var sorterp = new MPropertyNameSorter + sorterp.sort(mprops) + for mprop in mprops do + + end + pager.add_rule + end + end +end + +private class Pager + var content = new Buffer + var indent = 0 + fun add(text: String) do + add_indent + addn("{text}\n") + end + fun add_indent do addn(" " * indent) + fun addn(text: String) do content.append(text.escape) + fun add_rule do add("\n---\n") + fun render do sys.system("echo \"{content}\" | pager -r") +end redef class MModule + super IndexMatch # prototype of the module - # module ownername::name - private fun prototype: String do return "module {name}" + # module ownername::name + private fun prototype: String do return "module {name.bold}" # namespace of the module # ownername::name @@ -535,9 +369,99 @@ redef class MModule return "{public_owner.namespace}::{self.name}" end end + + redef fun preview(index, pager) do + if index.mbuilder.mmodule2nmodule.has_key(self) then + var node = index.mbuilder.mmodule2nmodule[self] + if not node.n_moduledecl.n_doc == null and not node.n_moduledecl.n_doc.short_comment.is_empty then + pager.add(node.n_moduledecl.n_doc.short_comment.green) + end + end + pager.add(prototype) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end + + redef fun content(index, pager) do + if index.mbuilder.mmodule2nmodule.has_key(self) then + var node = index.mbuilder.mmodule2nmodule[self] + if not node.n_moduledecl.n_doc == null and not node.n_moduledecl.n_doc.comment.is_empty then + for comment in node.n_moduledecl.n_doc.comment do pager.add(comment.green) + end + end + pager.add(prototype) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + pager.indent = pager.indent + 1 + var sorter = new MModuleNameSorter + # imported modules + var imports = new Array[MModule] + for mmodule in in_importation.direct_greaters.to_a do + if not in_nesting.direct_greaters.has(mmodule) then imports.add(mmodule) + end + if not imports.is_empty then + sorter.sort(imports) + pager.add("") + pager.add("== imported modules".bold) + pager.indent = pager.indent + 1 + for mmodule in imports do + pager.add("") + mmodule.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + # nested modules + var nested = in_nesting.direct_greaters.to_a + if not nested.is_empty then + sorter.sort(nested) + pager.add("") + pager.add("== nested modules".bold) + pager.indent = pager.indent + 1 + for mmodule in nested do + pager.add("") + mmodule.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + # mclassdefs + var csorter = new MClassDefNameSorter + var intros = new Array[MClassDef] + var redefs = new Array[MClassDef] + for mclassdef in mclassdefs do + if mclassdef.is_intro then + intros.add(mclassdef) + else + redefs.add(mclassdef) + end + end + # introductions + if not intros.is_empty then + csorter.sort(intros) + pager.add("") + pager.add("== introduced classes".bold) + pager.indent = pager.indent + 1 + for mclass in intros do + pager.add("") + mclass.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + # refinements + if not redefs.is_empty then + csorter.sort(redefs) + pager.add("") + pager.add("== refined classes".bold) + pager.indent = pager.indent + 1 + for mclass in redefs do + pager.add("") + mclass.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + pager.indent = pager.indent - 1 + end end redef class MClass + super IndexMatch # return the generic signature of the class # [E, F] private fun signature: String do @@ -568,25 +492,219 @@ redef class MClass private fun namespace: String do return "{intro_mmodule.namespace}::{name}" end + + redef fun preview(index, pager) do + intro.preview(index, pager) + end + + redef fun content(index, pager) do + # intro comment + if index.mbuilder.mclassdef2nclassdef.has_key(intro) then + var node = index.mbuilder.mclassdef2nclassdef[intro] + if node isa AStdClassdef and not node.n_doc == null and not node.n_doc.comment.is_empty then + for comment in node.n_doc.comment do pager.add(comment.green) + end + end + pager.add(intro.to_console) + pager.add("{intro.namespace}".bold.gray + " (lines {intro.location.lines})".gray) + pager.indent = pager.indent + 1 + # parents + var supers = self.in_hierarchy(index.mainmodule).direct_greaters.to_a + if not supers.is_empty then + var csorter = new MClassNameSorter + csorter.sort(supers) + pager.add("") + pager.add("== supers".bold) + pager.indent = pager.indent + 1 + for mclass in supers do + pager.add("") + mclass.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + # formal types + if not self.parameter_types.is_empty then + pager.add("") + pager.add("== formal types".bold) + pager.indent = pager.indent + 1 + for ft, bound in self.parameter_types do + pager.add("") + pager.add("{ft.to_s.bold.green}: {bound.to_console}") + end + pager.indent = pager.indent - 1 + end + # intro mproperties + var psorter = new MPropDefNameSorter + var mpropdefs = intro.mpropdefs + index.mainmodule.linearize_mpropdefs(mpropdefs) + for cat in intro.cats2mpropdefs.keys do + var defs = intro.cats2mpropdefs[cat].to_a + if defs.is_empty then continue + psorter.sort(defs) + pager.add("") + pager.add("== {cat}".bold) + pager.indent = pager.indent + 1 + for mpropdef in defs do + pager.add("") + mpropdef.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + # refinements + if not self.mclassdefs.is_empty then + pager.add("") + pager.add("== refinements".bold) + var mclassdefs = self.mclassdefs + index.mainmodule.linearize_mclassdefs(mclassdefs) + pager.indent = pager.indent + 1 + for mclassdef in mclassdefs do + if not mclassdef.is_intro then + pager.add("") + mclassdef.content(index, pager) + end + end + pager.indent = pager.indent - 1 + end + pager.indent = pager.indent - 1 + end end redef class MClassDef + super IndexMatch + private fun namespace: String do return "{mmodule.full_name}::{mclass.name}" end + + fun to_console: String do + var res = new Buffer + if not is_intro then res.append("redef ") + res.append(mclass.prototype) + return res.to_s + end + + redef fun preview(index, pager) do + if index.mbuilder.mclassdef2nclassdef.has_key(self) then + var node = index.mbuilder.mclassdef2nclassdef[self] + if node isa AStdClassdef and not node.n_doc == null and not node.n_doc.short_comment.is_empty then + pager.add(node.n_doc.short_comment.green) + end + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end + + redef fun content(index, pager) do + if index.mbuilder.mclassdef2nclassdef.has_key(self) then + var node = index.mbuilder.mclassdef2nclassdef[self] + if node isa AStdClassdef and not node.n_doc == null and not node.n_doc.comment.is_empty then + for comment in node.n_doc.comment do pager.add(comment.green) + end + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + pager.indent = pager.indent + 1 + var mpropdefs = self.mpropdefs + var psorter = new MPropDefNameSorter + index.mainmodule.linearize_mpropdefs(mpropdefs) + for cat in cats2mpropdefs.keys do + var defs = cats2mpropdefs[cat].to_a + psorter.sort(defs) + if defs.is_empty then continue + pager.add("") + pager.add("== {cat}".bold) + pager.indent = pager.indent + 1 + for mpropdef in defs do + pager.add("") + mpropdef.preview(index, pager) + end + pager.indent = pager.indent - 1 + end + pager.indent = pager.indent - 1 + end + + # get mpropdefs grouped by categories (vt, init, methods) + fun cats2mpropdefs: Map[String, Set[MPropDef]] do + var cats = new ArrayMap[String, Set[MPropDef]] + cats["virtual types"] = new HashSet[MPropDef] + cats["constructors"] = new HashSet[MPropDef] + cats["methods"] = new HashSet[MPropDef] + + for mpropdef in mpropdefs do + if mpropdef isa MAttributeDef then continue + if mpropdef isa MVirtualTypeDef then cats["virtual types"].add(mpropdef) + if mpropdef isa MMethodDef then + if mpropdef.mproperty.is_init then + cats["constructors"].add(mpropdef) + else + cats["methods"].add(mpropdef) + end + end + end + return cats + end end redef class MProperty + super IndexMatch + fun to_console: String do if visibility.to_s == "public" then return name.green if visibility.to_s == "private" then return name.red if visibility.to_s == "protected" then return name.yellow return name.bold end + + redef fun preview(index, pager) do + intro.preview(index, pager) + end + + redef fun content(index, pager) do + intro.content(index, pager) + pager.indent = pager.indent + 1 + var mpropdefs = self.mpropdefs + index.mainmodule.linearize_mpropdefs(mpropdefs) + for mpropdef in mpropdefs do + if mpropdef isa MAttributeDef then continue + if not mpropdef.is_intro then + pager.add("") + mpropdef.preview(index, pager) + end + end + pager.indent = pager.indent - 1 + end end redef class MPropDef + super IndexMatch + fun to_console: String is abstract + + private fun namespace: String do + return "{mclassdef.namespace}::{mproperty.name}" + end + + redef fun preview(index, pager) do + if index.mbuilder.mpropdef2npropdef.has_key(self) then + var nprop = index.mbuilder.mpropdef2npropdef[self] + if not nprop.n_doc == null and not nprop.n_doc.short_comment.is_empty then + pager.add(nprop.n_doc.short_comment.green) + end + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end + + redef fun content(index, pager) do + if index.mbuilder.mpropdef2npropdef.has_key(self) then + var nprop = index.mbuilder.mpropdef2npropdef[self] + if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then + for comment in nprop.n_doc.comment do pager.add(comment.green) + end + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end end redef class MMethodDef @@ -614,6 +732,16 @@ redef class MVirtualTypeDef end end +redef class MAttributeDef + redef fun to_console do + var res = new Buffer + res.append("var ") + res.append(mproperty.to_console.bold) + res.append(": {static_mtype.to_console}") + return res.to_s + end +end + redef class MSignature redef fun to_console do var res = new Buffer @@ -756,6 +884,12 @@ redef class String end end +redef class Location + fun lines: String do + return "{line_start}-{line_end}" + end +end + # Create a tool context to handle options and paths var toolcontext = new ToolContext toolcontext.process_options