irb 1.14.3 → 1.15.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/context.rb CHANGED
@@ -155,11 +155,6 @@ module IRB
155
155
  @command_aliases = IRB.conf[:COMMAND_ALIASES].dup
156
156
  end
157
157
 
158
- private def term_interactive?
159
- return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
160
- STDIN.tty? && ENV['TERM'] != 'dumb'
161
- end
162
-
163
158
  def use_tracer=(val)
164
159
  require_relative "ext/tracer" if val
165
160
  IRB.conf[:USE_TRACER] = val
@@ -177,45 +172,6 @@ module IRB
177
172
  __send__(__method__, val)
178
173
  end
179
174
 
180
- private def build_completor
181
- completor_type = IRB.conf[:COMPLETOR]
182
-
183
- # Gem repl_type_completor is added to bundled gems in Ruby 3.4.
184
- # Use :type as default completor only in Ruby 3.4 or later.
185
- verbose = !!completor_type
186
- completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp
187
-
188
- case completor_type
189
- when :regexp
190
- return RegexpCompletor.new
191
- when :type
192
- completor = build_type_completor(verbose: verbose)
193
- return completor if completor
194
- else
195
- warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
196
- end
197
- # Fallback to RegexpCompletor
198
- RegexpCompletor.new
199
- end
200
-
201
- private def build_type_completor(verbose:)
202
- if RUBY_ENGINE == 'truffleruby'
203
- # Avoid SyntaxError. truffleruby does not support endless method definition yet.
204
- warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
205
- return
206
- end
207
-
208
- begin
209
- require 'repl_type_completor'
210
- rescue LoadError => e
211
- warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
212
- return
213
- end
214
-
215
- ReplTypeCompletor.preload_rbs
216
- TypeCompletor.new(self)
217
- end
218
-
219
175
  def save_history=(val)
220
176
  IRB.conf[:SAVE_HISTORY] = val
221
177
  end
@@ -308,6 +264,8 @@ module IRB
308
264
  attr_reader :use_autocomplete
309
265
  # A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
310
266
  attr_reader :inspect_mode
267
+ # Inspector for the current context
268
+ attr_reader :inspect_method
311
269
 
312
270
  # A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
313
271
  attr_reader :prompt_mode
@@ -600,6 +558,8 @@ module IRB
600
558
  set_last_value(result)
601
559
  when Statement::Command
602
560
  statement.command_class.execute(self, statement.arg)
561
+ when Statement::IncorrectAlias
562
+ warn statement.message
603
563
  end
604
564
 
605
565
  nil
@@ -633,35 +593,60 @@ module IRB
633
593
  result
634
594
  end
635
595
 
636
- def parse_command(code)
596
+ def parse_input(code, is_assignment_expression)
637
597
  command_name, arg = code.strip.split(/\s+/, 2)
638
- return unless code.lines.size == 1 && command_name
639
-
640
598
  arg ||= ''
641
- command = command_name.to_sym
642
- # Command aliases are always command. example: $, @
643
- if (alias_name = command_aliases[command])
644
- return [alias_name, arg]
599
+
600
+ # command can only be 1 line
601
+ if code.lines.size != 1 ||
602
+ # command name is required
603
+ command_name.nil? ||
604
+ # local variable have precedence over command
605
+ local_variables.include?(command_name.to_sym) ||
606
+ # assignment expression is not a command
607
+ (is_assignment_expression ||
608
+ (arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)))
609
+ return Statement::Expression.new(code, is_assignment_expression)
645
610
  end
646
611
 
647
- # Assignment-like expression is not a command
648
- return if arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)
612
+ command = command_name.to_sym
649
613
 
650
- # Local variable have precedence over command
651
- return if local_variables.include?(command)
614
+ # Check command aliases
615
+ if aliased_name = command_aliases[command]
616
+ if command_class = Command.load_command(aliased_name)
617
+ command = aliased_name
618
+ elsif HelperMethod.helper_methods[aliased_name]
619
+ message = <<~MESSAGE
620
+ Using command alias `#{command}` for helper method `#{aliased_name}` is not supported.
621
+ Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
622
+ MESSAGE
623
+ return Statement::IncorrectAlias.new(message)
624
+ else
625
+ message = <<~MESSAGE
626
+ You're trying to use command alias `#{command}` for command `#{aliased_name}`, but `#{aliased_name}` does not exist.
627
+ Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
628
+ MESSAGE
629
+ return Statement::IncorrectAlias.new(message)
630
+ end
631
+ else
632
+ command_class = Command.load_command(command)
633
+ end
652
634
 
653
635
  # Check visibility
654
636
  public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
655
637
  private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
656
- if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
657
- [command, arg]
638
+ if command_class && Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
639
+ Statement::Command.new(code, command_class, arg)
640
+ else
641
+ Statement::Expression.new(code, is_assignment_expression)
658
642
  end
659
643
  end
660
644
 
661
645
  def colorize_input(input, complete:)
662
646
  if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable?
663
647
  lvars = local_variables || []
664
- if parse_command(input)
648
+ parsed_input = parse_input(input, false)
649
+ if parsed_input.is_a?(Statement::Command)
665
650
  name, sep, arg = input.split(/(\s+)/, 2)
666
651
  arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
667
652
  "#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
@@ -673,8 +658,12 @@ module IRB
673
658
  end
674
659
  end
675
660
 
676
- def inspect_last_value # :nodoc:
677
- @inspect_method.inspect_value(@last_value)
661
+ def inspect_last_value(output = +'') # :nodoc:
662
+ @inspect_method.inspect_value(@last_value, output)
663
+ end
664
+
665
+ def inspector_support_stream_output?
666
+ @inspect_method.support_stream_output?
678
667
  end
679
668
 
680
669
  NOPRINTING_IVARS = ["@last_value"] # :nodoc:
@@ -712,5 +701,51 @@ module IRB
712
701
  main_object = main
713
702
  Object === main_object ? main_object.__send__(method_name) : Object.instance_method(method_name).bind_call(main_object)
714
703
  end
704
+
705
+ private
706
+
707
+ def term_interactive?
708
+ return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
709
+ STDIN.tty? && ENV['TERM'] != 'dumb'
710
+ end
711
+
712
+ def build_completor
713
+ completor_type = IRB.conf[:COMPLETOR]
714
+
715
+ # Gem repl_type_completor is added to bundled gems in Ruby 3.4.
716
+ # Use :type as default completor only in Ruby 3.4 or later.
717
+ verbose = !!completor_type
718
+ completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp
719
+
720
+ case completor_type
721
+ when :regexp
722
+ return RegexpCompletor.new
723
+ when :type
724
+ completor = build_type_completor(verbose: verbose)
725
+ return completor if completor
726
+ else
727
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
728
+ end
729
+ # Fallback to RegexpCompletor
730
+ RegexpCompletor.new
731
+ end
732
+
733
+ def build_type_completor(verbose:)
734
+ if RUBY_ENGINE == 'truffleruby'
735
+ # Avoid SyntaxError. truffleruby does not support endless method definition yet.
736
+ warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
737
+ return
738
+ end
739
+
740
+ begin
741
+ require 'repl_type_completor'
742
+ rescue LoadError => e
743
+ warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
744
+ return
745
+ end
746
+
747
+ ReplTypeCompletor.preload_rbs
748
+ TypeCompletor.new(self)
749
+ end
715
750
  end
716
751
  end
@@ -9,6 +9,7 @@ require_relative "command/cd"
9
9
  require_relative "command/chws"
10
10
  require_relative "command/context"
11
11
  require_relative "command/continue"
12
+ require_relative "command/copy"
12
13
  require_relative "command/debug"
13
14
  require_relative "command/delete"
14
15
  require_relative "command/disable_irb"
@@ -218,7 +219,8 @@ module IRB
218
219
  )
219
220
 
220
221
  _register_with_aliases(:irb_show_doc, Command::ShowDoc,
221
- [:show_doc, NO_OVERRIDE]
222
+ [:show_doc, NO_OVERRIDE],
223
+ [:ri, NO_OVERRIDE]
222
224
  )
223
225
 
224
226
  _register_with_aliases(:irb_info, Command::IrbInfo)
@@ -249,6 +251,7 @@ module IRB
249
251
  )
250
252
 
251
253
  register(:cd, Command::CD)
254
+ register(:copy, Command::Copy)
252
255
  end
253
256
 
254
257
  ExtendCommand = Command
data/lib/irb/history.rb CHANGED
@@ -2,9 +2,13 @@ require "pathname"
2
2
 
3
3
  module IRB
4
4
  module History
5
+ DEFAULT_ENTRY_LIMIT = 1000
6
+
5
7
  class << self
6
8
  # Integer representation of <code>IRB.conf[:HISTORY_FILE]</code>.
7
9
  def save_history
10
+ return 0 if IRB.conf[:SAVE_HISTORY] == false
11
+ return DEFAULT_ENTRY_LIMIT if IRB.conf[:SAVE_HISTORY] == true
8
12
  IRB.conf[:SAVE_HISTORY].to_i
9
13
  end
10
14
 
data/lib/irb/init.rb CHANGED
@@ -93,7 +93,7 @@ module IRB # :nodoc:
93
93
  @CONF[:VERBOSE] = nil
94
94
 
95
95
  @CONF[:EVAL_HISTORY] = nil
96
- @CONF[:SAVE_HISTORY] = 1000
96
+ @CONF[:SAVE_HISTORY] = History::DEFAULT_ENTRY_LIMIT
97
97
 
98
98
  @CONF[:BACK_TRACE_LIMIT] = 16
99
99
 
@@ -194,6 +194,8 @@ module IRB # :nodoc:
194
194
  :'$' => :show_source,
195
195
  :'@' => :whereami,
196
196
  }
197
+
198
+ @CONF[:COPY_COMMAND] = ENV.fetch("IRB_COPY_COMMAND", nil)
197
199
  end
198
200
 
199
201
  def IRB.set_measure_callback(type = nil, arg = nil, &block)
data/lib/irb/inspector.rb CHANGED
@@ -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
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,83 @@ 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
+ write(text)
156
+ write("\n") unless text.end_with?("\n")
157
+ end
158
+
159
+ def write(text)
160
+ @string << text
161
+ if @multipage
162
+ if @delay_until && Time.now > @delay_until
163
+ @overflow_callback.call(@first_page_lines)
164
+ @delay_until = nil
165
+ end
166
+ return
167
+ end
168
+
169
+ overflow_size = (@width * (@height - @lines.size) + @width - @col) * MAX_CHAR_PER_CELL
170
+ if text.size >= overflow_size
171
+ text = text[0, overflow_size]
172
+ overflow = true
173
+ end
174
+
175
+ @buffer << text
176
+ @col += Reline::Unicode.calculate_width(text)
177
+ if text.include?("\n") || @col >= @width
178
+ @buffer.lines.each do |line|
179
+ wrapped_lines = Reline::Unicode.split_by_width(line.chomp, @width).first.compact
180
+ wrapped_lines.pop if wrapped_lines.last == ''
181
+ @lines.concat(wrapped_lines)
182
+ if @lines.empty?
183
+ @lines << "\n"
184
+ elsif line.end_with?("\n")
185
+ @lines[-1] += "\n"
186
+ end
187
+ end
188
+ @buffer.clear
189
+ @buffer << @lines.pop unless @lines.last.end_with?("\n")
190
+ @col = Reline::Unicode.calculate_width(@buffer)
191
+ end
192
+ if overflow || @lines.size > @height || (@lines.size == @height && @col > 0)
193
+ @first_page_lines = @lines.take(@height)
194
+ if !@delay_until || Time.now > @delay_until
195
+ @overflow_callback.call(@first_page_lines)
196
+ @delay_until = nil
197
+ end
198
+ @multipage = true
199
+ end
200
+ end
201
+
202
+ def multipage?
203
+ @multipage
204
+ end
205
+
206
+ alias print write
207
+ alias << write
208
+ end
99
209
  end
100
210
  end
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.15.0"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2024-12-18"
10
+ @LAST_UPDATE_DATE = "2025-01-21"
11
11
  end