module doc_console
import semantize
+import doc_commands
+import doc_poset
import doc::console_templates
+import model::model_index
# Nitx handles console I/O.
#
# 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: <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 "\nType lookup:"
+ print "\tparam: <Type>\t\tlookup methods using the corresponding 'Type' as parameter"
+ print "\treturn: <Type>\t\tlookup methods returning the corresponding 'Type'"
+ print "\tnew: <Type>\t\tlookup methods creating new instances of 'Type'"
+ print "\tcall: <name>\t\tlookup methods calling 'name'"
+ print "\nHierarchy lookup:"
+ print "\tparents: <Class>\tlist direct parents of 'Class'"
+ print "\tancestors: <Class>\tlist all ancestors of 'Class'"
+ print "\tchildren: <Class>\tlist direct children of 'Class'"
+ print "\tdescendants: <Class>\tlist all descendants of 'Class'"
+ print "\nCode lookup:"
+ print "\tcode: <name>\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
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
+ 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(self, 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
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
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
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
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)
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)
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
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)
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
+ 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
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).
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
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
super DocArticle
# The query linked to the result to display.
- var query: NitxQuery
+ var query: DocCommand
# The result to display.
var result: CodeMatch
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