model_utils: replace many sorters by MEntityNameSorter
[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 MEntityNameSorter
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 MEntityNameSorter
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 sorter.sort(mprops)
342 for mprop in mprops do
343
344 end
345 pager.add_rule
346 end
347 end
348 end
349
350 private class Pager
351 var content = new FlatBuffer
352 var indent = 0
353 fun add(text: String) do
354 add_indent
355 addn("{text}\n")
356 end
357 fun add_indent do addn(" " * indent)
358 fun addn(text: String) do content.append(text.escape)
359 fun add_rule do add("\n---\n")
360 fun render do sys.system("echo \"{content}\" | pager -r")
361 end
362
363 redef class MModule
364 super IndexMatch
365 # prototype of the module
366 # module name
367 private fun prototype: String do return "module {name.bold}"
368
369 # namespace of the module
370 # project::name
371 private fun namespace: String do
372 if mgroup == null or mgroup.mproject.name == self.name then
373 return self.name
374 else
375 return "{mgroup.mproject}::{self.name}"
376 end
377 end
378
379 redef fun preview(index, pager) do
380 var mdoc = self.mdoc
381 if mdoc != null then
382 pager.add(mdoc.short_comment.green)
383 end
384 pager.add(prototype)
385 pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray)
386 end
387
388 redef fun content(index, pager) do
389 var mdoc = self.mdoc
390 if mdoc != null then
391 for comment in mdoc.content do pager.add(comment.green)
392 end
393 pager.add(prototype)
394 pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray)
395 pager.indent = pager.indent + 1
396 var sorter = new MEntityNameSorter
397 # imported modules
398 var imports = new Array[MModule]
399 for mmodule in in_importation.direct_greaters.to_a do
400 imports.add(mmodule)
401 end
402 if not imports.is_empty then
403 sorter.sort(imports)
404 pager.add("")
405 pager.add("== imported modules".bold)
406 pager.indent = pager.indent + 1
407 for mmodule in imports do
408 pager.add("")
409 mmodule.preview(index, pager)
410 end
411 pager.indent = pager.indent - 1
412 end
413 # mclassdefs
414 var intros = new Array[MClassDef]
415 var redefs = new Array[MClassDef]
416 for mclassdef in mclassdefs do
417 if mclassdef.is_intro then
418 intros.add(mclassdef)
419 else
420 redefs.add(mclassdef)
421 end
422 end
423 # introductions
424 if not intros.is_empty then
425 sorter.sort(intros)
426 pager.add("")
427 pager.add("== introduced classes".bold)
428 pager.indent = pager.indent + 1
429 for mclass in intros do
430 pager.add("")
431 mclass.preview(index, pager)
432 end
433 pager.indent = pager.indent - 1
434 end
435 # refinements
436 if not redefs.is_empty then
437 sorter.sort(redefs)
438 pager.add("")
439 pager.add("== refined classes".bold)
440 pager.indent = pager.indent + 1
441 for mclass in redefs do
442 pager.add("")
443 mclass.preview(index, pager)
444 end
445 pager.indent = pager.indent - 1
446 end
447 pager.indent = pager.indent - 1
448 end
449 end
450
451 redef class MClass
452 super IndexMatch
453 # return the generic signature of the class
454 # [E, F]
455 private fun signature: String do
456 var res = new FlatBuffer
457 if arity > 0 then
458 res.append("[")
459 for i in [0..intro.parameter_names.length[ do
460 res.append(intro.parameter_names[i])
461 if i < intro.parameter_names.length - 1 then res.append(", ")
462 end
463 res.append("]")
464 end
465 return res.to_s
466 end
467
468 # return the prototype of the class
469 # class name is displayed with colors depending on visibility
470 # abstract interface Foo[E]
471 private fun prototype: String do
472 var res = new FlatBuffer
473 res.append("{kind} ")
474 if visibility.to_s == "public" then res.append("{name}{signature}".bold.green)
475 if visibility.to_s == "private" then res.append("{name}{signature}".bold.red)
476 if visibility.to_s == "protected" then res.append("{name}{signature}".bold.yellow)
477 return res.to_s
478 end
479
480 private fun namespace: String do
481 return "{intro_mmodule.namespace}::{name}"
482 end
483
484 redef fun preview(index, pager) do
485 intro.preview(index, pager)
486 end
487
488 redef fun content(index, pager) do
489 # intro comment
490 var sorter = new MEntityNameSorter
491 var mdoc = intro.mdoc
492 if mdoc != null then
493 for comment in mdoc.content do pager.add(comment.green)
494 end
495 pager.add(intro.to_console)
496 pager.add("{intro.namespace}".bold.gray + " (lines {intro.location.lines})".gray)
497 pager.indent = pager.indent + 1
498 # parents
499 var supers = self.in_hierarchy(index.mainmodule).direct_greaters.to_a
500 if not supers.is_empty then
501 sorter.sort(supers)
502 pager.add("")
503 pager.add("== supers".bold)
504 pager.indent = pager.indent + 1
505 for mclass in supers do
506 pager.add("")
507 mclass.preview(index, pager)
508 end
509 pager.indent = pager.indent - 1
510 end
511 # formal types
512 if not self.parameter_types.is_empty then
513 pager.add("")
514 pager.add("== formal types".bold)
515 pager.indent = pager.indent + 1
516 for ft, bound in self.parameter_types do
517 pager.add("")
518 pager.add("{ft.to_s.bold.green}: {bound.to_console}")
519 end
520 pager.indent = pager.indent - 1
521 end
522 # intro mproperties
523 var mpropdefs = intro.mpropdefs
524 index.mainmodule.linearize_mpropdefs(mpropdefs)
525 for cat in intro.cats2mpropdefs.keys do
526 var defs = intro.cats2mpropdefs[cat].to_a
527 if defs.is_empty then continue
528 sorter.sort(defs)
529 pager.add("")
530 pager.add("== {cat}".bold)
531 pager.indent = pager.indent + 1
532 for mpropdef in defs do
533 pager.add("")
534 mpropdef.preview(index, pager)
535 end
536 pager.indent = pager.indent - 1
537 end
538 # refinements
539 if not self.mclassdefs.is_empty then
540 pager.add("")
541 pager.add("== refinements".bold)
542 var mclassdefs = self.mclassdefs
543 index.mainmodule.linearize_mclassdefs(mclassdefs)
544 pager.indent = pager.indent + 1
545 for mclassdef in mclassdefs do
546 if not mclassdef.is_intro then
547 pager.add("")
548 mclassdef.content(index, pager)
549 end
550 end
551 pager.indent = pager.indent - 1
552 end
553 pager.indent = pager.indent - 1
554 end
555 end
556
557 redef class MClassDef
558 super IndexMatch
559
560 private fun namespace: String do
561 return "{mmodule.full_name}::{mclass.name}"
562 end
563
564 fun to_console: String do
565 var res = new FlatBuffer
566 if not is_intro then res.append("redef ")
567 res.append(mclass.prototype)
568 return res.to_s
569 end
570
571 redef fun preview(index, pager) do
572 var mdoc = self.mdoc
573 if mdoc != null then
574 pager.add(mdoc.short_comment.green)
575 end
576 pager.add(to_console)
577 pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray)
578 end
579
580 redef fun content(index, pager) do
581 var mdoc = self.mdoc
582 if mdoc != null then
583 for comment in mdoc.content do pager.add(comment.green)
584 end
585 pager.add(to_console)
586 pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray)
587 pager.indent = pager.indent + 1
588 var mpropdefs = self.mpropdefs
589 var sorter = new MEntityNameSorter
590 index.mainmodule.linearize_mpropdefs(mpropdefs)
591 for cat in cats2mpropdefs.keys do
592 var defs = cats2mpropdefs[cat].to_a
593 sorter.sort(defs)
594 if defs.is_empty then continue
595 pager.add("")
596 pager.add("== {cat}".bold)
597 pager.indent = pager.indent + 1
598 for mpropdef in defs do
599 pager.add("")
600 mpropdef.preview(index, pager)
601 end
602 pager.indent = pager.indent - 1
603 end
604 pager.indent = pager.indent - 1
605 end
606
607 # get mpropdefs grouped by categories (vt, init, methods)
608 fun cats2mpropdefs: Map[String, Set[MPropDef]] do
609 var cats = new ArrayMap[String, Set[MPropDef]]
610 cats["virtual types"] = new HashSet[MPropDef]
611 cats["constructors"] = new HashSet[MPropDef]
612 cats["methods"] = new HashSet[MPropDef]
613
614 for mpropdef in mpropdefs do
615 if mpropdef isa MAttributeDef then continue
616 if mpropdef isa MVirtualTypeDef then cats["virtual types"].add(mpropdef)
617 if mpropdef isa MMethodDef then
618 if mpropdef.mproperty.is_init then
619 cats["constructors"].add(mpropdef)
620 else
621 cats["methods"].add(mpropdef)
622 end
623 end
624 end
625 return cats
626 end
627 end
628
629 redef class MProperty
630 super IndexMatch
631
632 fun to_console: String do
633 if visibility.to_s == "public" then return name.green
634 if visibility.to_s == "private" then return name.red
635 if visibility.to_s == "protected" then return name.yellow
636 return name.bold
637 end
638
639 redef fun preview(index, pager) do
640 intro.preview(index, pager)
641 end
642
643 redef fun content(index, pager) do
644 intro.content(index, pager)
645 pager.indent = pager.indent + 1
646 var mpropdefs = self.mpropdefs
647 index.mainmodule.linearize_mpropdefs(mpropdefs)
648 for mpropdef in mpropdefs do
649 if mpropdef isa MAttributeDef then continue
650 if not mpropdef.is_intro then
651 pager.add("")
652 mpropdef.preview(index, pager)
653 end
654 end
655 pager.indent = pager.indent - 1
656 end
657 end
658
659 redef class MPropDef
660 super IndexMatch
661
662 fun to_console: String is abstract
663
664 private fun namespace: String do
665 return "{mclassdef.namespace}::{mproperty.name}"
666 end
667
668 redef fun preview(index, pager) do
669 var mdoc = self.mdoc
670 if mdoc != null then
671 pager.add(mdoc.short_comment.green)
672 end
673 pager.add(to_console)
674 pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray)
675 end
676
677 redef fun content(index, pager) do
678 var mdoc = self.mdoc
679 if mdoc != null then
680 for comment in mdoc.content do pager.add(comment.green)
681 end
682 pager.add(to_console)
683 pager.add("{namespace}".bold.gray + " (lines {location.lines})".gray)
684 end
685 end
686
687 redef class MMethodDef
688 redef fun to_console do
689 var res = new FlatBuffer
690 if not is_intro then res.append("redef ")
691 if not mproperty.is_init then res.append("fun ")
692 res.append(mproperty.to_console.bold)
693 if msignature != null then res.append(msignature.to_console)
694 if is_abstract then res.append " is abstract"
695 if is_intern then res.append " is intern"
696 if is_extern then res.append " is extern"
697 return res.to_s
698 end
699 end
700
701 redef class MVirtualTypeDef
702 redef fun to_console do
703 var res = new FlatBuffer
704 res.append("type ")
705 res.append(mproperty.to_console.bold)
706 res.append(": {bound.to_console}")
707 return res.to_s
708 end
709 end
710
711 redef class MAttributeDef
712 redef fun to_console do
713 var res = new FlatBuffer
714 res.append("var ")
715 res.append(mproperty.to_console.bold)
716 res.append(": {static_mtype.to_console}")
717 return res.to_s
718 end
719 end
720
721 redef class MSignature
722 redef fun to_console do
723 var res = new FlatBuffer
724 if not mparameters.is_empty then
725 res.append("(")
726 for i in [0..mparameters.length[ do
727 res.append(mparameters[i].to_console)
728 if i < mparameters.length - 1 then res.append(", ")
729 end
730 res.append(")")
731 end
732 if return_mtype != null then
733 res.append(": {return_mtype.to_console}")
734 end
735 return res.to_s
736 end
737 end
738
739 redef class MParameter
740 fun to_console: String do
741 var res = new FlatBuffer
742 res.append("{name}: {mtype.to_console}")
743 if is_vararg then res.append("...")
744 return res.to_s
745 end
746 end
747
748 redef class MType
749 fun to_console: String do return self.to_s
750 end
751
752 redef class MNullableType
753 redef fun to_console do return "nullable {mtype.to_console}"
754 end
755
756 redef class MGenericType
757 redef fun to_console do
758 var res = new FlatBuffer
759 res.append("{mclass.name}[")
760 for i in [0..arguments.length[ do
761 res.append(arguments[i].to_console)
762 if i < arguments.length - 1 then res.append(", ")
763 end
764 res.append("]")
765 return res.to_s
766 end
767 end
768
769 redef class MParameterType
770 redef fun to_console do return mclass.intro.parameter_names[rank]
771 end
772
773 redef class MVirtualType
774 redef fun to_console do return mproperty.name
775 end
776
777 redef class MDoc
778 private fun short_comment: String do
779 return content.first
780 end
781 end
782
783 redef class AAttrPropdef
784 private fun read_accessor: String do
785 var ret = "fun "
786 #FIXME bug with standard::stream::FDStream::fd
787 var name = mreadpropdef.mproperty.name
788 if mpropdef.mproperty.visibility.to_s == "public" then ret = "{ret}{name.green}"
789 if mpropdef.mproperty.visibility.to_s == "private" then ret = "{ret}{name.red}"
790 if mpropdef.mproperty.visibility.to_s == "protected" then ret = "{ret}{name.yellow}"
791 ret = "{ret}: {n_type.to_s}"
792 if n_kwredef != null then ret = "redef {ret}"
793 return ret
794 end
795
796 private fun write_accessor: String do
797 var ret = "fun "
798 var name = "{mreadpropdef.mproperty.name}="
799 if n_readable != null and n_readable.n_visibility != null then
800 if n_readable.n_visibility isa APublicVisibility then ret = "{ret}{name.green}"
801 if n_readable.n_visibility isa APrivateVisibility then ret = "{ret}{name.red}"
802 if n_readable.n_visibility isa AProtectedVisibility then ret = "{ret}{name.yellow}"
803 else
804 ret = "{ret}{name.red}"
805 end
806 ret = "{ret}({mreadpropdef.mproperty.name}: {n_type.to_s})"
807 if n_kwredef != null then ret = "redef {ret}"
808 return ret
809 end
810 end
811
812 # Redef String class to add a function to color the string
813 redef class String
814
815 private fun add_escape_char(escapechar: String): String do
816 return "{escapechar}{self}\\033[0m"
817 end
818
819 private fun esc: Char do return 27.ascii
820 private fun gray: String do return add_escape_char("{esc}[30m")
821 private fun red: String do return add_escape_char("{esc}[31m")
822 private fun green: String do return add_escape_char("{esc}[32m")
823 private fun yellow: String do return add_escape_char("{esc}[33m")
824 private fun blue: String do return add_escape_char("{esc}[34m")
825 private fun purple: String do return add_escape_char("{esc}[35m")
826 private fun cyan: String do return add_escape_char("{esc}[36m")
827 private fun light_gray: String do return add_escape_char("{esc}[37m")
828 private fun bold: String do return add_escape_char("{esc}[1m")
829 private fun underline: String do return add_escape_char("{esc}[4m")
830
831 private fun escape: String
832 do
833 var b = new FlatBuffer
834 for c in self.chars do
835 if c == '\n' then
836 b.append("\\n")
837 else if c == '\0' then
838 b.append("\\0")
839 else if c == '"' then
840 b.append("\\\"")
841 else if c == '\\' then
842 b.append("\\\\")
843 else if c == '`' then
844 b.append("'")
845 else if c.ascii < 32 then
846 b.append("\\{c.ascii.to_base(8, false)}")
847 else
848 b.add(c)
849 end
850 end
851 return b.to_s
852 end
853 end
854
855 redef class Location
856 fun lines: String do
857 return "{line_start}-{line_end}"
858 end
859 end
860
861 # Create a tool context to handle options and paths
862 var toolcontext = new ToolContext
863 toolcontext.tooldescription = "Usage: nitx [OPTION]... <file.nit> [query]\nDisplays specific pieces of API information from Nit source files."
864 toolcontext.process_options(args)
865
866 # Here we launch the nit index
867 var ni = new NitIndex(toolcontext)
868 ni.start
869
870 # TODO seek subclasses and super classes <.<class> >.<class>
871 # TODO seek subclasses and super types <:<type> >:<type>
872 # TODO seek with regexp
873 # TODO standardize namespaces with private option