mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
[ruby/irb] Add copy command (https://github.com/ruby/irb/pull/1044)
Closes https://github.com/ruby/irb/pull/753
a24ac53d48
This commit is contained in:
parent
89240eb2fb
commit
c6e8ee4514
Notes:
git
2025-01-22 14:08:53 +00:00
10 changed files with 190 additions and 10 deletions
|
@ -5,8 +5,8 @@ require_relative 'color'
|
|||
module IRB
|
||||
class ColorPrinter < ::PP
|
||||
class << self
|
||||
def pp(obj, out = $>, width = screen_width)
|
||||
q = ColorPrinter.new(out, width)
|
||||
def pp(obj, out = $>, width = screen_width, colorize: true)
|
||||
q = ColorPrinter.new(out, width, colorize: colorize)
|
||||
q.guard_inspect_key {q.pp obj}
|
||||
q.flush
|
||||
out << "\n"
|
||||
|
@ -21,6 +21,12 @@ module IRB
|
|||
end
|
||||
end
|
||||
|
||||
def initialize(out, width, colorize: true)
|
||||
@colorize = colorize
|
||||
|
||||
super(out, width)
|
||||
end
|
||||
|
||||
def pp(obj)
|
||||
if String === obj
|
||||
# Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
|
||||
|
@ -41,9 +47,9 @@ module IRB
|
|||
when ',', '=>', '[', ']', '{', '}', '..', '...', /\A@\w+\z/
|
||||
super(str, width)
|
||||
when /\A#</, '=', '>'
|
||||
super(Color.colorize(str, [:GREEN]), width)
|
||||
super(@colorize ? Color.colorize(str, [:GREEN]) : str, width)
|
||||
else
|
||||
super(Color.colorize_code(str, ignore_error: true), width)
|
||||
super(@colorize ? Color.colorize_code(str, ignore_error: true) : str, width)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
63
lib/irb/command/copy.rb
Normal file
63
lib/irb/command/copy.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module IRB
|
||||
module Command
|
||||
class Copy < Base
|
||||
category "Workspace"
|
||||
description "Copy command output to clipboard"
|
||||
|
||||
help_message(<<~HELP)
|
||||
Usage: copy [command]
|
||||
HELP
|
||||
|
||||
def execute(arg)
|
||||
# Copy last value if no expression was supplied
|
||||
arg = '_' if arg.to_s.strip.empty?
|
||||
|
||||
value = irb_context.workspace.binding.eval(arg)
|
||||
output = irb_context.inspect_method.inspect_value(value, colorize: false)
|
||||
|
||||
if clipboard_available?
|
||||
copy_to_clipboard(output)
|
||||
else
|
||||
warn "System clipboard not found"
|
||||
end
|
||||
rescue StandardError => e
|
||||
warn "Error: #{e}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def copy_to_clipboard(text)
|
||||
IO.popen(clipboard_program, 'w') do |io|
|
||||
io.write(text)
|
||||
end
|
||||
|
||||
raise IOError.new("Copying to clipboard failed") unless $? == 0
|
||||
|
||||
puts "Copied to system clipboard"
|
||||
rescue Errno::ENOENT => e
|
||||
warn e.message
|
||||
warn "Is IRB.conf[:COPY_COMMAND] set to a bad value?"
|
||||
end
|
||||
|
||||
def clipboard_program
|
||||
@clipboard_program ||= if IRB.conf[:COPY_COMMAND]
|
||||
IRB.conf[:COPY_COMMAND]
|
||||
elsif executable?("pbcopy")
|
||||
"pbcopy"
|
||||
elsif executable?("xclip")
|
||||
"xclip -selection clipboard"
|
||||
end
|
||||
end
|
||||
|
||||
def executable?(command)
|
||||
system("which #{command} > /dev/null 2>&1")
|
||||
end
|
||||
|
||||
def clipboard_available?
|
||||
!!clipboard_program
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -264,6 +264,8 @@ module IRB
|
|||
attr_reader :use_autocomplete
|
||||
# A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
|
||||
attr_reader :inspect_mode
|
||||
# Inspector for the current context
|
||||
attr_reader :inspect_method
|
||||
|
||||
# A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
|
||||
attr_reader :prompt_mode
|
||||
|
|
|
@ -9,6 +9,7 @@ require_relative "command/cd"
|
|||
require_relative "command/chws"
|
||||
require_relative "command/context"
|
||||
require_relative "command/continue"
|
||||
require_relative "command/copy"
|
||||
require_relative "command/debug"
|
||||
require_relative "command/delete"
|
||||
require_relative "command/disable_irb"
|
||||
|
@ -250,6 +251,7 @@ module IRB
|
|||
)
|
||||
|
||||
register(:cd, Command::CD)
|
||||
register(:copy, Command::Copy)
|
||||
end
|
||||
|
||||
ExtendCommand = Command
|
||||
|
|
|
@ -194,6 +194,8 @@ module IRB # :nodoc:
|
|||
:'$' => :show_source,
|
||||
:'@' => :whereami,
|
||||
}
|
||||
|
||||
@CONF[:COPY_COMMAND] = ENV.fetch("IRB_COPY_COMMAND", nil)
|
||||
end
|
||||
|
||||
def IRB.set_measure_callback(type = nil, arg = nil, &block)
|
||||
|
|
|
@ -93,8 +93,8 @@ module IRB # :nodoc:
|
|||
end
|
||||
|
||||
# Proc to call when the input is evaluated and output in irb.
|
||||
def inspect_value(v)
|
||||
@inspect.call(v)
|
||||
def inspect_value(v, colorize: true)
|
||||
@inspect.call(v, colorize: colorize)
|
||||
rescue => e
|
||||
puts "An error occurred when inspecting the object: #{e.inspect}"
|
||||
|
||||
|
@ -110,11 +110,11 @@ module IRB # :nodoc:
|
|||
end
|
||||
|
||||
Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s}
|
||||
Inspector.def_inspector([:p, :inspect]){|v|
|
||||
Color.colorize_code(v.inspect, colorable: Color.colorable? && Color.inspect_colorable?(v))
|
||||
Inspector.def_inspector([:p, :inspect]){|v, colorize: true|
|
||||
Color.colorize_code(v.inspect, colorable: colorize && Color.colorable? && Color.inspect_colorable?(v))
|
||||
}
|
||||
Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v|
|
||||
IRB::ColorPrinter.pp(v, +'').chomp
|
||||
Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v, colorize: true|
|
||||
IRB::ColorPrinter.pp(v, +'', colorize: colorize).chomp
|
||||
}
|
||||
Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
|
||||
begin
|
||||
|
|
|
@ -227,6 +227,8 @@ or
|
|||
.Sy type
|
||||
.
|
||||
.Pp
|
||||
.It Ev IRB_COPY_COMMAND
|
||||
Overrides the default program used to interface with the system clipboard.
|
||||
.El
|
||||
.Pp
|
||||
Also
|
||||
|
|
70
test/irb/command/test_copy.rb
Normal file
70
test/irb/command/test_copy.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'irb'
|
||||
|
||||
require_relative "../helper"
|
||||
|
||||
module TestIRB
|
||||
class CopyTest < IntegrationTestCase
|
||||
def setup
|
||||
super
|
||||
@envs['IRB_COPY_COMMAND'] = "ruby -e \"puts 'foo' + STDIN.read\""
|
||||
end
|
||||
|
||||
def test_copy_with_pbcopy
|
||||
write_ruby <<~'ruby'
|
||||
class Answer
|
||||
def initialize(answer)
|
||||
@answer = answer
|
||||
end
|
||||
end
|
||||
|
||||
binding.irb
|
||||
ruby
|
||||
|
||||
output = run_ruby_file do
|
||||
type "copy Answer.new(42)"
|
||||
type "exit"
|
||||
end
|
||||
|
||||
assert_match(/foo#<Answer:0x[0-9a-f]+ @answer=42/, output)
|
||||
assert_match(/Copied to system clipboard/, output)
|
||||
end
|
||||
|
||||
# copy puts 5 should:
|
||||
# - Print value to the console
|
||||
# - Copy nil to clipboard, since that is what the puts call evaluates to
|
||||
def test_copy_when_expression_has_side_effects
|
||||
write_ruby <<~'ruby'
|
||||
binding.irb
|
||||
ruby
|
||||
|
||||
output = run_ruby_file do
|
||||
type "copy puts 42"
|
||||
type "exit"
|
||||
end
|
||||
|
||||
assert_match(/^42\r\n/, output)
|
||||
assert_match(/foonil/, output)
|
||||
assert_match(/Copied to system clipboard/, output)
|
||||
refute_match(/foo42/, output)
|
||||
end
|
||||
|
||||
def test_copy_when_copy_command_is_invalid
|
||||
@envs['IRB_COPY_COMMAND'] = "lulz"
|
||||
|
||||
write_ruby <<~'ruby'
|
||||
binding.irb
|
||||
ruby
|
||||
|
||||
output = run_ruby_file do
|
||||
type "copy 42"
|
||||
type "exit"
|
||||
end
|
||||
|
||||
assert_match(/No such file or directory - lulz/, output)
|
||||
assert_match(/Is IRB\.conf\[:COPY_COMMAND\] set to a bad value/, output)
|
||||
refute_match(/Copied to system clipboard/, output)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -49,6 +49,19 @@ module TestIRB
|
|||
end
|
||||
end
|
||||
|
||||
def test_colorization_disabled
|
||||
{
|
||||
1 => "1\n",
|
||||
"a\nb" => %["a\\nb"\n],
|
||||
IRBTestColorPrinter.new('test') => "#<struct TestIRB::ColorPrinterTest::IRBTestColorPrinter a=\"test\">\n",
|
||||
Ripper::Lexer.new('1').scan => "[#<Ripper::Lexer::Elem: on_int@1:0 END token: \"1\">]\n",
|
||||
Class.new{define_method(:pretty_print){|q| q.text("[__FILE__, __LINE__, __ENCODING__]")}}.new => "[__FILE__, __LINE__, __ENCODING__]\n",
|
||||
}.each do |object, result|
|
||||
actual = with_term { IRB::ColorPrinter.pp(object, '', colorize: false) }
|
||||
assert_equal(result, actual, "Case: IRB::ColorPrinter.pp(#{object.inspect}, '')")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_term
|
||||
|
|
|
@ -163,6 +163,26 @@ module TestIRB
|
|||
IRB.conf[:USE_AUTOCOMPLETE] = orig_use_autocomplete_conf
|
||||
end
|
||||
|
||||
def test_copy_command_environment_variable
|
||||
orig_copy_command_env = ENV['IRB_COPY_COMMAND']
|
||||
orig_copy_command_conf = IRB.conf[:COPY_COMMAND]
|
||||
|
||||
ENV['IRB_COPY_COMMAND'] = nil
|
||||
IRB.setup(__FILE__)
|
||||
refute IRB.conf[:COPY_COMMAND]
|
||||
|
||||
ENV['IRB_COPY_COMMAND'] = ''
|
||||
IRB.setup(__FILE__)
|
||||
assert_equal('', IRB.conf[:COPY_COMMAND])
|
||||
|
||||
ENV['IRB_COPY_COMMAND'] = 'blah'
|
||||
IRB.setup(__FILE__)
|
||||
assert_equal('blah', IRB.conf[:COPY_COMMAND])
|
||||
ensure
|
||||
ENV['IRB_COPY_COMMAND'] = orig_copy_command_env
|
||||
IRB.conf[:COPY_COMMAND] = orig_copy_command_conf
|
||||
end
|
||||
|
||||
def test_completor_environment_variable
|
||||
orig_use_autocomplete_env = ENV['IRB_COMPLETOR']
|
||||
orig_use_autocomplete_conf = IRB.conf[:COMPLETOR]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue