irb 1.14.3 → 1.16.0

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.
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
@@ -92,9 +92,14 @@ module IRB # :nodoc:
92
92
  @init.call if @init
93
93
  end
94
94
 
95
+ def support_stream_output?
96
+ second_parameter_type = @inspect.parameters[1]&.first
97
+ second_parameter_type == :req || second_parameter_type == :opt
98
+ end
99
+
95
100
  # Proc to call when the input is evaluated and output in irb.
96
- def inspect_value(v)
97
- @inspect.call(v)
101
+ def inspect_value(v, output, colorize: true)
102
+ support_stream_output? ? @inspect.call(v, output, colorize: colorize) : output << @inspect.call(v, colorize: colorize)
98
103
  rescue => e
99
104
  puts "An error occurred when inspecting the object: #{e.inspect}"
100
105
 
@@ -110,11 +115,11 @@ module IRB # :nodoc:
110
115
  end
111
116
 
112
117
  Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s}
113
- Inspector.def_inspector([:p, :inspect]){|v|
114
- Color.colorize_code(v.inspect, colorable: Color.colorable? && Color.inspect_colorable?(v))
118
+ Inspector.def_inspector([:p, :inspect]){|v, colorize: true|
119
+ Color.colorize_code(v.inspect, colorable: colorize && Color.colorable? && Color.inspect_colorable?(v))
115
120
  }
116
- Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v|
117
- IRB::ColorPrinter.pp(v, +'').chomp
121
+ Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v, output, colorize: true|
122
+ IRB::ColorPrinter.pp(v, output, colorize: colorize)
118
123
  }
119
124
  Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
120
125
  begin
@@ -6,10 +6,10 @@ Usage: irb.rb [options] [programfile] [arguments]
6
6
  -U Set external and internal encodings to UTF-8.
7
7
  -E ex[:in] Set default external (ex) and internal (in) encodings
8
8
  (same as 'ruby -E').
9
- -w Suppress warnings (same as 'ruby -w').
9
+ -w Enable warnings (same as 'ruby -w' or 'ruby -W1').
10
10
  -W[level=2] Set warning level: 0=silence, 1=medium, 2=verbose
11
11
  (same as 'ruby -W').
12
- --context-mode n Set n[0-4] to method to create Binding Object,
12
+ --context-mode n Set n[0-5] to method to create Binding Object,
13
13
  when new workspace was created.
14
14
  --extra-doc-dir Add an extra doc dir for the doc dialog.
15
15
  --echo Show result (default).
@@ -8,7 +8,7 @@ Usage: irb.rb [options] [programfile] [arguments]
8
8
  -w ruby -w と同じ.
9
9
  -W[level=2] ruby -W と同じ.
10
10
  --context-mode n 新しいワークスペースを作成した時に関連する Binding
11
- オブジェクトの作成方法を 0 から 4 のいずれかに設定する.
11
+ オブジェクトの作成方法を 0 から 5 のいずれかに設定する.
12
12
  --extra-doc-dir 指定したディレクトリのドキュメントを追加で読み込む.
13
13
  --echo 実行結果を表示する(デフォルト).
14
14
  --noecho 実行結果を表示しない.
data/lib/irb/pager.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'reline'
4
+
3
5
  module IRB
4
6
  # The implementation of this class is borrowed from RDoc's lib/rdoc/ri/driver.rb.
5
7
  # Please do NOT use this class directly outside of IRB.
@@ -47,12 +49,42 @@ module IRB
47
49
  rescue Errno::EPIPE
48
50
  end
49
51
 
50
- private
51
-
52
52
  def should_page?
53
53
  IRB.conf[:USE_PAGER] && STDIN.tty? && (ENV.key?("TERM") && ENV["TERM"] != "dumb")
54
54
  end
55
55
 
56
+ def page_with_preview(width, height, formatter_proc)
57
+ overflow_callback = ->(lines) do
58
+ modified_output = formatter_proc.call(lines.join, true)
59
+ content, = take_first_page(width, [height - 2, 0].max) {|o| o.write modified_output }
60
+ content = content.chomp
61
+ content = "#{content}\e[0m" if Color.colorable?
62
+ $stdout.puts content
63
+ $stdout.puts 'Preparing full inspection value...'
64
+ end
65
+ out = PageOverflowIO.new(width, height, overflow_callback, delay: 0.1)
66
+ yield out
67
+ content = formatter_proc.call(out.string, out.multipage?)
68
+ if out.multipage?
69
+ page(retain_content: true) do |io|
70
+ io.puts content
71
+ end
72
+ else
73
+ $stdout.puts content
74
+ end
75
+ end
76
+
77
+ def take_first_page(width, height)
78
+ overflow_callback = proc do |lines|
79
+ return lines.join, true
80
+ end
81
+ out = Pager::PageOverflowIO.new(width, height, overflow_callback)
82
+ yield out
83
+ [out.string, false]
84
+ end
85
+
86
+ private
87
+
56
88
  def content_exceeds_screen_height?(content)
57
89
  screen_height, screen_width = begin
58
90
  Reline.get_screen_size
@@ -62,10 +94,10 @@ module IRB
62
94
 
63
95
  pageable_height = screen_height - 3 # leave some space for previous and the current prompt
64
96
 
65
- # If the content has more lines than the pageable height
66
- content.lines.count > pageable_height ||
67
- # Or if the content is a few long lines
68
- pageable_height * screen_width < Reline::Unicode.calculate_width(content, true)
97
+ return true if content.lines.size > pageable_height
98
+
99
+ _, overflow = take_first_page(screen_width, pageable_height) {|out| out.write content }
100
+ overflow
69
101
  end
70
102
 
71
103
  def setup_pager(retain_content:)
@@ -96,5 +128,86 @@ module IRB
96
128
  nil
97
129
  end
98
130
  end
131
+
132
+ # Writable IO that has page overflow callback
133
+ class PageOverflowIO
134
+ attr_reader :string, :first_page_lines
135
+
136
+ # Maximum size of a single cell in terminal
137
+ # Assumed worst case: "\e[1;3;4;9;38;2;255;128;128;48;2;128;128;255mA\e[0m"
138
+ # bold, italic, underline, crossed_out, RGB forgound, RGB background
139
+ MAX_CHAR_PER_CELL = 50
140
+
141
+ def initialize(width, height, overflow_callback, delay: nil)
142
+ @lines = []
143
+ @first_page_lines = nil
144
+ @width = width
145
+ @height = height
146
+ @buffer = +''
147
+ @overflow_callback = overflow_callback
148
+ @col = 0
149
+ @string = +''
150
+ @multipage = false
151
+ @delay_until = (Time.now + delay if delay)
152
+ end
153
+
154
+ def puts(text = '')
155
+ text = text.to_s unless text.is_a?(String)
156
+ write(text)
157
+ write("\n") unless text.end_with?("\n")
158
+ end
159
+
160
+ def write(text)
161
+ text = text.to_s unless text.is_a?(String)
162
+ @string << text
163
+ if @multipage
164
+ if @delay_until && Time.now > @delay_until
165
+ @overflow_callback.call(@first_page_lines)
166
+ @delay_until = nil
167
+ end
168
+ return
169
+ end
170
+
171
+ overflow_size = (@width * (@height - @lines.size) + @width - @col) * MAX_CHAR_PER_CELL
172
+ if text.size >= overflow_size
173
+ text = text[0, overflow_size]
174
+ overflow = true
175
+ end
176
+ @buffer << text
177
+ @col += Reline::Unicode.calculate_width(text, true)
178
+ if text.include?("\n") || @col >= @width
179
+ @buffer.lines.each do |line|
180
+ wrapped_lines = Reline::Unicode.split_by_width(line.chomp, @width).first.compact
181
+ wrapped_lines.pop if wrapped_lines.last == ''
182
+ @lines.concat(wrapped_lines)
183
+ if line.end_with?("\n")
184
+ if @lines.empty? || @lines.last.end_with?("\n")
185
+ @lines << "\n"
186
+ else
187
+ @lines[-1] += "\n"
188
+ end
189
+ end
190
+ end
191
+ @buffer.clear
192
+ @buffer << @lines.pop if !@lines.empty? && !@lines.last.end_with?("\n")
193
+ @col = Reline::Unicode.calculate_width(@buffer, true)
194
+ end
195
+ if overflow || @lines.size > @height || (@lines.size == @height && @col > 0)
196
+ @first_page_lines = @lines.take(@height)
197
+ if !@delay_until || Time.now > @delay_until
198
+ @overflow_callback.call(@first_page_lines)
199
+ @delay_until = nil
200
+ end
201
+ @multipage = true
202
+ end
203
+ end
204
+
205
+ def multipage?
206
+ @multipage
207
+ end
208
+
209
+ alias print write
210
+ alias << write
211
+ end
99
212
  end
100
213
  end
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
 
@@ -29,10 +29,13 @@ module IRB
29
29
 
30
30
  def colorized_content
31
31
  if !binary_file? && file_exist?
32
- end_line = find_end
33
32
  # To correctly colorize, we need to colorize full content and extract the relevant lines.
34
- colored = IRB::Color.colorize_code(file_content)
35
- colored.lines[@line - 1...end_line].join
33
+ colored_lines = IRB::Color.colorize_code(file_content).lines
34
+
35
+ # Handle wrong line number case: line_no passed to eval is wrong, file is edited after load, etc
36
+ return if colored_lines.size < @line
37
+
38
+ colored_lines[@line - 1...find_end].join
36
39
  elsif @ast_source
37
40
  IRB::Color.colorize_code(@ast_source)
38
41
  end
@@ -114,7 +117,7 @@ module IRB
114
117
  when "owner"
115
118
  target_method = owner_receiver.instance_method(method)
116
119
  when "receiver"
117
- target_method = owner_receiver.method(method)
120
+ target_method = Kernel.instance_method(:method).bind_call(owner_receiver, method)
118
121
  end
119
122
  super_level.times do |s|
120
123
  target_method = target_method.super_method if target_method
data/lib/irb/statement.rb CHANGED
@@ -54,6 +54,27 @@ module IRB
54
54
  end
55
55
  end
56
56
 
57
+ class IncorrectAlias < Statement
58
+ attr_reader :message
59
+
60
+ def initialize(message)
61
+ @code = ""
62
+ @message = message
63
+ end
64
+
65
+ def should_be_handled_by_debugger?
66
+ false
67
+ end
68
+
69
+ def is_assignment?
70
+ false
71
+ end
72
+
73
+ def suppresses_echo?
74
+ true
75
+ end
76
+ end
77
+
57
78
  class Command < Statement
58
79
  attr_reader :command_class, :arg
59
80
 
data/lib/irb/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
- VERSION = "1.14.3"
8
+ VERSION = "1.16.0"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2024-12-18"
10
+ @LAST_UPDATE_DATE = "2025-12-17"
11
11
  end
data/lib/irb/workspace.rb CHANGED
@@ -55,6 +55,15 @@ EOF
55
55
  # Note that this will typically be IRB::TOPLEVEL_BINDING
56
56
  # This is to avoid RubyGems' local variables (see issue #17623)
57
57
  @binding = TOPLEVEL_BINDING.dup
58
+
59
+ when 5 # binding in Ruby::Box
60
+ unless defined?(Ruby::Box)
61
+ puts 'Context-mode 5 (binding in Ruby::Box) requires Ruby 4.0 or later.'
62
+ raise NameError, 'Ruby::Box not defined'
63
+ end
64
+
65
+ puts 'Context-mode 5 (binding in Ruby::Box) is experimental. It may be removed or changed without notice.'
66
+ @binding = Ruby::Box.new.eval('Kernel.binding')
58
67
  end
59
68
  end
60
69