X-Git-Url: http://nitlanguage.org diff --git a/src/doc/doc_phases/doc_console.nit b/src/doc/doc_phases/doc_console.nit index ba8dc04..b118019 100644 --- a/src/doc/doc_phases/doc_console.nit +++ b/src/doc/doc_phases/doc_console.nit @@ -19,6 +19,9 @@ module doc_console import semantize +import doc_commands +import doc_extract +import doc_poset import doc::console_templates # Nitx handles console I/O. @@ -57,13 +60,24 @@ class Nitx # Displays the list of available commands. fun help do - print "\nCommands:" - print "\tname\t\tlookup module, class and property with the corresponding 'name'" + print "\nCommands:\n" + print "\tname\t\t\tlookup module, class and property with the corresponding 'name'" print "\tdoc: \tdisplay the documentation page of 'namespace'" - print "\tparam: \tlookup methods using the corresponding 'Type' as parameter" - print "\treturn: \tlookup methods returning the corresponding 'Type'" - print "\t:h\t\tdisplay this help message" - print "\t:q\t\tquit interactive mode" + print "\nType lookup:" + print "\tparam: \t\tlookup methods using the corresponding 'Type' as parameter" + print "\treturn: \t\tlookup methods returning the corresponding 'Type'" + print "\tnew: \t\tlookup methods creating new instances of 'Type'" + print "\tcall: \t\tlookup methods calling 'name'" + print "\nHierarchy lookup:" + print "\tparents: \tlist direct parents of 'Class'" + print "\tancestors: \tlist all ancestors of 'Class'" + print "\tchildren: \tlist direct children of 'Class'" + print "\tdescendants: \tlist all descendants of 'Class'" + print "\nCode lookup:" + print "\tcode: \t\tdisplay the source code associated to the 'name' entity" + print "\n" + print "\t:h\t\t\tdisplay this help message" + print "\t:q\t\t\tquit interactive mode" print "" end @@ -76,51 +90,30 @@ class Nitx # Processes the query string and performs it. fun do_query(str: String) do - var query = parse_query(str) - var res = query.perform(self, doc) - var page = query.make_results(self, res) - print page.write_to_string - end - - # Returns an `NitxQuery` from a raw query string. - fun parse_query(str: String): NitxQuery do - var query = new NitxQuery(str) + var query = new DocCommand(str) if query isa NitxCommand then query.execute(self) + return end - return query + var res = query.perform(self, doc) + var page = query.make_results(self, res) + print page.write_to_string end end -# A query performed on Nitx. -# -# Queries are responsible to collect matching results and render them as a -# DocPage. -# -# Used as a factory to concrete instances. -interface NitxQuery - - # Original query string. - fun query_string: String is abstract +redef interface DocCommand - # Query factory. - # - # Will return a concrete instance of NitxQuery. - new(query_string: String) do + redef new(query_string) do if query_string == ":q" then return new NitxQuit else if query_string == ":h" then 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) end - return new CommentQuery("comment: {query_string}") + var cmd = super(query_string) + if cmd isa UnknownCommand then + return new CommentCommand("comment: {query_string}") + end + return cmd end # Looks up the `doc` model and returns possible matches. @@ -128,55 +121,23 @@ interface NitxQuery # Pretty prints the results for the console. fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do - var page = new DocPage("Results") - page.root.add_child(new QueryResultArticle(self, results)) + var page = new DocPage("results", "Results") + page.root.add_child(new QueryResultArticle("results", "Results", self, results)) return page end - - redef fun to_s do return query_string end -# Something that matches a `NitxQuery`. +# Something that matches a `DocCommand`. abstract class NitxMatch # Query matched by `self`. - var query: NitxQuery + var query: DocCommand # Pretty prints `self` for console. fun make_list_item: String is abstract end -# A query that contains a meta command. -# -# In Nitx, commands are written such as `command: args...`. -abstract class MetaQuery - super NitxQuery - - redef var query_string - - # Meta command used. - var command: String is noinit - - # Arguments passed to the `command`. - var args = new Array[String] - - init do - # parse command - var str = new FlatBuffer - var i = 0 - while i < query_string.length do - var c = query_string[i] - i += 1 - if c == ':' then break - str.add c - end - command = str.write_to_string - # parse args - args.add query_string.substring_from(i).trim - end -end - -# A match between a `NitxQuery` and a `MEntity`. +# A match between a `DocCommand` and a `MEntity`. class MEntityMatch super NitxMatch @@ -186,14 +147,11 @@ class MEntityMatch redef fun make_list_item do return mentity.cs_list_item end -# A query to search a `MEntity` comment by its name or namespace. -class CommentQuery - super MetaQuery - +redef class CommentCommand redef fun perform(nitx, doc) do var name = args.first var res = new Array[NitxMatch] - for mentity in doc.search_mentities(name) do + for mentity in doc.mentities_by_name(name) do res.add new MEntityMatch(self, mentity) end return res @@ -204,8 +162,8 @@ class CommentQuery if len == 1 then var res = results.first.as(MEntityMatch) var mentity = res.mentity - var page = new DocPage("Results") - var article = new DefinitionArticle(mentity) + var page = new DocPage("resultats", "Results") + var article = new DefinitionArticle("results", "Results", mentity) article.cs_title = mentity.name article.cs_subtitle = mentity.cs_declaration page.root.add_child article @@ -217,9 +175,7 @@ class CommentQuery end # A query to search signatures using a specific `MType` as parameter. -class ParamQuery - super MetaQuery - +redef class ParamCommand redef fun perform(nitx, doc) do var res = new Array[NitxMatch] var mtype_name = args.first @@ -239,9 +195,7 @@ class ParamQuery end # A query to search signatures using a specific `MType` as return. -class ReturnQuery - super MetaQuery - +redef class ReturnCommand redef fun perform(nitx, doc) do var res = new Array[NitxMatch] var mtype_name = args.first @@ -259,14 +213,49 @@ class ReturnQuery end end -# A query to search a Nitdoc documentation page by its name. -class DocQuery - super MetaQuery +# A query to search methods creating new instances of a specific `MType`. +redef class NewCommand + 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`. +redef class CallCommand + 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. +redef class ArticleCommand redef fun perform(nitx, doc) do var res = new Array[NitxMatch] var name = args.first - for page in doc.pages do + for page in doc.pages.values do if name == "*" then # FIXME dev only res.add new PageMatch(self, page) else if page.title == name then @@ -280,7 +269,7 @@ class DocQuery redef fun make_results(nitx, results) do var len = results.length - # FIXME how to render the pager for one worded namespaces like "standard"? + # FIXME how to render the pager for one worded namespaces like "core"? if len == 1 then var page = results.first.as(PageMatch).page var pager = new Pager @@ -309,12 +298,128 @@ class PageMatch end end +# Search in class or module hierarchy of a `MEntity`. +# +# It actually searches for pages about the mentity and extracts the +# pre-calculated hierarchies by the `doc_post` phase. +abstract class HierarchiesQuery + super DocCommand + + redef fun make_results(nitx, results) do + var page = new DocPage("hierarchy", "Hierarchy") + for result in results do + if not result isa PageMatch then continue + var rpage = result.page + if not rpage isa MClassPage then continue + page.root.add_child build_article(rpage) + end + return page + end + + # Build an article containing the hierarchy list depending on subclasses. + private fun build_article(page: MClassPage): DocArticle is abstract +end + +# List all parents of a `MClass`. +class AncestorsQuery + super HierarchiesQuery + + redef fun build_article(page) do + return new MEntitiesListArticle( + "ancerstors", + "Ancestors for {page.mentity.name}", + page.ancestors.to_a) + end +end + +# List direct parents of a `MClass`. +class ParentsQuery + super HierarchiesQuery + + redef fun build_article(page) do + return new MEntitiesListArticle( + "parents", + "Parents for {page.mentity.name}", + page.parents.to_a) + end +end + +# List direct children of a `MClass`. +class ChildrenQuery + super HierarchiesQuery + + redef fun build_article(page) do + return new MEntitiesListArticle( + "children", + "Children for {page.mentity.name}", + page.children.to_a) + end +end + +# List all descendants of a `MClass`. +class DescendantsQuery + super HierarchiesQuery + + redef fun build_article(page) do + return new MEntitiesListArticle( + "descendants", + "Descendants for {page.mentity.name}", + page.children.to_a) + end +end + +# A query to search source code from a file name. +redef class CodeCommand + # 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.mentities_by_name(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("results", "Code Results") + for res in results do + page.add new CodeQueryArticle("results", "Results", 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 # `nitx` like displaying the help or quiting. interface NitxCommand - super NitxQuery + super DocCommand # Executes the command. fun execute(nitx: Nitx) is abstract @@ -336,29 +441,38 @@ end ## exploration -redef class DocModel - - # Lists all MEntities in the model. - private var mentities: Collection[MEntity] is lazy do - var res = new HashSet[MEntity] - res.add_all mprojects - res.add_all mgroups - res.add_all mmodules - res.add_all mclasses - res.add_all mclassdefs - res.add_all mproperties - res.add_all mpropdefs - return res +# 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 - # Search MEntities that match `name` by their name or namespace. - private fun search_mentities(name: String): Array[MEntity] do - var res = new Array[MEntity] - for mentity in mentities do - if mentity.name != name and mentity.cs_namespace != name then continue - res.add mentity - end - return res +# 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.as(not null).mproperty end end @@ -369,7 +483,7 @@ private class QueryResultArticle super DocArticle # Query linked to the results to display. - var query: NitxQuery + var query: DocCommand # Results to display. var results: Array[NitxMatch] @@ -377,9 +491,9 @@ private class QueryResultArticle redef fun render_title do var len = results.length if len == 0 then - add "No result found for '{query.query_string}'..." + add "No result found for '{query.string}'..." else - add "# {len} result(s) for '{query.query_string}'".green.bold + add "# {len} result(s) for '{query.string}'".green.bold end end @@ -392,6 +506,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: DocCommand + + # 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 @@ -419,8 +551,8 @@ private class Pager b.append("\\\\") else if c == '`' then b.append("'") - else if c.ascii < 32 then - b.append("\\{c.ascii.to_base(8, false)}") + else if c.code_point < 32 then + b.append("\\{c.code_point.to_base(8, false)}") else b.add(c) end