72c31c80f8e6a73e87463abf9b0af50997747a7e
[nit.git] / src / ni.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 module ni
18
19 import model_utils
20
21 private class Pager
22 var content: String = ""
23 fun add(text: String) do addn("{text}\n")
24 fun addn(text: String) do content += text.escape
25 fun add_rule do add("\n---\n")
26 fun render do sys.system("echo \"{content}\" | pager -r")
27 end
28
29 class NitIndex
30 private var toolcontext: ToolContext
31 private var model: Model
32 private var mbuilder: ModelBuilder
33 private var mainmodule: MModule
34 private var arguments: Array[String]
35
36 init(toolcontext: ToolContext) do
37 # We need a model to collect stufs
38 self.toolcontext = toolcontext
39 self.arguments = toolcontext.option_context.rest
40
41 if arguments.length > 2 then
42 print "usage: ni path/to/module.nit [expression]"
43 toolcontext.option_context.usage
44 exit(1)
45 end
46
47 model = new Model
48 mbuilder = new ModelBuilder(model, toolcontext)
49
50 # Here we load an process std modules
51 #var dir = "NIT_DIR".environ
52 #var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"])
53 var mmodules = mbuilder.parse_and_build([arguments.first])
54 if mmodules.is_empty then return
55 mbuilder.full_propdef_semantic_analysis
56 assert mmodules.length == 1
57 self.mainmodule = mmodules.first
58 end
59
60 fun start do
61 if arguments.length == 1 then
62 welcome
63 prompt
64 else
65 seek(arguments[1])
66 end
67 end
68
69 fun welcome do
70 print "Welcome in Nit Index.\n"
71 print "Loaded modules"
72 for m in mbuilder.nmodules do
73 print " - {m.mmodule.name}"
74 end
75 print "\nEnter the module, class or property name you want to look up."
76 print "Enter a blank line to exit.\n"
77 end
78
79 fun prompt do
80 printn ">> "
81 seek(stdin.read_line)
82 end
83
84 fun seek(entry: String) do
85 # quitting?
86 if entry.is_empty then exit(0)
87
88 var flag = false
89 for m in model.mmodules do
90 if m.name == entry then
91 flag = true
92 module_fulldoc(m)
93 end
94 end
95 for c in model.mclasses do
96 if c.name == entry then
97 if not mbuilder.mclassdef2nclassdef[c.intro] isa AStdClassdef then continue
98 flag = true
99 class_fulldoc(c)
100 end
101 end
102 var matches = new List[MProperty]
103 for p in model.mproperties do
104 if p.name == entry then
105 flag = true
106 matches.add(p)
107 end
108 end
109 if not matches.is_empty then props_fulldoc(matches)
110
111 if not flag then print "Nothing known about '{entry}'"
112 if arguments.length == 1 then prompt
113 end
114
115 private fun module_fulldoc(mmodule: MModule) do
116 var nmodule = mbuilder.mmodule2nmodule[mmodule]
117 var pager = new Pager
118 pager.add("# module {mmodule.name}\n".bold)
119 pager.add("import {mmodule.in_importation.direct_greaters.join(", ")}")
120 #TODO add kmown clients
121 pager.add_rule
122 pager.addn(nmodule.comment.green)
123 pager.add_rule
124
125 var cats = new HashMap[String, Collection[MClass]]
126 cats["introduced classes"] = mmodule.intro_mclasses
127 cats["refined classes"] = mmodule.redef_mclasses
128 cats["inherited classes"] = mmodule.imported_mclasses
129
130 for cat, list in cats do
131 if not list.is_empty then
132 pager.add("# {cat}\n".bold)
133 for mclass in list do
134 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro].as(AStdClassdef)
135 if not nclass.short_comment.is_empty then
136 pager.add("\t# {nclass.short_comment}")
137 end
138 if cat == "refined classes" then
139 pager.add("\tredef {mclass.short_doc}")
140 else
141 pager.add("\t{mclass.short_doc}")
142 end
143 if not mclass.intro_mmodule == mmodule then
144 pager.add("\t\t" + "introduced in {mmodule.full_name}::{mclass}".gray)
145 end
146 for mclassdef in mclass.mclassdefs do
147 if mclassdef != mclass.intro then
148 pager.add("\t\t" + "refined in {mclassdef.namespace}".gray)
149 end
150 end
151 pager.add("")
152 end
153 end
154 end
155 pager.add_rule
156 pager.render
157 end
158
159 private fun class_fulldoc(mclass: MClass) do
160 var nclass = mbuilder.mclassdef2nclassdef[mclass.intro].as(AStdClassdef)
161 var pager = new Pager
162 pager.add("# {mclass.intro_mmodule.public_owner.name}::{mclass.name}\n".bold)
163 pager.add("{mclass.short_doc} ")
164 #TODO add kmown subclasses
165 pager.add_rule
166 pager.addn(nclass.comment.green)
167 pager.add_rule
168 if not mclass.parameter_types.is_empty then
169 pager.add("# formal types\n".bold)
170 for ft, bound in mclass.parameter_types do
171 pager.add("\t{ft.to_s.green}: {bound}")
172 pager.add("")
173 end
174 end
175 if not mclass.virtual_types.is_empty then
176 pager.add("# virtual types\n".bold)
177 for vt in mclass.virtual_types do
178 if vt.visibility.to_s == "public" then pager.add("\t{vt.to_s.green}: {vt.intro.bound.to_s}")
179 if vt.visibility.to_s == "private" then pager.add("\t{vt.to_s.red}: {vt.intro.bound.to_s}")
180 if vt.visibility.to_s == "protected" then pager.add("\t{vt.to_s.yellow}: {vt.intro.bound.to_s}")
181 if vt.intro_mclassdef != mclass.intro then
182 pager.add("\t\t" + "introduced in {vt.intro_mclassdef.namespace}::{vt}".gray)
183 end
184 for mpropdef in vt.mpropdefs do
185 if mpropdef != vt.intro then
186 pager.add("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray)
187 end
188 end
189 pager.add("")
190 end
191 end
192 pager.add_rule
193
194 var cats = new HashMap[String, Collection[MMethod]]
195 cats["constructors"] = mclass.constructors
196 cats["introduced methods"] = mclass.intro_methods
197 cats["refined methods"] = mclass.redef_methods
198 cats["inherited methods"] = mclass.inherited_methods
199
200 for cat, list in cats do
201 if not list.is_empty then
202 pager.add("# {cat}\n".bold)
203 for mprop in list do
204 #TODO verifier cast
205 var nmethod = mbuilder.mpropdef2npropdef[mprop.intro].as(AMethPropdef)
206 if not nmethod.short_comment.is_empty then
207 pager.add("\t# {nmethod.short_comment}")
208 end
209 if cat == "refined methods" then
210 pager.add("\tredef {nmethod}")
211 else
212 pager.add("\t{nmethod}")
213 end
214 if not mprop.intro_mclassdef == mclass.intro then
215 pager.add("\t\t" + "introduced in {mprop.intro_mclassdef.namespace}".gray)
216 end
217 for mpropdef in mprop.mpropdefs do
218 if mpropdef != mprop.intro then
219 pager.add("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray)
220 end
221 end
222 pager.add("")
223 end
224 end
225 end
226 pager.render
227 end
228
229 private fun props_fulldoc(mprops: List[MProperty]) do
230 var pager = new Pager
231 for mprop in mprops do
232 var nprop = mbuilder.mpropdef2npropdef[mprop.intro]
233 if nprop isa AMethPropdef then
234 pager.add("# {mprop.intro_mclassdef.namespace.bold}\n")
235 if not nprop.short_comment.is_empty then
236 pager.add("\t# {nprop.short_comment}")
237 end
238 pager.add("\t{nprop}")
239 pager.add("\t\t" + "introduced in {mprop.intro_mclassdef.namespace}".gray)
240 for mpropdef in mprop.mpropdefs do
241 if mpropdef != mprop.intro then
242 pager.add("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray)
243 end
244 end
245 end
246 pager.add_rule
247 end
248 pager.render
249 end
250 end
251
252 # Printing facilities
253
254 redef class MClass
255
256 redef fun to_s: String do
257 if arity > 0 then
258 return "{name}[{intro.parameter_names.join(", ")}]"
259 else
260 return name
261 end
262 end
263
264 private fun short_doc: String do
265 var ret = ""
266 if is_interface then ret = "interface {ret}"
267 if is_enum then ret = "enum {ret}"
268 if is_class then ret = "class {ret}"
269 if is_abstract then ret = "abstract {ret}"
270 if visibility.to_s == "public" then ret = "{ret}{to_s.green}"
271 if visibility.to_s == "private" then ret = "{ret}{to_s.red}"
272 if visibility.to_s == "protected" then ret = "{ret}{to_s.yellow}"
273 ret = "{ret} super {parents.join(", ")}"
274 return ret
275 end
276 end
277
278 redef class MClassDef
279 private fun namespace: String do
280 return "{mmodule.full_name}::{mclass.name}"
281 end
282 end
283
284 redef class AModule
285 private fun comment: String do
286 var ret = ""
287 for t in n_moduledecl.n_doc.n_comment do
288 ret += "{t.text.replace("# ", "")}"
289 end
290 return ret
291 end
292 end
293
294 redef class AStdClassdef
295 private fun comment: String do
296 var ret = ""
297 if n_doc != null then
298 for t in n_doc.n_comment do
299 var txt = t.text.replace("# ", "")
300 txt = txt.replace("#", "")
301 ret += "{txt}"
302 end
303 end
304 return ret
305 end
306
307 private fun short_comment: String do
308 var ret = ""
309 if n_doc != null then
310 var txt = n_doc.n_comment.first.text
311 txt = txt.replace("# ", "")
312 txt = txt.replace("\n", "")
313 ret += txt
314 end
315 return ret
316 end
317 end
318
319 redef class AMethPropdef
320 private fun short_comment: String do
321 var ret = ""
322 if n_doc != null then
323 var txt = n_doc.n_comment.first.text
324 txt = txt.replace("# ", "")
325 txt = txt.replace("\n", "")
326 ret += txt
327 end
328 return ret
329 end
330
331 redef fun to_s do
332 var ret = ""
333 if not mpropdef.mproperty.is_init then
334 ret = "fun "
335 end
336 if mpropdef.mproperty.visibility.to_s == "public" then ret = "{ret}{mpropdef.mproperty.name.green}"
337 if mpropdef.mproperty.visibility.to_s == "private" then ret = "{ret}{mpropdef.mproperty.name.red}"
338 if mpropdef.mproperty.visibility.to_s == "protected" then ret = "{ret}{mpropdef.mproperty.name.yellow}"
339 if n_signature != null then ret = "{ret}{n_signature.to_s}"
340 if n_kwredef != null then ret = "redef {ret}"
341 if self isa ADeferredMethPropdef then ret = "{ret} is abstract"
342 if self isa AInternMethPropdef then ret = "{ret} is intern"
343 if self isa AExternMethPropdef then ret = "{ret} is extern"
344 return ret
345 end
346 end
347
348 redef class ASignature
349 redef fun to_s do
350 #TODO closures
351 var ret = ""
352 if not n_params.is_empty then
353 ret = "{ret}({n_params.join(", ")})"
354 end
355 if n_type != null then ret += ": {n_type.to_s}"
356 return ret
357 end
358 end
359
360 redef class AParam
361 redef fun to_s do
362 var ret = "{n_id.text}"
363 if n_type != null then
364 ret = "{ret}: {n_type.to_s}"
365 if n_dotdotdot != null then ret = "{ret}..."
366 end
367 return ret
368 end
369 end
370
371 redef class AType
372 redef fun to_s do
373 var ret = n_id.text
374 if n_kwnullable != null then ret = "nullable {ret}"
375 if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
376 return ret
377 end
378 end
379
380 # Redef String class to add a function to color the string
381 redef class String
382
383 private fun add_escape_char(escapechar: String): String do
384 return "{escapechar}{self}\\033[0m"
385 end
386
387 private fun esc: Char do return 27.ascii
388 private fun red: String do return add_escape_char("{esc}[1;31m")
389 private fun yellow: String do return add_escape_char("{esc}[1;33m")
390 private fun green: String do return add_escape_char("{esc}[1;32m")
391 private fun blue: String do return add_escape_char("{esc}[1;34m")
392 private fun cyan: String do return add_escape_char("{esc}[1;36m")
393 private fun gray: String do return add_escape_char("{esc}[30;1m")
394 private fun bold: String do return add_escape_char("{esc}[1m")
395 private fun underline: String do return add_escape_char("{esc}[4m")
396
397 private fun escape: String
398 do
399 var b = new Buffer
400 for c in self do
401 if c == '\n' then
402 b.append("\\n")
403 else if c == '\0' then
404 b.append("\\0")
405 else if c == '"' then
406 b.append("\\\"")
407 else if c == '\\' then
408 b.append("\\\\")
409 else if c == '`' then
410 b.append("'")
411 else if c.ascii < 32 then
412 b.append("\\{c.ascii.to_base(8, false)}")
413 else
414 b.add(c)
415 end
416 end
417 return b.to_s
418 end
419 end
420
421 # Create a tool context to handle options and paths
422 var toolcontext = new ToolContext
423 toolcontext.process_options
424
425 # Here we launch the nit index
426 var ni = new NitIndex(toolcontext)
427 ni.start
428
429 # TODO seek methods
430 # TODO lister les methods qui retournent un certain type
431 # TODO lister les methods qui utilisent un certain type
432 # TODO lister les sous-types connus d'une type
433 # TODO sorter par ordre alphabétique