irb 1.0.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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