0e0d34548ee4990c876fa8137c8f9f6770d5677d
[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 if is_abstract then res.append " is abstract"
698 if is_intern then res.append " is intern"
699 if is_extern then res.append " is extern"
700 return res.to_s
701 end
702 end
703
704 redef class MVirtualTypeDef
705 redef fun to_console do
706 var res = new FlatBuffer
707 res.append("type ")
708 res.append(mproperty.to_console.bold)
709 res.append(": {bound.to_console}")
710 return res.to_s
711 end
712 end
713
714 redef class MAttributeDef
715 redef fun to_console do
716 var res = new FlatBuffer
717 res.append("var ")
718 res.append(mproperty.to_console.bold)
719 res.append(": {static_mtype.to_console}")
720 return res.to_s
721 end
722 end
723
724 redef class MSignature
725 redef fun to_console do
726 var res = new FlatBuffer
727 if not mparameters.is_empty then
728 res.append("(")
729 for i in [0..mparameters.length[ do
730 res.append(mparameters[i].to_console)
731 if i < mparameters.length - 1 then res.append(", ")
732 end
733 res.append(")")
734 end
735 if return_mtype != null then
736 res.append(": {return_mtype.to_console}")
737 end
738 return res.to_s
739 end
740 end
741
742 redef class MParameter
743 fun to_console: String do
744 var res = new FlatBuffer
745 res.append("{name}: {mtype.to_console}")
746 if is_vararg then res.append("...")
747 return res.to_s
748 end
749 end
750
751 redef class MType
752 fun to_console: String do return self.to_s
753 end
754
755 redef class MNullableType
756 redef fun to_console do return "nullable {mtype.to_console}"
757 end
758
759 redef class MGenericType
760 redef fun to_console do
761 var res = new FlatBuffer
762 res.append("{mclass.name}[")
763 for i in [0..arguments.length[ do
764 res.append(arguments[i].to_console)
765 if i < arguments.length - 1 then res.append(", ")
766 end
767 res.append("]")
768 return res.to_s
769 end
770 end
771
772 redef class MParameterType
773 redef fun to_console do return mclass.intro.parameter_names[rank]
774 end
775
776 redef class MVirtualType
777 redef fun to_console do return mproperty.name
778 end
779
780 redef class MDoc
781 private fun short_comment: String do
782 return content.first
783 end
784 end
785
786 redef class AAttrPropdef
787 private fun read_accessor: String do
788 var ret = "fun "
789 #FIXME bug with standard::stream::FDStream::fd
790 var name = mreadpropdef.mproperty.name
791 if mpropdef.mproperty.visibility.to_s == "public" then ret = "{ret}{name.green}"
792 if mpropdef.mproperty.visibility.to_s == "private" then ret = "{ret}{name.red}"
793 if mpropdef.mproperty.visibility.to_s == "protected" then ret = "{ret}{name.yellow}"
794 ret = "{ret}: {n_type.to_s}"
795 if n_kwredef != null then ret = "redef {ret}"
796 return ret
797 end
798
799 private fun write_accessor: String do
800 var ret = "fun "
801 var name = "{mreadpropdef.mproperty.name}="
802 if n_readable != null and n_readable.n_visibility != null then
803 if n_readable.n_visibility isa APublicVisibility then ret = "{ret}{name.green}"
804 if n_readable.n_visibility isa APrivateVisibility then ret = "{ret}{name.red}"
805 if n_readable.n_visibility isa AProtectedVisibility then ret = "{ret}{name.yellow}"
806 else
807 ret = "{ret}{name.red}"
808 end
809 ret = "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
810 if n_kwredef != null then ret = "redef {ret}"
811 return ret
812 end
813 end
814
815 # Redef String class to add a function to color the string
816 redef class String
817
818 private fun add_escape_char(escapechar: String): String do
819 return "{escapechar}{self}\\033[0m"
820 end
821
822 private fun esc: Char do return 27.ascii
823 private fun gray: String do return add_escape_char("{esc}[30m")
824 private fun red: String do return add_escape_char("{esc}[31m")
825 private fun green: String do return add_escape_char("{esc}[32m")
826 private fun yellow: String do return add_escape_char("{esc}[33m")
827 private fun blue: String do return add_escape_char("{esc}[34m")
828 private fun purple: String do return add_escape_char("{esc}[35m")
829 private fun cyan: String do return add_escape_char("{esc}[36m")
830 private fun light_gray: String do return add_escape_char("{esc}[37m")
831 private fun bold: String do return add_escape_char("{esc}[1m")
832 private fun underline: String do return add_escape_char("{esc}[4m")
833
834 private fun escape: String
835 do
836 var b = new FlatBuffer
837 for c in self.chars do
838 if c == '\n' then
839 b.append("\\n")
840 else if c == '\0' then
841 b.append("\\0")
842 else if c == '"' then
843 b.append("\\\"")
844 else if c == '\\' then
845 b.append("\\\\")
846 else if c == '`' then
847 b.append("'")
848 else if c.ascii < 32 then
849 b.append("\\{c.ascii.to_base(8, false)}")
850 else
851 b.add(c)
852 end
853 end
854 return b.to_s
855 end
856 end
857
858 redef class Location
859 fun lines: String do
860 return "{line_start}-{line_end}"
861 end
862 end
863
864 # Create a tool context to handle options and paths
865 var toolcontext = new ToolContext
866 toolcontext.tooldescription = "Usage: nitx [OPTION]... <file.nit> [query]\nDisplays specific pieces of API information from Nit source files."
867 toolcontext.process_options(args)
868
869 # Here we launch the nit index
870 var ni = new NitIndex(toolcontext)
871 ni.start
872
873 # TODO seek subclasses and super classes <.<class> >.<class>
874 # TODO seek subclasses and super types <:<type> >:<type>
875 # TODO seek with regexp
876 # TODO standardize namespaces with private option