jwrapper: accept interfaces
[nit.git] / src / nitx.nit
index 7c441ed..69b34e7 100644 (file)
 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
+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([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
@@ -66,7 +68,7 @@ class NitIndex
                        welcome
                        prompt
                else
-                       seek(arguments[1])
+                       search(arguments[1])
                end
        end
 
@@ -76,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}"
@@ -90,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
@@ -110,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 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
+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
-                       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
+
+       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
+       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
@@ -368,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)
@@ -376,89 +328,127 @@ 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.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
-               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_console.has_prefix(entry) then matches.add(mprop)
-                       else if intro isa MAttributeDef then
-                               if intro.static_mtype.to_console.has_prefix(entry) then matches.add(mprop)
                        end
+                       pager.add_rule
                end
-               return matches
        end
+end
 
-       private fun seek_params(entry: String): List[MProperty] do
-               var matches = new List[MProperty]
-               for mprop in model.mproperties do
-                       var intro = mprop.intro
-                       if intro isa MMethodDef then
-                               var mparameters = intro.msignature.mparameters
-                               for mparameter in mparameters do
-                                       print mparameter.mtype.to_console
-                                       if mparameter.mtype.to_console.has_prefix(entry) then matches.add(mprop)
-                               end
-                       else if intro isa MAttributeDef then
-                               if intro.static_mtype.to_console.has_prefix(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 Buffer
+               var res = new FlatBuffer
                if arity > 0 then
                        res.append("[")
-                       for i in [0..intro.parameter_names.length[ do
-                               res.append(intro.parameter_names[i])
-                               if i < intro.parameter_names.length - 1 then 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
@@ -469,7 +459,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)
@@ -480,45 +470,227 @@ 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
+       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_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)
                if msignature != null then res.append(msignature.to_console)
-               # 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"
+               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_console do
-               var res = new Buffer
+               var res = new FlatBuffer
                res.append("type ")
                res.append(mproperty.to_console.bold)
                res.append(": {bound.to_console}")
@@ -526,9 +698,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
@@ -546,7 +728,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
@@ -563,7 +745,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)
@@ -575,53 +757,16 @@ redef class MGenericType
 end
 
 redef class MParameterType
-       redef fun to_console do return mclass.intro.parameter_names[rank]
+       redef fun to_console do return name
 end
 
 redef class MVirtualType
        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
 
@@ -646,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
@@ -668,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]... <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)