b6d08dca987c84cb3d19783ed4e8a6568ff25c54
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.
24 import doc
::console_templates
26 # Nitx handles console I/O.
28 # Using `prompt`, the command line can be turned on an interactive tool.
31 # ToolContext used to access options.
34 # DocModel that contains the informations to display.
37 # Comparator used to sort MEntities.
38 var sorter
= new MEntityNameSorter
40 # Displays the welcome message and start prompt.
46 # Displays the welcome message and the list of loaded modules.
48 print
"Welcome in the Nit Index."
50 print
"Loaded modules:"
51 var mmodules
= doc
.mmodules
.to_a
60 # Displays the list of available commands.
63 print
"\tname\t\t\tlookup module, class and property with the corresponding 'name'"
64 print
"\tdoc: <name::space>\tdisplay the documentation page of 'namespace'"
65 print
"\nType lookup:"
66 print
"\tparam: <Type>\t\tlookup methods using the corresponding 'Type' as parameter"
67 print
"\treturn: <Type>\t\tlookup methods returning the corresponding 'Type'"
68 print
"\tnew: <Type>\t\tlookup methods creating new instances of 'Type'"
69 print
"\tcall: <name>\t\tlookup methods calling 'name'"
70 print
"\nHierarchy lookup:"
71 print
"\tparents: <Class>\tlist direct parents of 'Class'"
72 print
"\tancestors: <Class>\tlist all ancestors of 'Class'"
73 print
"\tchildren: <Class>\tlist direct children of 'Class'"
74 print
"\tdescendants: <Class>\tlist all descendants of 'Class'"
75 print
"\nCode lookup:"
76 print
"\tcode: <name>\t\tdisplay the source code associated to the 'name' entity"
78 print
"\t:h\t\t\tdisplay this help message"
79 print
"\t:q\t\t\tquit interactive mode"
83 # Prompts the user for a command.
86 do_query
(sys
.stdin
.read_line
)
90 # Processes the query string and performs it.
91 fun do_query
(str
: String) do
92 var query
= new NitxQuery(str
)
93 if query
isa NitxCommand then
97 var res
= query
.perform
(self, doc
)
98 var page
= query
.make_results
(self, res
)
99 print page
.write_to_string
103 # A query performed on Nitx.
105 # Queries are responsible to collect matching results and render them as a
108 # Used as a factory to concrete instances.
111 # Original query string.
112 fun query_string
: String is abstract
116 # Will return a concrete instance of NitxQuery.
117 new(query_string
: String) do
118 if query_string
== ":q" then
120 else if query_string
== ":h" then
122 else if query_string
.has_prefix
("comment:") then
123 return new CommentQuery(query_string
)
124 else if query_string
.has_prefix
("doc:") then
125 return new DocQuery(query_string
)
126 else if query_string
.has_prefix
("param:") then
127 return new ParamQuery(query_string
)
128 else if query_string
.has_prefix
("return:") then
129 return new ReturnQuery(query_string
)
130 else if query_string
.has_prefix
("new:") then
131 return new NewQuery(query_string
)
132 else if query_string
.has_prefix
("call:") then
133 return new CallQuery(query_string
)
134 else if query_string
.has_prefix
("code:") then
135 return new CodeQuery(query_string
)
136 else if query_string
.has_prefix
("parents:") then
137 return new ParentsQuery(query_string
)
138 else if query_string
.has_prefix
("ancestors:") then
139 return new AncestorsQuery(query_string
)
140 else if query_string
.has_prefix
("children:") then
141 return new ChildrenQuery(query_string
)
142 else if query_string
.has_prefix
("descendants:") then
143 return new DescendantsQuery(query_string
)
145 return new CommentQuery("comment: {query_string}")
148 # Looks up the `doc` model and returns possible matches.
149 fun perform
(nitx
: Nitx, doc
: DocModel): Array[NitxMatch] is abstract
151 # Pretty prints the results for the console.
152 fun make_results
(nitx
: Nitx, results
: Array[NitxMatch]): DocPage do
153 var page
= new DocPage("results", "Results")
154 page
.root
.add_child
(new QueryResultArticle("results.article", "Results", self, results
))
158 redef fun to_s
do return query_string
161 # Something that matches a `NitxQuery`.
162 abstract class NitxMatch
164 # Query matched by `self`.
167 # Pretty prints `self` for console.
168 fun make_list_item
: String is abstract
171 # A query that contains a meta command.
173 # In Nitx, commands are written such as `command: args...`.
174 abstract class MetaQuery
177 redef var query_string
180 var command
: String is noinit
182 # Arguments passed to the `command`.
183 var args
= new Array[String]
187 var str
= new FlatBuffer
189 while i
< query_string
.length
do
190 var c
= query_string
[i
]
192 if c
== ':' then break
195 command
= str
.write_to_string
197 args
.add query_string
.substring_from
(i
).trim
201 # A match between a `NitxQuery` and a `MEntity`.
208 redef fun make_list_item
do return mentity
.cs_list_item
211 # A query to search a `MEntity` comment by its name or namespace.
215 redef fun perform
(nitx
, doc
) do
216 var name
= args
.first
217 var res
= new Array[NitxMatch]
218 for mentity
in doc
.mentities_by_name
(name
) do
219 res
.add
new MEntityMatch(self, mentity
)
224 redef fun make_results
(nitx
, results
) do
225 var len
= results
.length
227 var res
= results
.first
.as(MEntityMatch)
228 var mentity
= res
.mentity
229 var page
= new DocPage("results", "Results")
230 var article
= new DefinitionArticle("results.article", "Results", mentity
)
231 article
.cs_title
= mentity
.name
232 article
.cs_subtitle
= mentity
.cs_declaration
233 page
.root
.add_child article
241 # A query to search signatures using a specific `MType` as parameter.
245 redef fun perform
(nitx
, doc
) do
246 var res
= new Array[NitxMatch]
247 var mtype_name
= args
.first
248 for mproperty
in doc
.mproperties
do
249 if not mproperty
isa MMethod then continue
250 var msignature
= mproperty
.intro
.msignature
251 if msignature
!= null then
252 for mparam
in msignature
.mparameters
do
253 if mparam
.mtype
.name
== mtype_name
then
254 res
.add
new MEntityMatch(self, mproperty
)
263 # A query to search signatures using a specific `MType` as return.
267 redef fun perform
(nitx
, doc
) do
268 var res
= new Array[NitxMatch]
269 var mtype_name
= args
.first
270 for mproperty
in doc
.mproperties
do
271 if not mproperty
isa MMethod then continue
272 var msignature
= mproperty
.intro
.msignature
273 if msignature
!= null then
274 var mreturn
= msignature
.return_mtype
275 if mreturn
!= null and mreturn
.name
== mtype_name
then
276 res
.add
new MEntityMatch(self, mproperty
)
284 # A query to search methods creating new instances of a specific `MType`.
288 redef fun perform
(nitx
, doc
) do
289 var res
= new Array[NitxMatch]
290 var mtype_name
= args
.first
291 for mpropdef
in doc
.mpropdefs
do
292 var visitor
= new TypeInitVisitor(mtype_name
)
293 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
294 if npropdef
== null then continue
295 visitor
.enter_visit
(npropdef
)
296 for i
in visitor
.inits
do
297 res
.add
new MEntityMatch(self, mpropdef
)
304 # A query to search methods calling a specific `MProperty`.
308 redef fun perform
(nitx
, doc
) do
309 var res
= new Array[NitxMatch]
310 var mprop_name
= args
.first
311 for mpropdef
in doc
.mpropdefs
do
312 var visitor
= new MPropertyCallVisitor
313 var npropdef
= nitx
.ctx
.modelbuilder
.mpropdef2node
(mpropdef
)
314 if npropdef
== null then continue
315 visitor
.enter_visit
(npropdef
)
316 for mprop
in visitor
.calls
do
317 if mprop
.name
!= mprop_name
then continue
318 res
.add
new MEntityMatch(self, mpropdef
)
325 # A query to search a Nitdoc documentation page by its name.
329 redef fun perform
(nitx
, doc
) do
330 var res
= new Array[NitxMatch]
331 var name
= args
.first
332 for page
in doc
.pages
.values
do
333 if name
== "*" then # FIXME dev only
334 res
.add
new PageMatch(self, page
)
335 else if page
.title
== name
then
336 res
.add
new PageMatch(self, page
)
337 else if page
isa MEntityPage and page
.mentity
.cs_namespace
== name
then
338 res
.add
new PageMatch(self, page
)
344 redef fun make_results
(nitx
, results
) do
345 var len
= results
.length
346 # FIXME how to render the pager for one worded namespaces like "standard"?
348 var page
= results
.first
.as(PageMatch).page
349 var pager
= new Pager
350 pager
.add page
.write_to_string
359 # A match between a `DocPage` and a `MEntity`.
366 redef fun make_list_item
do
368 if page
isa MEntityPage then
369 return page
.mentity
.cs_list_item
371 return " * {page.title}"
375 # Search in class or module hierarchy of a `MEntity`.
377 # It actually searches for pages about the mentity and extracts the
378 # pre-calculated hierarchies by the `doc_post` phase.
379 abstract class HierarchiesQuery
382 redef fun make_results
(nitx
, results
) do
383 var page
= new DocPage("hierarchy", "Hierarchy")
384 for result
in results
do
385 if not result
isa PageMatch then continue
386 var rpage
= result
.page
387 if not rpage
isa MClassPage then continue
388 page
.root
.add_child build_article
(rpage
)
393 # Build an article containing the hierarchy list depending on subclasses.
394 private fun build_article
(page
: MClassPage): DocArticle is abstract
397 # List all parents of a `MClass`.
399 super HierarchiesQuery
401 redef fun build_article
(page
) do
402 return new MEntitiesListArticle(
404 "Ancestors for {page.mentity.name}",
409 # List direct parents of a `MClass`.
411 super HierarchiesQuery
413 redef fun build_article
(page
) do
414 return new MEntitiesListArticle(
416 "Parents for {page.mentity.name}",
421 # List direct children of a `MClass`.
423 super HierarchiesQuery
425 redef fun build_article
(page
) do
426 return new MEntitiesListArticle(
428 "Children for {page.mentity.name}",
433 # List all descendants of a `MClass`.
434 class DescendantsQuery
435 super HierarchiesQuery
437 redef fun build_article
(page
) do
438 return new MEntitiesListArticle(
440 "Descendants for {page.mentity.name}",
445 # A query to search source code from a file name.
449 # FIXME refactor this!
450 redef fun perform
(nitx
, doc
) do
451 var res
= new Array[NitxMatch]
452 var name
= args
.first
453 # if name is an existing sourcefile, opens it
454 if name
.file_exists
then
455 var fr
= new FileReader.open
(name
)
456 var content
= fr
.read_all
458 res
.add
new CodeMatch(self, name
, content
)
461 # else, lookup the model by name
462 for mentity
in doc
.mentities_by_name
(name
) do
463 if mentity
isa MClass then continue
464 if mentity
isa MProperty then continue
465 res
.add
new CodeMatch(self, mentity
.cs_location
, mentity
.cs_source_code
)
470 redef fun make_results
(nitx
, results
) do
471 var page
= new DocPage("results", "Code Results")
472 for res
in results
do
473 page
.add
new CodeQueryArticle("results.article", "Results", self, res
.as(CodeMatch))
479 # A match between a piece of code and a string.
483 # Location of the code match.
486 # Piece of code matched.
489 redef fun make_list_item
do return "* {location}"
493 # A query that contains a nitx command.
495 # These commands are prefixed with `:` and are used to control the execution of
496 # `nitx` like displaying the help or quiting.
497 interface NitxCommand
500 # Executes the command.
501 fun execute
(nitx
: Nitx) is abstract
508 redef fun execute
(nitx
) do exit
0
511 # Displays the help message.
515 redef fun execute
(nitx
) do nitx
.help
520 # Visitor looking for initialized `MType` (new T).
523 private class TypeInitVisitor
526 # `MType` name to look for.
527 var mtype_name
: String
529 var inits
= new HashSet[MType]
530 redef fun visit
(node
)
534 if not node
isa ANewExpr then return
535 var mtype
= node
.n_type
.mtype
536 if mtype
!= null and mtype
.name
== mtype_name
then inits
.add
(mtype
)
540 # Visitor looking for calls to a `MProperty` (new T).
543 private class MPropertyCallVisitor
546 var calls
= new HashSet[MProperty]
547 redef fun visit
(node
)
550 if not node
isa ASendExpr then return
551 calls
.add node
.callsite
.mproperty
557 # A `DocArticle` that displays query results.
558 private class QueryResultArticle
561 # Query linked to the results to display.
564 # Results to display.
565 var results
: Array[NitxMatch]
567 redef fun render_title
do
568 var len
= results
.length
570 add
"No result found for '{query.query_string}'..."
572 add
"# {len} result(s) for '{query.query_string}'".green
.bold
576 redef fun render_body
do
578 for result
in results
do
580 addn result
.make_list_item
585 # An article that displays a piece of code.
586 private class CodeQueryArticle
589 # The query linked to the result to display.
592 # The result to display.
593 var result
: CodeMatch
595 redef fun render_body
do
597 addn
"in {result.location}".gray
.bold
603 # A Pager is used to display data into a unix `less` container.
606 # Content to display.
607 var content
= new FlatBuffer
609 # Adds text to the pager.
610 fun add
(text
: String) do
611 content
.append
(escape
(text
))
614 fun render
do sys
.system
("echo \"{content}\
" | less -r")
616 fun escape
(str
: String): String
618 var b
= new FlatBuffer
619 for c
in str
.chars
do
622 else if c
== '\0' then
624 else if c
== '"' then
626 else if c == '\\' then
628 else if c == '`' then
630 else if c.ascii < 32 then
631 b.append("\\{c.ascii.to_base(8, false)}")