irb 1.4.2 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25f63261bbf06e8b1605b8a680e538dd90641ad2492c0bfb0f8e127f673e2793
4
- data.tar.gz: 9f113f6c97887e83f9d476dba5e4c0a5656d76cc554e1e818c94e8828b4051fa
3
+ metadata.gz: 025d4dccc426fe09dc77b79d7efb8157abc559b89a7455249e5d7e9b2ff1562c
4
+ data.tar.gz: fd2cffe5c013c56a5d454ce81331ac1c24b6826c81db6ae6f2240e6e2d5f8c06
5
5
  SHA512:
6
- metadata.gz: 024d8e4dc13978bbae5ed3404e3ecff763da3fcc01e26c43d3e7e54822121bbc708de49c5a9c951a15c0ceee05a60cf282cbbd6ae6ec42960e9a8943a9217417
7
- data.tar.gz: 146edac46f0d81a01e120493e56eefa8d8b51143851718876617673bcbddf19b54e246d55541833101926e17186ce55b6484e2fb20e6f57735ee49ddfe0b6890
6
+ metadata.gz: 582d9176beb3f3b9a016f6d44b90b9d781066230f4c64dec93ba5d645ae54df8f7f6f1a5dcaab20899390cd048050eaed6db9b45fdcf44c55a57098013855aa9
7
+ data.tar.gz: ab9b30b770c677778cc4871b249a0967177805e543bd8576077099be7d3a5d93e2ef33c35362326cd7f889cd1f855d956c2cb9bd992560029e152c6293720a94
data/Rakefile CHANGED
@@ -8,6 +8,31 @@ Rake::TestTask.new(:test) do |t|
8
8
  t.test_files = FileList["test/irb/test_*.rb"]
9
9
  end
10
10
 
11
+ # To make sure they have been correctly setup for Ruby CI.
12
+ desc "Run each irb test file in isolation."
13
+ task :test_in_isolation do
14
+ failed = false
15
+
16
+ FileList["test/irb/test_*.rb"].each do |test_file|
17
+ ENV["TEST"] = test_file
18
+ begin
19
+ Rake::Task["test"].execute
20
+ rescue => e
21
+ failed = true
22
+ msg = "Test '#{test_file}' failed when being executed in isolation. Please make sure 'rake test TEST=#{test_file}' passes."
23
+ separation_line = '=' * msg.length
24
+
25
+ puts <<~MSG
26
+ #{separation_line}
27
+ #{msg}
28
+ #{separation_line}
29
+ MSG
30
+ end
31
+ end
32
+
33
+ fail "Some tests failed when being executed in isolation" if failed
34
+ end
35
+
11
36
  Rake::TestTask.new(:test_yamatanooroti) do |t|
12
37
  t.libs << 'test' << "test/lib"
13
38
  t.libs << 'lib'
data/irb.gemspec CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
35
  spec.require_paths = ["lib"]
36
36
 
37
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
37
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6")
38
38
 
39
39
  spec.add_dependency "reline", ">= 0.3.0"
40
40
  end
data/lib/irb/cmd/ls.rb CHANGED
@@ -9,6 +9,15 @@ module IRB
9
9
 
10
10
  module ExtendCommand
11
11
  class Ls < Nop
12
+ def self.transform_args(args)
13
+ if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
14
+ args = match[:args]
15
+ "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
16
+ else
17
+ args
18
+ end
19
+ end
20
+
12
21
  def execute(*arg, grep: nil)
13
22
  o = Output.new(grep: grep)
14
23
 
@@ -9,6 +9,24 @@ module IRB
9
9
 
10
10
  module ExtendCommand
11
11
  class ShowSource < Nop
12
+ class << self
13
+ def transform_args(args)
14
+ # Return a string literal as is for backward compatibility
15
+ if args.empty? || string_literal?(args)
16
+ args
17
+ else # Otherwise, consider the input as a String for convenience
18
+ args.strip.dump
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def string_literal?(args)
25
+ sexp = Ripper.sexp(args)
26
+ sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
27
+ end
28
+ end
29
+
12
30
  def execute(str = nil)
13
31
  unless str.is_a?(String)
14
32
  puts "Error: Expected a string but got #{str.inspect}"
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 (ex: @, $)
490
+ command, args = line.split(/\s/, 2)
491
+ if original = symbol_alias(command)
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,15 @@ 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 a command name 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[command.to_sym]
552
+ end
521
553
  end
522
554
  end
@@ -73,7 +73,7 @@ module IRB
73
73
  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
@@ -147,6 +147,20 @@ module IRB # :nodoc:
147
147
 
148
148
  ]
149
149
 
150
+ # Convert a command name to its implementation class if such command exists
151
+ def self.load_command(command)
152
+ command = command.to_sym
153
+ @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
154
+ next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command }
155
+
156
+ if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
157
+ require_relative load_file
158
+ end
159
+ return ExtendCommand.const_get(cmd_class, false)
160
+ end
161
+ nil
162
+ end
163
+
150
164
  # Installs the default irb commands:
151
165
  #
152
166
  # +irb_current_working_workspace+:: Context#main
@@ -162,6 +176,11 @@ module IRB # :nodoc:
162
176
  # +irb_fg+:: JobManager#switch
163
177
  # +irb_kill+:: JobManager#kill
164
178
  # +irb_help+:: IRB@Command+line+options
179
+ # +irb_info+:: #inspect
180
+ # +irb_ls+:: Output#dump
181
+ # +irb_measure+:: IRB::unset_measure_callback
182
+ # +irb_show_source+:: #find_source, #show_source
183
+ # +irb_whereami+:: Workspace#code_around_binding
165
184
  def self.install_extend_commands
166
185
  for args in @EXTEND_COMMANDS
167
186
  def_extend_command(*args)
data/lib/irb/init.rb CHANGED
@@ -44,7 +44,7 @@ module IRB # :nodoc:
44
44
  @CONF[:IRB_RC] = nil
45
45
 
46
46
  @CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
47
- @CONF[:USE_COLORIZE] = !ENV['NO_COLOR']
47
+ @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
48
48
  @CONF[:USE_AUTOCOMPLETE] = true
49
49
  @CONF[:INSPECT_MODE] = true
50
50
  @CONF[:USE_TRACER] = false
@@ -158,6 +158,11 @@ module IRB # :nodoc:
158
158
  @CONF[:LC_MESSAGES] = Locale.new
159
159
 
160
160
  @CONF[:AT_EXIT] = []
161
+
162
+ @CONF[:COMMAND_ALIASES] = {
163
+ :'$' => :show_source,
164
+ :'@' => :whereami,
165
+ }
161
166
  end
162
167
 
163
168
  def IRB.set_measure_callback(type = nil, arg = nil, &block)
@@ -255,8 +260,20 @@ module IRB # :nodoc:
255
260
  when "--nosingleline", "--noreadline"
256
261
  @CONF[:USE_SINGLELINE] = false
257
262
  when "--multiline", "--reidline"
263
+ if opt == "--reidline"
264
+ warn <<~MSG.strip
265
+ --reidline is deprecated, please use --multiline instead.
266
+ MSG
267
+ end
268
+
258
269
  @CONF[:USE_MULTILINE] = true
259
270
  when "--nomultiline", "--noreidline"
271
+ if opt == "--noreidline"
272
+ warn <<~MSG.strip
273
+ --noreidline is deprecated, please use --nomultiline instead.
274
+ MSG
275
+ end
276
+
260
277
  @CONF[:USE_MULTILINE] = false
261
278
  when /^--extra-doc-dir(?:=(.+))?/
262
279
  opt = $1 || argv.shift
@@ -379,11 +396,9 @@ module IRB # :nodoc:
379
396
  end
380
397
  if xdg_config_home = ENV["XDG_CONFIG_HOME"]
381
398
  irb_home = File.join(xdg_config_home, "irb")
382
- unless File.exist? irb_home
383
- require 'fileutils'
384
- FileUtils.mkdir_p irb_home
399
+ if File.directory?(irb_home)
400
+ yield proc{|rc| irb_home + "/irb#{rc}"}
385
401
  end
386
- yield proc{|rc| irb_home + "/irb#{rc}"}
387
402
  end
388
403
  if home = ENV["HOME"]
389
404
  yield proc{|rc| home+"/.irb#{rc}"}
@@ -286,7 +286,8 @@ module IRB
286
286
  if IRB.conf[:USE_COLORIZE]
287
287
  proc do |output, complete: |
288
288
  next unless IRB::Color.colorable?
289
- IRB::Color.colorize_code(output, complete: complete)
289
+ lvars = IRB.CurrentContext&.local_variables || []
290
+ IRB::Color.colorize_code(output, complete: complete, local_variables: lvars)
290
291
  end
291
292
  else
292
293
  proc do |output|
@@ -295,8 +296,13 @@ module IRB
295
296
  end
296
297
  Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
297
298
  Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
299
+
298
300
  if IRB.conf[:USE_AUTOCOMPLETE]
299
- Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
301
+ begin
302
+ require 'rdoc'
303
+ Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
304
+ rescue LoadError
305
+ end
300
306
  end
301
307
  end
302
308
 
@@ -320,11 +326,6 @@ module IRB
320
326
  [195, 164], # The "ä" that appears when Alt+d is pressed on xterm.
321
327
  [226, 136, 130] # The "∂" that appears when Alt+d in pressed on iTerm2.
322
328
  ]
323
- begin
324
- require 'rdoc'
325
- rescue LoadError
326
- return nil
327
- end
328
329
 
329
330
  if just_cursor_moving and completion_journey_data.nil?
330
331
  return nil
@@ -460,7 +461,7 @@ module IRB
460
461
  # For debug message
461
462
  def inspect
462
463
  config = Reline::Config.new
463
- str = "ReidlineInputMethod with Reline #{Reline::VERSION}"
464
+ str = "RelineInputMethod with Reline #{Reline::VERSION}"
464
465
  if config.respond_to?(:inputrc_path)
465
466
  inputrc_path = File.expand_path(config.inputrc_path)
466
467
  else
data/lib/irb/ruby-lex.rb CHANGED
@@ -48,7 +48,7 @@ class RubyLex
48
48
  end
49
49
 
50
50
  # io functions
51
- def set_input(io, p = nil, context: nil, &block)
51
+ def set_input(io, p = nil, context:, &block)
52
52
  @io = io
53
53
  if @io.respond_to?(:check_termination)
54
54
  @io.check_termination do |code|
@@ -65,6 +65,12 @@ class RubyLex
65
65
  false
66
66
  end
67
67
  else
68
+ # Accept any single-line input starting with a non-identifier alias (ex: @, $)
69
+ command = code.split(/\s/, 2).first
70
+ if context.symbol_alias(command)
71
+ next true
72
+ end
73
+
68
74
  code.gsub!(/\s*\z/, '').concat("\n")
69
75
  ltype, indent, continue, code_block_open = check_state(code, context: context)
70
76
  if ltype or indent > 0 or continue or code_block_open
@@ -136,16 +142,18 @@ class RubyLex
136
142
  :on_param_error
137
143
  ]
138
144
 
145
+ def self.generate_local_variables_assign_code(local_variables)
146
+ "#{local_variables.join('=')}=nil;" unless local_variables.empty?
147
+ end
148
+
139
149
  def self.ripper_lex_without_warning(code, context: nil)
140
150
  verbose, $VERBOSE = $VERBOSE, nil
141
- if context
142
- lvars = context.workspace&.binding&.local_variables
143
- if lvars && !lvars.empty?
144
- code = "#{lvars.join('=')}=nil\n#{code}"
145
- line_no = 0
146
- else
147
- line_no = 1
148
- end
151
+ lvars_code = generate_local_variables_assign_code(context&.local_variables || [])
152
+ if lvars_code
153
+ code = "#{lvars_code}\n#{code}"
154
+ line_no = 0
155
+ else
156
+ line_no = 1
149
157
  end
150
158
 
151
159
  compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
@@ -162,7 +170,7 @@ class RubyLex
162
170
  end
163
171
  end
164
172
  else
165
- lexer.parse.reject { |it| it.pos.first == 0 }
173
+ lexer.parse.reject { |it| it.pos.first == 0 }.sort_by(&:pos)
166
174
  end
167
175
  end
168
176
  ensure
@@ -214,6 +222,8 @@ class RubyLex
214
222
  ltype = process_literal_type(tokens)
215
223
  indent = process_nesting_level(tokens)
216
224
  continue = process_continue(tokens)
225
+ lvars_code = self.class.generate_local_variables_assign_code(context.local_variables)
226
+ code = "#{lvars_code}\n#{code}" if lvars_code
217
227
  code_block_open = check_code_block(code, tokens)
218
228
  [ltype, indent, continue, code_block_open]
219
229
  end
@@ -233,13 +243,13 @@ class RubyLex
233
243
  @code_block_open = false
234
244
  end
235
245
 
236
- def each_top_level_statement
246
+ def each_top_level_statement(context)
237
247
  initialize_input
238
248
  catch(:TERM_INPUT) do
239
249
  loop do
240
250
  begin
241
251
  prompt
242
- unless l = lex
252
+ unless l = lex(context)
243
253
  throw :TERM_INPUT if @line == ''
244
254
  else
245
255
  @line_no += l.count("\n")
@@ -269,18 +279,15 @@ class RubyLex
269
279
  end
270
280
  end
271
281
 
272
- def lex
282
+ def lex(context)
273
283
  line = @input.call
274
284
  if @io.respond_to?(:check_termination)
275
285
  return line # multiline
276
286
  end
277
287
  code = @line + (line.nil? ? '' : line)
278
288
  code.gsub!(/\s*\z/, '').concat("\n")
279
- @tokens = self.class.ripper_lex_without_warning(code)
280
- @continue = process_continue
281
- @code_block_open = check_code_block(code)
282
- @indent = process_nesting_level
283
- @ltype = process_literal_type
289
+ @tokens = self.class.ripper_lex_without_warning(code, context: context)
290
+ @ltype, @indent, @continue, @code_block_open = check_state(code, @tokens, context: context)
284
291
  line
285
292
  end
286
293
 
@@ -706,6 +713,7 @@ class RubyLex
706
713
  i = 0
707
714
  start_token = []
708
715
  end_type = []
716
+ pending_heredocs = []
709
717
  while i < tokens.size
710
718
  t = tokens[i]
711
719
  case t.event
@@ -729,18 +737,27 @@ class RubyLex
729
737
  end
730
738
  end
731
739
  when :on_backtick
732
- start_token << t
733
- end_type << :on_tstring_end
740
+ if t.state.allbits?(Ripper::EXPR_BEG)
741
+ start_token << t
742
+ end_type << :on_tstring_end
743
+ end
734
744
  when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
735
745
  start_token << t
736
746
  end_type << :on_tstring_end
737
747
  when :on_heredoc_beg
738
- start_token << t
739
- end_type << :on_heredoc_end
748
+ pending_heredocs << t
749
+ end
750
+
751
+ if pending_heredocs.any? && t.tok.include?("\n")
752
+ pending_heredocs.reverse_each do |t|
753
+ start_token << t
754
+ end_type << :on_heredoc_end
755
+ end
756
+ pending_heredocs = []
740
757
  end
741
758
  i += 1
742
759
  end
743
- start_token.last.nil? ? nil : start_token.last
760
+ pending_heredocs.first || start_token.last
744
761
  end
745
762
 
746
763
  def process_literal_type(tokens = @tokens)
data/lib/irb/version.rb CHANGED
@@ -11,7 +11,7 @@
11
11
  #
12
12
 
13
13
  module IRB # :nodoc:
14
- VERSION = "1.4.2"
14
+ VERSION = "1.4.3"
15
15
  @RELEASE_VERSION = VERSION
16
- @LAST_UPDATE_DATE = "2022-10-03"
16
+ @LAST_UPDATE_DATE = "2022-11-17"
17
17
  end
data/lib/irb.rb CHANGED
@@ -426,6 +426,10 @@ module IRB
426
426
  def initialize(workspace = nil, input_method = nil)
427
427
  @context = Context.new(self, workspace, input_method)
428
428
  @context.main.extend ExtendCommandBundle
429
+ @context.command_aliases.each do |alias_name, cmd_name|
430
+ next if @context.symbol_alias(alias_name)
431
+ @context.main.install_alias_method(alias_name, cmd_name)
432
+ end
429
433
  @signal_status = :IN_IRB
430
434
  @scanner = RubyLex.new
431
435
  end
@@ -506,13 +510,15 @@ module IRB
506
510
 
507
511
  @scanner.set_auto_indent(@context) if @context.auto_indent_mode
508
512
 
509
- @scanner.each_top_level_statement do |line, line_no|
513
+ @scanner.each_top_level_statement(@context) do |line, line_no|
510
514
  signal_status(:IN_EVAL) do
511
515
  begin
512
516
  line.untaint if RUBY_VERSION < '2.7'
513
517
  if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
514
518
  IRB.set_measure_callback
515
519
  end
520
+ # Assignment expression check should be done before @context.evaluate to handle code like `a /2#/ if false; a = 1`
521
+ is_assignment = assignment_expression?(line)
516
522
  if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
517
523
  result = nil
518
524
  last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) }
@@ -529,7 +535,7 @@ module IRB
529
535
  @context.evaluate(line, line_no, exception: exc)
530
536
  end
531
537
  if @context.echo?
532
- if assignment_expression?(line)
538
+ if is_assignment
533
539
  if @context.echo_on_assignment?
534
540
  output_value(@context.echo_on_assignment? == :truncate)
535
541
  end
@@ -592,11 +598,7 @@ module IRB
592
598
 
593
599
  if exc.backtrace
594
600
  order = nil
595
- if '2.5.0' == RUBY_VERSION
596
- # Exception#full_message doesn't have keyword arguments.
597
- message = exc.full_message # the same of (highlight: true, order: bottom)
598
- order = :bottom
599
- elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
601
+ if RUBY_VERSION < '3.0.0'
600
602
  if STDOUT.tty?
601
603
  message = exc.full_message(order: :bottom)
602
604
  order = :bottom
@@ -827,9 +829,12 @@ module IRB
827
829
  # array of parsed expressions. The first element of each expression is the
828
830
  # expression's type.
829
831
  verbose, $VERBOSE = $VERBOSE, nil
830
- result = ASSIGNMENT_NODE_TYPES.include?(Ripper.sexp(line)&.dig(1,-1,0))
832
+ code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}"
833
+ # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
834
+ node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
835
+ ASSIGNMENT_NODE_TYPES.include?(node_type)
836
+ ensure
831
837
  $VERBOSE = verbose
832
- result
833
838
  end
834
839
 
835
840
  ATTR_TTY = "\e[%sm"
data/man/irb.1 CHANGED
@@ -173,8 +173,19 @@ The default value is 16.
173
173
  .El
174
174
  .Pp
175
175
  .Sh ENVIRONMENT
176
- .Bl -tag -compact
176
+ .Bl -tag -compact -width "XDG_CONFIG_HOME"
177
+ .It Ev IRB_LANG
178
+ The locale used for
179
+ .Nm .
180
+ .Pp
177
181
  .It Ev IRBRC
182
+ The path to the personal initialization file.
183
+ .Pp
184
+ .It Ev XDG_CONFIG_HOME
185
+ .Nm
186
+ respects XDG_CONFIG_HOME. If this is set, load
187
+ .Pa $XDG_CONFIG_HOME/irb/irbrc
188
+ as a personal initialization file.
178
189
  .Pp
179
190
  .El
180
191
  .Pp
@@ -186,7 +197,17 @@ depends on same variables as
186
197
  .Sh FILES
187
198
  .Bl -tag -compact
188
199
  .It Pa ~/.irbrc
189
- Personal irb initialization.
200
+ Personal irb initialization. If
201
+ .Ev IRBRC
202
+ is set, read
203
+ .Pa $IRBRC
204
+ instead. If
205
+ .Ev IRBRC
206
+ is not set and
207
+ .Ev XDG_CONFIG_HOME
208
+ is set,
209
+ .Pa $XDG_CONFIG_HOME/irb/irbrc
210
+ is loaded.
190
211
  .Pp
191
212
  .El
192
213
  .Pp
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-10-05 00:00:00.000000000 Z
12
+ date: 2022-11-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: reline
@@ -107,7 +107,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '2.5'
110
+ version: '2.6'
111
111
  required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - ">="