Merge: nitx: finish migration to new nitdoc architecture
authorJean Privat <jean@pryen.org>
Tue, 26 May 2015 10:12:01 +0000 (06:12 -0400)
committerJean Privat <jean@pryen.org>
Tue, 26 May 2015 12:38:00 +0000 (08:38 -0400)
`nitx` is now part of the `doc` group using the same architecture as `nitdoc`.

This PR also add two new features:
* locate calls to a MProperty
* search pieces of code

Fixes #262

Pull-Request: #1369
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

share/man/nitx.md
src/doc/console_templates/console_model.nit
src/doc/doc_phases/doc_console.nit
src/interpreter/debugger.nit
src/nitx.nit
tests/nitx.args
tests/sav/nitx.res
tests/sav/nitx_args1.res
tests/sav/nitx_args2.res
tests/sav/nitx_args3.res

index c655110..4ba5154 100644 (file)
@@ -10,9 +10,9 @@ nitx [*options*] FILE [COMMAND]
 
 # DESCRIPTION
 
-`nitx` in an interactive tool that display information about programs and libraries.
+`nitx` in an interactive tool that displays informations about programs and libraries.
 
-A command that query some information can be given as and argument.
+A command that query some information can be given as parameter.
 This will immediately displays the information then terminates the programs.
 
 If no command are given, the program starts an interactive session where commands are entered until `:q` is given.
@@ -31,6 +31,15 @@ If no command are given, the program starts an interactive session where command
 `new: Type`
 :   lookup methods creating new instances of 'Type'.
 
+`call: Property`
+:   lookup calls to 'Property'.
+
+`doc: name`
+:   lookup documentation pages about 'name'.
+
+`code: name`
+:   lookup source code related to 'name'.
+
 `:h`
 :   display an help message about the commands.
 
@@ -39,7 +48,8 @@ If no command are given, the program starts an interactive session where command
 
 # OPTIONS
 
-Only common options of the Nit tools are understood.
+`-q`
+:      execute a query, display results in console then quit.
 
 # SEE ALSO
 
index 8de793c..dee8a74 100644 (file)
@@ -121,6 +121,26 @@ redef class MEntity
        #
        # See module `console`.
        fun cs_visibility_color(string: String): String do return string.green
+
+       # Source code associated to this MEntity.
+       #
+       # Uses `cs_location` to locate the source code.
+       fun cs_source_code: String do
+               # FIXME up location to mentity
+               var loc = new Location.from_string(cs_location)
+               var fr = new FileReader.open(loc.file.filename)
+               var content = new FlatBuffer
+               var i = 0
+               while not fr.eof do
+                       i += 1
+                       var line = fr.read_line
+                       if i < loc.line_start or i > loc.line_end then continue
+                       # FIXME add nitlight for console
+                       content.append "{line}\n"
+               end
+               fr.close
+               return content.write_to_string
+       end
 end
 
 redef class MProject
index a7ef6b1..dbd1d39 100644 (file)
@@ -59,6 +59,12 @@ class Nitx
        fun help do
                print "\nCommands:"
                print "\tname\t\tlookup module, class and property with the corresponding 'name'"
+               print "\tdoc: <name::space>\tdisplay the documentation page of 'namespace'"
+               print "\tparam: <Type>\tlookup methods using the corresponding 'Type' as parameter"
+               print "\treturn: <Type>\tlookup methods returning the corresponding 'Type'"
+               print "\tnew: <Type>\tlookup methods creating new instances of 'Type'"
+               print "\tcall: <name>\tlookup methods calling 'name'"
+               print "\tcode: <name>\tdisplay the source code associated to the 'name' entity"
                print "\t:h\t\tdisplay this help message"
                print "\t:q\t\tquit interactive mode"
                print ""
@@ -110,6 +116,19 @@ interface NitxQuery
                        return new NitxHelp
                else if query_string.has_prefix("comment:") then
                        return new CommentQuery(query_string)
+               else if query_string.has_prefix("doc:") then
+                       return new DocQuery(query_string)
+               else if query_string.has_prefix("param:") then
+                       return new ParamQuery(query_string)
+               else if query_string.has_prefix("return:") then
+                       return new ReturnQuery(query_string)
+               else if query_string.has_prefix("new:") then
+                       return new NewQuery(query_string)
+               else if query_string.has_prefix("call:") then
+                       return new CallQuery(query_string)
+               else if query_string.has_prefix("code:") then
+                       return new CodeQuery(query_string)
+
                end
                return new CommentQuery("comment: {query_string}")
        end
@@ -207,6 +226,188 @@ class CommentQuery
        end
 end
 
+# A query to search signatures using a specific `MType` as parameter.
+class ParamQuery
+       super MetaQuery
+
+       redef fun perform(nitx, doc) do
+               var res = new Array[NitxMatch]
+               var mtype_name = args.first
+               for mproperty in doc.mproperties do
+                       if not mproperty isa MMethod then continue
+                       var msignature = mproperty.intro.msignature
+                       if msignature != null then
+                               for mparam in msignature.mparameters do
+                                       if mparam.mtype.name == mtype_name then
+                                               res.add new MEntityMatch(self, mproperty)
+                                       end
+                               end
+                       end
+               end
+               return res
+       end
+end
+
+# A query to search signatures using a specific `MType` as return.
+class ReturnQuery
+       super MetaQuery
+
+       redef fun perform(nitx, doc) do
+               var res = new Array[NitxMatch]
+               var mtype_name = args.first
+               for mproperty in doc.mproperties do
+                       if not mproperty isa MMethod then continue
+                       var msignature = mproperty.intro.msignature
+                       if msignature != null then
+                               var mreturn = msignature.return_mtype
+                               if mreturn != null and mreturn.name == mtype_name then
+                                       res.add new MEntityMatch(self, mproperty)
+                               end
+                       end
+               end
+               return res
+       end
+end
+
+# A query to search methods creating new instances of a specific `MType`.
+class NewQuery
+       super MetaQuery
+
+       redef fun perform(nitx, doc) do
+               var res = new Array[NitxMatch]
+               var mtype_name = args.first
+               for mpropdef in doc.mpropdefs do
+                       var visitor = new TypeInitVisitor(mtype_name)
+                       var npropdef = nitx.ctx.modelbuilder.mpropdef2node(mpropdef)
+                       if npropdef == null then continue
+                       visitor.enter_visit(npropdef)
+                       for i in visitor.inits do
+                               res.add new MEntityMatch(self, mpropdef)
+                       end
+               end
+               return res
+       end
+end
+
+# A query to search methods calling a specific `MProperty`.
+class CallQuery
+       super MetaQuery
+
+       redef fun perform(nitx, doc) do
+               var res = new Array[NitxMatch]
+               var mprop_name = args.first
+               for mpropdef in doc.mpropdefs do
+                       var visitor = new MPropertyCallVisitor
+                       var npropdef = nitx.ctx.modelbuilder.mpropdef2node(mpropdef)
+                       if npropdef == null then continue
+                       visitor.enter_visit(npropdef)
+                       for mprop in visitor.calls do
+                               if mprop.name != mprop_name then continue
+                               res.add new MEntityMatch(self, mpropdef)
+                       end
+               end
+               return res
+       end
+end
+
+# A query to search a Nitdoc documentation page by its name.
+class DocQuery
+       super MetaQuery
+
+       redef fun perform(nitx, doc) do
+               var res = new Array[NitxMatch]
+               var name = args.first
+               for page in doc.pages do
+                       if name == "*" then # FIXME dev only
+                               res.add new PageMatch(self, page)
+                       else if page.title == name then
+                               res.add new PageMatch(self, page)
+                       else if page isa MEntityPage and page.mentity.cs_namespace == name then
+                               res.add new PageMatch(self, page)
+                       end
+               end
+               return res
+       end
+
+       redef fun make_results(nitx, results) do
+               var len = results.length
+               # FIXME how to render the pager for one worded namespaces like "standard"?
+               if len == 1 then
+                       var page = results.first.as(PageMatch).page
+                       var pager = new Pager
+                       pager.add page.write_to_string
+                       pager.render
+                       return page
+               else
+                       return super
+               end
+       end
+end
+
+# A match between a `DocPage` and a `MEntity`.
+class PageMatch
+       super NitxMatch
+
+       # `DocPage` matched.
+       var page: DocPage
+
+       redef fun make_list_item do
+               var page = self.page
+               if page isa MEntityPage then
+                       return page.mentity.cs_list_item
+               end
+               return " * {page.title}"
+       end
+end
+
+# A query to search source code from a file name.
+class CodeQuery
+       super MetaQuery
+
+       # FIXME refactor this!
+       redef fun perform(nitx, doc) do
+               var res = new Array[NitxMatch]
+               var name = args.first
+               # if name is an existing sourcefile, opens it
+               if name.file_exists then
+                       var fr = new FileReader.open(name)
+                       var content = fr.read_all
+                       fr.close
+                       res.add new CodeMatch(self, name, content)
+                       return res
+               end
+               # else, lookup the model by name
+               for mentity in doc.search_mentities(name) do
+                       if mentity isa MClass then continue
+                       if mentity isa MProperty then continue
+                       res.add new CodeMatch(self, mentity.cs_location, mentity.cs_source_code)
+               end
+               return res
+       end
+
+       redef fun make_results(nitx, results) do
+               var page = new DocPage("Code Results")
+               for res in results do
+                       page.add new CodeQueryArticle(self, res.as(CodeMatch))
+               end
+               return page
+       end
+end
+
+# A match between a piece of code and a string.
+class CodeMatch
+       super NitxMatch
+
+       # Location of the code match.
+       var location: String
+
+       # Piece of code matched.
+       var content: String
+
+       redef fun make_list_item do return "* {location}"
+end
+
+
 # A query that contains a nitx command.
 #
 # These commands are prefixed with `:` and are used to control the execution of
@@ -260,6 +461,41 @@ redef class DocModel
        end
 end
 
+# Visitor looking for initialized `MType` (new T).
+#
+# See `NewQuery`.
+private class TypeInitVisitor
+       super Visitor
+
+       # `MType` name to look for.
+       var mtype_name: String
+
+       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 and mtype.name == mtype_name then inits.add(mtype)
+       end
+end
+
+# Visitor looking for calls to a `MProperty` (new T).
+#
+# See `CallQuery`.
+private class MPropertyCallVisitor
+       super Visitor
+
+       var calls = new HashSet[MProperty]
+       redef fun visit(node)
+       do
+               node.visit_all(self)
+               if not node isa ASendExpr then return
+               calls.add node.callsite.mproperty
+       end
+end
+
 # display
 
 # A `DocArticle` that displays query results.
@@ -290,6 +526,24 @@ private class QueryResultArticle
        end
 end
 
+# An article that displays a piece of code.
+private class CodeQueryArticle
+       super DocArticle
+
+       # The query linked to the result to display.
+       var query: NitxQuery
+
+       # The result to display.
+       var result: CodeMatch
+
+       redef fun render_body do
+               addn ""
+               addn "in {result.location}".gray.bold
+               addn ""
+               add result.content
+       end
+end
+
 # A Pager is used to display data into a unix `less` container.
 private class Pager
 
index 1765d9e..cf4e815 100644 (file)
@@ -18,7 +18,6 @@
 module debugger
 
 intrude import naive_interpreter
-import nitx
 intrude import semantize::local_var_init
 intrude import semantize::scope
 intrude import toolcontext
@@ -470,10 +469,6 @@ class Debugger
                else if command == "help" then
                        help
                        return true
-               # Opens a new NitIndex prompt on current model
-               else if command == "nitx" then
-                       new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt
-                       return true
                else if command == "bt" or command == "backtrack" then
                        print stack_trace
                        return true
index 69b34e7..23490a8 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# nit index, is a command tool used to display documentation
+# `nitx`, is a command tool that displays useful informations about the code.
+#
+# Features:
+#
+# * Display comment from name/namespace
+# * Display documentation page from Nitdoc in console
+# * Find type usage in parameters, returns and news.
+# * Find usage of a specific property.
+# * Find source code related to class/property by its name.
 module nitx
 
-import model_utils
-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 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
-
-               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.arguments = toolcontext.option_context.rest
-
-               if arguments.length > 2 then
-                       print toolcontext.tooldescription
-                       exit(1)
-               end
-
-               model = new Model
-               mbuilder = new ModelBuilder(model, toolcontext)
-
-               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
-               if arguments.length == 1 then
-                       welcome
-                       prompt
-               else
-                       search(arguments[1])
-               end
-       end
-
-       fun welcome do
-               print "Welcome in the Nit Index."
-               print ""
-               print "Loaded modules:"
-               var mmodules = new Array[MModule]
-               mmodules.add_all(model.mmodules)
-               var sorter = new MEntityNameSorter
-               sorter.sort(mmodules)
-               for m in mmodules do
-                       print "\t{m.name}"
-               end
-               print ""
-               help
-       end
-
-       fun help do
-               print "\nCommands:"
-               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 "\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 ">> "
-               search(sys.stdin.read_line)
-       end
+import modelbuilder
+import doc::doc_phases::doc_console
 
-       fun search(entry: String) do
-               if entry.is_empty then
-                       prompt
-                       return
-               end
-               if entry == ":h" then
-                       help
-                       prompt
-                       return
-               end
-               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
-               else
-                       matches.add_all(search_modules(query))
-                       matches.add_all(search_classes(query))
-                       matches.add_all(search_properties(query))
-               end
-               # no matches
-               if matches.is_empty then
-                       print "Nothing known about '{query.string}', type ':h' for help"
-               else
-                       renderer.render_matches(query, matches)
-               end
-               if arguments.length == 1 then prompt
-       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
-               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
-               end
-               return matches
-       end
-
-       # 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
-               end
-               return matches
-       end
-end
-
-private class IndexQuery
-       var string: String
-       var keyword: String
-end
-
-private class IndexQueryPair
-       super IndexQuery
-       var category: String
-end
+redef class ToolContext
 
-# 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
+       # Nittx generation phase.
+       var docx: Phase = new NitxPhase(self, null)
 
-# Code Analysis
+       # Used to shortcut the prompt and display directly the result in console.
+       var opt_query = new OptionString("Nitx query to perform", "-q", "--query")
 
-redef class ToolContext
-       private var nitx_phase: NitxPhase = new NitxPhase(self, [modelize_property_phase])
+       init do option_context.add_option opt_query
 end
 
-# Compiler phase for nitx
+# Nitx phase explores the model and prepares the console rendering.
 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
-                       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)
+       redef fun process_mainmodule(mainmodule, mmodules)
        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
-                       pager.indent = pager.indent - 1
-               end
-               pager.render
-       end
-
-       fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do
-               # group by module
-               var cats = new HashMap[MModule, Array[MProperty]]
-               for mprop in raw_mprops do
-                       if mprop isa MAttribute then continue
-                       var key = mprop.intro.mclassdef.mmodule
-                       if not cats.has_key(key) then cats[key] = new Array[MProperty]
-                       cats[key].add(mprop)
-               end
-               #sort groups
-               var sorter = new MEntityNameSorter
-               var sorted = new Array[MModule]
-               sorted.add_all(cats.keys)
-               sorter.sort(sorted)
-               # display
-               for mmodule in sorted do
-                       var mprops = cats[mmodule]
-                       pager.add("# matches in module {mmodule.namespace.bold}")
-                       sorter.sort(mprops)
-                       for mprop in mprops do
-
-                       end
-                       pager.add_rule
-               end
-       end
-end
-
-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
-
-redef class MModule
-       super IndexMatch
-       # prototype of the module
-       #       module name
-       private fun prototype: String do return "module {name.bold}"
-
-       # namespace of the module
-       #       project::name
-       private fun namespace: String do
-               if mgroup == null or mgroup.mproject.name == self.name then
-                       return self.name
-               else
-                       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
+               var doc = new DocModel(mainmodule.model, mainmodule)
 
-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
-                       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
+               var phases = [
+                       new ExtractionPhase(toolcontext, doc),
+                       new MakePagePhase(toolcontext, doc),
+                       new ConcernsPhase(toolcontext, doc),
+                       new StructurePhase(toolcontext, doc): DocPhase]
 
-       # 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 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)
-               if visibility.to_s == "protected" then res.append("{name}{signature}".bold.yellow)
-               return res.to_s
-       end
-
-       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
+               for phase in phases do
+                       toolcontext.info("# {phase.class_name}", 1)
+                       phase.apply
                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 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)
-               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 FlatBuffer
-               res.append("type ")
-               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_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_console)
-                               if i < mparameters.length - 1 then res.append(", ")
-                       end
-                       res.append(")")
-               end
-               if return_mtype != null then
-                       res.append(": {return_mtype.to_console}")
-               end
-               return res.to_s
-       end
-end
-
-redef class MParameter
-       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_console do return "nullable {mtype.to_console}"
-end
-
-redef class MGenericType
-       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_console)
-                       if i < arguments.length - 1 then res.append(", ")
-               end
-               res.append("]")
-               return res.to_s
-       end
-end
-
-redef class MParameterType
-       redef fun to_console do return name
-end
-
-redef class MVirtualType
-       redef fun to_console do return mproperty.name
-end
-
-redef class MDoc
-       private fun short_comment: String do
-               return content.first
-       end
-end
-
-# Redef String class to add a function to color the string
-redef class String
-
-       private fun add_escape_char(escapechar: String): String do
-               return "{escapechar}{self}\\033[0m"
-       end
-
-       private fun esc: Char do return 27.ascii
-       private fun gray: String do return add_escape_char("{esc}[30m")
-       private fun red: String do return add_escape_char("{esc}[31m")
-       private fun green: String do return add_escape_char("{esc}[32m")
-       private fun yellow: String do return add_escape_char("{esc}[33m")
-       private fun blue: String do return add_escape_char("{esc}[34m")
-       private fun purple: String do return add_escape_char("{esc}[35m")
-       private fun cyan: String do return add_escape_char("{esc}[36m")
-       private fun light_gray: String do return add_escape_char("{esc}[37m")
-       private fun bold: String do return add_escape_char("{esc}[1m")
-       private fun underline: String do return add_escape_char("{esc}[4m")
-
-       private fun escape: String
-       do
-               var b = new FlatBuffer
-               for c in self.chars do
-                       if c == '\n' then
-                               b.append("\\n")
-                       else if c == '\0' then
-                               b.append("\\0")
-                       else if c == '"' then
-                               b.append("\\\"")
-                       else if c == '\\' then
-                               b.append("\\\\")
-                       else if c == '`' then
-                               b.append("'")
-                       else if c.ascii < 32 then
-                               b.append("\\{c.ascii.to_base(8, false)}")
-                       else
-                               b.add(c)
-                       end
+               # start nitx
+               var nitx = new Nitx(toolcontext, doc)
+               var q = toolcontext.opt_query.value
+               if q != null then # shortcut prompt
+                       print ""
+                       nitx.do_query(q)
+                       return
                end
-               return b.to_s
-       end
-end
-
-redef class Location
-       fun lines: String do
-               return "{line_start}-{line_end}"
+               nitx.start
        end
 end
 
-# Create a tool context to handle options and paths
+# build toolcontext
 var toolcontext = new ToolContext
-toolcontext.tooldescription = "Usage: nitx [OPTION]... <file.nit> [query]\nDisplays specific pieces of API information from Nit source files."
+var tpl = new Template
+tpl.add "Usage: nitx [OPTION]... <file.nit>... [query]\n"
+tpl.add "Displays specific pieces of API information from Nit source files."
+toolcontext.tooldescription = tpl.write_to_string
+
+# process options
 toolcontext.process_options(args)
+var arguments = toolcontext.option_context.rest
 
-# Here we launch the nit index
-var ni = new NitIndex(toolcontext)
-ni.start
+# build model
+var model = new Model
+var mbuilder = new ModelBuilder(model, toolcontext)
+var mmodules = mbuilder.parse_full(arguments)
 
-# TODO seek subclasses and super classes <.<class> >.<class>
-# TODO seek subclasses and super types <:<type> >:<type>
-# TODO seek with regexp
-# TODO standardize namespaces with private option
+# process
+if mmodules.is_empty then return
+mbuilder.run_phases
+toolcontext.run_global_phases(mmodules)
index 402451a..7f05220 100644 (file)
@@ -1,3 +1,3 @@
-base_simple3.nit A
-base_simple3.nit foo
-base_simple3.nit base_simple3
+base_simple3.nit -q A
+base_simple3.nit -q foo
+base_simple3.nit -q base_simple3
index bb20768..ec462a0 100644 (file)
@@ -1,3 +1,3 @@
-Usage: nitx [OPTION]... <file.nit> [query]
+Usage: nitx [OPTION]... <file.nit>... [query]
 Displays specific pieces of API information from Nit source files.
 Use --help for help
index bf43dd3..f22bfee 100644 (file)
@@ -1,22 +1,13 @@
-\e[1m= result for 'A'\e[0m
 
-  class \e[32m\e[1mA\e[0m\e[0m
-  \e[30m\e[1mbase_simple3::A\e[0m\e[0m\e[30m (lines 29-32)\e[0m
-    
-    \e[1m== supers\e[0m
-      
-      interface \e[32m\e[1mObject\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::Object\e[0m\e[0m\e[30m (lines 19-20)\e[0m
-    
-    \e[1m== constructors\e[0m
-      
-      redef \e[1m\e[32minit\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::A::init\e[0m\e[0m\e[30m (lines 30-30)\e[0m
-    
-    \e[1m== methods\e[0m
-      
-      fun \e[1m\e[32mrun\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::A::run\e[0m\e[0m\e[30m (lines 31-31)\e[0m
-    
-    \e[1m== refinements\e[0m
+\e[1m\e[32m# 2 result(s) for 'comment: A'\e[m\e[m
+
\e[1m\e[32mC\e[m\e[m \e[1m\e[34mA\e[m\e[m
+   \e[1m\e[30mbase_simple3::A\e[m\e[m
+   class A
+   \e[30mbase_simple3.nit:29,1--32,3\e[m
+
\e[1m\e[32mC\e[m\e[m \e[1m\e[34mA\e[m\e[m
+   \e[1m\e[30mbase_simple3::base_simple3::A\e[m\e[m
+   class A
+   \e[30mbase_simple3.nit:29,1--32,3\e[m
 
index bdb2b9f..b7ca320 100644 (file)
@@ -1,5 +1,13 @@
-\e[1m= result for 'foo'\e[0m
 
-  fun \e[1m\e[32mfoo\e[0m\e[0m
-  \e[30m\e[1mbase_simple3::Sys::foo\e[0m\e[0m\e[30m (lines 49-49)\e[0m
+\e[1m\e[32m# 2 result(s) for 'comment: foo'\e[m\e[m
+
\e[1m\e[32mF\e[m\e[m \e[1m\e[34mfoo\e[m\e[m
+   \e[1m\e[30mbase_simple3::Sys::foo\e[m\e[m
+   fun foo
+   \e[30mbase_simple3.nit:49,1--19\e[m
+
\e[1m\e[32mF\e[m\e[m \e[1m\e[34mfoo\e[m\e[m
+   \e[1m\e[30mbase_simple3::base_simple3::Sys::foo\e[m\e[m
+   fun foo
+   \e[30mbase_simple3.nit:49,1--19\e[m
 
index 83a361e..b1fdd02 100644 (file)
@@ -1,28 +1,18 @@
-\e[1m= result for 'base_simple3'\e[0m
 
-  module \e[1mbase_simple3\e[0m
-  \e[30m\e[1mbase_simple3\e[0m\e[0m\e[30m (lines 17-66)\e[0m
-    
-    \e[1m== introduced classes\e[0m
-      
-      class \e[32m\e[1mA\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::A\e[0m\e[0m\e[30m (lines 29-32)\e[0m
-      
-      class \e[32m\e[1mB\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::B\e[0m\e[0m\e[30m (lines 34-42)\e[0m
-      
-      enum \e[32m\e[1mBool\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::Bool\e[0m\e[0m\e[30m (lines 22-23)\e[0m
-      
-      class \e[32m\e[1mC\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::C\e[0m\e[0m\e[30m (lines 44-47)\e[0m
-      
-      enum \e[32m\e[1mInt\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::Int\e[0m\e[0m\e[30m (lines 25-27)\e[0m
-      
-      interface \e[32m\e[1mObject\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::Object\e[0m\e[0m\e[30m (lines 19-20)\e[0m
-      
-      class \e[32m\e[1mSys\e[0m\e[0m
-      \e[30m\e[1mbase_simple3::Sys\e[0m\e[0m\e[30m (lines 49-49)\e[0m
+\e[1m\e[32m# 3 result(s) for 'comment: base_simple3'\e[m\e[m
+
\e[1m\e[32mP\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
+   \e[1m\e[30mbase_simple3\e[m\e[m
+   project base_simple3
+   \e[30mbase_simple3.nit:17,1--66,13\e[m
+
\e[1m\e[32mG\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
+   \e[1m\e[30mbase_simple3\e[m\e[m
+   group base_simple3
+   \e[30mbase_simple3.nit:17,1--66,13\e[m
+
\e[1m\e[32mM\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
+   \e[1m\e[30mbase_simple3::base_simple3\e[m\e[m
+   module base_simple3
+   \e[30mbase_simple3.nit:17,1--66,13\e[m