irb 1.4.2 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Next < DebugCommand
10
+ def execute(*args)
11
+ super(do_cmds: ["next", *args].join(" "))
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
data/lib/irb/cmd/nop.rb CHANGED
@@ -13,17 +13,41 @@ module IRB
13
13
  # :stopdoc:
14
14
 
15
15
  module ExtendCommand
16
+ class CommandArgumentError < StandardError; end
17
+
16
18
  class Nop
19
+ class << self
20
+ def category(category = nil)
21
+ @category = category if category
22
+ @category
23
+ end
24
+
25
+ def description(description = nil)
26
+ @description = description if description
27
+ @description
28
+ end
29
+
30
+ private
31
+
32
+ def string_literal?(args)
33
+ sexp = Ripper.sexp(args)
34
+ sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
35
+ end
36
+ end
17
37
 
18
38
  if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
19
39
  def self.execute(conf, *opts, **kwargs, &block)
20
40
  command = new(conf)
21
41
  command.execute(*opts, **kwargs, &block)
42
+ rescue CommandArgumentError => e
43
+ puts e.message
22
44
  end
23
45
  else
24
46
  def self.execute(conf, *opts, &block)
25
47
  command = new(conf)
26
48
  command.execute(*opts, &block)
49
+ rescue CommandArgumentError => e
50
+ puts e.message
27
51
  end
28
52
  end
29
53
 
@@ -18,12 +18,18 @@ module IRB
18
18
 
19
19
  module ExtendCommand
20
20
  class Workspaces < Nop
21
+ category "IRB"
22
+ description "Show workspaces."
23
+
21
24
  def execute(*obj)
22
25
  irb_context.workspaces.collect{|ws| ws.main}
23
26
  end
24
27
  end
25
28
 
26
29
  class PushWorkspace < Workspaces
30
+ category "IRB"
31
+ description "Push an object to the workspace stack."
32
+
27
33
  def execute(*obj)
28
34
  irb_context.push_workspace(*obj)
29
35
  super
@@ -31,6 +37,9 @@ module IRB
31
37
  end
32
38
 
33
39
  class PopWorkspace < Workspaces
40
+ category "IRB"
41
+ description "Pop a workspace from the workspace stack."
42
+
34
43
  def execute(*obj)
35
44
  irb_context.pop_workspace(*obj)
36
45
  super
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require_relative "nop"
5
+
6
+ module IRB
7
+ # :stopdoc:
8
+
9
+ module ExtendCommand
10
+ class ShowCmds < Nop
11
+ category "IRB"
12
+ description "List all available commands and their description."
13
+
14
+ def execute(*args)
15
+ commands_info = IRB::ExtendCommandBundle.all_commands_info
16
+ commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
17
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length
18
+
19
+ output = StringIO.new
20
+
21
+ commands_grouped_by_categories.each do |category, cmds|
22
+ output.puts Color.colorize(category, [:BOLD])
23
+
24
+ cmds.each do |cmd|
25
+ output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
26
+ end
27
+
28
+ output.puts
29
+ end
30
+
31
+ puts output.string
32
+
33
+ nil
34
+ end
35
+ end
36
+ end
37
+
38
+ # :startdoc:
39
+ end
@@ -9,12 +9,71 @@ module IRB
9
9
 
10
10
  module ExtendCommand
11
11
  class ShowSource < Nop
12
+ category "Context"
13
+ description "Show the source code of a given method or constant."
14
+
15
+ class << self
16
+ def transform_args(args)
17
+ # Return a string literal as is for backward compatibility
18
+ if args.empty? || string_literal?(args)
19
+ args
20
+ else # Otherwise, consider the input as a String for convenience
21
+ args.strip.dump
22
+ end
23
+ end
24
+
25
+ def find_source(str, irb_context)
26
+ case str
27
+ when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
28
+ eval(str, irb_context.workspace.binding) # trigger autoload
29
+ base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
30
+ file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
31
+ when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
32
+ owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
33
+ method = Regexp.last_match[:method]
34
+ if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
35
+ file, line = owner.instance_method(method).source_location
36
+ end
37
+ when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
38
+ receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
39
+ method = Regexp.last_match[:method]
40
+ file, line = receiver.method(method).source_location if receiver.respond_to?(method)
41
+ end
42
+ if file && line
43
+ Source.new(file: file, first_line: line, last_line: find_end(file, line))
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def find_end(file, first_line)
50
+ return first_line unless File.exist?(file)
51
+ lex = RubyLex.new
52
+ lines = File.read(file).lines[(first_line - 1)..-1]
53
+ tokens = RubyLex.ripper_lex_without_warning(lines.join)
54
+ prev_tokens = []
55
+
56
+ # chunk with line number
57
+ tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
58
+ code = lines[0..lnum].join
59
+ prev_tokens.concat chunk
60
+ continue = lex.process_continue(prev_tokens)
61
+ code_block_open = lex.check_code_block(code, prev_tokens)
62
+ if !continue && !code_block_open
63
+ return first_line + lnum
64
+ end
65
+ end
66
+ first_line
67
+ end
68
+ end
69
+
12
70
  def execute(str = nil)
13
71
  unless str.is_a?(String)
14
72
  puts "Error: Expected a string but got #{str.inspect}"
15
73
  return
16
74
  end
17
- source = find_source(str)
75
+
76
+ source = self.class.find_source(str, @irb_context)
18
77
  if source && File.exist?(source.file)
19
78
  show_source(source)
20
79
  else
@@ -35,48 +94,6 @@ module IRB
35
94
  puts
36
95
  end
37
96
 
38
- def find_source(str)
39
- case str
40
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
41
- eval(str, irb_context.workspace.binding) # trigger autoload
42
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
43
- file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
44
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
45
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
46
- method = Regexp.last_match[:method]
47
- if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
48
- file, line = owner.instance_method(method).source_location
49
- end
50
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
51
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
52
- method = Regexp.last_match[:method]
53
- file, line = receiver.method(method).source_location if receiver.respond_to?(method)
54
- end
55
- if file && line
56
- Source.new(file: file, first_line: line, last_line: find_end(file, line))
57
- end
58
- end
59
-
60
- def find_end(file, first_line)
61
- return first_line unless File.exist?(file)
62
- lex = RubyLex.new
63
- lines = File.read(file).lines[(first_line - 1)..-1]
64
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
65
- prev_tokens = []
66
-
67
- # chunk with line number
68
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
69
- code = lines[0..lnum].join
70
- prev_tokens.concat chunk
71
- continue = lex.process_continue(prev_tokens)
72
- code_block_open = lex.check_code_block(code, prev_tokens)
73
- if !continue && !code_block_open
74
- return first_line + lnum
75
- end
76
- end
77
- first_line
78
- end
79
-
80
97
  def bold(str)
81
98
  Color.colorize(str, [:BOLD])
82
99
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Step < DebugCommand
10
+ def execute(*args)
11
+ super(do_cmds: ["step", *args].join(" "))
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
@@ -10,31 +10,57 @@
10
10
  #
11
11
 
12
12
  require_relative "nop"
13
- require_relative "../ext/multi-irb"
14
13
 
15
14
  module IRB
16
15
  # :stopdoc:
17
16
 
18
17
  module ExtendCommand
19
- class IrbCommand < Nop
18
+ class MultiIRBCommand < Nop
19
+ def initialize(conf)
20
+ super
21
+ extend_irb_context
22
+ end
23
+
24
+ private
25
+
26
+ def extend_irb_context
27
+ # this extension patches IRB context like IRB.CurrentContext
28
+ require_relative "../ext/multi-irb"
29
+ end
30
+ end
31
+
32
+ class IrbCommand < MultiIRBCommand
33
+ category "IRB"
34
+ description "Start a child IRB."
35
+
20
36
  def execute(*obj)
21
37
  IRB.irb(nil, *obj)
22
38
  end
23
39
  end
24
40
 
25
- class Jobs < Nop
41
+ class Jobs < MultiIRBCommand
42
+ category "IRB"
43
+ description "List of current sessions."
44
+
26
45
  def execute
27
46
  IRB.JobManager
28
47
  end
29
48
  end
30
49
 
31
- class Foreground < Nop
32
- def execute(key)
50
+ class Foreground < MultiIRBCommand
51
+ category "IRB"
52
+ description "Switches to the session of the given number."
53
+
54
+ def execute(key = nil)
55
+ raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
33
56
  IRB.JobManager.switch(key)
34
57
  end
35
58
  end
36
59
 
37
- class Kill < Nop
60
+ class Kill < MultiIRBCommand
61
+ category "IRB"
62
+ description "Kills the session with the given number."
63
+
38
64
  def execute(*keys)
39
65
  IRB.JobManager.kill(*keys)
40
66
  end
@@ -7,6 +7,9 @@ module IRB
7
7
 
8
8
  module ExtendCommand
9
9
  class Whereami < Nop
10
+ category "Context"
11
+ description "Show the source code around binding.irb again."
12
+
10
13
  def execute(*)
11
14
  code = irb_context.workspace.code_around_binding
12
15
  if code
data/lib/irb/color.rb CHANGED
@@ -123,13 +123,15 @@ module IRB # :nodoc:
123
123
  # If `complete` is false (code is incomplete), this does not warn compile_error.
124
124
  # This option is needed to avoid warning a user when the compile_error is happening
125
125
  # because the input is not wrong but just incomplete.
126
- def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?)
126
+ def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
127
127
  return code unless colorable
128
128
 
129
129
  symbol_state = SymbolState.new
130
130
  colored = +''
131
+ lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
132
+ code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code
131
133
 
132
- scan(code, allow_last_error: !complete) do |token, str, expr|
134
+ scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr|
133
135
  # handle uncolorable code
134
136
  if token.nil?
135
137
  colored << Reline::Unicode.escape_for_print(str)
@@ -152,6 +154,11 @@ module IRB # :nodoc:
152
154
  end
153
155
  end
154
156
  end
157
+
158
+ if lvars_code
159
+ raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n")
160
+ colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors
161
+ end
155
162
  colored
156
163
  end
157
164
 
@@ -64,25 +64,27 @@ module IRB
64
64
  if File.respond_to?(:absolute_path?)
65
65
  File.absolute_path?(p)
66
66
  else
67
- if File.absolute_path(p) == p
68
- true
69
- else
70
- false
71
- end
67
+ File.absolute_path(p) == p
72
68
  end
73
69
  end
74
70
 
71
+ GEM_PATHS =
72
+ if defined?(Gem::Specification)
73
+ Gem::Specification.latest_specs(true).map { |s|
74
+ s.require_paths.map { |p|
75
+ if absolute_path?(p)
76
+ p
77
+ else
78
+ File.join(s.full_gem_path, p)
79
+ end
80
+ }
81
+ }.flatten
82
+ else
83
+ []
84
+ end.freeze
85
+
75
86
  def self.retrieve_gem_and_system_load_path
76
- gem_paths = Gem::Specification.latest_specs(true).map { |s|
77
- s.require_paths.map { |p|
78
- if absolute_path?(p)
79
- p
80
- else
81
- File.join(s.full_gem_path, p)
82
- end
83
- }
84
- }.flatten if defined?(Gem::Specification)
85
- candidates = (gem_paths.to_a | $LOAD_PATH)
87
+ candidates = (GEM_PATHS | $LOAD_PATH)
86
88
  candidates.map do |p|
87
89
  if p.respond_to?(:to_path)
88
90
  p.to_path
@@ -171,10 +173,10 @@ module IRB
171
173
  receiver = $1
172
174
  message = $3
173
175
 
174
- candidates = String.instance_methods.collect{|m| m.to_s}
175
176
  if doc_namespace
176
177
  "String.#{message}"
177
178
  else
179
+ candidates = String.instance_methods.collect{|m| m.to_s}
178
180
  select_message(receiver, message, candidates)
179
181
  end
180
182
 
@@ -183,10 +185,10 @@ module IRB
183
185
  receiver = $1
184
186
  message = $2
185
187
 
186
- candidates = Regexp.instance_methods.collect{|m| m.to_s}
187
188
  if doc_namespace
188
189
  "Regexp.#{message}"
189
190
  else
191
+ candidates = Regexp.instance_methods.collect{|m| m.to_s}
190
192
  select_message(receiver, message, candidates)
191
193
  end
192
194
 
@@ -195,10 +197,10 @@ module IRB
195
197
  receiver = $1
196
198
  message = $2
197
199
 
198
- candidates = Array.instance_methods.collect{|m| m.to_s}
199
200
  if doc_namespace
200
201
  "Array.#{message}"
201
202
  else
203
+ candidates = Array.instance_methods.collect{|m| m.to_s}
202
204
  select_message(receiver, message, candidates)
203
205
  end
204
206
 
@@ -207,29 +209,33 @@ module IRB
207
209
  receiver = $1
208
210
  message = $2
209
211
 
210
- proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
211
- hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
212
212
  if doc_namespace
213
213
  ["Proc.#{message}", "Hash.#{message}"]
214
214
  else
215
+ proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
216
+ hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
215
217
  select_message(receiver, message, proc_candidates | hash_candidates)
216
218
  end
217
219
 
218
220
  when /^(:[^:.]*)$/
219
221
  # Symbol
220
- return nil if doc_namespace
221
- sym = $1
222
- candidates = Symbol.all_symbols.collect do |s|
223
- ":" + s.id2name.encode(Encoding.default_external)
224
- rescue EncodingError
225
- # ignore
222
+ if doc_namespace
223
+ nil
224
+ else
225
+ sym = $1
226
+ candidates = Symbol.all_symbols.collect do |s|
227
+ ":" + s.id2name.encode(Encoding.default_external)
228
+ rescue EncodingError
229
+ # ignore
230
+ end
231
+ candidates.grep(/^#{Regexp.quote(sym)}/)
226
232
  end
227
- candidates.grep(/^#{Regexp.quote(sym)}/)
228
-
229
233
  when /^::([A-Z][^:\.\(\)]*)$/
230
234
  # Absolute Constant or class methods
231
235
  receiver = $1
236
+
232
237
  candidates = Object.constants.collect{|m| m.to_s}
238
+
233
239
  if doc_namespace
234
240
  candidates.find { |i| i == receiver }
235
241
  else
@@ -240,16 +246,18 @@ module IRB
240
246
  # Constant or class methods
241
247
  receiver = $1
242
248
  message = $2
243
- begin
244
- candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
245
- candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
246
- rescue Exception
247
- candidates = []
248
- end
249
+
249
250
  if doc_namespace
250
251
  "#{receiver}::#{message}"
251
252
  else
252
- select_message(receiver, message, candidates, "::")
253
+ begin
254
+ candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
255
+ candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
256
+ rescue Exception
257
+ candidates = []
258
+ end
259
+
260
+ select_message(receiver, message, candidates.sort, "::")
253
261
  end
254
262
 
255
263
  when /^(:[^:.]+)(\.|::)([^.]*)$/
@@ -258,10 +266,10 @@ module IRB
258
266
  sep = $2
259
267
  message = $3
260
268
 
261
- candidates = Symbol.instance_methods.collect{|m| m.to_s}
262
269
  if doc_namespace
263
270
  "Symbol.#{message}"
264
271
  else
272
+ candidates = Symbol.instance_methods.collect{|m| m.to_s}
265
273
  select_message(receiver, message, candidates, sep)
266
274
  end
267
275
 
@@ -273,6 +281,7 @@ module IRB
273
281
 
274
282
  begin
275
283
  instance = eval(receiver, bind)
284
+
276
285
  if doc_namespace
277
286
  "#{instance.class.name}.#{message}"
278
287
  else
@@ -283,7 +292,7 @@ module IRB
283
292
  if doc_namespace
284
293
  nil
285
294
  else
286
- candidates = []
295
+ []
287
296
  end
288
297
  end
289
298
 
@@ -305,7 +314,7 @@ module IRB
305
314
  if doc_namespace
306
315
  nil
307
316
  else
308
- candidates = []
317
+ []
309
318
  end
310
319
  end
311
320
 
@@ -313,6 +322,7 @@ module IRB
313
322
  # global var
314
323
  gvar = $1
315
324
  all_gvars = global_variables.collect{|m| m.to_s}
325
+
316
326
  if doc_namespace
317
327
  all_gvars.find{ |i| i == gvar }
318
328
  else
@@ -351,11 +361,13 @@ module IRB
351
361
  to_ignore = ignored_modules
352
362
  ObjectSpace.each_object(Module){|m|
353
363
  next if (to_ignore.include?(m) rescue true)
364
+ next unless m.respond_to?(:instance_methods) # JRuby has modules that represent java packages. They don't include many common ruby methods
354
365
  candidates.concat m.instance_methods(false).collect{|x| x.to_s}
355
366
  }
356
367
  candidates.sort!
357
368
  candidates.uniq!
358
369
  end
370
+
359
371
  if doc_namespace
360
372
  rec_class = rec.is_a?(Module) ? rec : rec.class
361
373
  "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
@@ -370,10 +382,11 @@ module IRB
370
382
  message = $1
371
383
 
372
384
  candidates = String.instance_methods(true).collect{|m| m.to_s}
385
+
373
386
  if doc_namespace
374
387
  "String.#{candidates.find{ |i| i == message }}"
375
388
  else
376
- select_message(receiver, message, candidates)
389
+ select_message(receiver, message, candidates.sort)
377
390
  end
378
391
 
379
392
  else
@@ -390,7 +403,7 @@ module IRB
390
403
  else
391
404
  candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
392
405
  candidates |= ReservedWords
393
- candidates.grep(/^#{Regexp.quote(input)}/)
406
+ candidates.grep(/^#{Regexp.quote(input)}/).sort
394
407
  end
395
408
  end
396
409
  end
data/lib/irb/context.rb CHANGED
@@ -49,12 +49,15 @@ module IRB
49
49
  if IRB.conf.has_key?(:USE_MULTILINE)
50
50
  @use_multiline = IRB.conf[:USE_MULTILINE]
51
51
  elsif IRB.conf.has_key?(:USE_RELINE) # backward compatibility
52
+ warn <<~MSG.strip
53
+ USE_RELINE is deprecated, please use USE_MULTILINE instead.
54
+ MSG
52
55
  @use_multiline = IRB.conf[:USE_RELINE]
53
56
  elsif IRB.conf.has_key?(:USE_REIDLINE)
54
57
  warn <<~MSG.strip
55
- USE_REIDLINE is deprecated, please use USE_RELINE instead.
58
+ USE_REIDLINE is deprecated, please use USE_MULTILINE instead.
56
59
  MSG
57
- @use_multiline = IRB.conf[:USE_RELINE]
60
+ @use_multiline = IRB.conf[:USE_REIDLINE]
58
61
  else
59
62
  @use_multiline = nil
60
63
  end
@@ -149,6 +152,8 @@ module IRB
149
152
  if @newline_before_multiline_output.nil?
150
153
  @newline_before_multiline_output = true
151
154
  end
155
+
156
+ @command_aliases = IRB.conf[:COMMAND_ALIASES]
152
157
  end
153
158
 
154
159
  # The top-level workspace, see WorkSpace#main
@@ -326,6 +331,9 @@ module IRB
326
331
  # See IRB@Command+line+options for more command line options.
327
332
  attr_accessor :back_trace_limit
328
333
 
334
+ # User-defined IRB command aliases
335
+ attr_accessor :command_aliases
336
+
329
337
  # Alias for #use_multiline
330
338
  alias use_multiline? use_multiline
331
339
  # Alias for #use_singleline
@@ -477,6 +485,20 @@ module IRB
477
485
  line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
478
486
  @workspace.local_variable_set(:_, exception)
479
487
  end
488
+
489
+ # Transform a non-identifier alias (@, $) or keywords (next, break)
490
+ command, args = line.split(/\s/, 2)
491
+ if original = command_aliases[command.to_sym]
492
+ line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
493
+ command = original
494
+ end
495
+
496
+ # Hook command-specific transformation
497
+ command_class = ExtendCommandBundle.load_command(command)
498
+ if command_class&.respond_to?(:transform_args)
499
+ line = "#{command} #{command_class.transform_args(args)}"
500
+ end
501
+
480
502
  set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
481
503
  end
482
504
 
@@ -518,5 +540,21 @@ module IRB
518
540
  end
519
541
  alias __to_s__ to_s
520
542
  alias to_s inspect
543
+
544
+ def local_variables # :nodoc:
545
+ workspace.binding.local_variables
546
+ end
547
+
548
+ # Return true if it's aliased from the argument and it's not an identifier.
549
+ def symbol_alias?(command)
550
+ return nil if command.match?(/\A\w+\z/)
551
+ command_aliases.key?(command.to_sym)
552
+ end
553
+
554
+ # Return true if the command supports transforming args
555
+ def transform_args?(command)
556
+ command = command_aliases.fetch(command.to_sym, command)
557
+ ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
558
+ end
521
559
  end
522
560
  end
@@ -70,10 +70,10 @@ module IRB
70
70
  end
71
71
  history_file = IRB.rc_file("_history") unless history_file
72
72
  if File.exist?(history_file)
73
- open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
73
+ File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
74
74
  f.each { |l|
75
75
  l = l.chomp
76
- if self.class == ReidlineInputMethod and history.last&.end_with?("\\")
76
+ if self.class == RelineInputMethod and history.last&.end_with?("\\")
77
77
  history.last.delete_suffix!("\\")
78
78
  history.last << "\n" << l
79
79
  else