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