aad0b027f0f7a8e1006140f1e514740cf1dd84db
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 Nit Index.\n"
73 print
"Loaded modules"
74 for m
in mbuilder
.nmodules
do
75 print
" - {m.mmodule.name}"
77 print
"\nEnter the module, class or property name you want to look up."
78 print
"Enter a blank line to exit.\n"
86 fun seek
(entry
: String) do
87 if entry
.is_empty
then exit
(0)
90 if entry
.has_prefix
("return:") then
91 var ret
= entry
.split_with
(":")[1].replace
(" ", "")
92 var matches
= seek_returns
(ret
)
93 if not matches
.is_empty
then
95 props_fulldoc
(matches
)
97 else if entry
.has_prefix
("param:") then
98 var param
= entry
.split_with
(":")[1].replace
(" ", "")
99 var matches
= seek_params
(param
)
100 if not matches
.is_empty
then
102 props_fulldoc
(matches
)
106 var mmatches
= new List[MModule]
107 for m
in model
.mmodules
do
108 if m
.name
== entry
then
113 if not mmatches
.is_empty
then modules_fulldoc
(mmatches
)
115 var cmatches
= new List[MClass]
116 for c
in model
.mclasses
do
117 if c
.name
== entry
then
122 if not cmatches
.is_empty
then classes_fulldoc
(cmatches
)
123 # seek for properties
124 var matches
= new List[MProperty]
125 for p
in model
.mproperties
do
126 if p
.name
== entry
then
131 if not matches
.is_empty
then props_fulldoc
(matches
)
134 if not flag
then print
"Nothing known about '{entry}'"
135 if arguments
.length
== 1 then prompt
138 private fun modules_fulldoc
(mmodules
: List[MModule]) do
139 var pager
= new Pager
140 for mmodule
in mmodules
do
141 var nmodule
= mbuilder
.mmodule2nmodule
[mmodule
]
142 pager
.add
("# module {mmodule.namespace}\n".bold
)
143 if not mmodule
.in_importation
.direct_greaters
.is_empty
then
144 pager
.add
("import ".bold
+ "{mmodule.in_importation.direct_greaters.join(", ")}\n")
146 if not mmodule
.in_importation
.direct_smallers
.is_empty
then
147 pager
.add
("known clients: ".bold
+ "{mmodule.in_importation.direct_smallers.join(", ")}\n")
150 pager
.addn
(nmodule
.n_moduledecl
.n_doc
.comment
.green
)
153 var cats
= new HashMap[String, Collection[MClass]]
154 cats
["introduced classes"] = mmodule
.intro_mclasses
155 cats
["refined classes"] = mmodule
.redef_mclasses
156 cats
["imported classes"] = mmodule
.imported_mclasses
158 for cat
, list
in cats
do
159 if not list
.is_empty
then
160 pager
.add
("\n# {cat}".bold
)
162 var sorted
= new Array[MClass]
164 var sorter
= new MClassNameSorter
166 for mclass
in sorted
do
167 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
169 if not nclass
.n_doc
== null and not nclass
.n_doc
.short_comment
.is_empty
then
170 pager
.add
("\t# {nclass.n_doc.short_comment}")
172 if cat
== "refined classes" then
173 pager
.add
("\tredef {mclass.short_doc}")
175 pager
.add
("\t{mclass.short_doc}")
177 if cat
!= "introduced classes" then
178 pager
.add
("\t\t" + "introduced in {mmodule.full_name}::{mclass}".gray
)
180 for mclassdef
in mclass
.mclassdefs
do
181 if mclassdef
!= mclass
.intro
then
182 pager
.add
("\t\t" + "refined in {mclassdef.namespace}".gray
)
193 private fun classes_fulldoc
(mclasses
: List[MClass]) do
194 var pager
= new Pager
195 for mclass
in mclasses
do
196 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
198 pager
.add
("# {mclass.namespace}\n".bold
)
199 pager
.add
("{mclass.short_doc}")
200 if not nclass
.n_doc
== null then
202 pager
.addn
(nclass
.n_doc
.comment
.green
)
205 if not mclass
.parameter_types
.is_empty
then
206 pager
.add
("# formal types".bold
)
207 for ft
, bound
in mclass
.parameter_types
do
209 pager
.add
("\t{ft.to_s.green}: {bound}")
212 if not mclass
.virtual_types
.is_empty
then
213 pager
.add
("# virtual types".bold
)
214 for vt
in mclass
.virtual_types
do
216 mpropdef_fulldoc
(pager
, vt
.intro
)
221 var cats
= new HashMap[String, Collection[MMethod]]
222 cats
["constructors"] = mclass
.constructors
223 cats
["introduced methods"] = mclass
.intro_methods
224 cats
["refined methods"] = mclass
.redef_methods
225 cats
["inherited methods"] = mclass
.inherited_methods
227 for cat
, list
in cats
do
228 if not list
.is_empty
then
230 var sorted
= new Array[MMethod]
232 var sorter
= new MPropertyNameSorter
234 pager
.add
("\n# {cat}".bold
)
235 for mprop
in sorted
do
237 mpropdef_fulldoc
(pager
, mprop
.intro
)
246 private fun props_fulldoc
(raw_mprops
: List[MProperty]) do
247 var pager
= new Pager
249 var cats
= new HashMap[MClass, Array[MProperty]]
250 for mprop
in raw_mprops
do
251 if not mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then continue
252 if mprop
isa MAttribute then continue
253 var mclass
= mprop
.intro_mclassdef
.mclass
254 if not cats
.has_key
(mclass
) then cats
[mclass
] = new Array[MProperty]
255 cats
[mclass
].add
(mprop
)
258 var sorter
= new MClassNameSorter
259 var sorted
= new Array[MClass]
260 sorted
.add_all
(cats
.keys
)
263 for mclass
in sorted
do
264 var mprops
= cats
[mclass
]
265 pager
.add
("# {mclass.namespace}".bold
)
266 var sorterp
= new MPropertyNameSorter
268 for mprop
in mprops
do
270 mpropdef_fulldoc
(pager
, mprop
.intro
)
277 private fun seek_returns
(entry
: String): List[MProperty] do
278 # TODO how to match with generic types?
279 var matches
= new List[MProperty]
280 for mprop
in model
.mproperties
do
281 var intro
= mprop
.intro
282 if intro
isa MMethodDef then
283 if intro
.msignature
.return_mtype
!= null and intro
.msignature
.return_mtype
.to_s
== entry
then matches
.add
(mprop
)
284 else if intro
isa MAttributeDef then
285 if intro
.static_mtype
.to_s
== entry
then matches
.add
(mprop
)
291 private fun seek_params
(entry
: String): List[MProperty] do
292 # TODO how to match with generic types?
293 var matches
= new List[MProperty]
294 for mprop
in model
.mproperties
do
295 var intro
= mprop
.intro
296 if intro
isa MMethodDef then
297 var mparameters
= intro
.msignature
.mparameters
298 for mparameter
in mparameters
do
299 if mparameter
.mtype
.to_s
== entry
then matches
.add
(mprop
)
301 else if intro
isa MAttributeDef then
302 if intro
.static_mtype
.to_s
== entry
then matches
.add
(mprop
)
308 private fun mpropdef_fulldoc
(pager
: Pager, mpropdef
: MPropDef) do
309 if mbuilder
.mpropdef2npropdef
.has_key
(mpropdef
) then
310 var nprop
= mbuilder
.mpropdef2npropdef
[mpropdef
]
311 if not nprop
.n_doc
== null and not nprop
.n_doc
.short_comment
.is_empty
then
312 pager
.add
("\t# {nprop.n_doc.short_comment}")
315 pager
.add
("\t{mpropdef}")
316 pager
.add
("\t\t" + "introduced in {mpropdef.mproperty.intro_mclassdef.namespace}".gray
)
317 for mpdef
in mpropdef
.mproperty
.mpropdefs
do
318 if not mpdef
.is_intro
then
319 pager
.add
("\t\t" + "refined in {mpdef.mclassdef.namespace}".gray
)
325 # Printing facilities
328 private fun namespace
: String do
334 redef fun to_s
: String do
336 return "{name}[{intro.parameter_names.join(", ")}]"
342 private fun short_doc
: String do
344 if is_interface
then ret
= "interface {ret}"
345 if is_enum
then ret
= "enum {ret}"
346 if is_class
then ret
= "class {ret}"
347 if is_abstract
then ret
= "abstract {ret}"
348 if visibility
.to_s
== "public" then ret
= "{ret}{to_s.green}"
349 if visibility
.to_s
== "private" then ret
= "{ret}{to_s.red}"
350 if visibility
.to_s
== "protected" then ret
= "{ret}{to_s.yellow}"
351 if not parents
.is_empty
then
352 ret
= "{ret} super {parents.join(", ")}"
357 private fun namespace
: String do
358 if not intro_mmodule
.public_owner
== null then
359 return "{intro_mmodule.public_owner.name}::{name}"
361 return "{intro_mmodule.name}::{name}"
366 redef class MClassDef
367 private fun namespace
: String do
368 return "{mmodule.full_name}::{mclass.name}"
372 redef class MMethodDef
375 if not is_intro
then res
.append
("redef ")
376 if not mproperty
.is_init
then res
.append
("fun ")
377 if mproperty
.visibility
.to_s
== "public" then res
.append
(mproperty
.name
.green
)
378 if mproperty
.visibility
.to_s
== "private" then res
.append
(mproperty
.name
.red
)
379 if mproperty
.visibility
.to_s
== "protected" then res
.append
(mproperty
.name
.yellow
)
380 if msignature
!= null then res
.append
(msignature
.to_s
)
381 # FIXME: modifiers should be accessible via the model
382 #if self isa ADeferredMethPropdef then ret = "{ret} is abstract"
383 #if self isa AInternMethPropdef then ret = "{ret} is intern"
384 #if self isa AExternMethPropdef then ret = "{ret} is extern"
389 redef class MVirtualTypeDef
392 if mproperty
.visibility
.to_s
== "public" then res
.append
(mproperty
.name
.green
)
393 if mproperty
.visibility
.to_s
== "private" then res
.append
(mproperty
.name
.red
)
394 if mproperty
.visibility
.to_s
== "protected" then res
.append
(mproperty
.name
.yellow
)
395 res
.append
(": {bound.to_s}")
400 redef class MSignature
403 if not mparameters
.is_empty
then
405 for i
in [0..mparameters
.length
[ do
406 res
.append
(mparameters
[i
].to_s
)
407 if i
< mparameters
.length
- 1 then res
.append
(", ")
411 if return_mtype
!= null then
412 res
.append
(": {return_mtype.to_s}")
418 redef class MParameter
421 res
.append
("{name}: {mtype}")
422 if is_vararg
then res
.append
("...")
427 redef class MNullableType
428 redef fun to_s
do return "nullable {mtype}"
431 redef class MGenericType
434 res
.append
("{mclass.name}[")
435 for i
in [0..arguments
.length
[ do
436 res
.append
(arguments
[i
].to_s
)
437 if i
< arguments
.length
- 1 then res
.append
(", ")
444 redef class MParameterType
445 redef fun to_s
do return mclass
.intro
.parameter_names
[rank
]
448 redef class MVirtualType
449 redef fun to_s
do return mproperty
.intro
.to_s
453 private fun comment
: String do
455 for t
in n_comment
do
456 res
.append
(t
.text
.replace
("# ", "").replace
("#", ""))
461 private fun short_comment
: String do
462 return n_comment
.first
.text
.replace
("# ", "").replace
("\n", "")
466 redef class AAttrPropdef
467 private fun read_accessor
: String do
469 #FIXME bug with standard::stream::FDStream::fd
470 var name
= mreadpropdef
.mproperty
.name
471 if mpropdef
.mproperty
.visibility
.to_s
== "public" then ret
= "{ret}{name.green}"
472 if mpropdef
.mproperty
.visibility
.to_s
== "private" then ret
= "{ret}{name.red}"
473 if mpropdef
.mproperty
.visibility
.to_s
== "protected" then ret
= "{ret}{name.yellow}"
474 ret
= "{ret}: {n_type.to_s}"
475 if n_kwredef
!= null then ret
= "redef {ret}"
479 private fun write_accessor
: String do
481 var name
= "{mreadpropdef.mproperty.name}="
482 if n_readable
!= null and n_readable
.n_visibility
!= null then
483 if n_readable
.n_visibility
isa APublicVisibility then ret
= "{ret}{name.green}"
484 if n_readable
.n_visibility
isa APrivateVisibility then ret
= "{ret}{name.red}"
485 if n_readable
.n_visibility
isa AProtectedVisibility then ret
= "{ret}{name.yellow}"
487 ret
= "{ret}{name.red}"
489 ret
= "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
490 if n_kwredef
!= null then ret
= "redef {ret}"
495 # Redef String class to add a function to color the string
498 private fun add_escape_char
(escapechar
: String): String do
499 return "{escapechar}{self}\\033[0m"
502 private fun esc
: Char do return 27.ascii
503 private fun red
: String do return add_escape_char
("{esc}[1;31m")
504 private fun yellow
: String do return add_escape_char
("{esc}[1;33m")
505 private fun green
: String do return add_escape_char
("{esc}[1;32m")
506 private fun blue
: String do return add_escape_char
("{esc}[1;34m")
507 private fun cyan
: String do return add_escape_char
("{esc}[1;36m")
508 private fun gray
: String do return add_escape_char
("{esc}[30;1m")
509 private fun bold
: String do return add_escape_char
("{esc}[1m")
510 private fun underline
: String do return add_escape_char
("{esc}[4m")
512 private fun escape
: String
518 else if c
== '\0' then
520 else if c
== '"' then
522 else if c == '\\' then
524 else if c == '`' then
526 else if c.ascii < 32 then
527 b.append("\\{c.ascii.to_base(8, false)}")
536 # Create a tool context to handle options and paths
537 var toolcontext = new ToolContext
538 toolcontext.process_options
540 # Here we launch the nit index
541 var ni = new NitIndex(toolcontext)
544 # TODO seek subclasses and super classes <.<class> >.<class>
545 # TODO seek subclasses and super types <:<type> >:<type>
546 # TODO seek with regexp
547 # TODO standardize namespaces with private option