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.
22 import doc
::console_templates
24 # Nitx handles console I/O.
26 # Using `prompt`, the command line can be turned on an interactive tool.
29 # ToolContext used to access options.
32 # DocModel that contains the informations to display.
35 # Comparator used to sort MEntities.
36 var sorter
= new MEntityNameSorter
38 # Displays the welcome message and start prompt.
44 # Displays the welcome message and the list of loaded modules.
46 print
"Welcome in the Nit Index."
48 print
"Loaded modules:"
49 var mmodules
= doc
.mmodules
.to_a
58 # Displays the list of available commands.
61 print
"\tname\t\tlookup module, class and property with the corresponding 'name'"
62 print
"\tdoc: <name::space>\tdisplay the documentation page of 'namespace'"
63 print
"\tparam: <Type>\tlookup methods using the corresponding 'Type' as parameter"
64 print
"\treturn: <Type>\tlookup methods returning the corresponding 'Type'"
65 print
"\tnew: <Type>\tlookup methods creating new instances of 'Type'"
66 print
"\tcall: <name>\tlookup methods calling 'name'"
67 print
"\tcode: <name>\tdisplay the source code associated to the 'name' entity"
68 print
"\t:h\t\tdisplay this help message"
69 print
"\t:q\t\tquit interactive mode"
73 # Prompts the user for a command.
76 do_query
(sys
.stdin
.read_line
)
80 # Processes the query string and performs it.
81 fun do_query
(str
: String) do
82 var query
= parse_query
(str
)
83 var res
= query
.perform
(self, doc
)
84 var page
= query
.make_results
(self, res
)
85 print page
.write_to_string
88 # Returns an `NitxQuery` from a raw query string.
89 fun parse_query
(str
: String): NitxQuery do
90 var query
= new NitxQuery(str
)
91 if query
isa NitxCommand then
98 # A query performed on Nitx.
100 # Queries are responsible to collect matching results and render them as a
103 # Used as a factory to concrete instances.
106 # Original query string.
107 fun query_string
: String is abstract
111 # Will return a concrete instance of NitxQuery.
112 new(query_string
: String) do
113 if query_string
== ":q" then
115 else if query_string
== ":h" then
117 else if query_string
.has_prefix
("comment:") then
118 return new CommentQuery(query_string
)
119 else if query_string
.has_prefix
("doc:") then
120 return new DocQuery(query_string
)
121 else if query_string
.has_prefix
("param:") then
122 return new ParamQuery(query_string
)
123 else if query_string
.has_prefix
("return:") then
124 return new ReturnQuery(query_string
)
125 else if query_string
.has_prefix
("new:") then
126 return new NewQuery(query_string
)
127 else if query_string
.has_prefix
("call:") then
128 return new CallQuery(query_string
)
129 else if query_string
.has_prefix
("code:") then
130 return new CodeQuery(query_string
)
133 return new CommentQuery("comment: {query_string}")
136 # Looks up the `doc` model and returns possible matches.
137 fun perform
(nitx
: Nitx, doc
: DocModel): Array[NitxMatch] is abstract
139 # Pretty prints the results for the console.
140 fun make_results
(nitx
: Nitx, results
: Array[NitxMatch]): DocPage do
141 var page
= new DocPage("Results")
142 page
.root
.add_child
(new QueryResultArticle(self, results
))
146 redef fun to_s
do return query_string
149 # Something that matches a `NitxQuery`.
150 abstract class NitxMatch
152 # Query matched by `self`.
155 # Pretty prints `self` for console.
156 fun make_list_item
: String is abstract
159 # A query that contains a meta command.
161 # In Nitx, commands are written such as `command: args...`.
162 abstract class MetaQuery
165 redef var query_string
168 var command
: String is noinit
170 # Arguments passed to the `command`.
171 var args
= new Array[String]
175 var str
= new FlatBuffer
177 while i
< query_string
.length
do
178 var c
= query_string
[i
]
180 if c
== ':' then break
183 command
= str
.write_to_string
185 args
.add query_string
.substring_from
(i
).trim
189 # A match between a `NitxQuery` and a `MEntity`.
196 redef fun make_list_item
do return mentity
.cs_list_item
199 # A query to search a `MEntity` comment by its name or namespace.
203 redef fun perform
(nitx
, doc
) do
204 var name
= args
.first
205 var res
= new Array[NitxMatch]
206 for mentity
in doc
.search_mentities
(name
) do
207 res
.add
new MEntityMatch(self, mentity
)
212 redef fun make_results
(nitx
, results
) do
213 var len
= results
.length
215 var res
= results
.first
.as(MEntityMatch)
216 var mentity
= res
.mentity
217 var page
= new DocPage("Results")
218 var article
= new DefinitionArticle(mentity
)
219 article
.cs_title
= mentity
.name
220 article
.cs_subtitle
= mentity
.cs_declaration
221 page
.root
.add_child article
229 # A query to search signatures using a specific `MType` as parameter.
233 redef fun perform
(nitx
, doc
) do
234 var res
= new Array[NitxMatch]
235 var mtype_name
= args
.first
236 for mproperty
in doc
.mproperties
do
237 if not mproperty
isa MMethod then continue
238 var msignature
= mproperty
.intro
.msignature
239 if msignature
!= null then
240 for mparam
in msignature
.mparameters
do
241 if mparam
.mtype
.name
== mtype_name
then
242 res
.add
new MEntityMatch(self, mproperty
)
251 # A query to search signatures using a specific `MType` as return.
255 redef fun perform
(nitx
, doc
) do
256 var res
= new Array[NitxMatch]
257 var mtype_name
= args
.first
258 for mproperty
in doc
.mproperties
do
259 if not mproperty
isa MMethod then continue
260 var msignature
= mproperty
.intro
.msignature
261 if msignature
!= null then
262 var mreturn
= msignature
.return_mtype
263 if mreturn
!= null and mreturn
.name
== mtype_name
then
264 res
.add
new MEntityMatch(self, mproperty
)
272 # A query to search methods creating new instances of a specific `MType`.
276 redef fun perform
(nitx
, doc
) do
277 var res
= new Array[NitxMatch]
278 var mtype_name
= args
.first
279 for mpropdef
in doc
.mpropdefs
do
280 var visitor
= new TypeInitVisitor(mtype_name
)
281 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
282 if npropdef
== null then continue
283 visitor
.enter_visit
(npropdef
)
284 for i
in visitor
.inits
do
285 res
.add
new MEntityMatch(self, mpropdef
)
292 # A query to search methods calling a specific `MProperty`.
296 redef fun perform
(nitx
, doc
) do
297 var res
= new Array[NitxMatch]
298 var mprop_name
= args
.first
299 for mpropdef
in doc
.mpropdefs
do
300 var visitor
= new MPropertyCallVisitor
301 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
302 if npropdef
== null then continue
303 visitor
.enter_visit
(npropdef
)
304 for mprop
in visitor
.calls
do
305 if mprop
.name
!= mprop_name
then continue
306 res
.add
new MEntityMatch(self, mpropdef
)
313 # A query to search a Nitdoc documentation page by its name.
317 redef fun perform
(nitx
, doc
) do
318 var res
= new Array[NitxMatch]
319 var name
= args
.first
320 for page
in doc
.pages
do
321 if name
== "*" then # FIXME dev only
322 res
.add
new PageMatch(self, page
)
323 else if page
.title
== name
then
324 res
.add
new PageMatch(self, page
)
325 else if page
isa MEntityPage and page
.mentity
.cs_namespace
== name
then
326 res
.add
new PageMatch(self, page
)
332 redef fun make_results
(nitx
, results
) do
333 var len
= results
.length
334 # FIXME how to render the pager for one worded namespaces like "standard"?
336 var page
= results
.first
.as(PageMatch).page
337 var pager
= new Pager
338 pager
.add page
.write_to_string
347 # A match between a `DocPage` and a `MEntity`.
354 redef fun make_list_item
do
356 if page
isa MEntityPage then
357 return page
.mentity
.cs_list_item
359 return " * {page.title}"
363 # A query to search source code from a file name.
367 # FIXME refactor this!
368 redef fun perform
(nitx
, doc
) do
369 var res
= new Array[NitxMatch]
370 var name
= args
.first
371 # if name is an existing sourcefile, opens it
372 if name
.file_exists
then
373 var fr
= new FileReader.open
(name
)
374 var content
= fr
.read_all
376 res
.add
new CodeMatch(self, name
, content
)
379 # else, lookup the model by name
380 for mentity
in doc
.search_mentities
(name
) do
381 if mentity
isa MClass then continue
382 if mentity
isa MProperty then continue
383 res
.add
new CodeMatch(self, mentity
.cs_location
, mentity
.cs_source_code
)
388 redef fun make_results
(nitx
, results
) do
389 var page
= new DocPage("Code Results")
390 for res
in results
do
391 page
.add
new CodeQueryArticle(self, res
.as(CodeMatch))
397 # A match between a piece of code and a string.
401 # Location of the code match.
404 # Piece of code matched.
407 redef fun make_list_item
do return "* {location}"
411 # A query that contains a nitx command.
413 # These commands are prefixed with `:` and are used to control the execution of
414 # `nitx` like displaying the help or quiting.
415 interface NitxCommand
418 # Executes the command.
419 fun execute
(nitx
: Nitx) is abstract
426 redef fun execute
(nitx
) do exit
0
429 # Displays the help message.
433 redef fun execute
(nitx
) do nitx
.help
440 # Lists all MEntities in the model.
441 private var mentities
: Collection[MEntity] is lazy
do
442 var res
= new HashSet[MEntity]
443 res
.add_all mprojects
447 res
.add_all mclassdefs
448 res
.add_all mproperties
449 res
.add_all mpropdefs
453 # Search MEntities that match `name` by their name or namespace.
454 private fun search_mentities
(name
: String): Array[MEntity] do
455 var res
= new Array[MEntity]
456 for mentity
in mentities
do
457 if mentity
.name
!= name
and mentity
.cs_namespace
!= name
then continue
464 # Visitor looking for initialized `MType` (new T).
467 private class TypeInitVisitor
470 # `MType` name to look for.
471 var mtype_name
: String
473 var inits
= new HashSet[MType]
474 redef fun visit
(node
)
478 if not node
isa ANewExpr then return
479 var mtype
= node
.n_type
.mtype
480 if mtype
!= null and mtype
.name
== mtype_name
then inits
.add
(mtype
)
484 # Visitor looking for calls to a `MProperty` (new T).
487 private class MPropertyCallVisitor
490 var calls
= new HashSet[MProperty]
491 redef fun visit
(node
)
494 if not node
isa ASendExpr then return
495 calls
.add node
.callsite
.mproperty
501 # A `DocArticle` that displays query results.
502 private class QueryResultArticle
505 # Query linked to the results to display.
508 # Results to display.
509 var results
: Array[NitxMatch]
511 redef fun render_title
do
512 var len
= results
.length
514 add
"No result found for '{query.query_string}'..."
516 add
"# {len} result(s) for '{query.query_string}'".green
.bold
520 redef fun render_body
do
522 for result
in results
do
524 addn result
.make_list_item
529 # An article that displays a piece of code.
530 private class CodeQueryArticle
533 # The query linked to the result to display.
536 # The result to display.
537 var result
: CodeMatch
539 redef fun render_body
do
541 addn
"in {result.location}".gray
.bold
547 # A Pager is used to display data into a unix `less` container.
550 # Content to display.
551 var content
= new FlatBuffer
553 # Adds text to the pager.
554 fun add
(text
: String) do
555 content
.append
(escape
(text
))
558 fun render
do sys
.system
("echo \"{content}\
" | less -r")
560 fun escape
(str
: String): String
562 var b
= new FlatBuffer
563 for c
in str
.chars
do
566 else if c
== '\0' then
568 else if c
== '"' then
570 else if c == '\\' then
572 else if c == '`' then
574 else if c.ascii < 32 then
575 b.append("\\{c.ascii.to_base(8, false)}")