1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Nitx related components
17 # This module is a place holder for `nitx` related services.
18 # No `doc_phase` can be found here, only components used by Nitx tool.
25 import doc
::console_templates
27 # Nitx handles console I/O.
29 # Using `prompt`, the command line can be turned on an interactive tool.
32 # ToolContext used to access options.
35 # DocModel that contains the informations to display.
38 # Comparator used to sort MEntities.
39 var sorter
= new MEntityNameSorter
41 # Displays the welcome message and start prompt.
47 # Displays the welcome message and the list of loaded modules.
49 print
"Welcome in the Nit Index."
51 print
"Loaded modules:"
52 var mmodules
= doc
.mmodules
.to_a
61 # Displays the list of available commands.
64 print
"\tname\t\t\tlookup module, class and property with the corresponding 'name'"
65 print
"\tdoc: <name::space>\tdisplay the documentation page of 'namespace'"
66 print
"\nType lookup:"
67 print
"\tparam: <Type>\t\tlookup methods using the corresponding 'Type' as parameter"
68 print
"\treturn: <Type>\t\tlookup methods returning the corresponding 'Type'"
69 print
"\tnew: <Type>\t\tlookup methods creating new instances of 'Type'"
70 print
"\tcall: <name>\t\tlookup methods calling 'name'"
71 print
"\nHierarchy lookup:"
72 print
"\tparents: <Class>\tlist direct parents of 'Class'"
73 print
"\tancestors: <Class>\tlist all ancestors of 'Class'"
74 print
"\tchildren: <Class>\tlist direct children of 'Class'"
75 print
"\tdescendants: <Class>\tlist all descendants of 'Class'"
76 print
"\nCode lookup:"
77 print
"\tcode: <name>\t\tdisplay the source code associated to the 'name' entity"
79 print
"\t:h\t\t\tdisplay this help message"
80 print
"\t:q\t\t\tquit interactive mode"
84 # Prompts the user for a command.
87 do_query
(sys
.stdin
.read_line
)
91 # Processes the query string and performs it.
92 fun do_query
(str
: String) do
93 var query
= new DocCommand(str
)
94 if query
isa NitxCommand then
98 var res
= query
.perform
(self, doc
)
99 var page
= query
.make_results
(self, res
)
100 print page
.write_to_string
104 redef interface DocCommand
106 redef new(query_string
) do
107 if query_string
== ":q" then
109 else if query_string
== ":h" then
112 var cmd
= super(query_string
)
113 if cmd
isa UnknownCommand then
114 return new CommentCommand("comment: {query_string}")
119 # Looks up the `doc` model and returns possible matches.
120 fun perform
(nitx
: Nitx, doc
: DocModel): Array[NitxMatch] is abstract
122 # Pretty prints the results for the console.
123 fun make_results
(nitx
: Nitx, results
: Array[NitxMatch]): DocPage do
124 var page
= new DocPage("results", "Results")
125 page
.root
.add_child
(new QueryResultArticle("results", "Results", self, results
))
130 # Something that matches a `DocCommand`.
131 abstract class NitxMatch
133 # Query matched by `self`.
134 var query
: DocCommand
136 # Pretty prints `self` for console.
137 fun make_list_item
: String is abstract
140 # A match between a `DocCommand` and a `MEntity`.
147 redef fun make_list_item
do return mentity
.cs_list_item
150 redef class CommentCommand
151 redef fun perform
(nitx
, doc
) do
152 var name
= args
.first
153 var res
= new Array[NitxMatch]
154 for mentity
in doc
.mentities_by_name
(name
) do
155 res
.add
new MEntityMatch(self, mentity
)
160 redef fun make_results
(nitx
, results
) do
161 var len
= results
.length
163 var res
= results
.first
.as(MEntityMatch)
164 var mentity
= res
.mentity
165 var page
= new DocPage("resultats", "Results")
166 var article
= new DefinitionArticle("results", "Results", mentity
)
167 article
.cs_title
= mentity
.name
168 article
.cs_subtitle
= mentity
.cs_declaration
169 page
.root
.add_child article
177 # A query to search signatures using a specific `MType` as parameter.
178 redef class ParamCommand
179 redef fun perform
(nitx
, doc
) do
180 var res
= new Array[NitxMatch]
181 var mtype_name
= args
.first
182 for mproperty
in doc
.mproperties
do
183 if not mproperty
isa MMethod then continue
184 var msignature
= mproperty
.intro
.msignature
185 if msignature
!= null then
186 for mparam
in msignature
.mparameters
do
187 if mparam
.mtype
.name
== mtype_name
then
188 res
.add
new MEntityMatch(self, mproperty
)
197 # A query to search signatures using a specific `MType` as return.
198 redef class ReturnCommand
199 redef fun perform
(nitx
, doc
) do
200 var res
= new Array[NitxMatch]
201 var mtype_name
= args
.first
202 for mproperty
in doc
.mproperties
do
203 if not mproperty
isa MMethod then continue
204 var msignature
= mproperty
.intro
.msignature
205 if msignature
!= null then
206 var mreturn
= msignature
.return_mtype
207 if mreturn
!= null and mreturn
.name
== mtype_name
then
208 res
.add
new MEntityMatch(self, mproperty
)
216 # A query to search methods creating new instances of a specific `MType`.
217 redef class NewCommand
218 redef fun perform
(nitx
, doc
) do
219 var res
= new Array[NitxMatch]
220 var mtype_name
= args
.first
221 for mpropdef
in doc
.mpropdefs
do
222 var visitor
= new TypeInitVisitor(mtype_name
)
223 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
224 if npropdef
== null then continue
225 visitor
.enter_visit
(npropdef
)
226 for i
in visitor
.inits
do
227 res
.add
new MEntityMatch(self, mpropdef
)
234 # A query to search methods calling a specific `MProperty`.
235 redef class CallCommand
236 redef fun perform
(nitx
, doc
) do
237 var res
= new Array[NitxMatch]
238 var mprop_name
= args
.first
239 for mpropdef
in doc
.mpropdefs
do
240 var visitor
= new MPropertyCallVisitor
241 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
242 if npropdef
== null then continue
243 visitor
.enter_visit
(npropdef
)
244 for mprop
in visitor
.calls
do
245 if mprop
.name
!= mprop_name
then continue
246 res
.add
new MEntityMatch(self, mpropdef
)
253 # A query to search a Nitdoc documentation page by its name.
254 redef class ArticleCommand
255 redef fun perform
(nitx
, doc
) do
256 var res
= new Array[NitxMatch]
257 var name
= args
.first
258 for page
in doc
.pages
.values
do
259 if name
== "*" then # FIXME dev only
260 res
.add
new PageMatch(self, page
)
261 else if page
.title
== name
then
262 res
.add
new PageMatch(self, page
)
263 else if page
isa MEntityPage and page
.mentity
.cs_namespace
== name
then
264 res
.add
new PageMatch(self, page
)
270 redef fun make_results
(nitx
, results
) do
271 var len
= results
.length
272 # FIXME how to render the pager for one worded namespaces like "core"?
274 var page
= results
.first
.as(PageMatch).page
275 var pager
= new Pager
276 pager
.add page
.write_to_string
285 # A match between a `DocPage` and a `MEntity`.
292 redef fun make_list_item
do
294 if page
isa MEntityPage then
295 return page
.mentity
.cs_list_item
297 return " * {page.title}"
301 # Search in class or module hierarchy of a `MEntity`.
303 # It actually searches for pages about the mentity and extracts the
304 # pre-calculated hierarchies by the `doc_post` phase.
305 abstract class HierarchiesQuery
308 redef fun make_results
(nitx
, results
) do
309 var page
= new DocPage("hierarchy", "Hierarchy")
310 for result
in results
do
311 if not result
isa PageMatch then continue
312 var rpage
= result
.page
313 if not rpage
isa MClassPage then continue
314 page
.root
.add_child build_article
(rpage
)
319 # Build an article containing the hierarchy list depending on subclasses.
320 private fun build_article
(page
: MClassPage): DocArticle is abstract
323 # List all parents of a `MClass`.
325 super HierarchiesQuery
327 redef fun build_article
(page
) do
328 return new MEntitiesListArticle(
330 "Ancestors for {page.mentity.name}",
335 # List direct parents of a `MClass`.
337 super HierarchiesQuery
339 redef fun build_article
(page
) do
340 return new MEntitiesListArticle(
342 "Parents for {page.mentity.name}",
347 # List direct children of a `MClass`.
349 super HierarchiesQuery
351 redef fun build_article
(page
) do
352 return new MEntitiesListArticle(
354 "Children for {page.mentity.name}",
359 # List all descendants of a `MClass`.
360 class DescendantsQuery
361 super HierarchiesQuery
363 redef fun build_article
(page
) do
364 return new MEntitiesListArticle(
366 "Descendants for {page.mentity.name}",
371 # A query to search source code from a file name.
372 redef class CodeCommand
373 # FIXME refactor this!
374 redef fun perform
(nitx
, doc
) do
375 var res
= new Array[NitxMatch]
376 var name
= args
.first
377 # if name is an existing sourcefile, opens it
378 if name
.file_exists
then
379 var fr
= new FileReader.open
(name
)
380 var content
= fr
.read_all
382 res
.add
new CodeMatch(self, name
, content
)
385 # else, lookup the model by name
386 for mentity
in doc
.mentities_by_name
(name
) do
387 if mentity
isa MClass then continue
388 if mentity
isa MProperty then continue
389 res
.add
new CodeMatch(self, mentity
.cs_location
, mentity
.cs_source_code
)
394 redef fun make_results
(nitx
, results
) do
395 var page
= new DocPage("results", "Code Results")
396 for res
in results
do
397 page
.add
new CodeQueryArticle("results", "Results", self, res
.as(CodeMatch))
403 # A match between a piece of code and a string.
407 # Location of the code match.
410 # Piece of code matched.
413 redef fun make_list_item
do return "* {location}"
417 # A query that contains a nitx command.
419 # These commands are prefixed with `:` and are used to control the execution of
420 # `nitx` like displaying the help or quiting.
421 interface NitxCommand
424 # Executes the command.
425 fun execute
(nitx
: Nitx) is abstract
432 redef fun execute
(nitx
) do exit
0
435 # Displays the help message.
439 redef fun execute
(nitx
) do nitx
.help
444 # Visitor looking for initialized `MType` (new T).
447 private class TypeInitVisitor
450 # `MType` name to look for.
451 var mtype_name
: String
453 var inits
= new HashSet[MType]
454 redef fun visit
(node
)
458 if not node
isa ANewExpr then return
459 var mtype
= node
.n_type
.mtype
460 if mtype
!= null and mtype
.name
== mtype_name
then inits
.add
(mtype
)
464 # Visitor looking for calls to a `MProperty` (new T).
467 private class MPropertyCallVisitor
470 var calls
= new HashSet[MProperty]
471 redef fun visit
(node
)
474 if not node
isa ASendExpr then return
475 calls
.add node
.callsite
.as(not null).mproperty
481 # A `DocArticle` that displays query results.
482 private class QueryResultArticle
485 # Query linked to the results to display.
486 var query
: DocCommand
488 # Results to display.
489 var results
: Array[NitxMatch]
491 redef fun render_title
do
492 var len
= results
.length
494 add
"No result found for '{query.string}'..."
496 add
"# {len} result(s) for '{query.string}'".green
.bold
500 redef fun render_body
do
502 for result
in results
do
504 addn result
.make_list_item
509 # An article that displays a piece of code.
510 private class CodeQueryArticle
513 # The query linked to the result to display.
514 var query
: DocCommand
516 # The result to display.
517 var result
: CodeMatch
519 redef fun render_body
do
521 addn
"in {result.location}".gray
.bold
527 # A Pager is used to display data into a unix `less` container.
530 # Content to display.
531 var content
= new FlatBuffer
533 # Adds text to the pager.
534 fun add
(text
: String) do
535 content
.append
(escape
(text
))
538 fun render
do sys
.system
("echo \"{content}\
" | less -r")
540 fun escape
(str
: String): String
542 var b
= new FlatBuffer
543 for c
in str
.chars
do
546 else if c
== '\0' then
548 else if c
== '"' then
550 else if c == '\\' then
552 else if c == '`' then
554 else if c.ascii < 32 then
555 b.append("\\{c.ascii.to_base(8, false)}")