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