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
"\t:h\t\tdisplay this help message"
63 print
"\t:q\t\tquit interactive mode"
67 # Prompts the user for a command.
70 do_query
(sys
.stdin
.read_line
)
74 # Processes the query string and performs it.
75 fun do_query
(str
: String) do
76 var query
= parse_query
(str
)
77 var res
= query
.perform
(self, doc
)
78 var page
= query
.make_results
(self, res
)
79 print page
.write_to_string
82 # Returns an `NitxQuery` from a raw query string.
83 fun parse_query
(str
: String): NitxQuery do
84 var query
= new NitxQuery(str
)
85 if query
isa NitxCommand then
92 # A query performed on Nitx.
94 # Queries are responsible to collect matching results and render them as a
97 # Used as a factory to concrete instances.
100 # Original query string.
101 fun query_string
: String is abstract
105 # Will return a concrete instance of NitxQuery.
106 new(query_string
: String) do
107 if query_string
== ":q" then
109 else if query_string
== ":h" then
111 else if query_string
.has_prefix
("comment:") then
112 return new CommentQuery(query_string
)
114 return new CommentQuery("comment: {query_string}")
117 # Looks up the `doc` model and returns possible matches.
118 fun perform
(nitx
: Nitx, doc
: DocModel): Array[NitxMatch] is abstract
120 # Pretty prints the results for the console.
121 fun make_results
(nitx
: Nitx, results
: Array[NitxMatch]): DocPage do
122 var page
= new DocPage("Results")
123 page
.root
.add_child
(new QueryResultArticle(self, results
))
127 redef fun to_s
do return query_string
130 # Something that matches a `NitxQuery`.
131 abstract class NitxMatch
133 # Query matched by `self`.
136 # Pretty prints `self` for console.
137 fun make_list_item
: String is abstract
140 # A query that contains a meta command.
142 # In Nitx, commands are written such as `command: args...`.
143 abstract class MetaQuery
146 redef var query_string
149 var command
: String is noinit
151 # Arguments passed to the `command`.
152 var args
= new Array[String]
156 var str
= new FlatBuffer
158 while i
< query_string
.length
do
159 var c
= query_string
[i
]
161 if c
== ':' then break
164 command
= str
.write_to_string
166 args
.add query_string
.substring_from
(i
).trim
170 # A match between a `NitxQuery` and a `MEntity`.
177 redef fun make_list_item
do return mentity
.cs_list_item
180 # A query to search a `MEntity` comment by its name or namespace.
184 redef fun perform
(nitx
, doc
) do
185 var name
= args
.first
186 var res
= new Array[NitxMatch]
187 for mentity
in doc
.search_mentities
(name
) do
188 res
.add
new MEntityMatch(self, mentity
)
193 redef fun make_results
(nitx
, results
) do
194 var len
= results
.length
196 var res
= results
.first
.as(MEntityMatch)
197 var mentity
= res
.mentity
198 var page
= new DocPage("Results")
199 var article
= new DefinitionArticle(mentity
)
200 article
.cs_title
= mentity
.name
201 article
.cs_subtitle
= mentity
.cs_declaration
202 page
.root
.add_child article
210 # A query that contains a nitx command.
212 # These commands are prefixed with `:` and are used to control the execution of
213 # `nitx` like displaying the help or quiting.
214 interface NitxCommand
217 # Executes the command.
218 fun execute
(nitx
: Nitx) is abstract
225 redef fun execute
(nitx
) do exit
0
228 # Displays the help message.
232 redef fun execute
(nitx
) do nitx
.help
239 # Lists all MEntities in the model.
240 private var mentities
: Collection[MEntity] is lazy
do
241 var res
= new HashSet[MEntity]
242 res
.add_all mprojects
246 res
.add_all mclassdefs
247 res
.add_all mproperties
248 res
.add_all mpropdefs
252 # Search MEntities that match `name` by their name or namespace.
253 private fun search_mentities
(name
: String): Array[MEntity] do
254 var res
= new Array[MEntity]
255 for mentity
in mentities
do
256 if mentity
.name
!= name
and mentity
.cs_namespace
!= name
then continue
265 # A `DocArticle` that displays query results.
266 private class QueryResultArticle
269 # Query linked to the results to display.
272 # Results to display.
273 var results
: Array[NitxMatch]
275 redef fun render_title
do
276 var len
= results
.length
278 add
"No result found for '{query.query_string}'..."
280 add
"# {len} result(s) for '{query.query_string}'".green
.bold
284 redef fun render_body
do
286 for result
in results
do
288 addn result
.make_list_item
293 # A Pager is used to display data into a unix `less` container.
296 # Content to display.
297 var content
= new FlatBuffer
299 # Adds text to the pager.
300 fun add
(text
: String) do
301 content
.append
(escape
(text
))
304 fun render
do sys
.system
("echo \"{content}\
" | less -r")
306 fun escape
(str
: String): String
308 var b
= new FlatBuffer
309 for c
in str
.chars
do
312 else if c
== '\0' then
314 else if c
== '"' then
316 else if c == '\\' then
318 else if c == '`' then
320 else if c.ascii < 32 then
321 b.append("\\{c.ascii.to_base(8, false)}")