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 # ni or nit index, is a command tool used to display documentation
21 var content
= new Buffer
22 fun add
(text
: String) do addn
("{text}\n")
23 fun addn
(text
: String) do content
.append
(text
.escape
)
24 fun add_rule
do add
("\n---\n")
25 fun render
do sys
.system
("echo \"{content}\
" | pager -r")
28 # Main class of the nit index tool
29 # NitIndex build the model using the toolcontext argument
30 # then wait for query on std in to display documentation
32 private var toolcontext
: ToolContext
33 private var model
: Model
34 private var mbuilder
: ModelBuilder
35 private var mainmodule
: MModule
36 private var arguments
: Array[String]
38 init(toolcontext
: ToolContext) do
39 # We need a model to collect stufs
40 self.toolcontext
= toolcontext
41 self.toolcontext
.option_context
.options
.clear
42 self.arguments
= toolcontext
.option_context
.rest
44 if arguments
.is_empty
or arguments
.length
> 2 then
45 print
"usage: ni path/to/module.nit [expression]"
46 toolcontext
.option_context
.usage
51 mbuilder
= new ModelBuilder(model
, toolcontext
)
53 # Here we load an process std modules
54 #var dir = "NIT_DIR".environ
55 #var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"])
56 var mmodules
= mbuilder
.parse_and_build
([arguments
.first
])
57 if mmodules
.is_empty
then return
58 assert mmodules
.length
== 1
59 self.mainmodule
= mmodules
.first
63 if arguments
.length
== 1 then
72 print
"Welcome in the Nit Index."
74 print
"\tname\t\tlookup module, class and property with the corresponding 'name'"
75 print
"\tparam: Type\tlookup methods using the corresponding 'Type' as parameter"
76 print
"\treturn: Type\tlookup methods returning the corresponding 'Type'"
77 print
"\tEnter a blank line to exit.\n"
78 print
"\nLoaded modules:"
79 var mmodules
= new Array[MModule]
80 mmodules
.add_all
(model
.mmodules
)
81 var sorter
= new MModuleNameSorter
93 fun seek
(entry
: String) do
94 if entry
.is_empty
then exit
(0)
97 if entry
.has_prefix
("return:") then
98 var ret
= entry
.split_with
(":")[1].replace
(" ", "")
99 var matches
= seek_returns
(ret
)
100 if not matches
.is_empty
then
102 props_fulldoc
(matches
)
104 else if entry
.has_prefix
("param:") then
105 var param
= entry
.split_with
(":")[1].replace
(" ", "")
106 var matches
= seek_params
(param
)
107 if not matches
.is_empty
then
109 props_fulldoc
(matches
)
113 var mmatches
= new List[MModule]
114 for m
in model
.mmodules
do
115 if m
.name
== entry
then
120 if not mmatches
.is_empty
then modules_fulldoc
(mmatches
)
122 var cmatches
= new List[MClass]
123 for c
in model
.mclasses
do
124 if c
.name
== entry
then
129 if not cmatches
.is_empty
then classes_fulldoc
(cmatches
)
130 # seek for properties
131 var matches
= new List[MProperty]
132 for p
in model
.mproperties
do
133 if p
.name
== entry
then
138 if not matches
.is_empty
then props_fulldoc
(matches
)
141 if not flag
then print
"Nothing known about '{entry}'"
142 if arguments
.length
== 1 then prompt
145 private fun modules_fulldoc
(mmodules
: List[MModule]) do
146 var pager
= new Pager
147 for mmodule
in mmodules
do
148 var nmodule
= mbuilder
.mmodule2nmodule
[mmodule
]
149 pager
.add
("# module {mmodule.namespace}\n".bold
)
150 if not mmodule
.in_importation
.direct_greaters
.is_empty
then
151 pager
.add
("import ".bold
+ "{mmodule.in_importation.direct_greaters.join(", ")}\n")
153 if not mmodule
.in_importation
.direct_smallers
.is_empty
then
154 pager
.add
("known clients: ".bold
+ "{mmodule.in_importation.direct_smallers.join(", ")}\n")
157 pager
.addn
(nmodule
.n_moduledecl
.n_doc
.comment
.green
)
160 var cats
= new HashMap[String, Collection[MClass]]
161 cats
["introduced classes"] = mmodule
.intro_mclasses
162 cats
["refined classes"] = mmodule
.redef_mclasses
163 cats
["imported classes"] = mmodule
.imported_mclasses
165 for cat
, list
in cats
do
166 if not list
.is_empty
then
167 pager
.add
("\n# {cat}".bold
)
169 var sorted
= new Array[MClass]
171 var sorter
= new MClassNameSorter
173 for mclass
in sorted
do
174 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
176 if not nclass
.n_doc
== null and not nclass
.n_doc
.short_comment
.is_empty
then
177 pager
.add
("\t# {nclass.n_doc.short_comment}")
179 if cat
== "refined classes" then
180 pager
.add
("\tredef {mclass.short_doc}")
182 pager
.add
("\t{mclass.short_doc}")
184 if cat
!= "introduced classes" then
185 pager
.add
("\t\t" + "introduced in {mmodule.full_name}::{mclass}".gray
)
187 for mclassdef
in mclass
.mclassdefs
do
188 if mclassdef
!= mclass
.intro
then
189 pager
.add
("\t\t" + "refined in {mclassdef.namespace}".gray
)
200 private fun classes_fulldoc
(mclasses
: List[MClass]) do
201 var pager
= new Pager
202 for mclass
in mclasses
do
203 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
205 pager
.add
("# {mclass.namespace}\n".bold
)
206 pager
.add
("{mclass.short_doc}")
207 if not nclass
.n_doc
== null then
209 pager
.addn
(nclass
.n_doc
.comment
.green
)
212 if not mclass
.parameter_types
.is_empty
then
213 pager
.add
("# formal types".bold
)
214 for ft
, bound
in mclass
.parameter_types
do
216 pager
.add
("\t{ft.to_s.green}: {bound}")
219 if not mclass
.virtual_types
.is_empty
then
220 pager
.add
("# virtual types".bold
)
221 for vt
in mclass
.virtual_types
do
223 mpropdef_fulldoc
(pager
, vt
.intro
)
228 var cats
= new HashMap[String, Collection[MMethod]]
229 cats
["constructors"] = mclass
.constructors
230 cats
["introduced methods"] = mclass
.intro_methods
231 cats
["refined methods"] = mclass
.redef_methods
232 cats
["inherited methods"] = mclass
.inherited_methods
234 for cat
, list
in cats
do
235 if not list
.is_empty
then
237 var sorted
= new Array[MMethod]
239 var sorter
= new MPropertyNameSorter
241 pager
.add
("\n# {cat}".bold
)
242 for mprop
in sorted
do
244 mpropdef_fulldoc
(pager
, mprop
.intro
)
253 private fun props_fulldoc
(raw_mprops
: List[MProperty]) do
254 var pager
= new Pager
256 var cats
= new HashMap[MClass, Array[MProperty]]
257 for mprop
in raw_mprops
do
258 if not mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then continue
259 if mprop
isa MAttribute then continue
260 var mclass
= mprop
.intro_mclassdef
.mclass
261 if not cats
.has_key
(mclass
) then cats
[mclass
] = new Array[MProperty]
262 cats
[mclass
].add
(mprop
)
265 var sorter
= new MClassNameSorter
266 var sorted
= new Array[MClass]
267 sorted
.add_all
(cats
.keys
)
270 for mclass
in sorted
do
271 var mprops
= cats
[mclass
]
272 pager
.add
("# {mclass.namespace}".bold
)
273 var sorterp
= new MPropertyNameSorter
275 for mprop
in mprops
do
277 mpropdef_fulldoc
(pager
, mprop
.intro
)
284 private fun seek_returns
(entry
: String): List[MProperty] do
285 # TODO how to match with generic types?
286 var matches
= new List[MProperty]
287 for mprop
in model
.mproperties
do
288 var intro
= mprop
.intro
289 if intro
isa MMethodDef then
290 if intro
.msignature
.return_mtype
!= null and intro
.msignature
.return_mtype
.to_s
== entry
then matches
.add
(mprop
)
291 else if intro
isa MAttributeDef then
292 if intro
.static_mtype
.to_s
== entry
then matches
.add
(mprop
)
298 private fun seek_params
(entry
: String): List[MProperty] do
299 # TODO how to match with generic types?
300 var matches
= new List[MProperty]
301 for mprop
in model
.mproperties
do
302 var intro
= mprop
.intro
303 if intro
isa MMethodDef then
304 var mparameters
= intro
.msignature
.mparameters
305 for mparameter
in mparameters
do
306 if mparameter
.mtype
.to_s
== entry
then matches
.add
(mprop
)
308 else if intro
isa MAttributeDef then
309 if intro
.static_mtype
.to_s
== entry
then matches
.add
(mprop
)
315 private fun mpropdef_fulldoc
(pager
: Pager, mpropdef
: MPropDef) do
316 if mbuilder
.mpropdef2npropdef
.has_key
(mpropdef
) then
317 var nprop
= mbuilder
.mpropdef2npropdef
[mpropdef
]
318 if not nprop
.n_doc
== null and not nprop
.n_doc
.short_comment
.is_empty
then
319 pager
.add
("\t# {nprop.n_doc.short_comment}")
322 pager
.add
("\t{mpropdef}")
323 pager
.add
("\t\t" + "introduced in {mpropdef.mproperty.intro_mclassdef.namespace}".gray
)
324 for mpdef
in mpropdef
.mproperty
.mpropdefs
do
325 if not mpdef
.is_intro
then
326 pager
.add
("\t\t" + "refined in {mpdef.mclassdef.namespace}".gray
)
332 # Printing facilities
335 private fun namespace
: String do
341 redef fun to_s
: String do
343 return "{name}[{intro.parameter_names.join(", ")}]"
349 private fun short_doc
: String do
351 if is_interface
then ret
= "interface {ret}"
352 if is_enum
then ret
= "enum {ret}"
353 if is_class
then ret
= "class {ret}"
354 if is_abstract
then ret
= "abstract {ret}"
355 if visibility
.to_s
== "public" then ret
= "{ret}{to_s.green}"
356 if visibility
.to_s
== "private" then ret
= "{ret}{to_s.red}"
357 if visibility
.to_s
== "protected" then ret
= "{ret}{to_s.yellow}"
358 if not parents
.is_empty
then
359 ret
= "{ret} super {parents.join(", ")}"
364 private fun namespace
: String do
365 if not intro_mmodule
.public_owner
== null then
366 return "{intro_mmodule.public_owner.name}::{name}"
368 return "{intro_mmodule.name}::{name}"
373 redef class MClassDef
374 private fun namespace
: String do
375 return "{mmodule.full_name}::{mclass.name}"
379 redef class MMethodDef
382 if not is_intro
then res
.append
("redef ")
383 if not mproperty
.is_init
then res
.append
("fun ")
384 if mproperty
.visibility
.to_s
== "public" then res
.append
(mproperty
.name
.green
)
385 if mproperty
.visibility
.to_s
== "private" then res
.append
(mproperty
.name
.red
)
386 if mproperty
.visibility
.to_s
== "protected" then res
.append
(mproperty
.name
.yellow
)
387 if msignature
!= null then res
.append
(msignature
.to_s
)
388 # FIXME: modifiers should be accessible via the model
389 #if self isa ADeferredMethPropdef then ret = "{ret} is abstract"
390 #if self isa AInternMethPropdef then ret = "{ret} is intern"
391 #if self isa AExternMethPropdef then ret = "{ret} is extern"
396 redef class MVirtualTypeDef
399 if mproperty
.visibility
.to_s
== "public" then res
.append
(mproperty
.name
.green
)
400 if mproperty
.visibility
.to_s
== "private" then res
.append
(mproperty
.name
.red
)
401 if mproperty
.visibility
.to_s
== "protected" then res
.append
(mproperty
.name
.yellow
)
402 res
.append
(": {bound.to_s}")
407 redef class MSignature
410 if not mparameters
.is_empty
then
412 for i
in [0..mparameters
.length
[ do
413 res
.append
(mparameters
[i
].to_s
)
414 if i
< mparameters
.length
- 1 then res
.append
(", ")
418 if return_mtype
!= null then
419 res
.append
(": {return_mtype.to_s}")
425 redef class MParameter
428 res
.append
("{name}: {mtype}")
429 if is_vararg
then res
.append
("...")
434 redef class MNullableType
435 redef fun to_s
do return "nullable {mtype}"
438 redef class MGenericType
441 res
.append
("{mclass.name}[")
442 for i
in [0..arguments
.length
[ do
443 res
.append
(arguments
[i
].to_s
)
444 if i
< arguments
.length
- 1 then res
.append
(", ")
451 redef class MParameterType
452 redef fun to_s
do return mclass
.intro
.parameter_names
[rank
]
455 redef class MVirtualType
456 redef fun to_s
do return mproperty
.intro
.to_s
460 private fun comment
: String do
462 for t
in n_comment
do
463 res
.append
(t
.text
.replace
("# ", "").replace
("#", ""))
468 private fun short_comment
: String do
469 return n_comment
.first
.text
.replace
("# ", "").replace
("\n", "")
473 redef class AAttrPropdef
474 private fun read_accessor
: String do
476 #FIXME bug with standard::stream::FDStream::fd
477 var name
= mreadpropdef
.mproperty
.name
478 if mpropdef
.mproperty
.visibility
.to_s
== "public" then ret
= "{ret}{name.green}"
479 if mpropdef
.mproperty
.visibility
.to_s
== "private" then ret
= "{ret}{name.red}"
480 if mpropdef
.mproperty
.visibility
.to_s
== "protected" then ret
= "{ret}{name.yellow}"
481 ret
= "{ret}: {n_type.to_s}"
482 if n_kwredef
!= null then ret
= "redef {ret}"
486 private fun write_accessor
: String do
488 var name
= "{mreadpropdef.mproperty.name}="
489 if n_readable
!= null and n_readable
.n_visibility
!= null then
490 if n_readable
.n_visibility
isa APublicVisibility then ret
= "{ret}{name.green}"
491 if n_readable
.n_visibility
isa APrivateVisibility then ret
= "{ret}{name.red}"
492 if n_readable
.n_visibility
isa AProtectedVisibility then ret
= "{ret}{name.yellow}"
494 ret
= "{ret}{name.red}"
496 ret
= "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
497 if n_kwredef
!= null then ret
= "redef {ret}"
502 # Redef String class to add a function to color the string
505 private fun add_escape_char
(escapechar
: String): String do
506 return "{escapechar}{self}\\033[0m"
509 private fun esc
: Char do return 27.ascii
510 private fun red
: String do return add_escape_char
("{esc}[1;31m")
511 private fun yellow
: String do return add_escape_char
("{esc}[1;33m")
512 private fun green
: String do return add_escape_char
("{esc}[1;32m")
513 private fun blue
: String do return add_escape_char
("{esc}[1;34m")
514 private fun cyan
: String do return add_escape_char
("{esc}[1;36m")
515 private fun gray
: String do return add_escape_char
("{esc}[30;1m")
516 private fun bold
: String do return add_escape_char
("{esc}[1m")
517 private fun underline
: String do return add_escape_char
("{esc}[4m")
519 private fun escape
: String
525 else if c
== '\0' then
527 else if c
== '"' then
529 else if c == '\\' then
531 else if c == '`' then
533 else if c.ascii < 32 then
534 b.append("\\{c.ascii.to_base(8, false)}")
543 # Create a tool context to handle options and paths
544 var toolcontext = new ToolContext
545 toolcontext.process_options
547 # Here we launch the nit index
548 var ni = new NitIndex(toolcontext)
551 # TODO seek subclasses and super classes <.<class> >.<class>
552 # TODO seek subclasses and super types <:<type> >:<type>
553 # TODO seek with regexp
554 # TODO standardize namespaces with private option