X-Git-Url: http://nitlanguage.org diff --git a/src/nitx.nit b/src/nitx.nit index dcf71d2..456a5eb 100644 --- a/src/nitx.nit +++ b/src/nitx.nit @@ -18,14 +18,6 @@ module nitx import model_utils import modelize_property -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 - # 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 @@ -35,6 +27,20 @@ class NitIndex private var mbuilder: ModelBuilder private var mainmodule: MModule private var arguments: Array[String] + private var renderer: PagerMatchesRenderer + + # New constructor to use the pre-calculated model when interpreting a module + init with_infos(mbuilder: ModelBuilder, mmodule: MModule) do + + self.model = mbuilder.model + self.mbuilder = mbuilder + + self.mainmodule = mmodule + self.toolcontext = mbuilder.toolcontext + self.arguments = toolcontext.option_context.rest + + renderer = new PagerMatchesRenderer(self) + end init(toolcontext: ToolContext) do # We need a model to collect stufs @@ -59,6 +65,8 @@ class NitIndex mbuilder.run_phases assert mmodules.length == 1 self.mainmodule = mmodules.first + + renderer = new PagerMatchesRenderer(self) end fun start do @@ -66,7 +74,7 @@ class NitIndex welcome prompt else - seek(arguments[1]) + search(arguments[1]) end end @@ -90,17 +98,18 @@ class NitIndex print "\tname\t\tlookup module, class and property with the corresponding 'name'" print "\tparam: Type\tlookup methods using the corresponding 'Type' as parameter" print "\treturn: Type\tlookup methods returning the corresponding 'Type'" - print "\tEnter ':q' to exit" - print "\tEnter ':h' to display this help message" + print "\tnew: Type\tlookup methods creating new instances of 'Type'" + print "\t:h\t\tdisplay this help message" + print "\t:q\t\texit" print "" end 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 @@ -110,252 +119,210 @@ class NitIndex prompt return end - if entry == ":q" then exit(0) - var pager = new Pager - # seek return types - if entry.has_prefix("return:") then - var ret = entry.split_with(":")[1].replace(" ", "") - var matches = seek_returns(ret) - props_fulldoc(pager, matches) - else if entry.has_prefix("param:") then - var param = entry.split_with(":")[1].replace(" ", "") - var matches = seek_params(param) - props_fulldoc(pager, matches) - 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) + if entry == ":q" then return + + # Parse query string + var query = parse_query(entry) + + # search in index + var matches = new HashSet[IndexMatch] + if query isa IndexQueryPair then + if query.category == "return" then + # seek return types + matches.add_all(search_returns(query)) + else if query.category == "param" then + # seek param types + matches.add_all(search_params(query)) + else if query.category == "new" then + # seek type inits + matches.add_all(search_inits(query)) 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) + else + 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 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 + private fun parse_query(str: String): IndexQuery do + var parts = str.split_with(":") + if parts.length == 1 then + return new IndexQuery(str, parts[0]) + else + var category = parts[0] + var keyword = parts[1] + if keyword.chars.first == ' ' then keyword = keyword.substring_from(1) + return new IndexQueryPair(str, keyword, category) + end + end + + # 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 + + # 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 + + # 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 + + # 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(query.keyword) then matches.add(mproperty) + else if intro isa MAttributeDef then + if intro.static_mtype.to_console.has_prefix(query.keyword) then matches.add(mproperty) 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 + return matches + end + + # 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 + 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(query.keyword) then matches.add(mproperty) end - #TODO add inherited classes? - pager.add_rule 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) + # 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 HashSet[MPropDef] + for mtype in mtype2mpropdefs.keys do + if mtype.to_console.has_prefix(query.keyword) then + for mpropdef in mtype2mpropdefs[mtype] do + matches.add(mpropdef) 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 + return matches + 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 + private var nitx_phase: NitxPhase = new NitxPhase(self, [modelize_property_phase]) +end + +# Compiler phase for nitx +private class NitxPhase + super Phase + + var mtype2mpropdefs = new HashMap[MType, Set[MPropDef]] + redef fun process_npropdef(npropdef) do + var visitor = new TypeInitVisitor + visitor.enter_visit(npropdef) + for mtype in visitor.inits do + if not mtype2mpropdefs.has_key(mtype) then + mtype2mpropdefs[mtype] = new HashSet[MPropDef] 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 + mtype2mpropdefs[mtype].add(npropdef.mpropdef.as(not null)) + end + end +end + +# Visitor looking for initialized mtype (new T) +private class TypeInitVisitor + super Visitor + + var inits = new HashSet[MType] + redef fun visit(node) + do + node.visit_all(self) + # look for init + if not node isa ANewExpr then return + var mtype = node.n_type.mtype + if mtype != null then inits.add(mtype) + end +end + +# 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 - # 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 + pager.indent = pager.indent - 1 end + pager.render end private fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do @@ -379,82 +346,125 @@ class NitIndex 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 - end - end - private fun seek_returns(entry: String): List[MProperty] do - # TODO how to match with generic types? - var matches = new List[MProperty] - for mprop in model.mproperties do - var intro = mprop.intro - if intro isa MMethodDef then - if intro.msignature.return_mtype != null and intro.msignature.return_mtype.to_s == entry then matches.add(mprop) - else if intro isa MAttributeDef then - if intro.static_mtype.to_s == entry then matches.add(mprop) end + pager.add_rule end - return matches end +end - private fun seek_params(entry: String): List[MProperty] do - # TODO how to match with generic types? - var matches = new List[MProperty] - for mprop in model.mproperties do - var intro = mprop.intro - if intro isa MMethodDef then - var mparameters = intro.msignature.mparameters - for mparameter in mparameters do - if mparameter.mtype.to_s == entry then matches.add(mprop) - end - else if intro isa MAttributeDef then - if intro.static_mtype.to_s == entry then matches.add(mprop) - end - end - return matches +private class Pager + var content = new FlatBuffer + 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 -# Printing facilities - redef class MModule + super IndexMatch # prototype of the module - # module ownername::name - private fun prototype: String do return "module {name}" + # module name + private fun prototype: String do return "module {name.bold}" # namespace of the module - # ownername::name + # project::name private fun namespace: String do - if public_owner == null then + if mgroup == null or mgroup.mproject.name == self.name then return self.name else - return "{public_owner.namespace}::{self.name}" + return "{mgroup.mproject}::{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 node.n_moduledecl != null and 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 node.n_moduledecl != null and 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 + 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 + # 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 - var res = new Buffer + var res = new FlatBuffer if arity > 0 then res.append("[") for i in [0..intro.parameter_names.length[ do @@ -470,7 +480,7 @@ redef class MClass # class name is displayed with colors depending on visibility # abstract interface Foo[E] private fun prototype: String do - var res = new Buffer + var res = new FlatBuffer res.append("{kind} ") if visibility.to_s == "public" then res.append("{name}{signature}".bold.green) if visibility.to_s == "private" then res.append("{name}{signature}".bold.red) @@ -481,30 +491,224 @@ 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 FlatBuffer + 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 redef fun to_console do - var res = new Buffer + var res = new FlatBuffer if not is_intro then res.append("redef ") if not mproperty.is_init then res.append("fun ") res.append(mproperty.to_console.bold) @@ -519,7 +723,7 @@ end redef class MVirtualTypeDef redef fun to_console do - var res = new Buffer + var res = new FlatBuffer res.append("type ") res.append(mproperty.to_console.bold) res.append(": {bound.to_console}") @@ -527,9 +731,19 @@ redef class MVirtualTypeDef end end +redef class MAttributeDef + redef fun to_console do + var res = new FlatBuffer + 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 + var res = new FlatBuffer if not mparameters.is_empty then res.append("(") for i in [0..mparameters.length[ do @@ -547,7 +761,7 @@ end redef class MParameter fun to_console: String do - var res = new Buffer + var res = new FlatBuffer res.append("{name}: {mtype.to_console}") if is_vararg then res.append("...") return res.to_s @@ -564,7 +778,7 @@ end redef class MGenericType redef fun to_console do - var res = new Buffer + var res = new FlatBuffer res.append("{mclass.name}[") for i in [0..arguments.length[ do res.append(arguments[i].to_console) @@ -647,8 +861,8 @@ redef class String private fun escape: String do - var b = new Buffer - for c in self do + var b = new FlatBuffer + for c in self.chars do if c == '\n' then b.append("\\n") else if c == '\0' then @@ -669,6 +883,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