# See the License for the specific language governing permissions and
# limitations under the License.
-# ni or nit index, is a command tool used to display documentation
-module ni
+# nit index, is a command tool used to display documentation
+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
welcome
prompt
else
- seek(arguments[1])
+ search(arguments[1])
end
end
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}"
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
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
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)
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)
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("]")
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
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
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]... <file.nit> [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)