irb 0.9.6 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.document +4 -0
  3. data/Gemfile +10 -2
  4. data/LICENSE.txt +3 -3
  5. data/README.md +3 -3
  6. data/Rakefile +17 -1
  7. data/doc/irb/irb-tools.rd.ja +184 -0
  8. data/doc/irb/irb.rd.ja +427 -0
  9. data/irb.gemspec +18 -4
  10. data/lib/irb/cmd/fork.rb +2 -4
  11. data/lib/irb/cmd/help.rb +10 -5
  12. data/lib/irb/cmd/info.rb +32 -0
  13. data/lib/irb/cmd/ls.rb +101 -0
  14. data/lib/irb/cmd/measure.rb +43 -0
  15. data/lib/irb/cmd/nop.rb +10 -4
  16. data/lib/irb/cmd/pushws.rb +0 -1
  17. data/lib/irb/cmd/show_source.rb +93 -0
  18. data/lib/irb/cmd/whereami.rb +20 -0
  19. data/lib/irb/color.rb +246 -0
  20. data/lib/irb/color_printer.rb +47 -0
  21. data/lib/irb/completion.rb +254 -55
  22. data/lib/irb/context.rb +165 -72
  23. data/lib/irb/easter-egg.rb +138 -0
  24. data/lib/irb/ext/change-ws.rb +0 -1
  25. data/lib/irb/ext/history.rb +47 -11
  26. data/lib/irb/ext/loader.rb +46 -20
  27. data/lib/irb/ext/multi-irb.rb +7 -7
  28. data/lib/irb/ext/save-history.rb +36 -11
  29. data/lib/irb/ext/tracer.rb +14 -2
  30. data/lib/irb/ext/use-loader.rb +4 -3
  31. data/lib/irb/ext/workspaces.rb +0 -1
  32. data/lib/irb/extend-command.rb +113 -63
  33. data/lib/irb/frame.rb +12 -7
  34. data/lib/irb/help.rb +0 -1
  35. data/lib/irb/init.rb +146 -26
  36. data/lib/irb/input-method.rb +287 -9
  37. data/lib/irb/inspector.rb +15 -11
  38. data/lib/irb/lc/error.rb +55 -16
  39. data/lib/irb/lc/help-message +25 -13
  40. data/lib/irb/lc/ja/error.rb +55 -14
  41. data/lib/irb/lc/ja/help-message +11 -6
  42. data/lib/irb/locale.rb +13 -4
  43. data/lib/irb/notifier.rb +12 -8
  44. data/lib/irb/output-method.rb +6 -6
  45. data/lib/irb/ruby-lex.rb +673 -992
  46. data/lib/irb/ruby_logo.aa +37 -0
  47. data/lib/irb/version.rb +2 -2
  48. data/lib/irb/workspace.rb +65 -21
  49. data/lib/irb/xmp.rb +1 -1
  50. data/lib/irb.rb +276 -96
  51. data/man/irb.1 +229 -0
  52. metadata +25 -31
  53. data/.gitignore +0 -9
  54. data/.travis.yml +0 -6
  55. data/lib/irb/lc/.document +0 -4
  56. data/lib/irb/ruby-token.rb +0 -267
  57. data/lib/irb/slex.rb +0 -282
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ require 'pp'
3
+ require 'irb/color'
4
+
5
+ module IRB
6
+ class ColorPrinter < ::PP
7
+ class << self
8
+ def pp(obj, out = $>, width = screen_width)
9
+ q = ColorPrinter.new(out, width)
10
+ q.guard_inspect_key {q.pp obj}
11
+ q.flush
12
+ out << "\n"
13
+ end
14
+
15
+ private
16
+
17
+ def screen_width
18
+ Reline.get_screen_size.last
19
+ rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
20
+ 79
21
+ end
22
+ end
23
+
24
+ def pp(obj)
25
+ if obj.is_a?(String)
26
+ # Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
27
+ text(obj.inspect)
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def text(str, width = nil)
34
+ unless str.is_a?(String)
35
+ str = str.inspect
36
+ end
37
+ width ||= str.length
38
+
39
+ case str
40
+ when /\A#</, '=', '>'
41
+ super(Color.colorize(str, [:GREEN]), width)
42
+ else
43
+ super(Color.colorize_code(str, ignore_error: true), width)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -7,7 +7,7 @@
7
7
  # From Original Idea of shugo@ruby-lang.org
8
8
  #
9
9
 
10
- require "readline"
10
+ require_relative 'ruby-lex'
11
11
 
12
12
  module IRB
13
13
  module InputCompletor # :nodoc:
@@ -16,11 +16,12 @@ module IRB
16
16
  # Set of reserved words used by Ruby, you should not use these for
17
17
  # constants or variables
18
18
  ReservedWords = %w[
19
+ __ENCODING__ __LINE__ __FILE__
19
20
  BEGIN END
20
21
  alias and
21
22
  begin break
22
23
  case class
23
- def defined do
24
+ def defined? do
24
25
  else elsif end ensure
25
26
  false for
26
27
  if in
@@ -35,118 +36,267 @@ module IRB
35
36
  yield
36
37
  ]
37
38
 
38
- CompletionProc = proc { |input|
39
- bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
39
+ BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
40
40
 
41
+ def self.absolute_path?(p) # TODO Remove this method after 2.6 EOL.
42
+ if File.respond_to?(:absolute_path?)
43
+ File.absolute_path?(p)
44
+ else
45
+ if File.absolute_path(p) == p
46
+ true
47
+ else
48
+ false
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.retrieve_gem_and_system_load_path
54
+ gem_paths = Gem::Specification.latest_specs(true).map { |s|
55
+ s.require_paths.map { |p|
56
+ if absolute_path?(p)
57
+ p
58
+ else
59
+ File.join(s.full_gem_path, p)
60
+ end
61
+ }
62
+ }.flatten if defined?(Gem::Specification)
63
+ (gem_paths.to_a | $LOAD_PATH).sort
64
+ end
65
+
66
+ def self.retrieve_files_to_require_from_load_path
67
+ @@files_from_load_path ||=
68
+ (
69
+ shortest = []
70
+ rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result|
71
+ begin
72
+ names = Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: path)
73
+ rescue Errno::ENOENT
74
+ nil
75
+ end
76
+ next if names.empty?
77
+ names.map! { |n| n.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '') }.sort!
78
+ shortest << names.shift
79
+ result.concat(names)
80
+ }
81
+ shortest.sort! | rest
82
+ )
83
+ end
84
+
85
+ def self.retrieve_files_to_require_relative_from_current_dir
86
+ @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
87
+ path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
88
+ }
89
+ end
90
+
91
+ CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
92
+ if target =~ /\A(['"])([^'"]+)\Z/
93
+ quote = $1
94
+ actual_target = $2
95
+ else
96
+ return nil # It's not String literal
97
+ end
98
+ tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
99
+ tok = nil
100
+ tokens.reverse_each do |t|
101
+ unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
102
+ tok = t
103
+ break
104
+ end
105
+ end
106
+ result = []
107
+ if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
108
+ case tok.tok
109
+ when 'require'
110
+ result = retrieve_files_to_require_from_load_path.select { |path|
111
+ path.start_with?(actual_target)
112
+ }.map { |path|
113
+ quote + path
114
+ }
115
+ when 'require_relative'
116
+ result = retrieve_files_to_require_relative_from_current_dir.select { |path|
117
+ path.start_with?(actual_target)
118
+ }.map { |path|
119
+ quote + path
120
+ }
121
+ end
122
+ end
123
+ result
124
+ }
125
+
126
+ CompletionProc = lambda { |target, preposing = nil, postposing = nil|
127
+ if preposing && postposing
128
+ result = CompletionRequireProc.(target, preposing, postposing)
129
+ unless result
130
+ result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
131
+ end
132
+ result
133
+ else
134
+ retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
135
+ end
136
+ }
137
+
138
+ def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
41
139
  case input
42
140
  when /^((["'`]).*\2)\.([^.]*)$/
43
141
  # String
44
142
  receiver = $1
45
- message = Regexp.quote($3)
143
+ message = $3
46
144
 
47
145
  candidates = String.instance_methods.collect{|m| m.to_s}
48
- select_message(receiver, message, candidates)
146
+ if doc_namespace
147
+ "String.#{message}"
148
+ else
149
+ select_message(receiver, message, candidates)
150
+ end
49
151
 
50
152
  when /^(\/[^\/]*\/)\.([^.]*)$/
51
153
  # Regexp
52
154
  receiver = $1
53
- message = Regexp.quote($2)
155
+ message = $2
54
156
 
55
157
  candidates = Regexp.instance_methods.collect{|m| m.to_s}
56
- select_message(receiver, message, candidates)
158
+ if doc_namespace
159
+ "Regexp.#{message}"
160
+ else
161
+ select_message(receiver, message, candidates)
162
+ end
57
163
 
58
164
  when /^([^\]]*\])\.([^.]*)$/
59
165
  # Array
60
166
  receiver = $1
61
- message = Regexp.quote($2)
167
+ message = $2
62
168
 
63
169
  candidates = Array.instance_methods.collect{|m| m.to_s}
64
- select_message(receiver, message, candidates)
170
+ if doc_namespace
171
+ "Array.#{message}"
172
+ else
173
+ select_message(receiver, message, candidates)
174
+ end
65
175
 
66
176
  when /^([^\}]*\})\.([^.]*)$/
67
177
  # Proc or Hash
68
178
  receiver = $1
69
- message = Regexp.quote($2)
179
+ message = $2
70
180
 
71
- candidates = Proc.instance_methods.collect{|m| m.to_s}
72
- candidates |= Hash.instance_methods.collect{|m| m.to_s}
73
- select_message(receiver, message, candidates)
181
+ proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
182
+ hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
183
+ if doc_namespace
184
+ ["Proc.#{message}", "Hash.#{message}"]
185
+ else
186
+ select_message(receiver, message, proc_candidates | hash_candidates)
187
+ end
74
188
 
75
189
  when /^(:[^:.]*)$/
76
190
  # Symbol
77
- if Symbol.respond_to?(:all_symbols)
78
- sym = $1
79
- candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
80
- candidates.grep(/^#{Regexp.quote(sym)}/)
81
- else
82
- []
191
+ return nil if doc_namespace
192
+ sym = $1
193
+ candidates = Symbol.all_symbols.collect do |s|
194
+ ":" + s.id2name.encode(Encoding.default_external)
195
+ rescue EncodingError
196
+ # ignore
83
197
  end
198
+ candidates.grep(/^#{Regexp.quote(sym)}/)
84
199
 
85
- when /^::([A-Z][^:\.\(]*)$/
200
+ when /^::([A-Z][^:\.\(\)]*)$/
86
201
  # Absolute Constant or class methods
87
202
  receiver = $1
88
203
  candidates = Object.constants.collect{|m| m.to_s}
89
- candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
204
+ if doc_namespace
205
+ candidates.find { |i| i == receiver }
206
+ else
207
+ candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
208
+ end
90
209
 
91
210
  when /^([A-Z].*)::([^:.]*)$/
92
211
  # Constant or class methods
93
212
  receiver = $1
94
- message = Regexp.quote($2)
213
+ message = $2
95
214
  begin
96
215
  candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
97
216
  candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
98
217
  rescue Exception
99
218
  candidates = []
100
219
  end
101
- select_message(receiver, message, candidates, "::")
220
+ if doc_namespace
221
+ "#{receiver}::#{message}"
222
+ else
223
+ select_message(receiver, message, candidates, "::")
224
+ end
102
225
 
103
226
  when /^(:[^:.]+)(\.|::)([^.]*)$/
104
227
  # Symbol
105
228
  receiver = $1
106
229
  sep = $2
107
- message = Regexp.quote($3)
230
+ message = $3
108
231
 
109
232
  candidates = Symbol.instance_methods.collect{|m| m.to_s}
110
- select_message(receiver, message, candidates, sep)
233
+ if doc_namespace
234
+ "Symbol.#{message}"
235
+ else
236
+ select_message(receiver, message, candidates, sep)
237
+ end
111
238
 
112
- when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)(\.|::)([^.]*)$/
239
+ when /^(?<num>-?(?:0[dbo])?[0-9_]+(?:\.[0-9_]+)?(?:(?:[eE][+-]?[0-9]+)?i?|r)?)(?<sep>\.|::)(?<mes>[^.]*)$/
113
240
  # Numeric
114
- receiver = $1
115
- sep = $5
116
- message = Regexp.quote($6)
241
+ receiver = $~[:num]
242
+ sep = $~[:sep]
243
+ message = $~[:mes]
117
244
 
118
245
  begin
119
- candidates = eval(receiver, bind).methods.collect{|m| m.to_s}
246
+ instance = eval(receiver, bind)
247
+ if doc_namespace
248
+ "#{instance.class.name}.#{message}"
249
+ else
250
+ candidates = instance.methods.collect{|m| m.to_s}
251
+ select_message(receiver, message, candidates, sep)
252
+ end
120
253
  rescue Exception
121
- candidates = []
254
+ if doc_namespace
255
+ nil
256
+ else
257
+ candidates = []
258
+ end
122
259
  end
123
- select_message(receiver, message, candidates, sep)
124
260
 
125
261
  when /^(-?0x[0-9a-fA-F_]+)(\.|::)([^.]*)$/
126
262
  # Numeric(0xFFFF)
127
263
  receiver = $1
128
264
  sep = $2
129
- message = Regexp.quote($3)
265
+ message = $3
130
266
 
131
267
  begin
132
- candidates = eval(receiver, bind).methods.collect{|m| m.to_s}
268
+ instance = eval(receiver, bind)
269
+ if doc_namespace
270
+ "#{instance.class.name}.#{message}"
271
+ else
272
+ candidates = instance.methods.collect{|m| m.to_s}
273
+ select_message(receiver, message, candidates, sep)
274
+ end
133
275
  rescue Exception
134
- candidates = []
276
+ if doc_namespace
277
+ nil
278
+ else
279
+ candidates = []
280
+ end
135
281
  end
136
- select_message(receiver, message, candidates, sep)
137
282
 
138
283
  when /^(\$[^.]*)$/
139
284
  # global var
140
- regmessage = Regexp.new(Regexp.quote($1))
141
- candidates = global_variables.collect{|m| m.to_s}.grep(regmessage)
285
+ gvar = $1
286
+ all_gvars = global_variables.collect{|m| m.to_s}
287
+ if doc_namespace
288
+ all_gvars.find{ |i| i == gvar }
289
+ else
290
+ all_gvars.grep(Regexp.new(Regexp.quote(gvar)))
291
+ end
142
292
 
143
- when /^([^."].*)(\.|::)([^.]*)$/
293
+ when /^([^.:"].*)(\.|::)([^.]*)$/
144
294
  # variable.func or func.func
145
295
  receiver = $1
146
296
  sep = $2
147
- message = Regexp.quote($3)
297
+ message = $3
148
298
 
149
- gv = eval("global_variables", bind).collect{|m| m.to_s}
299
+ gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil")
150
300
  lv = eval("local_variables", bind).collect{|m| m.to_s}
151
301
  iv = eval("instance_variables", bind).collect{|m| m.to_s}
152
302
  cv = eval("self.class.constants", bind).collect{|m| m.to_s}
@@ -177,21 +327,76 @@ module IRB
177
327
  candidates.sort!
178
328
  candidates.uniq!
179
329
  end
180
- select_message(receiver, message, candidates, sep)
330
+ if doc_namespace
331
+ rec_class = rec.is_a?(Module) ? rec : rec.class
332
+ "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
333
+ else
334
+ select_message(receiver, message, candidates, sep)
335
+ end
181
336
 
182
337
  when /^\.([^.]*)$/
183
338
  # unknown(maybe String)
184
339
 
185
340
  receiver = ""
186
- message = Regexp.quote($1)
341
+ message = $1
187
342
 
188
343
  candidates = String.instance_methods(true).collect{|m| m.to_s}
189
- select_message(receiver, message, candidates)
344
+ if doc_namespace
345
+ "String.#{candidates.find{ |i| i == message }}"
346
+ else
347
+ select_message(receiver, message, candidates)
348
+ end
190
349
 
191
350
  else
192
- candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
351
+ if doc_namespace
352
+ vars = eval("local_variables | instance_variables", bind).collect{|m| m.to_s}
353
+ perfect_match_var = vars.find{|m| m.to_s == input}
354
+ if perfect_match_var
355
+ eval("#{perfect_match_var}.class.name", bind)
356
+ else
357
+ candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
358
+ candidates |= ReservedWords
359
+ candidates.find{ |i| i == input }
360
+ end
361
+ else
362
+ candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
363
+ candidates |= ReservedWords
364
+ candidates.grep(/^#{Regexp.quote(input)}/)
365
+ end
366
+ end
367
+ end
193
368
 
194
- (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
369
+ PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
370
+ begin
371
+ require 'rdoc'
372
+ rescue LoadError
373
+ return
374
+ end
375
+
376
+ RDocRIDriver ||= RDoc::RI::Driver.new
377
+
378
+ if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
379
+ IRB.__send__(:easter_egg)
380
+ return
381
+ end
382
+
383
+ namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
384
+ return unless namespace
385
+
386
+ if namespace.is_a?(Array)
387
+ out = RDoc::Markup::Document.new
388
+ namespace.each do |m|
389
+ begin
390
+ RDocRIDriver.add_method(out, m)
391
+ rescue RDoc::RI::Driver::NotFoundError
392
+ end
393
+ end
394
+ RDocRIDriver.display(out)
395
+ else
396
+ begin
397
+ RDocRIDriver.display_names([namespace])
398
+ rescue RDoc::RI::Driver::NotFoundError
399
+ end
195
400
  end
196
401
  }
197
402
 
@@ -199,7 +404,7 @@ module IRB
199
404
  Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
200
405
 
201
406
  def self.select_message(receiver, message, candidates, sep = ".")
202
- candidates.grep(/^#{message}/).collect do |e|
407
+ candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
203
408
  case e
204
409
  when /^[a-zA-Z_]/
205
410
  receiver + sep + e
@@ -225,7 +430,7 @@ module IRB
225
430
  end
226
431
  end
227
432
 
228
- %i(IRB SLex RubyLex RubyToken).each do |sym|
433
+ %i(IRB RubyLex).each do |sym|
229
434
  next unless Object.const_defined?(sym)
230
435
  scanner.call(Object.const_get(sym))
231
436
  end
@@ -236,9 +441,3 @@ module IRB
236
441
  end
237
442
  end
238
443
  end
239
-
240
- if Readline.respond_to?("basic_word_break_characters=")
241
- Readline.basic_word_break_characters= " \t\n`><=;|&{("
242
- end
243
- Readline.completion_append_character = nil
244
- Readline.completion_proc = IRB::InputCompletor::CompletionProc