nitx: added lookup for instance creation.
[nit.git] / src / nitx.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # nit index, is a command tool used to display documentation
16 module nitx
17
18 import model_utils
19 import modelize_property
20 import frontend
21
22 private class Pager
23 var content = new Buffer
24 fun add(text: String) do addn("{text}\n")
25 fun addn(text: String) do content.append(text.escape)
26 fun add_rule do add("\n---\n")
27 fun render do sys.system("echo \"{content}\" | pager -r")
28 end
29
30 private class Query
31 var keyword: String
32 init(keyword: String) do
33 self.keyword = keyword
34 end
35 end
36
37 private class QueryPair
38 super Query
39 var category: String
40 init(keyword: String, category: String) do
41 super(keyword)
42 self.category = category
43 end
44 end
45
46 # Main class of the nit index tool
47 # NitIndex build the model using the toolcontext argument
48 # then wait for query on std in to display documentation
49 class NitIndex
50 private var toolcontext: ToolContext
51 private var model: Model
52 private var mbuilder: ModelBuilder
53 private var mainmodule: MModule
54 private var arguments: Array[String]
55
56 init(toolcontext: ToolContext) do
57 # We need a model to collect stufs
58 self.toolcontext = toolcontext
59 self.toolcontext.option_context.options.clear
60 self.arguments = toolcontext.option_context.rest
61
62 if arguments.is_empty or arguments.length > 2 then
63 print "usage: ni path/to/module.nit [expression]"
64 toolcontext.option_context.usage
65 exit(1)
66 end
67
68 model = new Model
69 mbuilder = new ModelBuilder(model, toolcontext)
70
71 # Here we load an process std modules
72 #var dir = "NIT_DIR".environ
73 #var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"])
74 var mmodules = mbuilder.parse([arguments.first])
75 if mmodules.is_empty then return
76 mbuilder.run_phases
77 assert mmodules.length == 1
78 self.mainmodule = mmodules.first
79 end
80
81 fun start do
82 if arguments.length == 1 then
83 welcome
84 prompt
85 else
86 seek(arguments[1])
87 end
88 end
89
90 fun welcome do
91 print "Welcome in the Nit Index."
92 print ""
93 print "Loaded modules:"
94 var mmodules = new Array[MModule]
95 mmodules.add_all(model.mmodules)
96 var sorter = new MModuleNameSorter
97 sorter.sort(mmodules)
98 for m in mmodules do
99 print "\t{m.name}"
100 end
101 print ""
102 help
103 end
104
105 fun help do
106 print "\nCommands:"
107 print "\tname\t\tlookup module, class and property with the corresponding 'name'"
108 print "\tparam: Type\tlookup methods using the corresponding 'Type' as parameter"
109 print "\treturn: Type\tlookup methods returning the corresponding 'Type'"
110 print "\tnew: Type\tlookup methods creating new instances of 'Type'"
111 print "\t:h\t\tdisplay this help message"
112 print "\t:q\t\texit"
113 print ""
114 end
115
116 fun prompt do
117 printn ">> "
118 seek(stdin.read_line)
119 end
120
121 fun seek(entry: String) do
122 if entry.is_empty then
123 prompt
124 return
125 end
126 if entry == ":h" then
127 help
128 prompt
129 return
130 end
131 if entry == ":q" then exit(0)
132 var pager = new Pager
133 var query = parse_query(entry)
134 if query isa QueryPair then
135 # seek return types
136 if query.category == "return" then
137 var matches = seek_returns(query.keyword)
138 props_fulldoc(pager, matches)
139 # seek param types
140 else if query.category == "param" then
141 var matches = seek_params(query.keyword)
142 props_fulldoc(pager, matches)
143 # seek type inits
144 else if query.category == "new" then
145 var matches = seek_inits(query.keyword)
146 props_fulldoc(pager, matches)
147 end
148 else
149 # seek for modules
150 var mmatches = new List[MModule]
151 for m in model.mmodules do
152 if m.name == entry then mmatches.add(m)
153 end
154 if not mmatches.is_empty then modules_fulldoc(pager, mmatches)
155 # seek for classes
156 var cmatches = new List[MClass]
157 for c in model.mclasses do
158 if c.name == entry then cmatches.add(c)
159 end
160 if not cmatches.is_empty then classes_fulldoc(pager, cmatches)
161 # seek for properties
162 var matches = new List[MProperty]
163 for p in model.mproperties do
164 if p.name == entry then matches.add(p)
165 end
166 if not matches.is_empty then props_fulldoc(pager, matches)
167 end
168 # no matches
169 if pager.content.is_empty then
170 print "Nothing known about '{entry}', type ':h' for help"
171 else
172 pager.render
173 end
174 if arguments.length == 1 then prompt
175 end
176
177 private fun parse_query(str: String): Query do
178 var parts = str.split_with(":")
179 if parts.length == 1 then
180 return new Query(parts[0])
181 else
182 var category = parts[0]
183 var keyword = parts[1]
184 if keyword.first == ' ' then keyword = keyword.substring_from(1)
185 return new QueryPair(keyword, category)
186 end
187 end
188
189 private fun modules_fulldoc(pager: Pager, mmodules: List[MModule]) do
190 for mmodule in mmodules do
191 # name and prototype
192 pager.add("# {mmodule.namespace}\n".bold)
193 # comment
194 if mbuilder.mmodule2nmodule.has_key(mmodule) then
195 var nmodule = mbuilder.mmodule2nmodule[mmodule]
196 if not nmodule.n_moduledecl.n_doc == null then
197 for comment in nmodule.n_moduledecl.n_doc.comment do pager.add(comment.green)
198 end
199 end
200 pager.add("{mmodule.prototype}\n")
201 # imports
202 var msorter = new MModuleNameSorter
203 var ms = mmodule.in_importation.greaters.to_a
204 if ms.length > 1 then
205 msorter.sort(ms)
206 pager.add("## imported modules".bold)
207 pager.addn("\t")
208 for i in [0..ms.length[ do
209 if ms[i] == mmodule then continue
210 pager.addn(ms[i].name)
211 if i < ms.length - 1 then pager.addn(", ")
212 end
213 pager.add("\n")
214 end
215 # clients
216 ms = mmodule.in_importation.smallers.to_a
217 if ms.length > 1 then
218 msorter.sort(ms)
219 pager.add("## known modules".bold)
220 pager.addn("\t")
221 for i in [0..ms.length[ do
222 if ms[i] == mmodule then continue
223 pager.addn(ms[i].name)
224 if i < ms.length - 1 then pager.addn(", ")
225 end
226 pager.add("\n")
227 end
228 # local classes and interfaces
229 var sorter = new MClassDefNameSorter
230 var intro_mclassdefs = new Array[MClassDef]
231 var redef_mclassdefs = new Array[MClassDef]
232 for mclassdef in mmodule.mclassdefs do
233 if mclassdef.is_intro then
234 intro_mclassdefs.add(mclassdef)
235 else
236 redef_mclassdefs.add(mclassdef)
237 end
238 end
239 # intro
240 if not intro_mclassdefs.is_empty then
241 sorter.sort(intro_mclassdefs)
242 pager.add("\n## introduced classes".bold)
243 for mclassdef in intro_mclassdefs do
244 pager.add("")
245 var nclass = mbuilder.mclassdef2nclassdef[mclassdef]
246 if nclass isa AStdClassdef and not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then
247 pager.add("\t{nclass.n_doc.short_comment.green}")
248 end
249 pager.add("\t{mclassdef.mclass.prototype}")
250 #TODO add redefs?
251 end
252 end
253 # redefs
254 if not redef_mclassdefs.is_empty then
255 sorter.sort(redef_mclassdefs)
256 pager.add("\n## refined classes".bold)
257 for mclassdef in redef_mclassdefs do
258 pager.add("")
259 #TODO intro comment?
260 var nclass = mbuilder.mclassdef2nclassdef[mclassdef]
261 if nclass isa AStdClassdef and not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then
262 pager.add("\t# {nclass.n_doc.short_comment.green}")
263 end
264 pager.add("\t{mclassdef.mclass.prototype}")
265 pager.add("\t\t" + "introduced in {mclassdef.mclass.intro.mmodule.namespace.bold}".gray)
266 for odef in mclassdef.mclass.mclassdefs do
267 if odef.is_intro or odef == mclassdef or mclassdef.mmodule == mmodule then continue
268 pager.add("\t\t" + "refined in {mclassdef.mmodule.namespace.bold}".gray)
269 end
270 end
271 end
272 #TODO add inherited classes?
273 pager.add_rule
274 end
275 end
276
277 private fun classes_fulldoc(pager: Pager, mclasses: List[MClass]) do
278 for mclass in mclasses do
279 # title
280 pager.add("# {mclass.namespace}\n".bold)
281 # comment
282 if mbuilder.mclassdef2nclassdef.has_key(mclass.intro) then
283 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
284 if nclass isa AStdClassdef and not nclass.n_doc == null then
285 for comment in nclass.n_doc.comment do pager.add(comment.green)
286 end
287 end
288 pager.addn("{mclass.prototype}")
289 if not mclass.in_hierarchy(mainmodule).direct_greaters.is_empty then
290 var supers = mclass.in_hierarchy(mainmodule).direct_greaters.to_a
291 pager.addn(" super ")
292 for i in [0..supers.length[ do
293 if supers[i] == mclass then continue
294 pager.addn("{supers[i].name}{supers[i].signature}")
295 if i < mclass.in_hierarchy(mainmodule).direct_greaters.length -1 then pager.addn(", ")
296 end
297 end
298 pager.add("\n")
299 # formal types
300 if not mclass.parameter_types.is_empty then
301 pager.add("## formal types".bold)
302 for ft, bound in mclass.parameter_types do
303 pager.add("")
304 pager.add("\t{ft.to_s.green}: {bound.to_console}")
305 end
306 pager.add("")
307 end
308 # get properties
309 var cats = new ArrayMap[String, Set[MPropDef]]
310 cats["virtual types"] = new HashSet[MPropDef]
311 cats["constructors"] = new HashSet[MPropDef]
312 cats["introduced methods"] = new HashSet[MPropDef]
313 cats["refined methods"] = new HashSet[MPropDef]
314
315 for mclassdef in mclass.mclassdefs do
316 for mpropdef in mclassdef.mpropdefs do
317 if mpropdef isa MAttributeDef then continue
318 if mpropdef isa MVirtualTypeDef then cats["virtual types"].add(mpropdef)
319 if mpropdef isa MMethodDef then
320 if mpropdef.mproperty.is_init then
321 cats["constructors"].add(mpropdef)
322 else if mpropdef.is_intro then
323 cats["introduced methods"].add(mpropdef)
324 else
325 cats["refined methods"].add(mpropdef)
326 end
327 end
328 end
329 end
330 # local mproperties
331 for cat, list in cats do
332 if not list.is_empty then
333 #sort list
334 var sorted = new Array[MPropDef]
335 sorted.add_all(list)
336 var sorter = new MPropDefNameSorter
337 sorter.sort(sorted)
338 pager.add("## {cat}".bold)
339 for mpropdef in sorted do
340 pager.add("")
341 if mbuilder.mpropdef2npropdef.has_key(mpropdef) then
342 var nprop = mbuilder.mpropdef2npropdef[mpropdef]
343 if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then
344 for comment in nprop.n_doc.comment do pager.add("\t{comment.green}")
345 else
346 nprop = mbuilder.mpropdef2npropdef[mpropdef.mproperty.intro]
347 if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then
348 for comment in nprop.n_doc.comment do pager.add("\t{comment.green}")
349 end
350 end
351 end
352 pager.add("\t{mpropdef.to_console}")
353 mainmodule.linearize_mpropdefs(mpropdef.mproperty.mpropdefs)
354 var previous_defs = new Array[MPropDef]
355 for def in mpropdef.mproperty.mpropdefs do
356 if def == mpropdef then continue
357 if def.is_intro then continue
358 if mclass.in_hierarchy(mainmodule) < def.mclassdef.mclass then
359 previous_defs.add(def)
360 end
361 end
362 if not mpropdef.is_intro then
363 pager.add("\t\t" + "introduced by {mpropdef.mproperty.intro.mclassdef.namespace.bold}".gray)
364 end
365 if not previous_defs.is_empty then
366 for def in previous_defs do pager.add("\t\t" + "inherited from {def.mclassdef.namespace.bold}".gray)
367 end
368 end
369 pager.add("")
370 end
371 end
372 # inherited mproperties
373 var inhs = new ArrayMap[MClass, Array[MProperty]]
374 var ancestors = mclass.in_hierarchy(mainmodule).greaters.to_a
375 mainmodule.linearize_mclasses(ancestors)
376 for a in ancestors do
377 if a == mclass then continue
378 for c in a.mclassdefs do
379 for p in c.intro_mproperties do
380 if p.intro_mclassdef == c then
381 if not inhs.has_key(a) then inhs[a] = new Array[MProperty]
382 inhs[a].add(p)
383 end
384 end
385 end
386 end
387 if not inhs.is_empty then
388 pager.add("## inherited properties".bold)
389 for a, ps in inhs do
390 pager.add("\n\tfrom {a.namespace.bold}: {ps.join(", ")}")
391 end
392 end
393 pager.add_rule
394 end
395 end
396
397 private fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do
398 # group by module
399 var cats = new HashMap[MModule, Array[MProperty]]
400 for mprop in raw_mprops do
401 if mprop isa MAttribute then continue
402 var key = mprop.intro.mclassdef.mmodule
403 if not cats.has_key(key) then cats[key] = new Array[MProperty]
404 cats[key].add(mprop)
405 end
406 #sort groups
407 var sorter = new MModuleNameSorter
408 var sorted = new Array[MModule]
409 sorted.add_all(cats.keys)
410 sorter.sort(sorted)
411 # display
412 for mmodule in sorted do
413 var mprops = cats[mmodule]
414 pager.add("# matches in module {mmodule.namespace.bold}")
415 var sorterp = new MPropertyNameSorter
416 sorterp.sort(mprops)
417 for mprop in mprops do
418 pager.add("")
419 if mbuilder.mpropdef2npropdef.has_key(mprop.intro) then
420 var nprop = mbuilder.mpropdef2npropdef[mprop.intro]
421 if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then
422 for comment in nprop.n_doc.comment do pager.add("\t{comment.green}")
423 end
424 end
425 pager.add("\t{mprop.intro.to_console}")
426 pager.add("\t\t" + "introduced in {mprop.intro_mclassdef.namespace.bold}".gray)
427 var mpropdefs = mprop.mpropdefs
428 mainmodule.linearize_mpropdefs(mpropdefs)
429 for mpdef in mpropdefs do
430 if not mpdef.is_intro then
431 pager.add("\t\t" + "refined in {mpdef.mclassdef.namespace.bold}".gray)
432 end
433 end
434 end
435 pager.add_rule
436 end
437 end
438
439 private fun seek_returns(entry: String): List[MProperty] do
440 var matches = new List[MProperty]
441 for mprop in model.mproperties do
442 var intro = mprop.intro
443 if intro isa MMethodDef then
444 if intro.msignature.return_mtype != null and intro.msignature.return_mtype.to_console.has_prefix(entry) then matches.add(mprop)
445 else if intro isa MAttributeDef then
446 if intro.static_mtype.to_console.has_prefix(entry) then matches.add(mprop)
447 end
448 end
449 return matches
450 end
451
452 private fun seek_params(entry: String): List[MProperty] do
453 var matches = new List[MProperty]
454 for mprop in model.mproperties do
455 var intro = mprop.intro
456 if intro isa MMethodDef then
457 var mparameters = intro.msignature.mparameters
458 for mparameter in mparameters do
459 print mparameter.mtype.to_console
460 if mparameter.mtype.to_console.has_prefix(entry) then matches.add(mprop)
461 end
462 else if intro isa MAttributeDef then
463 if intro.static_mtype.to_console.has_prefix(entry) then matches.add(mprop)
464 end
465 end
466 return matches
467 end
468
469 #TODO should be returning a List[MPropDef]
470 private fun seek_inits(entry: String): List[MProperty] do
471 var mtype2mpropdefs = toolcontext.nitx_phase.mtype2mpropdefs
472 var matches = new List[MProperty]
473 for mtype in mtype2mpropdefs.keys do
474 if mtype.to_console.has_prefix(entry) then
475 for mpropdef in mtype2mpropdefs[mtype] do
476 matches.add(mpropdef.mproperty)
477 end
478 end
479 end
480 return matches
481 end
482 end
483
484 # Code Analysis
485
486 redef class ToolContext
487 var nitx_phase: NitxPhase = new NitxPhase(self, [typing_phase])
488 end
489
490 # Compiler phase for nitx
491 private class NitxPhase
492 super Phase
493
494 var mtype2mpropdefs = new HashMap[MType, Set[MPropDef]]
495 redef fun process_npropdef(npropdef) do
496 var visitor = new TypeInitVisitor
497 visitor.enter_visit(npropdef)
498 for mtype in visitor.inits do
499 if not mtype2mpropdefs.has_key(mtype) then
500 mtype2mpropdefs[mtype] = new HashSet[MPropDef]
501 end
502 mtype2mpropdefs[mtype].add(npropdef.mpropdef.as(not null))
503 end
504 end
505 end
506
507 # Visitor looking for initialized mtype (new T)
508 private class TypeInitVisitor
509 super Visitor
510
511 var inits = new HashSet[MType]
512 redef fun visit(node)
513 do
514 node.visit_all(self)
515 # look for init
516 if not node isa ANewExpr then return
517 var mtype = node.n_type.mtype
518 if mtype != null then inits.add(mtype)
519 end
520 end
521
522 # Printing facilities
523
524 redef class MModule
525 # prototype of the module
526 # module ownername::name
527 private fun prototype: String do return "module {name}"
528
529 # namespace of the module
530 # ownername::name
531 private fun namespace: String do
532 if public_owner == null then
533 return self.name
534 else
535 return "{public_owner.namespace}::{self.name}"
536 end
537 end
538 end
539
540 redef class MClass
541 # return the generic signature of the class
542 # [E, F]
543 private fun signature: String do
544 var res = new Buffer
545 if arity > 0 then
546 res.append("[")
547 for i in [0..intro.parameter_names.length[ do
548 res.append(intro.parameter_names[i])
549 if i < intro.parameter_names.length - 1 then res.append(", ")
550 end
551 res.append("]")
552 end
553 return res.to_s
554 end
555
556 # return the prototype of the class
557 # class name is displayed with colors depending on visibility
558 # abstract interface Foo[E]
559 private fun prototype: String do
560 var res = new Buffer
561 res.append("{kind} ")
562 if visibility.to_s == "public" then res.append("{name}{signature}".bold.green)
563 if visibility.to_s == "private" then res.append("{name}{signature}".bold.red)
564 if visibility.to_s == "protected" then res.append("{name}{signature}".bold.yellow)
565 return res.to_s
566 end
567
568 private fun namespace: String do
569 return "{intro_mmodule.namespace}::{name}"
570 end
571 end
572
573 redef class MClassDef
574 private fun namespace: String do
575 return "{mmodule.full_name}::{mclass.name}"
576 end
577 end
578
579 redef class MProperty
580 fun to_console: String do
581 if visibility.to_s == "public" then return name.green
582 if visibility.to_s == "private" then return name.red
583 if visibility.to_s == "protected" then return name.yellow
584 return name.bold
585 end
586 end
587
588 redef class MPropDef
589 fun to_console: String is abstract
590 end
591
592 redef class MMethodDef
593 redef fun to_console do
594 var res = new Buffer
595 if not is_intro then res.append("redef ")
596 if not mproperty.is_init then res.append("fun ")
597 res.append(mproperty.to_console.bold)
598 if msignature != null then res.append(msignature.to_console)
599 # FIXME: modifiers should be accessible via the model
600 #if self isa ADeferredMethPropdef then ret = "{ret} is abstract"
601 #if self isa AInternMethPropdef then ret = "{ret} is intern"
602 #if self isa AExternMethPropdef then ret = "{ret} is extern"
603 return res.to_s
604 end
605 end
606
607 redef class MVirtualTypeDef
608 redef fun to_console do
609 var res = new Buffer
610 res.append("type ")
611 res.append(mproperty.to_console.bold)
612 res.append(": {bound.to_console}")
613 return res.to_s
614 end
615 end
616
617 redef class MSignature
618 redef fun to_console do
619 var res = new Buffer
620 if not mparameters.is_empty then
621 res.append("(")
622 for i in [0..mparameters.length[ do
623 res.append(mparameters[i].to_console)
624 if i < mparameters.length - 1 then res.append(", ")
625 end
626 res.append(")")
627 end
628 if return_mtype != null then
629 res.append(": {return_mtype.to_console}")
630 end
631 return res.to_s
632 end
633 end
634
635 redef class MParameter
636 fun to_console: String do
637 var res = new Buffer
638 res.append("{name}: {mtype.to_console}")
639 if is_vararg then res.append("...")
640 return res.to_s
641 end
642 end
643
644 redef class MType
645 fun to_console: String do return self.to_s
646 end
647
648 redef class MNullableType
649 redef fun to_console do return "nullable {mtype.to_console}"
650 end
651
652 redef class MGenericType
653 redef fun to_console do
654 var res = new Buffer
655 res.append("{mclass.name}[")
656 for i in [0..arguments.length[ do
657 res.append(arguments[i].to_console)
658 if i < arguments.length - 1 then res.append(", ")
659 end
660 res.append("]")
661 return res.to_s
662 end
663 end
664
665 redef class MParameterType
666 redef fun to_console do return mclass.intro.parameter_names[rank]
667 end
668
669 redef class MVirtualType
670 redef fun to_console do return mproperty.name
671 end
672
673 redef class ADoc
674 private fun comment: List[String] do
675 var res = new List[String]
676 for t in n_comment do
677 res.add(t.text.replace("\n", ""))
678 end
679 return res
680 end
681
682 private fun short_comment: String do
683 return n_comment.first.text.replace("\n", "")
684 end
685 end
686
687 redef class AAttrPropdef
688 private fun read_accessor: String do
689 var ret = "fun "
690 #FIXME bug with standard::stream::FDStream::fd
691 var name = mreadpropdef.mproperty.name
692 if mpropdef.mproperty.visibility.to_s == "public" then ret = "{ret}{name.green}"
693 if mpropdef.mproperty.visibility.to_s == "private" then ret = "{ret}{name.red}"
694 if mpropdef.mproperty.visibility.to_s == "protected" then ret = "{ret}{name.yellow}"
695 ret = "{ret}: {n_type.to_s}"
696 if n_kwredef != null then ret = "redef {ret}"
697 return ret
698 end
699
700 private fun write_accessor: String do
701 var ret = "fun "
702 var name = "{mreadpropdef.mproperty.name}="
703 if n_readable != null and n_readable.n_visibility != null then
704 if n_readable.n_visibility isa APublicVisibility then ret = "{ret}{name.green}"
705 if n_readable.n_visibility isa APrivateVisibility then ret = "{ret}{name.red}"
706 if n_readable.n_visibility isa AProtectedVisibility then ret = "{ret}{name.yellow}"
707 else
708 ret = "{ret}{name.red}"
709 end
710 ret = "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
711 if n_kwredef != null then ret = "redef {ret}"
712 return ret
713 end
714 end
715
716 # Redef String class to add a function to color the string
717 redef class String
718
719 private fun add_escape_char(escapechar: String): String do
720 return "{escapechar}{self}\\033[0m"
721 end
722
723 private fun esc: Char do return 27.ascii
724 private fun gray: String do return add_escape_char("{esc}[30m")
725 private fun red: String do return add_escape_char("{esc}[31m")
726 private fun green: String do return add_escape_char("{esc}[32m")
727 private fun yellow: String do return add_escape_char("{esc}[33m")
728 private fun blue: String do return add_escape_char("{esc}[34m")
729 private fun purple: String do return add_escape_char("{esc}[35m")
730 private fun cyan: String do return add_escape_char("{esc}[36m")
731 private fun light_gray: String do return add_escape_char("{esc}[37m")
732 private fun bold: String do return add_escape_char("{esc}[1m")
733 private fun underline: String do return add_escape_char("{esc}[4m")
734
735 private fun escape: String
736 do
737 var b = new Buffer
738 for c in self do
739 if c == '\n' then
740 b.append("\\n")
741 else if c == '\0' then
742 b.append("\\0")
743 else if c == '"' then
744 b.append("\\\"")
745 else if c == '\\' then
746 b.append("\\\\")
747 else if c == '`' then
748 b.append("'")
749 else if c.ascii < 32 then
750 b.append("\\{c.ascii.to_base(8, false)}")
751 else
752 b.add(c)
753 end
754 end
755 return b.to_s
756 end
757 end
758
759 # Create a tool context to handle options and paths
760 var toolcontext = new ToolContext
761 toolcontext.process_options
762
763 # Here we launch the nit index
764 var ni = new NitIndex(toolcontext)
765 ni.start
766
767 # TODO seek subclasses and super classes <.<class> >.<class>
768 # TODO seek subclasses and super types <:<type> >:<type>
769 # TODO seek with regexp
770 # TODO standardize namespaces with private option