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