e14fa46e945ae1504928cf39979d635ec443987e
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.
23 import doc
::console_templates
25 # Nitx handles console I/O.
27 # Using `prompt`, the command line can be turned on an interactive tool.
30 # ToolContext used to access options.
33 # DocModel that contains the informations to display.
36 # Comparator used to sort MEntities.
37 var sorter
= new MEntityNameSorter
39 # Displays the welcome message and start prompt.
45 # Displays the welcome message and the list of loaded modules.
47 print
"Welcome in the Nit Index."
49 print
"Loaded modules:"
50 var mmodules
= doc
.mmodules
.to_a
59 # Displays the list of available commands.
62 print
"\tname\t\t\tlookup module, class and property with the corresponding 'name'"
63 print
"\tdoc: <name::space>\tdisplay the documentation page of 'namespace'"
64 print
"\nType lookup:"
65 print
"\tparam: <Type>\t\tlookup methods using the corresponding 'Type' as parameter"
66 print
"\treturn: <Type>\t\tlookup methods returning the corresponding 'Type'"
67 print
"\tnew: <Type>\t\tlookup methods creating new instances of 'Type'"
68 print
"\tcall: <name>\t\tlookup methods calling 'name'"
69 print
"\nCode lookup:"
70 print
"\tcode: <name>\t\tdisplay the source code associated to the 'name' entity"
72 print
"\t:h\t\t\tdisplay this help message"
73 print
"\t:q\t\t\tquit interactive mode"
77 # Prompts the user for a command.
80 do_query
(sys
.stdin
.read_line
)
84 # Processes the query string and performs it.
85 fun do_query
(str
: String) do
86 var query
= new NitxQuery(str
)
87 if query
isa NitxCommand then
91 var res
= query
.perform
(self, doc
)
92 var page
= query
.make_results
(self, res
)
93 print page
.write_to_string
97 # A query performed on Nitx.
99 # Queries are responsible to collect matching results and render them as a
102 # Used as a factory to concrete instances.
105 # Original query string.
106 fun query_string
: String is abstract
110 # Will return a concrete instance of NitxQuery.
111 new(query_string
: String) do
112 if query_string
== ":q" then
114 else if query_string
== ":h" then
116 else if query_string
.has_prefix
("comment:") then
117 return new CommentQuery(query_string
)
118 else if query_string
.has_prefix
("doc:") then
119 return new DocQuery(query_string
)
120 else if query_string
.has_prefix
("param:") then
121 return new ParamQuery(query_string
)
122 else if query_string
.has_prefix
("return:") then
123 return new ReturnQuery(query_string
)
124 else if query_string
.has_prefix
("new:") then
125 return new NewQuery(query_string
)
126 else if query_string
.has_prefix
("call:") then
127 return new CallQuery(query_string
)
128 else if query_string
.has_prefix
("code:") then
129 return new CodeQuery(query_string
)
132 return new CommentQuery("comment: {query_string}")
135 # Looks up the `doc` model and returns possible matches.
136 fun perform
(nitx
: Nitx, doc
: DocModel): Array[NitxMatch] is abstract
138 # Pretty prints the results for the console.
139 fun make_results
(nitx
: Nitx, results
: Array[NitxMatch]): DocPage do
140 var page
= new DocPage("results", "Results")
141 page
.root
.add_child
(new QueryResultArticle("results.article", "Results", self, results
))
145 redef fun to_s
do return query_string
148 # Something that matches a `NitxQuery`.
149 abstract class NitxMatch
151 # Query matched by `self`.
154 # Pretty prints `self` for console.
155 fun make_list_item
: String is abstract
158 # A query that contains a meta command.
160 # In Nitx, commands are written such as `command: args...`.
161 abstract class MetaQuery
164 redef var query_string
167 var command
: String is noinit
169 # Arguments passed to the `command`.
170 var args
= new Array[String]
174 var str
= new FlatBuffer
176 while i
< query_string
.length
do
177 var c
= query_string
[i
]
179 if c
== ':' then break
182 command
= str
.write_to_string
184 args
.add query_string
.substring_from
(i
).trim
188 # A match between a `NitxQuery` and a `MEntity`.
195 redef fun make_list_item
do return mentity
.cs_list_item
198 # A query to search a `MEntity` comment by its name or namespace.
202 redef fun perform
(nitx
, doc
) do
203 var name
= args
.first
204 var res
= new Array[NitxMatch]
205 for mentity
in doc
.mentities_by_name
(name
) do
206 res
.add
new MEntityMatch(self, mentity
)
211 redef fun make_results
(nitx
, results
) do
212 var len
= results
.length
214 var res
= results
.first
.as(MEntityMatch)
215 var mentity
= res
.mentity
216 var page
= new DocPage("results", "Results")
217 var article
= new DefinitionArticle("results.article", "Results", mentity
)
218 article
.cs_title
= mentity
.name
219 article
.cs_subtitle
= mentity
.cs_declaration
220 page
.root
.add_child article
228 # A query to search signatures using a specific `MType` as parameter.
232 redef fun perform
(nitx
, doc
) do
233 var res
= new Array[NitxMatch]
234 var mtype_name
= args
.first
235 for mproperty
in doc
.mproperties
do
236 if not mproperty
isa MMethod then continue
237 var msignature
= mproperty
.intro
.msignature
238 if msignature
!= null then
239 for mparam
in msignature
.mparameters
do
240 if mparam
.mtype
.name
== mtype_name
then
241 res
.add
new MEntityMatch(self, mproperty
)
250 # A query to search signatures using a specific `MType` as return.
254 redef fun perform
(nitx
, doc
) do
255 var res
= new Array[NitxMatch]
256 var mtype_name
= args
.first
257 for mproperty
in doc
.mproperties
do
258 if not mproperty
isa MMethod then continue
259 var msignature
= mproperty
.intro
.msignature
260 if msignature
!= null then
261 var mreturn
= msignature
.return_mtype
262 if mreturn
!= null and mreturn
.name
== mtype_name
then
263 res
.add
new MEntityMatch(self, mproperty
)
271 # A query to search methods creating new instances of a specific `MType`.
275 redef fun perform
(nitx
, doc
) do
276 var res
= new Array[NitxMatch]
277 var mtype_name
= args
.first
278 for mpropdef
in doc
.mpropdefs
do
279 var visitor
= new TypeInitVisitor(mtype_name
)
280 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
281 if npropdef
== null then continue
282 visitor
.enter_visit
(npropdef
)
283 for i
in visitor
.inits
do
284 res
.add
new MEntityMatch(self, mpropdef
)
291 # A query to search methods calling a specific `MProperty`.
295 redef fun perform
(nitx
, doc
) do
296 var res
= new Array[NitxMatch]
297 var mprop_name
= args
.first
298 for mpropdef
in doc
.mpropdefs
do
299 var visitor
= new MPropertyCallVisitor
300 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
301 if npropdef
== null then continue
302 visitor
.enter_visit
(npropdef
)
303 for mprop
in visitor
.calls
do
304 if mprop
.name
!= mprop_name
then continue
305 res
.add
new MEntityMatch(self, mpropdef
)
312 # A query to search a Nitdoc documentation page by its name.
316 redef fun perform
(nitx
, doc
) do
317 var res
= new Array[NitxMatch]
318 var name
= args
.first
319 for page
in doc
.pages
.values
do
320 if name
== "*" then # FIXME dev only
321 res
.add
new PageMatch(self, page
)
322 else if page
.title
== name
then
323 res
.add
new PageMatch(self, page
)
324 else if page
isa MEntityPage and page
.mentity
.cs_namespace
== name
then
325 res
.add
new PageMatch(self, page
)
331 redef fun make_results
(nitx
, results
) do
332 var len
= results
.length
333 # FIXME how to render the pager for one worded namespaces like "standard"?
335 var page
= results
.first
.as(PageMatch).page
336 var pager
= new Pager
337 pager
.add page
.write_to_string
346 # A match between a `DocPage` and a `MEntity`.
353 redef fun make_list_item
do
355 if page
isa MEntityPage then
356 return page
.mentity
.cs_list_item
358 return " * {page.title}"
362 # A query to search source code from a file name.
366 # FIXME refactor this!
367 redef fun perform
(nitx
, doc
) do
368 var res
= new Array[NitxMatch]
369 var name
= args
.first
370 # if name is an existing sourcefile, opens it
371 if name
.file_exists
then
372 var fr
= new FileReader.open
(name
)
373 var content
= fr
.read_all
375 res
.add
new CodeMatch(self, name
, content
)
378 # else, lookup the model by name
379 for mentity
in doc
.mentities_by_name
(name
) do
380 if mentity
isa MClass then continue
381 if mentity
isa MProperty then continue
382 res
.add
new CodeMatch(self, mentity
.cs_location
, mentity
.cs_source_code
)
387 redef fun make_results
(nitx
, results
) do
388 var page
= new DocPage("results", "Code Results")
389 for res
in results
do
390 page
.add
new CodeQueryArticle("results.article", "Results", self, res
.as(CodeMatch))
396 # A match between a piece of code and a string.
400 # Location of the code match.
403 # Piece of code matched.
406 redef fun make_list_item
do return "* {location}"
410 # A query that contains a nitx command.
412 # These commands are prefixed with `:` and are used to control the execution of
413 # `nitx` like displaying the help or quiting.
414 interface NitxCommand
417 # Executes the command.
418 fun execute
(nitx
: Nitx) is abstract
425 redef fun execute
(nitx
) do exit
0
428 # Displays the help message.
432 redef fun execute
(nitx
) do nitx
.help
437 # Visitor looking for initialized `MType` (new T).
440 private class TypeInitVisitor
443 # `MType` name to look for.
444 var mtype_name
: String
446 var inits
= new HashSet[MType]
447 redef fun visit
(node
)
451 if not node
isa ANewExpr then return
452 var mtype
= node
.n_type
.mtype
453 if mtype
!= null and mtype
.name
== mtype_name
then inits
.add
(mtype
)
457 # Visitor looking for calls to a `MProperty` (new T).
460 private class MPropertyCallVisitor
463 var calls
= new HashSet[MProperty]
464 redef fun visit
(node
)
467 if not node
isa ASendExpr then return
468 calls
.add node
.callsite
.mproperty
474 # A `DocArticle` that displays query results.
475 private class QueryResultArticle
478 # Query linked to the results to display.
481 # Results to display.
482 var results
: Array[NitxMatch]
484 redef fun render_title
do
485 var len
= results
.length
487 add
"No result found for '{query.query_string}'..."
489 add
"# {len} result(s) for '{query.query_string}'".green
.bold
493 redef fun render_body
do
495 for result
in results
do
497 addn result
.make_list_item
502 # An article that displays a piece of code.
503 private class CodeQueryArticle
506 # The query linked to the result to display.
509 # The result to display.
510 var result
: CodeMatch
512 redef fun render_body
do
514 addn
"in {result.location}".gray
.bold
520 # A Pager is used to display data into a unix `less` container.
523 # Content to display.
524 var content
= new FlatBuffer
526 # Adds text to the pager.
527 fun add
(text
: String) do
528 content
.append
(escape
(text
))
531 fun render
do sys
.system
("echo \"{content}\
" | less -r")
533 fun escape
(str
: String): String
535 var b
= new FlatBuffer
536 for c
in str
.chars
do
539 else if c
== '\0' then
541 else if c
== '"' then
543 else if c == '\\' then
545 else if c == '`' then
547 else if c.ascii < 32 then
548 b.append("\\{c.ascii.to_base(8, false)}")