irb 1.15.2 → 1.15.3

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.
@@ -12,26 +12,6 @@ module IRB
12
12
 
13
13
  # Set of reserved words used by Ruby, you should not use these for
14
14
  # constants or variables
15
- ReservedWords = %w[
16
- __ENCODING__ __LINE__ __FILE__
17
- BEGIN END
18
- alias and
19
- begin break
20
- case class
21
- def defined? do
22
- else elsif end ensure
23
- false for
24
- if in
25
- module
26
- next nil not
27
- or
28
- redo rescue retry return
29
- self super
30
- then true
31
- undef unless until
32
- when while
33
- yield
34
- ]
35
15
 
36
16
  HELP_COMMAND_PREPOSING = /\Ahelp\s+/
37
17
 
@@ -60,13 +40,13 @@ module IRB
60
40
 
61
41
  def retrieve_gem_and_system_load_path
62
42
  candidates = (GEM_PATHS | $LOAD_PATH)
63
- candidates.map do |p|
43
+ candidates.filter_map do |p|
64
44
  if p.respond_to?(:to_path)
65
45
  p.to_path
66
46
  else
67
47
  String(p) rescue nil
68
48
  end
69
- end.compact.sort
49
+ end.sort
70
50
  end
71
51
 
72
52
  def retrieve_files_to_require_from_load_path
@@ -127,7 +107,14 @@ module IRB
127
107
 
128
108
  return commands unless result
129
109
 
130
- commands | result.completion_candidates.map { target + _1 }
110
+ encoded_candidates = result.completion_candidates.filter_map do |i|
111
+ encoded = i.encode(Encoding.default_external)
112
+ target + encoded
113
+ rescue Encoding::UndefinedConversionError
114
+ # If the string cannot be converted, we just ignore it
115
+ nil
116
+ end
117
+ commands | encoded_candidates
131
118
  end
132
119
 
133
120
  def doc_namespace(preposing, matched, _postposing, bind:)
@@ -179,7 +166,7 @@ module IRB
179
166
  else
180
167
  return nil # It's not String literal
181
168
  end
182
- tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
169
+ tokens = RubyLex.ripper_lex_without_warning(preposing.rstrip)
183
170
  tok = nil
184
171
  tokens.reverse_each do |t|
185
172
  unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
@@ -191,16 +178,12 @@ module IRB
191
178
 
192
179
  case tok.tok
193
180
  when 'require'
194
- retrieve_files_to_require_from_load_path.select { |path|
195
- path.start_with?(actual_target)
196
- }.map { |path|
197
- quote + path
181
+ retrieve_files_to_require_from_load_path.filter_map { |path|
182
+ quote + path if path.start_with?(actual_target)
198
183
  }
199
184
  when 'require_relative'
200
- retrieve_files_to_require_relative_from_current_dir.select { |path|
201
- path.start_with?(actual_target)
202
- }.map { |path|
203
- quote + path
185
+ retrieve_files_to_require_relative_from_current_dir.filter_map { |path|
186
+ quote + path if path.start_with?(actual_target)
204
187
  }
205
188
  end
206
189
  end
@@ -218,7 +201,12 @@ module IRB
218
201
  # It doesn't make sense to propose commands with other preposing
219
202
  commands = [] unless preposing.empty?
220
203
 
221
- completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
204
+ completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.filter_map do |i|
205
+ i.encode(Encoding.default_external)
206
+ rescue Encoding::UndefinedConversionError
207
+ # If the string cannot be converted, we just ignore it
208
+ nil
209
+ end
222
210
  commands | completion_data
223
211
  end
224
212
 
@@ -287,7 +275,7 @@ module IRB
287
275
  nil
288
276
  else
289
277
  sym = $1
290
- candidates = Symbol.all_symbols.collect do |s|
278
+ candidates = Symbol.all_symbols.filter_map do |s|
291
279
  s.inspect
292
280
  rescue EncodingError
293
281
  # ignore
@@ -459,12 +447,19 @@ module IRB
459
447
  eval("#{perfect_match_var}.class.name", bind) rescue nil
460
448
  else
461
449
  candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
462
- candidates |= ReservedWords
450
+ candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
463
451
  candidates.find{ |i| i == input }
464
452
  end
465
453
  else
466
454
  candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
467
- candidates |= ReservedWords
455
+ candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
456
+
457
+ target_encoding = Encoding.default_external
458
+ candidates = candidates.compact.filter_map do |candidate|
459
+ candidate.encoding == target_encoding ? candidate : candidate.encode(target_encoding)
460
+ rescue EncodingError
461
+ nil
462
+ end
468
463
  candidates.grep(/^#{Regexp.quote(input)}/).sort
469
464
  end
470
465
  end
data/lib/irb/debug.rb CHANGED
@@ -49,7 +49,7 @@ module IRB
49
49
  def DEBUGGER__.capture_frames(*args)
50
50
  frames = capture_frames_without_irb(*args)
51
51
  frames.reject! do |frame|
52
- frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
52
+ frame.realpath&.start_with?(IRB_DIR) || frame.path.start_with?("<internal:")
53
53
  end
54
54
  frames
55
55
  end
@@ -60,7 +60,7 @@ module IRB
60
60
  if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod)
61
61
  Reline.output_modifier_proc = proc do |input, complete:|
62
62
  unless input.strip.empty?
63
- cmd = input.split(/\s/, 2).first
63
+ cmd = input[/\S+/]
64
64
 
65
65
  if !complete && DEBUGGER__.commands.key?(cmd)
66
66
  input = input.sub(/\n$/, " # debug command\n")
@@ -121,12 +121,13 @@ module IRB
121
121
  interrupted = false
122
122
  prev_trap = trap("SIGINT") { interrupted = true }
123
123
  canvas = Canvas.new(Reline.get_screen_size)
124
+ otio = Reline::IOGate.prep
124
125
  Reline::IOGate.set_winch_handler do
125
126
  canvas = Canvas.new(Reline.get_screen_size)
126
127
  end
127
128
  ruby_model = RubyModel.new
128
129
  print "\e[?25l" # hide cursor
129
- 0.step do |i| # TODO (0..).each needs Ruby 2.6 or later
130
+ (0..).each do |i|
130
131
  buff = canvas.draw do
131
132
  ruby_model.render_frame(i) do |p1, p2|
132
133
  canvas.line(p1, p2)
@@ -139,6 +140,7 @@ module IRB
139
140
  end
140
141
  rescue Interrupt
141
142
  ensure
143
+ Reline::IOGate.deprep(otio)
142
144
  print "\e[?25h" # show cursor
143
145
  trap("SIGINT", prev_trap)
144
146
  end
@@ -223,7 +223,7 @@ module IRB
223
223
  Readline.input = @stdin
224
224
  Readline.output = @stdout
225
225
  if l = Readline.readline(@prompt, false)
226
- Readline::HISTORY.push(l) if !l.empty?
226
+ Readline::HISTORY.push(l) if !l.empty? && l != Readline::HISTORY.to_a.last
227
227
  @line[@line_no += 1] = l + "\n"
228
228
  else
229
229
  @eof = true
@@ -480,7 +480,7 @@ module IRB
480
480
  Reline.prompt_proc = @prompt_proc
481
481
  Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
482
482
  if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
483
- Reline::HISTORY.push(l) if !l.empty?
483
+ Reline::HISTORY.push(l) if !l.empty? && l != Reline::HISTORY.to_a.last
484
484
  @line[@line_no += 1] = l + "\n"
485
485
  else
486
486
  @eof = true
data/lib/irb/inspector.rb CHANGED
@@ -46,7 +46,7 @@ module IRB # :nodoc:
46
46
  # Determines the inspector to use where +inspector+ is one of the keys passed
47
47
  # during inspector definition.
48
48
  def keys_with_inspector(inspector)
49
- INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
49
+ INSPECTORS.filter_map {|k, v| k if v == inspector}
50
50
  end
51
51
 
52
52
  # Example
data/lib/irb/ruby-lex.rb CHANGED
@@ -52,6 +52,27 @@ module IRB
52
52
  on_words_beg on_qwords_beg
53
53
  ]
54
54
 
55
+ RESERVED_WORDS = %i[
56
+ __ENCODING__ __LINE__ __FILE__
57
+ BEGIN END
58
+ alias and
59
+ begin break
60
+ case class
61
+ def defined? do
62
+ else elsif end ensure
63
+ false for
64
+ if in
65
+ module
66
+ next nil not
67
+ or
68
+ redo rescue retry return
69
+ self super
70
+ then true
71
+ undef unless until
72
+ when while
73
+ yield
74
+ ]
75
+
55
76
  class TerminateLineInput < StandardError
56
77
  def initialize
57
78
  super("Terminate Line Input")
@@ -77,6 +98,10 @@ module IRB
77
98
  end
78
99
 
79
100
  def generate_local_variables_assign_code(local_variables)
101
+ # Some reserved words could be a local variable
102
+ # Example: def f(if: 1); binding.irb; end
103
+ # These reserved words should be removed from assignment code
104
+ local_variables -= RESERVED_WORDS
80
105
  "#{local_variables.join('=')}=nil;" unless local_variables.empty?
81
106
  end
82
107
 
@@ -114,7 +114,7 @@ module IRB
114
114
  when "owner"
115
115
  target_method = owner_receiver.instance_method(method)
116
116
  when "receiver"
117
- target_method = owner_receiver.method(method)
117
+ target_method = Kernel.instance_method(:method).bind_call(owner_receiver, method)
118
118
  end
119
119
  super_level.times do |s|
120
120
  target_method = target_method.super_method if target_method
data/lib/irb/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
- VERSION = "1.15.2"
8
+ VERSION = "1.15.3"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2025-04-03"
10
+ @LAST_UPDATE_DATE = "2025-11-01"
11
11
  end
data/lib/irb.rb CHANGED
@@ -87,6 +87,7 @@ module IRB
87
87
  # Creates a new irb session
88
88
  def initialize(workspace = nil, input_method = nil, from_binding: false)
89
89
  @from_binding = from_binding
90
+ @prompt_part_cache = nil
90
91
  @context = Context.new(self, workspace, input_method)
91
92
  @context.workspace.load_helper_methods_to_main
92
93
  @signal_status = :IN_IRB
@@ -238,13 +239,7 @@ module IRB
238
239
  end
239
240
  end
240
241
 
241
- def readmultiline
242
- prompt = generate_prompt([], false, 0)
243
-
244
- # multiline
245
- return read_input(prompt) if @context.io.respond_to?(:check_termination)
246
-
247
- # nomultiline
242
+ def read_input_nomultiline(prompt)
248
243
  code = +''
249
244
  line_offset = 0
250
245
  loop do
@@ -265,6 +260,20 @@ module IRB
265
260
  end
266
261
  end
267
262
 
263
+ def readmultiline
264
+ with_prompt_part_cached do
265
+ prompt = generate_prompt([], false, 0)
266
+
267
+ if @context.io.respond_to?(:check_termination)
268
+ # multiline
269
+ read_input(prompt)
270
+ else
271
+ # nomultiline
272
+ read_input_nomultiline(prompt)
273
+ end
274
+ end
275
+ end
276
+
268
277
  def each_top_level_statement
269
278
  loop do
270
279
  code = readmultiline
@@ -567,6 +576,13 @@ module IRB
567
576
 
568
577
  private
569
578
 
579
+ def with_prompt_part_cached
580
+ @prompt_part_cache = {}
581
+ yield
582
+ ensure
583
+ @prompt_part_cache = nil
584
+ end
585
+
570
586
  def generate_prompt(opens, continue, line_offset)
571
587
  ltype = @scanner.ltype_from_open_tokens(opens)
572
588
  indent = @scanner.calc_indent_level(opens)
@@ -598,25 +614,29 @@ module IRB
598
614
  end
599
615
 
600
616
  def truncate_prompt_main(str) # :nodoc:
601
- str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
602
- if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
603
- str
604
- else
605
- str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
617
+ if str.size > PROMPT_MAIN_TRUNCATE_LENGTH
618
+ str = str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
606
619
  end
620
+ str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
607
621
  end
608
622
 
609
623
  def format_prompt(format, ltype, indent, line_no) # :nodoc:
624
+ # @prompt_part_cache could be nil in unit tests
625
+ part_cache = @prompt_part_cache || {}
610
626
  format.gsub(/%([0-9]+)?([a-zA-Z%])/) do
611
627
  case $2
612
628
  when "N"
613
629
  @context.irb_name
614
630
  when "m"
615
- main_str = @context.safe_method_call_on_main(:to_s) rescue "!#{$!.class}"
616
- truncate_prompt_main(main_str)
631
+ part_cache[:m] ||= (
632
+ main_str = "#{@context.safe_method_call_on_main(:to_s)}" rescue "!#{$!.class}"
633
+ truncate_prompt_main(main_str)
634
+ )
617
635
  when "M"
618
- main_str = @context.safe_method_call_on_main(:inspect) rescue "!#{$!.class}"
619
- truncate_prompt_main(main_str)
636
+ part_cache[:M] ||= (
637
+ main_str = "#{@context.safe_method_call_on_main(:inspect)}" rescue "!#{$!.class}"
638
+ truncate_prompt_main(main_str)
639
+ )
620
640
  when "l"
621
641
  ltype
622
642
  when "i"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.2
4
+ version: 1.15.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  - Keiju ISHITSUKA
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-03 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reline
@@ -61,16 +61,20 @@ executables:
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
+ - ".rdoc_options"
65
+ - CONTRIBUTING.md
66
+ - EXTEND_IRB.md
64
67
  - Gemfile
65
68
  - LICENSE.txt
66
69
  - README.md
67
- - Rakefile
68
- - bin/console
69
- - bin/setup
70
+ - doc/COMMAND_LINE_OPTIONS.md
71
+ - doc/COMPARED_WITH_PRY.md
72
+ - doc/Configurations.md
73
+ - doc/EXTEND_IRB.md
74
+ - doc/Index.md
70
75
  - doc/irb/irb-tools.rd.ja
71
76
  - doc/irb/irb.rd.ja
72
77
  - exe/irb
73
- - irb.gemspec
74
78
  - lib/irb.rb
75
79
  - lib/irb/cmd/nop.rb
76
80
  - lib/irb/color.rb
@@ -170,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
174
  - !ruby/object:Gem::Version
171
175
  version: '0'
172
176
  requirements: []
173
- rubygems_version: 3.6.3
177
+ rubygems_version: 3.6.7
174
178
  specification_version: 4
175
179
  summary: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).
176
180
  test_files: []
data/Rakefile DELETED
@@ -1,52 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
- require "rdoc/task"
4
-
5
- Rake::TestTask.new(:test) do |t|
6
- t.libs << "test" << "test/lib"
7
- t.libs << "lib"
8
- t.ruby_opts << "-rhelper"
9
- t.test_files = FileList["test/irb/**/test_*.rb"]
10
- end
11
-
12
- # To make sure they have been correctly setup for Ruby CI.
13
- desc "Run each irb test file in isolation."
14
- task :test_in_isolation do
15
- failed = false
16
-
17
- FileList["test/irb/**/test_*.rb"].each do |test_file|
18
- ENV["TEST"] = test_file
19
- begin
20
- Rake::Task["test"].execute
21
- rescue
22
- failed = true
23
- msg = "Test '#{test_file}' failed when being executed in isolation. Please make sure 'rake test TEST=#{test_file}' passes."
24
- separation_line = '=' * msg.length
25
-
26
- puts <<~MSG
27
- #{separation_line}
28
- #{msg}
29
- #{separation_line}
30
- MSG
31
- end
32
- end
33
-
34
- fail "Some tests failed when being executed in isolation" if failed
35
- end
36
-
37
- Rake::TestTask.new(:test_yamatanooroti) do |t|
38
- t.libs << 'test' << "test/lib"
39
- t.libs << 'lib'
40
- #t.loader = :direct
41
- t.ruby_opts << "-rhelper"
42
- t.pattern = 'test/irb/yamatanooroti/test_*.rb'
43
- end
44
-
45
- task :default => :test
46
-
47
- RDoc::Task.new do |rdoc|
48
- rdoc.title = "IRB Documentation"
49
- rdoc.main = "Index.md"
50
- rdoc.rdoc_dir = "_site"
51
- rdoc.options.push("lib")
52
- end
data/bin/console DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require_relative "../lib/irb"
5
-
6
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
data/irb.gemspec DELETED
@@ -1,48 +0,0 @@
1
- begin
2
- require_relative "lib/irb/version"
3
- rescue LoadError
4
- # for Ruby core repository
5
- require_relative "version"
6
- end
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = "irb"
10
- spec.version = IRB::VERSION
11
- spec.authors = ["aycabta", "Keiju ISHITSUKA"]
12
- spec.email = ["aycabta@gmail.com", "keiju@ruby-lang.org"]
13
-
14
- spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
15
- spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
16
- spec.homepage = "https://github.com/ruby/irb"
17
- spec.licenses = ["Ruby", "BSD-2-Clause"]
18
-
19
- spec.metadata["homepage_uri"] = spec.homepage
20
- spec.metadata["source_code_uri"] = spec.homepage
21
- spec.metadata["documentation_uri"] = "https://ruby.github.io/irb/"
22
- spec.metadata["changelog_uri"] = "#{spec.homepage}/releases"
23
-
24
- spec.files = [
25
- "Gemfile",
26
- "LICENSE.txt",
27
- "README.md",
28
- "Rakefile",
29
- "bin/console",
30
- "bin/setup",
31
- "doc/irb/irb-tools.rd.ja",
32
- "doc/irb/irb.rd.ja",
33
- "exe/irb",
34
- "irb.gemspec",
35
- "man/irb.1",
36
- ] + Dir.chdir(File.expand_path('..', __FILE__)) do
37
- Dir.glob("lib/**/*").map {|f| f unless File.directory?(f) }.compact
38
- end
39
- spec.bindir = "exe"
40
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
41
- spec.require_paths = ["lib"]
42
-
43
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
44
-
45
- spec.add_dependency "reline", ">= 0.4.2"
46
- spec.add_dependency "rdoc", ">= 4.0.0"
47
- spec.add_dependency "pp", ">= 0.6.0"
48
- end