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
: String = ""
22 fun add
(text
: String) do addn
("{text}\n")
23 fun addn
(text
: String) do content
+= 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 mbuilder
.full_propdef_semantic_analysis
56 assert mmodules
.length
== 1
57 self.mainmodule
= mmodules
.first
61 if arguments
.length
== 1 then
70 print
"Welcome in Nit Index.\n"
71 print
"Loaded modules"
72 for m
in mbuilder
.nmodules
do
73 print
" - {m.mmodule.name}"
75 print
"\nEnter the module, class or property name you want to look up."
76 print
"Enter a blank line to exit.\n"
84 fun seek
(entry
: String) do
85 if entry
.is_empty
then exit
(0)
88 if entry
.has_prefix
("return:") then
89 var ret
= entry
.split_with
(":")[1].replace
(" ", "")
90 var matches
= seek_returns
(ret
)
91 if not matches
.is_empty
then
93 props_fulldoc
(matches
)
95 else if entry
.has_prefix
("param:") then
96 var param
= entry
.split_with
(":")[1].replace
(" ", "")
97 var matches
= seek_params
(param
)
98 if not matches
.is_empty
then
100 props_fulldoc
(matches
)
104 var mmatches
= new List[MModule]
105 for m
in model
.mmodules
do
106 if m
.name
== entry
then
111 if not mmatches
.is_empty
then modules_fulldoc
(mmatches
)
113 var cmatches
= new List[MClass]
114 for c
in model
.mclasses
do
115 if c
.name
== entry
then
120 if not cmatches
.is_empty
then classes_fulldoc
(cmatches
)
121 # seek for properties
122 var matches
= new List[MProperty]
123 for p
in model
.mproperties
do
124 if p
.name
== entry
then
129 if not matches
.is_empty
then props_fulldoc
(matches
)
132 if not flag
then print
"Nothing known about '{entry}'"
133 if arguments
.length
== 1 then prompt
136 private fun modules_fulldoc
(mmodules
: List[MModule]) do
137 var pager
= new Pager
138 for mmodule
in mmodules
do
139 var nmodule
= mbuilder
.mmodule2nmodule
[mmodule
]
140 pager
.add
("# module {mmodule.namespace}\n".bold
)
141 if not mmodule
.in_importation
.direct_greaters
.is_empty
then
142 pager
.add
("import ".bold
+ "{mmodule.in_importation.direct_greaters.join(", ")}\n")
144 if not mmodule
.in_importation
.direct_smallers
.is_empty
then
145 pager
.add
("known clients: ".bold
+ "{mmodule.in_importation.direct_smallers.join(", ")}\n")
148 pager
.addn
(nmodule
.n_moduledecl
.n_doc
.comment
.green
)
151 var cats
= new HashMap[String, Collection[MClass]]
152 cats
["introduced classes"] = mmodule
.intro_mclasses
153 cats
["refined classes"] = mmodule
.redef_mclasses
154 cats
["imported classes"] = mmodule
.imported_mclasses
156 for cat
, list
in cats
do
157 if not list
.is_empty
then
158 pager
.add
("\n# {cat}".bold
)
160 var sorted
= new Array[MClass]
162 var sorter
= new ComparableSorter[MClass]
164 for mclass
in sorted
do
165 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
167 if not nclass
.n_doc
== null and not nclass
.n_doc
.short_comment
.is_empty
then
168 pager
.add
("\t# {nclass.n_doc.short_comment}")
170 if cat
== "refined classes" then
171 pager
.add
("\tredef {mclass.short_doc}")
173 pager
.add
("\t{mclass.short_doc}")
175 if cat
!= "introduced classes" then
176 pager
.add
("\t\t" + "introduced in {mmodule.full_name}::{mclass}".gray
)
178 for mclassdef
in mclass
.mclassdefs
do
179 if mclassdef
!= mclass
.intro
then
180 pager
.add
("\t\t" + "refined in {mclassdef.namespace}".gray
)
191 private fun classes_fulldoc
(mclasses
: List[MClass]) do
192 var pager
= new Pager
193 for mclass
in mclasses
do
194 var nclass
= mbuilder
.mclassdef2nclassdef
[mclass
.intro
].as(AStdClassdef)
196 pager
.add
("# {mclass.namespace}\n".bold
)
197 pager
.add
("{mclass.short_doc}")
199 pager
.addn
(nclass
.n_doc
.comment
.green
)
201 if not mclass
.parameter_types
.is_empty
then
202 pager
.add
("# formal types".bold
)
203 for ft
, bound
in mclass
.parameter_types
do
205 pager
.add
("\t{ft.to_s.green}: {bound}")
208 if not mclass
.virtual_types
.is_empty
then
209 pager
.add
("# virtual types".bold
)
210 for vt
in mclass
.virtual_types
do
212 vt_fulldoc
(pager
, vt
)
217 var cats
= new HashMap[String, Collection[MMethod]]
218 cats
["constructors"] = mclass
.constructors
219 cats
["introduced methods"] = mclass
.intro_methods
220 cats
["refined methods"] = mclass
.redef_methods
221 cats
["inherited methods"] = mclass
.inherited_methods
223 for cat
, list
in cats
do
224 if not list
.is_empty
then
226 var sorted
= new Array[MMethod]
228 var sorter
= new ComparableSorter[MMethod]
230 pager
.add
("\n# {cat}".bold
)
231 for mprop
in sorted
do
233 method_fulldoc
(pager
, mprop
)
242 private fun props_fulldoc
(raw_mprops
: List[MProperty]) do
243 var pager
= new Pager
245 var cats
= new HashMap[MClass, Array[MProperty]]
246 for mprop
in raw_mprops
do
247 if not mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then continue
248 if mprop
isa MAttribute then continue
249 var mclass
= mprop
.intro_mclassdef
.mclass
250 if not cats
.has_key
(mclass
) then cats
[mclass
] = new Array[MProperty]
251 cats
[mclass
].add
(mprop
)
254 var sorter
= new ComparableSorter[MClass]
255 var sorted
= new Array[MClass]
256 sorted
.add_all
(cats
.keys
)
259 for mclass
in sorted
do
260 var mprops
= cats
[mclass
]
261 pager
.add
("# {mclass.namespace}".bold
)
262 var sorterp
= new ComparableSorter[MProperty]
264 for mprop
in mprops
do
265 if mprop
isa MMethod and mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then
267 method_fulldoc
(pager
, mprop
)
268 else if mprop
isa MVirtualTypeProp then
270 vt_fulldoc
(pager
, mprop
)
278 private fun seek_returns
(entry
: String): List[MProperty] do
279 # TODO how to match with generic types?
280 var matches
= new List[MProperty]
281 for mprop
in model
.mproperties
do
282 var intro
= mprop
.intro
283 if intro
isa MMethodDef then
284 if intro
.msignature
.return_mtype
!= null and intro
.msignature
.return_mtype
.to_s
== entry
then matches
.add
(mprop
)
285 else if intro
isa MAttributeDef then
286 if intro
.static_mtype
.to_s
== entry
then matches
.add
(mprop
)
292 private fun seek_params
(entry
: String): List[MProperty] do
293 # TODO how to match with generic types?
294 var matches
= new List[MProperty]
295 for mprop
in model
.mproperties
do
296 var intro
= mprop
.intro
297 if intro
isa MMethodDef then
298 var mparameters
= intro
.msignature
.mparameters
299 for mparameter
in mparameters
do
300 if mparameter
.mtype
.to_s
== entry
then matches
.add
(mprop
)
302 else if intro
isa MAttributeDef then
303 if intro
.static_mtype
.to_s
== entry
then matches
.add
(mprop
)
309 private fun method_fulldoc
(pager
: Pager, mprop
: MMethod) do
310 if mbuilder
.mpropdef2npropdef
.has_key
(mprop
.intro
) then
311 var nprop
= mbuilder
.mpropdef2npropdef
[mprop
.intro
]
312 if not nprop
.n_doc
== null and not nprop
.n_doc
.short_comment
.is_empty
then
313 pager
.add
("\t# {nprop.n_doc.short_comment}")
315 if nprop
isa AAttrPropdef then
316 pager
.add
("\t{nprop.read_accessor}")
317 pager
.add
("\t{nprop.write_accessor}")
318 else if nprop
isa AMethPropdef then
319 pager
.add
("\t{nprop}")
321 pager
.add
("\t\t" + "introduced in {mprop.intro_mclassdef.namespace}".gray
)
322 for mpropdef
in mprop
.mpropdefs
do
323 if mpropdef
!= mprop
.intro
then
324 pager
.add
("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray
)
330 private fun vt_fulldoc
(pager
: Pager, vt
: MVirtualTypeProp) do
331 pager
.add
("\t{vt.short_doc}")
332 pager
.add
("\t\t" + "introduced in {vt.intro_mclassdef.namespace}::{vt}".gray
)
333 for mpropdef
in vt
.mpropdefs
do
334 if mpropdef
!= vt
.intro
then
335 pager
.add
("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray
)
341 # Printing facilities
345 redef type OTHER: MModule
346 redef fun <(other
: OTHER): Bool do return self.name
< other
.name
348 private fun namespace
: String do
355 redef type OTHER: MClass
356 redef fun <(other
: OTHER): Bool do return self.name
< other
.name
358 redef fun to_s
: String do
360 return "{name}[{intro.parameter_names.join(", ")}]"
366 private fun short_doc
: String do
368 if is_interface
then ret
= "interface {ret}"
369 if is_enum
then ret
= "enum {ret}"
370 if is_class
then ret
= "class {ret}"
371 if is_abstract
then ret
= "abstract {ret}"
372 if visibility
.to_s
== "public" then ret
= "{ret}{to_s.green}"
373 if visibility
.to_s
== "private" then ret
= "{ret}{to_s.red}"
374 if visibility
.to_s
== "protected" then ret
= "{ret}{to_s.yellow}"
375 if not parents
.is_empty
then
376 ret
= "{ret} super {parents.join(", ")}"
381 private fun namespace
: String do
382 return "{intro_mmodule.public_owner.name}::{name}"
386 redef class MClassDef
387 private fun namespace
: String do
388 return "{mmodule.full_name}::{mclass.name}"
392 redef class MProperty
394 redef type OTHER: MProperty
395 redef fun <(other
: OTHER): Bool do return self.name
< other
.name
398 redef class MVirtualTypeProp
399 private fun short_doc
: String do
401 if visibility
.to_s
== "public" then ret
= "{to_s.green}: {intro.bound.to_s}"
402 if visibility
.to_s
== "private" then ret
= "\t{to_s.red}: {intro.bound.to_s}"
403 if visibility
.to_s
== "protected" then ret
= "\t{to_s.yellow}: {intro.bound.to_s}"
409 private fun comment
: String do
411 for t
in n_comment
do
412 res
.append
(t
.text
.replace
("# ", "").replace
("#", ""))
417 private fun short_comment
: String do
418 return n_comment
.first
.text
.replace
("# ", "").replace
("\n", "")
422 redef class AAttrPropdef
423 private fun read_accessor
: String do
425 #FIXME bug with standard::stream::FDStream::fd
426 var name
= mreadpropdef
.mproperty
.name
427 if mpropdef
.mproperty
.visibility
.to_s
== "public" then ret
= "{ret}{name.green}"
428 if mpropdef
.mproperty
.visibility
.to_s
== "private" then ret
= "{ret}{name.red}"
429 if mpropdef
.mproperty
.visibility
.to_s
== "protected" then ret
= "{ret}{name.yellow}"
430 ret
= "{ret}: {n_type.to_s}"
431 if n_kwredef
!= null then ret
= "redef {ret}"
435 private fun write_accessor
: String do
437 var name
= "{mreadpropdef.mproperty.name}="
438 if n_readable
!= null and n_readable
.n_visibility
!= null then
439 if n_readable
.n_visibility
isa APublicVisibility then ret
= "{ret}{name.green}"
440 if n_readable
.n_visibility
isa APrivateVisibility then ret
= "{ret}{name.red}"
441 if n_readable
.n_visibility
isa AProtectedVisibility then ret
= "{ret}{name.yellow}"
443 ret
= "{ret}{name.red}"
445 ret
= "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
446 if n_kwredef
!= null then ret
= "redef {ret}"
451 redef class AMethPropdef
454 if not mpropdef
.mproperty
.is_init
then
457 if mpropdef
.mproperty
.visibility
.to_s
== "public" then ret
= "{ret}{mpropdef.mproperty.name.green}"
458 if mpropdef
.mproperty
.visibility
.to_s
== "private" then ret
= "{ret}{mpropdef.mproperty.name.red}"
459 if mpropdef
.mproperty
.visibility
.to_s
== "protected" then ret
= "{ret}{mpropdef.mproperty.name.yellow}"
460 if n_signature
!= null then ret
= "{ret}{n_signature.to_s}"
461 if n_kwredef
!= null then ret
= "redef {ret}"
462 if self isa ADeferredMethPropdef then ret
= "{ret} is abstract"
463 if self isa AInternMethPropdef then ret
= "{ret} is intern"
464 if self isa AExternMethPropdef then ret
= "{ret} is extern"
469 redef class ASignature
473 if not n_params
.is_empty
then
474 ret
= "{ret}({n_params.join(", ")})"
476 if n_type
!= null then ret
+= ": {n_type.to_s}"
483 var ret
= "{n_id.text}"
484 if n_type
!= null then
485 ret
= "{ret}: {n_type.to_s}"
486 if n_dotdotdot
!= null then ret
= "{ret}..."
495 if n_kwnullable
!= null then ret
= "nullable {ret}"
496 if not n_types
.is_empty
then ret
= "{ret}[{n_types.join(", ")}]"
501 # Redef String class to add a function to color the string
504 private fun add_escape_char
(escapechar
: String): String do
505 return "{escapechar}{self}\\033[0m"
508 private fun esc
: Char do return 27.ascii
509 private fun red
: String do return add_escape_char
("{esc}[1;31m")
510 private fun yellow
: String do return add_escape_char
("{esc}[1;33m")
511 private fun green
: String do return add_escape_char
("{esc}[1;32m")
512 private fun blue
: String do return add_escape_char
("{esc}[1;34m")
513 private fun cyan
: String do return add_escape_char
("{esc}[1;36m")
514 private fun gray
: String do return add_escape_char
("{esc}[30;1m")
515 private fun bold
: String do return add_escape_char
("{esc}[1m")
516 private fun underline
: String do return add_escape_char
("{esc}[4m")
518 private fun escape
: String
524 else if c
== '\0' then
526 else if c
== '"' then
528 else if c == '\\' then
530 else if c == '`' then
532 else if c.ascii < 32 then
533 b.append("\\{c.ascii.to_base(8, false)}")
542 # Create a tool context to handle options and paths
543 var toolcontext = new ToolContext
544 toolcontext.process_options
546 # Here we launch the nit index
547 var ni = new NitIndex(toolcontext)
550 # TODO seek subclasses and super classes <.<class> >.<class>
551 # TODO seek subclasses and super types <:<type> >:<type>
552 # TODO seek with regexp
553 # TODO standardize namespaces with private option