[ruby/irb] Page evaluation result's output

(https://github.com/ruby/irb/pull/784)

* Page evaluation result's output

This will make it easier to work with long output that exceeds the terminal's height.

* Use consistent TERM in rendering tests

This makes sure we get consistent result on all platforms.

https://github.com/ruby/irb/commit/4fedce93d3
This commit is contained in:
Stan Lo 2023-11-30 15:22:17 +00:00 коммит произвёл git
Родитель cc393b4f80
Коммит f193f96d31
7 изменённых файлов: 67 добавлений и 25 удалений

Просмотреть файл

@ -20,6 +20,7 @@ require_relative "irb/color"
require_relative "irb/version" require_relative "irb/version"
require_relative "irb/easter-egg" require_relative "irb/easter-egg"
require_relative "irb/debug" require_relative "irb/debug"
require_relative "irb/pager"
# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby # IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
# expressions read from the standard input. # expressions read from the standard input.
@ -859,11 +860,12 @@ module IRB
end end
end end
end end
if multiline_p && @context.newline_before_multiline_output? if multiline_p && @context.newline_before_multiline_output?
printf @context.return_format, "\n#{str}" str = "\n" + str
else
printf @context.return_format, str
end end
Pager.page_content(format(@context.return_format, str), retain_content: true)
end end
# Outputs the local variables to this current session, including # Outputs the local variables to this current session, including

Просмотреть файл

@ -7,9 +7,9 @@ module IRB
PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq
class << self class << self
def page_content(content) def page_content(content, **options)
if content_exceeds_screen_height?(content) if content_exceeds_screen_height?(content)
page do |io| page(**options) do |io|
io.puts content io.puts content
end end
else else
@ -17,8 +17,8 @@ module IRB
end end
end end
def page def page(retain_content: false)
if IRB.conf[:USE_PAGER] && STDIN.tty? && pager = setup_pager if IRB.conf[:USE_PAGER] && STDIN.tty? && pager = setup_pager(retain_content: retain_content)
begin begin
pid = pager.pid pid = pager.pid
yield pager yield pager
@ -55,19 +55,20 @@ module IRB
pageable_height * screen_width < Reline::Unicode.calculate_width(content, true) pageable_height * screen_width < Reline::Unicode.calculate_width(content, true)
end end
def setup_pager def setup_pager(retain_content:)
require 'shellwords' require 'shellwords'
PAGE_COMMANDS.each do |pager| PAGE_COMMANDS.each do |pager_cmd|
pager = Shellwords.split(pager) cmd = Shellwords.split(pager_cmd)
next if pager.empty? next if cmd.empty?
if pager.first == 'less' if cmd.first == 'less'
pager << '-R' unless pager.include?('-R') cmd << '-R' unless cmd.include?('-R')
cmd << '-X' if retain_content && !cmd.include?('-X')
end end
begin begin
io = IO.popen(pager, 'w') io = IO.popen(cmd, 'w')
rescue rescue
next next
end end

Просмотреть файл

@ -93,6 +93,10 @@ module TestIRB
if ruby_core? if ruby_core?
omit "This test works only under ruby/irb" omit "This test works only under ruby/irb"
end end
write_rc <<~RUBY
IRB.conf[:USE_PAGER] = false
RUBY
end end
def teardown def teardown
@ -197,8 +201,14 @@ module TestIRB
end end
def write_rc(content) def write_rc(content)
@irbrc = Tempfile.new('irbrc') # Append irbrc content if a tempfile for it already exists
@tmpfiles << @irbrc if @irbrc
@irbrc = File.open(@irbrc, "a")
else
@irbrc = Tempfile.new('irbrc')
@tmpfiles << @irbrc
end
@irbrc.write(content) @irbrc.write(content)
@irbrc.close @irbrc.close
@envs['IRBRC'] = @irbrc.path @envs['IRBRC'] = @irbrc.path

Просмотреть файл

@ -11,6 +11,7 @@ module TestIRB
IRB.init_config(nil) IRB.init_config(nil)
IRB.conf[:USE_SINGLELINE] = false IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false IRB.conf[:VERBOSE] = false
IRB.conf[:USE_PAGER] = false
workspace = IRB::WorkSpace.new(Object.new) workspace = IRB::WorkSpace.new(Object.new)
@context = IRB::Context.new(nil, workspace, TestInputMethod.new) @context = IRB::Context.new(nil, workspace, TestInputMethod.new)

Просмотреть файл

@ -346,10 +346,6 @@ module TestIRB
end end
def test_show_cmds_display_different_content_when_debugger_is_enabled def test_show_cmds_display_different_content_when_debugger_is_enabled
write_rc <<~RUBY
IRB.conf[:USE_PAGER] = false
RUBY
write_ruby <<~'ruby' write_ruby <<~'ruby'
binding.irb binding.irb
ruby ruby

Просмотреть файл

@ -6,10 +6,6 @@ require_relative "helper"
module TestIRB module TestIRB
class InputTest < IntegrationTestCase class InputTest < IntegrationTestCase
def test_symbol_aliases_are_handled_correctly def test_symbol_aliases_are_handled_correctly
write_rc <<~RUBY
IRB.conf[:USE_PAGER] = false
RUBY
write_ruby <<~'RUBY' write_ruby <<~'RUBY'
class Foo class Foo
end end
@ -26,7 +22,6 @@ module TestIRB
def test_symbol_aliases_are_handled_correctly_with_singleline_mode def test_symbol_aliases_are_handled_correctly_with_singleline_mode
write_rc <<~RUBY write_rc <<~RUBY
IRB.conf[:USE_PAGER] = false
IRB.conf[:USE_SINGLELINE] = true IRB.conf[:USE_SINGLELINE] = true
RUBY RUBY

Просмотреть файл

@ -10,6 +10,8 @@ end
class IRB::RenderingTest < Yamatanooroti::TestCase class IRB::RenderingTest < Yamatanooroti::TestCase
def setup def setup
@original_term = ENV['TERM']
ENV['TERM'] = "xterm-256color"
@pwd = Dir.pwd @pwd = Dir.pwd
suffix = '%010d' % Random.rand(0..65535) suffix = '%010d' % Random.rand(0..65535)
@tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}") @tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
@ -27,6 +29,7 @@ class IRB::RenderingTest < Yamatanooroti::TestCase
def teardown def teardown
FileUtils.rm_rf(@tmpdir) FileUtils.rm_rf(@tmpdir)
ENV['IRBRC'] = @irbrc_backup ENV['IRBRC'] = @irbrc_backup
ENV['TERM'] = @original_term
ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT'] ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT']
end end
@ -377,6 +380,40 @@ class IRB::RenderingTest < Yamatanooroti::TestCase
assert_match(/foobar/, screen) assert_match(/foobar/, screen)
end end
def test_long_evaluation_output_is_paged
write_irbrc <<~'LINES'
puts 'start IRB'
require "irb/pager"
LINES
start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
write("'a' * 80 * 11\n")
write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
close
screen = result.join("\n").sub(/\n*\z/, "\n")
assert_match(/(a{80}\n){8}/, screen)
# because pager is invoked, foobar will not be evaluated
assert_not_match(/foobar/, screen)
end
def test_long_evaluation_output_is_preserved_after_paging
write_irbrc <<~'LINES'
puts 'start IRB'
require "irb/pager"
LINES
start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
write("'a' * 80 * 11\n")
write("q") # quit pager
write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
close
screen = result.join("\n").sub(/\n*\z/, "\n")
# confirm pager has exited
assert_match(/foobar/, screen)
# confirm output is preserved
assert_match(/(a{80}\n){6}/, screen)
end
def test_debug_integration_hints_debugger_commands def test_debug_integration_hints_debugger_commands
write_irbrc <<~'LINES' write_irbrc <<~'LINES'
IRB.conf[:USE_COLORIZE] = false IRB.conf[:USE_COLORIZE] = false