irb 1.14.3 → 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.
@@ -4,12 +4,9 @@ require_relative 'color'
4
4
 
5
5
  module IRB
6
6
  class ColorPrinter < ::PP
7
- METHOD_RESPOND_TO = Object.instance_method(:respond_to?)
8
- METHOD_INSPECT = Object.instance_method(:inspect)
9
-
10
7
  class << self
11
- def pp(obj, out = $>, width = screen_width)
12
- q = ColorPrinter.new(out, width)
8
+ def pp(obj, out = $>, width = screen_width, colorize: true)
9
+ q = ColorPrinter.new(out, width, colorize: colorize)
13
10
  q.guard_inspect_key {q.pp obj}
14
11
  q.flush
15
12
  out << "\n"
@@ -24,12 +21,16 @@ module IRB
24
21
  end
25
22
  end
26
23
 
24
+ def initialize(out, width, colorize: true)
25
+ @colorize = colorize
26
+
27
+ super(out, width)
28
+ end
29
+
27
30
  def pp(obj)
28
31
  if String === obj
29
32
  # Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
30
33
  text(obj.inspect)
31
- elsif !METHOD_RESPOND_TO.bind(obj).call(:inspect)
32
- text(METHOD_INSPECT.bind(obj).call)
33
34
  else
34
35
  super
35
36
  end
@@ -46,9 +47,9 @@ module IRB
46
47
  when ',', '=>', '[', ']', '{', '}', '..', '...', /\A@\w+\z/
47
48
  super(str, width)
48
49
  when /\A#</, '=', '>'
49
- super(Color.colorize(str, [:GREEN]), width)
50
+ super(@colorize ? Color.colorize(str, [:GREEN]) : str, width)
50
51
  else
51
- super(Color.colorize_code(str, ignore_error: true), width)
52
+ super(@colorize ? Color.colorize_code(str, ignore_error: true) : str, width)
52
53
  end
53
54
  end
54
55
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ module Command
5
+ class Copy < Base
6
+ category "Misc"
7
+ description "Copy expression output to clipboard"
8
+
9
+ help_message(<<~HELP)
10
+ Usage: copy ([expression])
11
+
12
+ When given:
13
+ - an expression, copy the inspect result of the expression to the clipboard.
14
+ - no arguments, copy the last evaluated result (`_`) to the clipboard.
15
+
16
+ Examples:
17
+
18
+ copy Foo.new
19
+ copy User.all.to_a
20
+ copy
21
+ HELP
22
+
23
+ def execute(arg)
24
+ # Copy last value if no expression was supplied
25
+ arg = '_' if arg.to_s.strip.empty?
26
+
27
+ value = irb_context.workspace.binding.eval(arg)
28
+ output = irb_context.inspect_method.inspect_value(value, +'', colorize: false).chomp
29
+
30
+ if clipboard_available?
31
+ copy_to_clipboard(output)
32
+ else
33
+ warn "System clipboard not found"
34
+ end
35
+ rescue StandardError => e
36
+ warn "Error: #{e}"
37
+ end
38
+
39
+ private
40
+
41
+ def copy_to_clipboard(text)
42
+ IO.popen(clipboard_program, 'w') do |io|
43
+ io.write(text)
44
+ end
45
+
46
+ raise IOError.new("Copying to clipboard failed") unless $? == 0
47
+
48
+ puts "Copied to system clipboard"
49
+ rescue Errno::ENOENT => e
50
+ warn e.message
51
+ warn "Is IRB.conf[:COPY_COMMAND] set to a bad value?"
52
+ end
53
+
54
+ def clipboard_program
55
+ @clipboard_program ||= if IRB.conf[:COPY_COMMAND]
56
+ IRB.conf[:COPY_COMMAND]
57
+ elsif executable?("pbcopy")
58
+ "pbcopy"
59
+ elsif executable?("xclip")
60
+ "xclip -selection clipboard"
61
+ end
62
+ end
63
+
64
+ def executable?(command)
65
+ system("which #{command} > /dev/null 2>&1")
66
+ end
67
+
68
+ def clipboard_available?
69
+ !!clipboard_program
70
+ end
71
+ end
72
+ end
73
+ end
@@ -14,7 +14,7 @@ module IRB
14
14
 
15
15
  def execute(arg)
16
16
 
17
- if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/))
17
+ if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\z/))
18
18
  grep = Regexp.new(match[:grep])
19
19
  end
20
20
 
@@ -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/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
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")
@@ -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
@@ -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
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)
@@ -26,10 +26,11 @@ module IRB
26
26
 
27
27
  def winsize
28
28
  if instance_variable_defined?(:@stdout) && @stdout.tty?
29
- @stdout.winsize
30
- else
31
- [24, 80]
29
+ winsize = @stdout.winsize
30
+ # If width or height is 0, something is wrong.
31
+ return winsize unless winsize.include? 0
32
32
  end
33
+ [24, 80]
33
34
  end
34
35
 
35
36
  # Whether this input method is still readable when there is no more data to
@@ -175,10 +176,15 @@ module IRB
175
176
  class ReadlineInputMethod < StdioInputMethod
176
177
  class << self
177
178
  def initialize_readline
178
- require "readline"
179
- rescue LoadError
180
- else
181
- include ::Readline
179
+ return if defined?(self::Readline)
180
+
181
+ begin
182
+ require 'readline'
183
+ const_set(:Readline, ::Readline)
184
+ rescue LoadError
185
+ const_set(:Readline, ::Reline)
186
+ end
187
+ const_set(:HISTORY, self::Readline::HISTORY)
182
188
  end
183
189
  end
184
190
 
@@ -216,8 +222,8 @@ module IRB
216
222
  def gets
217
223
  Readline.input = @stdin
218
224
  Readline.output = @stdout
219
- if l = readline(@prompt, false)
220
- HISTORY.push(l) if !l.empty?
225
+ if l = Readline.readline(@prompt, false)
226
+ Readline::HISTORY.push(l) if !l.empty? && l != Readline::HISTORY.to_a.last
221
227
  @line[@line_no += 1] = l + "\n"
222
228
  else
223
229
  @eof = true
@@ -239,7 +245,7 @@ module IRB
239
245
 
240
246
  # For debug message
241
247
  def inspect
242
- readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
248
+ readline_impl = Readline == ::Reline ? 'Reline' : 'ext/readline'
243
249
  str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
244
250
  inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
245
251
  str += " and #{inputrc_path}" if File.exist?(inputrc_path)
@@ -474,7 +480,7 @@ module IRB
474
480
  Reline.prompt_proc = @prompt_proc
475
481
  Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
476
482
  if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
477
- Reline::HISTORY.push(l) if !l.empty?
483
+ Reline::HISTORY.push(l) if !l.empty? && l != Reline::HISTORY.to_a.last
478
484
  @line[@line_no += 1] = l + "\n"
479
485
  else
480
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
@@ -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