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")
29 private var toolcontext
: ToolContext
30 private var model
: Model
31 private var mbuilder
: ModelBuilder
32 private var mainmodule
: MModule
33 private var arguments
: Array[String]
35 init(toolcontext
: ToolContext) do
36 # We need a model to collect stufs
37 self.toolcontext
= toolcontext
38 self.toolcontext
.option_context
.options
.clear
39 self.arguments
= toolcontext
.option_context
.rest
41 if arguments
.is_empty
or arguments
.length
> 2 then
42 print
"usage: ni path/to/module.nit [expression]"
43 toolcontext
.option_context
.usage
48 mbuilder
= new ModelBuilder(model
, toolcontext
)
50 # Here we load an process std modules
51 #var dir = "NIT_DIR".environ
52 #var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"])
53 var mmodules
= mbuilder
.parse_and_build
([arguments
.first
])
54 if mmodules
.is_empty
then return
55 assert mmodules
.length
== 1
56 self.mainmodule
= mmodules
.first
60 if arguments
.length
== 1 then
69 print
"Welcome in Nit Index.\n"
70 print
"Loaded modules"
71 for m
in mbuilder
.nmodules
do
72 print
" - {m.mmodule.name}"
74 print
"\nEnter the module, class or property name you want to look up."
75 print
"Enter a blank line to exit.\n"
83 fun seek
(entry
: String) do
84 if entry
.is_empty
then exit
(0)
87 if entry
.has_prefix
("return:") then
88 var ret
= entry
.split_with
(":")[1].replace
(" ", "")
89 var matches
= seek_returns
(ret
)
90 if not matches
.is_empty
then
92 props_fulldoc
(matches
)
94 else if entry
.has_prefix
("param:") then
95 var param
= entry
.split_with
(":")[1].replace
(" ", "")
96 var matches
= seek_params
(param
)
97 if not matches
.is_empty
then
99 props_fulldoc
(matches
)
103 var mmatches
= new List[MModule]
104 for m
in model
.mmodules
do
105 if m
.name
== entry
then
110 if not mmatches
.is_empty
then modules_fulldoc
(mmatches
)
112 var cmatches
= new List[MClass]
113 for c
in model
.mclasses
do
114 if c
.name
== entry
then
119 if not cmatches
.is_empty
then classes_fulldoc
(cmatches
)
120 # seek for properties
121 var matches
= new List[MProperty]
122 for p
in model
.mproperties
do
123 if p
.name
== entry
then
128 if not matches
.is_empty
then props_fulldoc
(matches
)
131 if not flag
then print
"Nothing known about '{entry}'"
132 if arguments
.length
== 1 then prompt
135 private fun modules_fulldoc
(mmodules
: List[MModule]) do
136 var pager
= new Pager
137 for mmodule
in mmodules
do
138 var nmodule
= mbuilder
.mmodule2nmodule
[mmodule
]
139 pager
.add
("# module {mmodule.namespace}\n".bold
)
140 if not mmodule
.in_importation
.direct_greaters
.is_empty
then
141 pager
.add
("import ".bold
+ "{mmodule.in_importation.direct_greaters.join(", ")}\n")
143 if not mmodule
.in_importation
.direct_smallers
.is_empty
then
144 pager
.add
("known clients: ".bold
+ "{mmodule.in_importation.direct_smallers.join(", ")}\n")
147 pager
.addn
(nmodule
.n_moduledecl
.n_doc
.comment
.green
)
150 var cats
= new HashMap[String, Collection[MClass]]
151 cats
["introduced classes"] = mmodule
.intro_mclasses
152 cats
["refined classes"] = mmodule
.redef_mclasses
153 cats
["imported classes"] = mmodule
.imported_mclasses
155 for cat
, list
in cats
do
156 if not list
.is_empty
then
157 pager
.add
("\n# {cat}".bold
)
159 var sorted
= new Array[MClass]
161 var sorter
= new ComparableSorter[MClass]
163 for mclass
in sorted
do
164 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
166 if not nclass
.n_doc
== null and not nclass
.n_doc
.short_comment
.is_empty
then
167 pager
.add
("\t# {nclass.n_doc.short_comment}")
169 if cat
== "refined classes" then
170 pager
.add
("\tredef {mclass.short_doc}")
172 pager
.add
("\t{mclass.short_doc}")
174 if cat
!= "introduced classes" then
175 pager
.add
("\t\t" + "introduced in {mmodule.full_name}::{mclass}".gray
)
177 for mclassdef
in mclass
.mclassdefs
do
178 if mclassdef
!= mclass
.intro
then
179 pager
.add
("\t\t" + "refined in {mclassdef.namespace}".gray
)
190 private fun classes_fulldoc
(mclasses
: List[MClass]) do
191 var pager
= new Pager
192 for mclass
in mclasses
do
193 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
195 pager
.add
("# {mclass.namespace}\n".bold
)
196 pager
.add
("{mclass.short_doc}")
198 pager
.addn
(nclass
.n_doc
.comment
.green
)
200 if not mclass
.parameter_types
.is_empty
then
201 pager
.add
("# formal types".bold
)
202 for ft
, bound
in mclass
.parameter_types
do
204 pager
.add
("\t{ft.to_s.green}: {bound}")
207 if not mclass
.virtual_types
.is_empty
then
208 pager
.add
("# virtual types".bold
)
209 for vt
in mclass
.virtual_types
do
211 vt_fulldoc
(pager
, vt
)
216 var cats
= new HashMap[String, Collection[MMethod]]
217 cats
["constructors"] = mclass
.constructors
218 cats
["introduced methods"] = mclass
.intro_methods
219 cats
["refined methods"] = mclass
.redef_methods
220 cats
["inherited methods"] = mclass
.inherited_methods
222 for cat
, list
in cats
do
223 if not list
.is_empty
then
225 var sorted
= new Array[MMethod]
227 var sorter
= new ComparableSorter[MMethod]
229 pager
.add
("\n# {cat}".bold
)
230 for mprop
in sorted
do
232 method_fulldoc
(pager
, mprop
)
241 private fun props_fulldoc
(raw_mprops
: List[MProperty]) do
242 var pager
= new Pager
244 var cats
= new HashMap[MClass, Array[MProperty]]
245 for mprop
in raw_mprops
do
246 if not mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then continue
247 if mprop
isa MAttribute then continue
248 var mclass
= mprop
.intro_mclassdef
.mclass
249 if not cats
.has_key
(mclass
) then cats
[mclass
] = new Array[MProperty]
250 cats
[mclass
].add
(mprop
)
253 var sorter
= new ComparableSorter[MClass]
254 var sorted
= new Array[MClass]
255 sorted
.add_all
(cats
.keys
)
258 for mclass
in sorted
do
259 var mprops
= cats
[mclass
]
260 pager
.add
("# {mclass.namespace}".bold
)
261 var sorterp
= new ComparableSorter[MProperty]
263 for mprop
in mprops
do
264 if mprop
isa MMethod and mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then
266 method_fulldoc
(pager
, mprop
)
267 else if mprop
isa MVirtualTypeProp then
269 vt_fulldoc
(pager
, mprop
)
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 method_fulldoc
(pager
: Pager, mprop
: MMethod) do
309 if mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then
310 var nprop
= mbuilder
.mpropdef2npropdef
[mprop
.intro
]
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}")
314 if nprop
isa AAttrPropdef then
315 pager
.add
("\t{nprop.read_accessor}")
316 pager
.add
("\t{nprop.write_accessor}")
317 else if nprop
isa AMethPropdef then
318 pager
.add
("\t{nprop}")
320 pager
.add
("\t\t" + "introduced in {mprop.intro_mclassdef.namespace}".gray
)
321 for mpropdef
in mprop
.mpropdefs
do
322 if mpropdef
!= mprop
.intro
then
323 pager
.add
("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray
)
329 private fun vt_fulldoc
(pager
: Pager, vt
: MVirtualTypeProp) do
330 pager
.add
("\t{vt.short_doc}")
331 pager
.add
("\t\t" + "introduced in {vt.intro_mclassdef.namespace}::{vt}".gray
)
332 for mpropdef
in vt
.mpropdefs
do
333 if mpropdef
!= vt
.intro
then
334 pager
.add
("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray
)
340 # Printing facilities
344 redef type OTHER: MModule
345 redef fun <(other
: OTHER): Bool do return self.name
< other
.name
347 private fun namespace
: String do
354 redef type OTHER: MClass
355 redef fun <(other
: OTHER): Bool do return self.name
< other
.name
357 redef fun to_s
: String do
359 return "{name}[{intro.parameter_names.join(", ")}]"
365 private fun short_doc
: String do
367 if is_interface
then ret
= "interface {ret}"
368 if is_enum
then ret
= "enum {ret}"
369 if is_class
then ret
= "class {ret}"
370 if is_abstract
then ret
= "abstract {ret}"
371 if visibility
.to_s
== "public" then ret
= "{ret}{to_s.green}"
372 if visibility
.to_s
== "private" then ret
= "{ret}{to_s.red}"
373 if visibility
.to_s
== "protected" then ret
= "{ret}{to_s.yellow}"
374 if not parents
.is_empty
then
375 ret
= "{ret} super {parents.join(", ")}"
380 private fun namespace
: String do
381 if not intro_mmodule
.public_owner
== null then
382 return "{intro_mmodule.public_owner.name}::{name}"
384 return "{intro_mmodule.name}::{name}"
389 redef class MClassDef
390 private fun namespace
: String do
391 return "{mmodule.full_name}::{mclass.name}"
395 redef class MProperty
397 redef type OTHER: MProperty
398 redef fun <(other
: OTHER): Bool do return self.name
< other
.name
401 redef class MVirtualTypeProp
402 private fun short_doc
: String do
404 if visibility
.to_s
== "public" then ret
= "{to_s.green}: {intro.bound.to_s}"
405 if visibility
.to_s
== "private" then ret
= "\t{to_s.red}: {intro.bound.to_s}"
406 if visibility
.to_s
== "protected" then ret
= "\t{to_s.yellow}: {intro.bound.to_s}"
412 private fun comment
: String do
414 for t
in n_comment
do
415 res
.append
(t
.text
.replace
("# ", "").replace
("#", ""))
420 private fun short_comment
: String do
421 return n_comment
.first
.text
.replace
("# ", "").replace
("\n", "")
425 redef class AAttrPropdef
426 private fun read_accessor
: String do
428 #FIXME bug with standard::stream::FDStream::fd
429 var name
= mreadpropdef
.mproperty
.name
430 if mpropdef
.mproperty
.visibility
.to_s
== "public" then ret
= "{ret}{name.green}"
431 if mpropdef
.mproperty
.visibility
.to_s
== "private" then ret
= "{ret}{name.red}"
432 if mpropdef
.mproperty
.visibility
.to_s
== "protected" then ret
= "{ret}{name.yellow}"
433 ret
= "{ret}: {n_type.to_s}"
434 if n_kwredef
!= null then ret
= "redef {ret}"
438 private fun write_accessor
: String do
440 var name
= "{mreadpropdef.mproperty.name}="
441 if n_readable
!= null and n_readable
.n_visibility
!= null then
442 if n_readable
.n_visibility
isa APublicVisibility then ret
= "{ret}{name.green}"
443 if n_readable
.n_visibility
isa APrivateVisibility then ret
= "{ret}{name.red}"
444 if n_readable
.n_visibility
isa AProtectedVisibility then ret
= "{ret}{name.yellow}"
446 ret
= "{ret}{name.red}"
448 ret
= "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
449 if n_kwredef
!= null then ret
= "redef {ret}"
454 redef class AMethPropdef
457 if not mpropdef
.mproperty
.is_init
then
460 if mpropdef
.mproperty
.visibility
.to_s
== "public" then ret
= "{ret}{mpropdef.mproperty.name.green}"
461 if mpropdef
.mproperty
.visibility
.to_s
== "private" then ret
= "{ret}{mpropdef.mproperty.name.red}"
462 if mpropdef
.mproperty
.visibility
.to_s
== "protected" then ret
= "{ret}{mpropdef.mproperty.name.yellow}"
463 if n_signature
!= null then ret
= "{ret}{n_signature.to_s}"
464 if n_kwredef
!= null then ret
= "redef {ret}"
465 if self isa ADeferredMethPropdef then ret
= "{ret} is abstract"
466 if self isa AInternMethPropdef then ret
= "{ret} is intern"
467 if self isa AExternMethPropdef then ret
= "{ret} is extern"
472 redef class ASignature
476 if not n_params
.is_empty
then
477 ret
= "{ret}({n_params.join(", ")})"
479 if n_type
!= null then ret
+= ": {n_type.to_s}"
486 var ret
= "{n_id.text}"
487 if n_type
!= null then
488 ret
= "{ret}: {n_type.to_s}"
489 if n_dotdotdot
!= null then ret
= "{ret}..."
498 if n_kwnullable
!= null then ret
= "nullable {ret}"
499 if not n_types
.is_empty
then ret
= "{ret}[{n_types.join(", ")}]"
504 # Redef String class to add a function to color the string
507 private fun add_escape_char
(escapechar
: String): String do
508 return "{escapechar}{self}\\033[0m"
511 private fun esc
: Char do return 27.ascii
512 private fun red
: String do return add_escape_char
("{esc}[1;31m")
513 private fun yellow
: String do return add_escape_char
("{esc}[1;33m")
514 private fun green
: String do return add_escape_char
("{esc}[1;32m")
515 private fun blue
: String do return add_escape_char
("{esc}[1;34m")
516 private fun cyan
: String do return add_escape_char
("{esc}[1;36m")
517 private fun gray
: String do return add_escape_char
("{esc}[30;1m")
518 private fun bold
: String do return add_escape_char
("{esc}[1m")
519 private fun underline
: String do return add_escape_char
("{esc}[4m")
521 private fun escape
: String
527 else if c
== '\0' then
529 else if c
== '"' then
531 else if c == '\\' then
533 else if c == '`' then
535 else if c.ascii < 32 then
536 b.append("\\{c.ascii.to_base(8, false)}")
545 # Create a tool context to handle options and paths
546 var toolcontext = new ToolContext
547 toolcontext.process_options
549 # Here we launch the nit index
550 var ni = new NitIndex(toolcontext)
553 # TODO seek subclasses and super classes <.<class> >.<class>
554 # TODO seek subclasses and super types <:<type> >:<type>
555 # TODO seek with regexp
556 # TODO standardize namespaces with private option