X-Git-Url: http://nitlanguage.org diff --git a/src/nitx.nit b/src/nitx.nit index 11db74a..69b34e7 100644 --- a/src/nitx.nit +++ b/src/nitx.nit @@ -16,47 +16,51 @@ module nitx import model_utils - -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 +import modelize # 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 class NitIndex private var toolcontext: ToolContext - private var model: Model - private var mbuilder: ModelBuilder - private var mainmodule: MModule - private var arguments: Array[String] + private var model: Model is noinit + private var mbuilder: ModelBuilder is noinit + private var mainmodule: MModule is noinit + private var arguments: Array[String] is noinit + private var renderer: PagerMatchesRenderer is noinit + + # 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 - init(toolcontext: ToolContext) do + self.mainmodule = mmodule + self.toolcontext = mbuilder.toolcontext + self.arguments = toolcontext.option_context.rest + + renderer = new PagerMatchesRenderer(self) + end + + init do # We need a model to collect stufs - self.toolcontext = toolcontext - self.toolcontext.option_context.options.clear self.arguments = toolcontext.option_context.rest - if arguments.is_empty or arguments.length > 2 then - print "usage: ni path/to/module.nit [expression]" - toolcontext.option_context.usage + if arguments.length > 2 then + print toolcontext.tooldescription exit(1) end model = new Model mbuilder = new ModelBuilder(model, toolcontext) - # Here we load an process std modules - #var dir = "NIT_DIR".environ - #var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"]) - var mmodules = mbuilder.parse_and_build([arguments.first]) + var mmodules = mbuilder.parse([arguments.first]) if mmodules.is_empty then return + mbuilder.run_phases assert mmodules.length == 1 self.mainmodule = mmodules.first + + renderer = new PagerMatchesRenderer(self) end fun start do @@ -64,7 +68,7 @@ class NitIndex welcome prompt else - seek(arguments[1]) + search(arguments[1]) end end @@ -74,7 +78,7 @@ class NitIndex print "Loaded modules:" var mmodules = new Array[MModule] mmodules.add_all(model.mmodules) - var sorter = new MModuleNameSorter + var sorter = new MEntityNameSorter sorter.sort(mmodules) for m in mmodules do print "\t{m.name}" @@ -88,17 +92,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(sys.stdin.read_line) end - fun seek(entry: String) do + fun search(entry: String) do if entry.is_empty then prompt return @@ -108,255 +113,204 @@ 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) + 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 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) + 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 mclass.in_hierarchy(mainmodule).direct_greaters.length > 1 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) - if i < mclass.in_hierarchy(mainmodule).direct_greaters.length -1 then pager.addn(", ") - end - pager.add("\n") + end + return matches + end +end + +private class IndexQuery + var string: String + var keyword: String +end + +private class IndexQueryPair + super IndexQuery + var category: String +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 - # 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}") - 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 + + 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}") - 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 + 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 @@ -366,7 +320,7 @@ class NitIndex cats[key].add(mprop) end #sort groups - var sorter = new MModuleNameSorter + var sorter = new MEntityNameSorter var sorted = new Array[MModule] sorted.add_all(cats.keys) sorter.sort(sorted) @@ -374,96 +328,138 @@ class NitIndex for mmodule in sorted do var mprops = cats[mmodule] pager.add("# matches in module {mmodule.namespace.bold}") - var sorterp = new MPropertyNameSorter - sorterp.sort(mprops) + sorter.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}") - 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}\" | less -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 + var mdoc = self.mdoc + if mdoc != null then + pager.add(mdoc.short_comment.green) end + pager.add(prototype) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end + + redef fun content(index, pager) do + var mdoc = self.mdoc + if mdoc != null then + for comment in mdoc.content do pager.add(comment.green) + end + pager.add(prototype) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + pager.indent = pager.indent + 1 + var sorter = new MEntityNameSorter + # 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 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 + sorter.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 + sorter.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 FlatBuffer if arity > 0 then - return "[{intro.parameter_names.join(", ")}]" - else - return "" + res.append("[") + for i in [0..mparameters.length[ do + res.append(mparameters[i].name) + if i < mparameters.length - 1 then res.append(", ") + end + res.append("]") end + return res.to_s end # return the prototype of the class # 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) @@ -474,85 +470,285 @@ 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 + var sorter = new MEntityNameSorter + var mdoc = intro.mdoc + if mdoc != null then + for comment in mdoc.content do pager.add(comment.green) + 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 + sorter.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 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 + sorter.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 + var mdoc = self.mdoc + if mdoc != null then + pager.add(mdoc.short_comment.green) + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end + + redef fun content(index, pager) do + var mdoc = self.mdoc + if mdoc != null then + for comment in mdoc.content do pager.add(comment.green) + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + pager.indent = pager.indent + 1 + var mpropdefs = self.mpropdefs + var sorter = new MEntityNameSorter + index.mainmodule.linearize_mpropdefs(mpropdefs) + for cat in cats2mpropdefs.keys do + var defs = cats2mpropdefs[cat].to_a + sorter.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 - redef fun to_s do + 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 + var mdoc = self.mdoc + if mdoc != null then + pager.add(mdoc.short_comment.green) + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end + + redef fun content(index, pager) do + var mdoc = self.mdoc + if mdoc != null then + for comment in mdoc.content do pager.add(comment.green) + end + pager.add(to_console) + pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray) + end end redef class MMethodDef - redef fun to_s do - var res = new Buffer + redef fun to_console do + 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_s.bold) - if msignature != null then res.append(msignature.to_s) - # FIXME: modifiers should be accessible via the model - #if self isa ADeferredMethPropdef then ret = "{ret} is abstract" - #if self isa AInternMethPropdef then ret = "{ret} is intern" - #if self isa AExternMethPropdef then ret = "{ret} is extern" + res.append(mproperty.to_console.bold) + if msignature != null then res.append(msignature.to_console) + if is_abstract then res.append " is abstract" + if is_intern then res.append " is intern" + if is_extern then res.append " is extern" return res.to_s end end redef class MVirtualTypeDef - redef fun to_s do - var res = new Buffer + redef fun to_console do + var res = new FlatBuffer res.append("type ") - res.append(mproperty.to_s.bold) - res.append(": {bound.to_s}") + res.append(mproperty.to_console.bold) + res.append(": {bound.to_console}") + return res.to_s + 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_s do - var res = new Buffer + redef fun to_console do + var res = new FlatBuffer if not mparameters.is_empty then res.append("(") for i in [0..mparameters.length[ do - res.append(mparameters[i].to_s) + res.append(mparameters[i].to_console) if i < mparameters.length - 1 then res.append(", ") end res.append(")") end if return_mtype != null then - res.append(": {return_mtype.to_s}") + res.append(": {return_mtype.to_console}") end return res.to_s end end redef class MParameter - redef fun to_s do - var res = new Buffer - res.append("{name}: {mtype}") + fun to_console: String do + var res = new FlatBuffer + res.append("{name}: {mtype.to_console}") if is_vararg then res.append("...") return res.to_s end end +redef class MType + fun to_console: String do return self.to_s +end + redef class MNullableType - redef fun to_s do return "nullable {mtype}" + redef fun to_console do return "nullable {mtype.to_console}" end redef class MGenericType - redef fun to_s do - var res = new Buffer + redef fun to_console do + var res = new FlatBuffer res.append("{mclass.name}[") for i in [0..arguments.length[ do - res.append(arguments[i].to_s) + res.append(arguments[i].to_console) if i < arguments.length - 1 then res.append(", ") end res.append("]") @@ -561,53 +757,16 @@ redef class MGenericType end redef class MParameterType - redef fun to_s do return mclass.intro.parameter_names[rank] + redef fun to_console do return name end redef class MVirtualType - redef fun to_s do return mproperty.intro.to_s + redef fun to_console do return mproperty.name end -redef class ADoc - private fun comment: List[String] do - var res = new List[String] - for t in n_comment do - res.add(t.text.replace("\n", "")) - end - return res - end - +redef class MDoc private fun short_comment: String do - return n_comment.first.text.replace("\n", "") - end -end - -redef class AAttrPropdef - private fun read_accessor: String do - var ret = "fun " - #FIXME bug with standard::stream::FDStream::fd - var name = mreadpropdef.mproperty.name - if mpropdef.mproperty.visibility.to_s == "public" then ret = "{ret}{name.green}" - if mpropdef.mproperty.visibility.to_s == "private" then ret = "{ret}{name.red}" - if mpropdef.mproperty.visibility.to_s == "protected" then ret = "{ret}{name.yellow}" - ret = "{ret}: {n_type.to_s}" - if n_kwredef != null then ret = "redef {ret}" - return ret - end - - private fun write_accessor: String do - var ret = "fun " - var name = "{mreadpropdef.mproperty.name}=" - if n_readable != null and n_readable.n_visibility != null then - if n_readable.n_visibility isa APublicVisibility then ret = "{ret}{name.green}" - if n_readable.n_visibility isa APrivateVisibility then ret = "{ret}{name.red}" - if n_readable.n_visibility isa AProtectedVisibility then ret = "{ret}{name.yellow}" - else - ret = "{ret}{name.red}" - end - ret = "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})" - if n_kwredef != null then ret = "redef {ret}" - return ret + return content.first end end @@ -632,8 +791,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 @@ -654,9 +813,16 @@ 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 +toolcontext.tooldescription = "Usage: nitx [OPTION]... [query]\nDisplays specific pieces of API information from Nit source files." +toolcontext.process_options(args) # Here we launch the nit index var ni = new NitIndex(toolcontext)