module doc_console
import semantize
+import doc_extract
+import doc_poset
import doc::console_templates
# 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 "\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
# 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)
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
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)
+ else if query_string.has_prefix("parents:") then
+ return new ParentsQuery(query_string)
+ else if query_string.has_prefix("ancestors:") then
+ return new AncestorsQuery(query_string)
+ else if query_string.has_prefix("children:") then
+ return new ChildrenQuery(query_string)
+ else if query_string.has_prefix("descendants:") then
+ return new DescendantsQuery(query_string)
end
return new CommentQuery("comment: {query_string}")
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))
+ var page = new DocPage("results", "Results")
+ page.root.add_child(new QueryResultArticle("results.article", "Results", self, results))
return page
end
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
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("results", "Results")
+ var article = new DefinitionArticle("results.article", "Results", mentity)
article.cs_title = mentity.name
article.cs_subtitle = mentity.cs_declaration
page.root.add_child article
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
+ 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
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 DocQuery
+
+ 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.
+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.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.article", "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
## 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.mproperty
end
end
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