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