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