X-Git-Url: http://nitlanguage.org?ds=sidebyside diff --git a/src/doc/doc_phases/doc_console.nit b/src/doc/doc_phases/doc_console.nit index 2d9ad8e..6a5d045 100644 --- a/src/doc/doc_phases/doc_console.nit +++ b/src/doc/doc_phases/doc_console.nit @@ -19,7 +19,10 @@ module doc_console import semantize +import doc_commands +import doc_poset import doc::console_templates +import model::model_index # Nitx handles console I/O. # @@ -57,16 +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 "\tnew: \tlookup methods creating new instances of 'Type'" - print "\tcall: \tlookup methods calling 'name'" - print "\tcode: \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 "\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 @@ -77,116 +88,93 @@ class Nitx prompt end + # Parser used to process doc commands + var parser: DocCommandParser is lazy do + var parser = new DocCommandParser + parser.allowed_commands = ["doc", "comment", "list", "param", "return", + "new", "call", "code"] + return parser + end + # Processes the query string and performs it. fun do_query(str: String) do - var query = parse_query(str) + if str == ":q" then + exit 0 + else if str == ":h" then + help + return + end + var query = parser.parse(str) + if query == null then + query = new CommentCommand(str) + query.arg = str + end 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) - if query isa NitxCommand then - query.execute(self) + var suggest = null + if res.is_empty then + suggest = query.suggest(self, doc) end - return query + var page = query.make_results(self, res, suggest) + 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 - - # Query factory. - # - # Will return a concrete instance of NitxQuery. - new(query_string: 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) - 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 +redef class DocCommand # Looks up the `doc` model and returns possible matches. fun perform(nitx: Nitx, doc: DocModel): Array[NitxMatch] is abstract + # Looks up the `doc` model and returns possible suggestions. + fun suggest(nitx: Nitx, doc: DocModel): nullable Array[MEntity] do + return find_suggestions(doc, arg) + end + # 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)) + fun make_results(nitx: Nitx, results: Array[NitxMatch], suggest: nullable Array[MEntity]): DocPage do + var page = new DocPage("results", "Results") + page.root.add_child(new QueryResultArticle("results", "Results", self, results, suggest)) return page end - redef fun to_s do return query_string + # Lookup mentities based on a `query` string. + # + # 1- lookup by first name (returns always one value) + # 2- lookup by name (can return conflicts) + fun find_mentities(doc: DocModel, query: String): Array[MEntityMatch] do + var res = new Array[MEntityMatch] + + # First lookup by full_name + var mentity = doc.mentity_by_full_name(query) + if mentity != null then + res.add new MEntityMatch(self, mentity) + return res + end + + # If no results, lookup by name + for m in doc.mentities_by_name(query) do + res.add new MEntityMatch(self, m) + end + + return res + end + + # Suggest some mentities based on a `query` string. + fun find_suggestions(doc: DocModel, query: String): Array[MEntity] do + return doc.find(query, 3) + end 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 @@ -196,26 +184,16 @@ 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 return find_mentities(doc, arg) - redef fun perform(nitx, doc) do - var name = args.first - var res = new Array[NitxMatch] - for mentity in doc.mentities_by_name(name) do - res.add new MEntityMatch(self, mentity) - end - return res - end - - redef fun make_results(nitx, results) do + redef fun make_results(nitx, results, suggest) do var len = results.length 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 @@ -227,12 +205,10 @@ 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 + var mtype_name = arg for mproperty in doc.mproperties do if not mproperty isa MMethod then continue var msignature = mproperty.intro.msignature @@ -249,12 +225,10 @@ 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 + var mtype_name = arg for mproperty in doc.mproperties do if not mproperty isa MMethod then continue var msignature = mproperty.intro.msignature @@ -270,12 +244,10 @@ class ReturnQuery end # A query to search methods creating new instances of a specific `MType`. -class NewQuery - super MetaQuery - +redef class NewCommand redef fun perform(nitx, doc) do var res = new Array[NitxMatch] - var mtype_name = args.first + var mtype_name = arg for mpropdef in doc.mpropdefs do var visitor = new TypeInitVisitor(mtype_name) var npropdef = nitx.ctx.modelbuilder.mpropdef2node(mpropdef) @@ -290,12 +262,10 @@ class NewQuery end # A query to search methods calling a specific `MProperty`. -class CallQuery - super MetaQuery - +redef class CallCommand redef fun perform(nitx, doc) do var res = new Array[NitxMatch] - var mprop_name = args.first + var mprop_name = arg for mpropdef in doc.mpropdefs do var visitor = new MPropertyCallVisitor var npropdef = nitx.ctx.modelbuilder.mpropdef2node(mpropdef) @@ -310,40 +280,6 @@ class CallQuery 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 @@ -360,14 +296,82 @@ class PageMatch end end -# A query to search source code from a file name. -class CodeQuery - super MetaQuery +# 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, suggest) 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 + var name = arg # if name is an existing sourcefile, opens it if name.file_exists then var fr = new FileReader.open(name) @@ -377,18 +381,18 @@ class CodeQuery 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) + for match in find_mentities(doc, name) do + if match.mentity isa MClass then continue + if match.mentity isa MProperty then continue + res.add new CodeMatch(self, match.mentity.cs_location, match.mentity.cs_source_code) end return res end - redef fun make_results(nitx, results) do - var page = new DocPage("Code Results") + redef fun make_results(nitx, results, suggest) do + var page = new DocPage("results", "Code Results") for res in results do - page.add new CodeQueryArticle(self, res.as(CodeMatch)) + page.add new CodeQueryArticle("results", "Results", self, res.as(CodeMatch)) end return page end @@ -407,32 +411,6 @@ class CodeMatch 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 - - # Executes the command. - fun execute(nitx: Nitx) is abstract -end - -# Exits nitx. -class NitxQuit - super NitxCommand - - redef fun execute(nitx) do exit 0 -end - -# Displays the help message. -class NitxHelp - super NitxCommand - - redef fun execute(nitx) do nitx.help -end - ## exploration # Visitor looking for initialized `MType` (new T). @@ -466,7 +444,7 @@ private class MPropertyCallVisitor do node.visit_all(self) if not node isa ASendExpr then return - calls.add node.callsite.mproperty + calls.add node.callsite.as(not null).mproperty end end @@ -477,17 +455,32 @@ 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] + # Optional suggestion when no matches where found + var suggest: nullable Array[MEntity] = null is optional + redef fun render_title do var len = results.length if len == 0 then - add "No result found for '{query.query_string}'..." + addn "No result found for '{query.string}'..." + var suggest = self.suggest + if suggest != null and suggest.not_empty then + add "\nDid you mean " + var i = 0 + for s in suggest do + add "`{s.full_name}`" + if i == suggest.length - 2 then add ", " + if i == suggest.length - 1 then add " or " + i += 1 + end + add "?" + end else - add "# {len} result(s) for '{query.query_string}'".green.bold + add "# {len} result(s) for '{query.string}'".green.bold end end @@ -505,7 +498,7 @@ private class CodeQueryArticle super DocArticle # The query linked to the result to display. - var query: NitxQuery + var query: DocCommand # The result to display. var result: CodeMatch @@ -545,8 +538,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)}") else b.add(c) end