mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
Updated Windows Ansi escape codes detection, through ConPty.
Fixed unit tests to also work on Windows.
This commit is contained in:
parent
b58c7bd86c
commit
7c08e24dba
13 changed files with 157 additions and 97 deletions
|
@ -37,7 +37,8 @@ class Pry
|
|||
|
||||
def use_ansi_codes?
|
||||
Pry::Helpers::Platform.windows_ansi? ||
|
||||
((term = Pry::Env['TERM']) && term != "dumb")
|
||||
Pry::Helpers::Platform.windows_conpty? ||
|
||||
(term = Pry::Env['TERM']; term != nil && term != "dumb")
|
||||
end
|
||||
|
||||
def colorize_code(code)
|
||||
|
|
|
@ -10,25 +10,60 @@ class Pry
|
|||
module Platform
|
||||
# @return [Boolean]
|
||||
def self.mac_osx?
|
||||
!!(RbConfig::CONFIG['host_os'] =~ /\Adarwin/i)
|
||||
RbConfig::CONFIG['host_os'].match? /\Adarwin/i
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.linux?
|
||||
!!(RbConfig::CONFIG['host_os'] =~ /linux/i)
|
||||
RbConfig::CONFIG['host_os'].match? /linux/i
|
||||
end
|
||||
|
||||
# @return [Boolean] true when Pry is running on Windows with ANSI support,
|
||||
# false otherwise
|
||||
def self.windows?
|
||||
!!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/)
|
||||
RbConfig::CONFIG['host_os'].match? /mswin|mingw/
|
||||
end
|
||||
|
||||
# Checks older version of Windows console that required alternative
|
||||
# libraries to work with Ansi escapes codes.
|
||||
# @return [Boolean]
|
||||
def self.windows_ansi?
|
||||
return false unless windows?
|
||||
# ensures that ConPty isn't available before checking anything else
|
||||
windows? && !windows_conpty? && !!(defined?(Win32::Console) || Pry::Env['ANSICON'] || mri_2?)
|
||||
end
|
||||
|
||||
!!(defined?(Win32::Console) || Pry::Env['ANSICON'] || mri_2?)
|
||||
# New version of Windows console that understands Ansi escapes codes.
|
||||
# @return [Boolean]
|
||||
def self.windows_conpty?
|
||||
@conpty ||= windows? && begin
|
||||
require 'fiddle/import'
|
||||
require 'fiddle/types'
|
||||
|
||||
kernel32 = Module.new do
|
||||
extend Fiddle::Importer
|
||||
dlload 'kernel32'
|
||||
include Fiddle::Win32Types
|
||||
extern 'HANDLE GetStdHandle(DWORD)'
|
||||
extern 'BOOL GetConsoleMode(HANDLE, DWORD*)'
|
||||
end
|
||||
|
||||
mode = kernel32.create_value('DWORD')
|
||||
|
||||
std_output_handle = -11
|
||||
enable_virtual_terminal_processing = 0x4
|
||||
|
||||
stdout_handle = kernel32.GetStdHandle(std_output_handle)
|
||||
|
||||
stdout_handle > 0 &&
|
||||
kernel32.GetConsoleMode(stdout_handle, mode) != 0 &&
|
||||
mode.value & enable_virtual_terminal_processing != 0
|
||||
|
||||
rescue
|
||||
false
|
||||
ensure
|
||||
Fiddle.free mode.to_ptr if mode
|
||||
kernel32.handler.handlers.each(&:close) if kernel32
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
|
@ -53,7 +88,7 @@ class Pry
|
|||
|
||||
# @return [Boolean]
|
||||
def self.mri_2?
|
||||
mri? && RUBY_VERSION.start_with?('2')
|
||||
mri? && RUBY_VERSION.start_with?('2.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -144,7 +144,7 @@ class Pry
|
|||
@system_pager =
|
||||
begin
|
||||
pager_executable = default_pager.split(' ').first
|
||||
if Helpers::Platform.windows? || Helpers::Platform.windows_ansi?
|
||||
if Helpers::Platform.windows?
|
||||
`where /Q #{pager_executable}`
|
||||
else
|
||||
`which #{pager_executable}`
|
||||
|
|
|
@ -143,7 +143,12 @@ you can add "Pry.config.windows_console_warning = false" to your pryrc.
|
|||
load_requires if Pry.config.should_load_requires
|
||||
load_history if Pry.config.history_load
|
||||
load_traps if Pry.config.should_trap_interrupts
|
||||
load_win32console if Helpers::Platform.windows? && !Helpers::Platform.windows_ansi?
|
||||
|
||||
windows_no_ansi = Helpers::Platform.windows? &&
|
||||
!Helpers::Platform.windows_ansi? &&
|
||||
!Helpers::Platform.windows_conpty?
|
||||
|
||||
load_win32console if windows_no_ansi
|
||||
end
|
||||
|
||||
# Start a Pry REPL.
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
describe "hist" do
|
||||
before do
|
||||
Pry.history.clear
|
||||
@hist = Pry.history
|
||||
# different platforms require different types of Readline, so best not to rely on it for these tests:
|
||||
@hist = Pry.history = Pry::History.new
|
||||
|
||||
@str_output = StringIO.new
|
||||
@t = pry_tester history: @hist do
|
||||
|
|
|
@ -13,6 +13,7 @@ RSpec.describe Pry::Config do
|
|||
specify { expect(subject.unrescued_exceptions).to be_an(Array) }
|
||||
specify { expect(subject.hooks).to be_a(Pry::Hooks) }
|
||||
specify { expect(subject.pager).to be(true).or be(false) }
|
||||
specify { expect(subject.escape_headers).to be(true).or be(false) }
|
||||
specify { expect(subject.system).to be_a(Method) }
|
||||
specify { expect(subject.color).to be(true).or be(false) }
|
||||
specify { expect(subject.default_window_size).to be_a(Numeric) }
|
||||
|
|
|
@ -57,6 +57,7 @@ describe Pry::Editor do
|
|||
|
||||
context "when no editor is detected" do
|
||||
before do
|
||||
allow(Pry::Helpers::Platform).to receive(:windows?).and_return(false)
|
||||
allow(ENV).to receive(:key?).and_return(false)
|
||||
allow(Kernel).to receive(:system)
|
||||
end
|
||||
|
@ -71,7 +72,11 @@ describe Pry::Editor do
|
|||
end
|
||||
end
|
||||
|
||||
describe "build_editor_invocation_string", skip: !Pry::Helpers::Platform.windows? do
|
||||
describe "build_editor_invocation_string" do
|
||||
before do
|
||||
allow(Pry::Helpers::Platform).to receive(:windows?).and_return(false)
|
||||
end
|
||||
|
||||
it 'should shell-escape files' do
|
||||
invocation_str = @editor.build_editor_invocation_string(@tf_path, 5, true)
|
||||
expect(invocation_str).to match(/#{@tf_dir}.+hello\\ world\.rb/)
|
||||
|
|
|
@ -4,21 +4,13 @@ require 'tempfile'
|
|||
require 'rbconfig'
|
||||
|
||||
RSpec.describe Pry::History do
|
||||
|
||||
# different platforms require different types of Readline, so best not to rely on it for these tests:
|
||||
let(:history) { Pry::History.new }
|
||||
|
||||
before do
|
||||
Pry.history.clear
|
||||
|
||||
@saved_history = "1\n2\n3\ninvalid\0 line\n"
|
||||
|
||||
Pry.history.loader = proc do |&blk|
|
||||
@saved_history.lines.each { |l| blk.call(l) }
|
||||
end
|
||||
|
||||
Pry.load_history
|
||||
end
|
||||
|
||||
after do
|
||||
Pry.history.clear
|
||||
Pry.history.instance_variable_set(:@original_lines, 0)
|
||||
# don't write anywhere by default
|
||||
history.saver = ->(_line) {}
|
||||
end
|
||||
|
||||
describe ".default_file" do
|
||||
|
@ -51,34 +43,34 @@ RSpec.describe Pry::History do
|
|||
context "when $XDG_DATA_HOME is defined" do
|
||||
it "returns config location relative to $XDG_DATA_HOME" do
|
||||
stub_hist has_default: false, xdg_home: '/my/path'
|
||||
expect(described_class.default_file).to eq('/my/path/pry/pry_history')
|
||||
expect(described_class.default_file).to end_with('/my/path/pry/pry_history')
|
||||
end
|
||||
|
||||
it "returns config location relative to $XDG_DATA_HOME when ~/.pryrc exists" do
|
||||
stub_hist has_default: true, xdg_home: '/my/path'
|
||||
expect(described_class.default_file).to eq('/my/path/pry/pry_history')
|
||||
expect(described_class.default_file).to end_with('/my/path/pry/pry_history')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#push' do
|
||||
it "does not record duplicated lines" do
|
||||
Pry.history << '3'
|
||||
Pry.history << '_ += 1'
|
||||
Pry.history << '_ += 1'
|
||||
expect(Pry.history.to_a.grep('_ += 1').count).to eq 1
|
||||
history << '3'
|
||||
history << '_ += 1'
|
||||
history << '_ += 1'
|
||||
expect(history.to_a.grep('_ += 1').count).to eq 1
|
||||
end
|
||||
|
||||
it "does not record lines that contain a NULL byte" do
|
||||
c = Pry.history.to_a.size
|
||||
Pry.history << "a\0b"
|
||||
expect(Pry.history.to_a.size).to eq c
|
||||
c = history.to_a.size
|
||||
history << "a\0b"
|
||||
expect(history.to_a.size).to eq c
|
||||
end
|
||||
|
||||
it "does not record empty lines" do
|
||||
c = Pry.history.to_a.size
|
||||
Pry.history << ''
|
||||
expect(Pry.history.to_a.size).to eq c
|
||||
c = history.to_a.size
|
||||
history << ''
|
||||
expect(history.to_a.size).to eq c
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -87,8 +79,8 @@ RSpec.describe Pry::History do
|
|||
@old_file = Pry.config.history_file
|
||||
@hist_file_path = File.expand_path('spec/fixtures/pry_history')
|
||||
Pry.config.history_file = @hist_file_path
|
||||
Pry.history.clear
|
||||
Pry.load_history
|
||||
history.clear
|
||||
history.load
|
||||
end
|
||||
|
||||
after do
|
||||
|
@ -96,15 +88,15 @@ RSpec.describe Pry::History do
|
|||
end
|
||||
|
||||
it "clears this session's history" do
|
||||
expect(Pry.history.to_a.size).to be > 0
|
||||
Pry.history.clear
|
||||
expect(Pry.history.to_a.size).to eq 0
|
||||
expect(Pry.history.original_lines).to eq 0
|
||||
expect(history.to_a.size).to be > 0
|
||||
history.clear
|
||||
expect(history.to_a.size).to eq 0
|
||||
expect(history.original_lines).to eq 0
|
||||
end
|
||||
|
||||
it "doesn't affect the contents of the history file" do
|
||||
expect(Pry.history.to_a.size).to eq 3
|
||||
Pry.history.clear
|
||||
expect(history.to_a.size).to eq 3
|
||||
history.clear
|
||||
|
||||
File.open(@hist_file_path, 'r') do |fh|
|
||||
file = fh.to_a
|
||||
|
@ -117,28 +109,37 @@ RSpec.describe Pry::History do
|
|||
|
||||
describe "#history_line_count" do
|
||||
it "counts entries in history" do
|
||||
Pry.history.clear
|
||||
history.clear
|
||||
saved_history = "olgierd\ngustlik\njanek\ngrzes\ntomek\n"
|
||||
Pry.history.loader = proc do |&blk|
|
||||
history.loader = proc do |&blk|
|
||||
saved_history.lines.each { |l| blk.call(l) }
|
||||
end
|
||||
Pry.load_history
|
||||
history.load
|
||||
|
||||
expect(Pry.history.history_line_count).to eq 5
|
||||
expect(history.history_line_count).to eq 5
|
||||
end
|
||||
end
|
||||
|
||||
describe "#session_line_count" do
|
||||
it "returns the number of lines in history from just this session" do
|
||||
Pry.history << 'you?'
|
||||
Pry.history << 'you are so precious'
|
||||
expect(Pry.history.session_line_count).to eq 2
|
||||
history << 'you?'
|
||||
history << 'you are so precious'
|
||||
expect(history.session_line_count).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
describe ".load_history" do
|
||||
|
||||
before do
|
||||
history.loader = proc do |&blk|
|
||||
"1\n2\n3\ninvalid\0 line\n".each_line { |l| blk.call(l) }
|
||||
end
|
||||
|
||||
history.load
|
||||
end
|
||||
|
||||
it "reads the contents of the file" do
|
||||
expect(Pry.history.to_a[-2..-1]).to eq %w[2 3]
|
||||
expect(history.to_a[-2..-1]).to eq %w[2 3]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -203,13 +204,18 @@ RSpec.describe Pry::History do
|
|||
|
||||
[Errno::EACCES, Errno::ENOENT].each do |error_class|
|
||||
it "handles #{error_class} failure to read from history" do
|
||||
expect(File).to receive(:foreach).and_raise(error_class)
|
||||
allow(File).to receive(:exist?).and_return(true)
|
||||
allow(File).to receive(:foreach).and_raise(error_class)
|
||||
expect(history).to receive(:warn).with(/Unable to read history file:/)
|
||||
expect { history.load }.to_not raise_error
|
||||
end
|
||||
|
||||
it "handles #{error_class} failure to write history" do
|
||||
Pry.config.history_save = true
|
||||
|
||||
# restore default saver
|
||||
history.saver = history.method(:save_to_file)
|
||||
|
||||
expect(File).to receive(:open).with(file_path, "a", 0o600).and_raise(error_class)
|
||||
expect(history).to receive(:warn).with(/Unable to write history file:/)
|
||||
expect { history.push("anything") }.to_not raise_error
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
require 'rbconfig'
|
||||
|
||||
RSpec.describe 'Bundler' do
|
||||
let(:ruby) { RbConfig.ruby.shellescape }
|
||||
let(:pry_dir) { File.expand_path(File.join(__FILE__, '../../../lib')).shellescape }
|
||||
RSpec.describe 'Bundler', slow: true do
|
||||
let(:ruby) { RbConfig.ruby }
|
||||
let(:pry_dir) { File.expand_path(File.join(__FILE__, '../../../lib')) }
|
||||
|
||||
context "when Pry requires Gemfile, which doesn't specify Pry as a dependency" do
|
||||
it "loads auto-completion correctly" do
|
||||
|
@ -23,7 +23,7 @@ RSpec.describe 'Bundler' do
|
|||
end
|
||||
exit 42 if Pry.config.completer
|
||||
RUBY
|
||||
`#{ruby} -I#{pry_dir} -e'#{code}'`
|
||||
IO.popen([ruby, '-I', pry_dir, '-e', code, err: [:child, :out]], &:read)
|
||||
expect($CHILD_STATUS.exitstatus).to eq(42)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,39 +2,48 @@
|
|||
|
||||
require 'rbconfig'
|
||||
|
||||
RSpec.describe 'The bin/pry CLI' do
|
||||
let(:ruby) { RbConfig.ruby.shellescape }
|
||||
let(:pry_dir) { File.expand_path(File.join(__FILE__, '../../../lib')).shellescape }
|
||||
let(:clean_output) do
|
||||
# Pry will emit silent garbage because of our auto indent feature.
|
||||
# This lambda cleans the output of that garbage.
|
||||
->(out) { out.strip.sub("\e[0G", "") }
|
||||
end
|
||||
RSpec.describe 'The bin/pry CLI', slow: true do
|
||||
|
||||
let(:call_pry) {
|
||||
->(*args) {
|
||||
pry_dir = File.expand_path(File.join(__FILE__, '../../../lib'))
|
||||
|
||||
# the :err option is equivalent to 2>&1
|
||||
out = IO.popen([RbConfig.ruby, "-I", pry_dir, 'bin/pry', *args, err: [:child, :out]], &:read)
|
||||
status = $CHILD_STATUS
|
||||
|
||||
# Pry will emit silent garbage because of our auto indent feature.
|
||||
# This lambda cleans the output of that garbage.
|
||||
out = out.strip.sub(/^\e\[0[FG]/, "")
|
||||
|
||||
[out, status]
|
||||
}
|
||||
}
|
||||
|
||||
context "ARGV forwarding" do
|
||||
let(:code) { "p(ARGV) and exit".shellescape }
|
||||
let(:code) { "p(ARGV) and exit" }
|
||||
|
||||
it "forwards ARGV as an empty array when - is passed without following arguments" do
|
||||
out = clean_output.call(`#{ruby} -I#{pry_dir} bin/pry -e #{code} -`)
|
||||
out, status = call_pry.call('-e', code, '-')
|
||||
expect(status).to be_success
|
||||
expect(out).to eq([].inspect)
|
||||
end
|
||||
|
||||
it "forwards ARGV as an empty array when -- is passed without following arguments" do
|
||||
out = clean_output.call(`#{ruby} -I#{pry_dir} bin/pry -e #{code} --`)
|
||||
out, status = call_pry.call('-e', code, '--')
|
||||
expect(status).to be_success
|
||||
expect(out).to eq([].inspect)
|
||||
end
|
||||
|
||||
it "forwards its remaining arguments as ARGV when - is passed" do
|
||||
out = clean_output.call(
|
||||
`#{ruby} -I#{pry_dir} bin/pry -e #{code} - 1 -foo --bar --baz daz`
|
||||
)
|
||||
out, status = call_pry.call('-e', code, '-', '1', '-foo', '--bar', '--baz', 'daz')
|
||||
expect(status).to be_success
|
||||
expect(out).to eq(%w[1 -foo --bar --baz daz].inspect)
|
||||
end
|
||||
|
||||
it "forwards its remaining arguments as ARGV when -- is passed" do
|
||||
out = clean_output.call(
|
||||
`#{ruby} -I#{pry_dir} bin/pry -e #{code} -- 1 -foo --bar --baz daz`
|
||||
)
|
||||
out, status = call_pry.call('-e', code, '--', '1', '-foo', '--bar', '--baz', 'daz')
|
||||
expect(status).to be_success
|
||||
expect(out).to eq(%w[1 -foo --bar --baz daz].inspect)
|
||||
end
|
||||
end
|
||||
|
@ -42,19 +51,15 @@ RSpec.describe 'The bin/pry CLI' do
|
|||
context '-I path' do
|
||||
it 'adds an additional path to $LOAD_PATH' do
|
||||
code = 'p($LOAD_PATH) and exit'
|
||||
out = clean_output.call(
|
||||
`#{ruby} -I#{pry_dir} bin/pry -I /added/at/cli -e '#{code}'`
|
||||
)
|
||||
out, status = call_pry.call('-I', '/added/at/cli', '-e', code)
|
||||
expect(status).to be_success
|
||||
expect(out).to include('/added/at/cli')
|
||||
end
|
||||
|
||||
it 'adds multiple additional paths to $LOAD_PATH' do
|
||||
code = 'p($LOAD_PATH) and exit'
|
||||
out = clean_output.call(
|
||||
# rubocop:disable Metrics/LineLength
|
||||
`#{ruby} -I#{pry_dir} bin/pry -I /added-1/at/cli -I /added/at/cli/also -e '#{code}'`
|
||||
# rubocop:enable Metrics/LineLength
|
||||
)
|
||||
out, status = call_pry.call('-I', '/added-1/at/cli', '-I', '/added/at/cli/also', '-e', code)
|
||||
expect(status).to be_success
|
||||
expect(out).to include('/added-1/at/cli')
|
||||
expect(out).to include('/added/at/cli/also')
|
||||
end
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "shellwords"
|
||||
require 'rbconfig'
|
||||
|
||||
RSpec.describe "Hanami integration" do
|
||||
RSpec.describe "Hanami integration", slow: true do
|
||||
before :all do
|
||||
@ruby = RbConfig.ruby.shellescape
|
||||
@pry_dir = File.expand_path(File.join(__FILE__, '../../../lib')).shellescape
|
||||
@ruby = RbConfig.ruby
|
||||
@pry_dir = File.expand_path(File.join(__FILE__, '../../../lib'))
|
||||
end
|
||||
|
||||
it "does not enter an infinite loop (#1471, #1621)" do
|
||||
|
@ -36,7 +35,7 @@ RSpec.describe "Hanami integration" do
|
|||
Timeout.timeout(1) { Action.new.call("define prison, in the abstract sense") }
|
||||
exit 42
|
||||
RUBY
|
||||
`#{@ruby} -I#{@pry_dir} -e'#{code}'`
|
||||
IO.popen([@ruby, '-I', @pry_dir, '-e', code, err: [:child, :out]], &:read)
|
||||
expect($CHILD_STATUS.exitstatus).to eq(42)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
# These specs ensure that Pry doesn't require readline until the first time a
|
||||
# REPL is started.
|
||||
|
||||
require "shellwords"
|
||||
require 'rbconfig'
|
||||
|
||||
RSpec.describe "Readline" do
|
||||
RSpec.describe "Readline", slow: true do
|
||||
before :all do
|
||||
@ruby = RbConfig.ruby.shellescape
|
||||
@pry_dir = File.expand_path(File.join(__FILE__, '../../../lib')).shellescape
|
||||
@ruby = RbConfig.ruby
|
||||
@pry_dir = File.expand_path(File.join(__FILE__, '../../../lib'))
|
||||
end
|
||||
|
||||
it "is not loaded on requiring 'pry'" do
|
||||
|
@ -17,7 +16,8 @@ RSpec.describe "Readline" do
|
|||
require "pry"
|
||||
p defined?(Readline)
|
||||
RUBY
|
||||
expect(`#{@ruby} -I #{@pry_dir} -e '#{code}'`).to eq("nil\n")
|
||||
out = IO.popen([@ruby, '-I', @pry_dir, '-e', code, err: [:child, :out]], &:read)
|
||||
expect(out).to eq("nil\n")
|
||||
end
|
||||
|
||||
it "is loaded on invoking 'pry'" do
|
||||
|
@ -26,7 +26,8 @@ RSpec.describe "Readline" do
|
|||
Pry.start self, input: StringIO.new("exit-all"), output: StringIO.new
|
||||
puts defined?(Readline)
|
||||
RUBY
|
||||
expect(`#{@ruby} -I #{@pry_dir} -e '#{code}'`.end_with?("constant\n")).to eq(true)
|
||||
out = IO.popen([@ruby, '-I', @pry_dir, '-e', code, err: [:child, :out]], &:read)
|
||||
expect(out).to end_with("constant\n")
|
||||
end
|
||||
|
||||
it "is not loaded on invoking 'pry' if Pry.input is set" do
|
||||
|
@ -36,6 +37,7 @@ RSpec.describe "Readline" do
|
|||
Pry.start self, output: StringIO.new
|
||||
p defined?(Readline)
|
||||
RUBY
|
||||
expect(`#{@ruby} -I #{@pry_dir} -e '#{code}'`.end_with?("nil\n")).to eq(true)
|
||||
out = IO.popen([@ruby, '-I', @pry_dir, '-e', code, err: [:child, :out]], &:read)
|
||||
expect(out).to end_with("nil\n")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -87,7 +87,7 @@ describe Pry::REPL do
|
|||
end
|
||||
end
|
||||
|
||||
ReplTester.start(commands: set) do
|
||||
ReplTester.start(commands: set, auto_indent: true) do
|
||||
input 'def x'
|
||||
output ''
|
||||
prompt(/\* $/)
|
||||
|
@ -125,6 +125,7 @@ describe Pry::REPL do
|
|||
|
||||
describe "autoindent" do
|
||||
it "should raise no exception when indented with a tab" do
|
||||
allow(Pry::Helpers::BaseHelpers).to receive(:use_ansi_codes?).and_return(true)
|
||||
ReplTester.start(correct_indent: true, auto_indent: true) do
|
||||
output = @pry.config.output
|
||||
def output.tty?
|
||||
|
|
Loading…
Add table
Reference in a new issue