Skip to content

Commit 4fedce9

Browse files
authored
Page evaluation result's output (#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.
1 parent a4868a5 commit 4fedce9

File tree

7 files changed

+67
-25
lines changed

7 files changed

+67
-25
lines changed

lib/irb.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
require_relative "irb/version"
2121
require_relative "irb/easter-egg"
2222
require_relative "irb/debug"
23+
require_relative "irb/pager"
2324

2425
# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
2526
# expressions read from the standard input.
@@ -859,11 +860,12 @@ def output_value(omit = false) # :nodoc:
859860
end
860861
end
861862
end
863+
862864
if multiline_p && @context.newline_before_multiline_output?
863-
printf @context.return_format, "\n#{str}"
864-
else
865-
printf @context.return_format, str
865+
str = "\n" + str
866866
end
867+
868+
Pager.page_content(format(@context.return_format, str), retain_content: true)
867869
end
868870

869871
# Outputs the local variables to this current session, including

lib/irb/pager.rb

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ class Pager
77
PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq
88

99
class << self
10-
def page_content(content)
10+
def page_content(content, **options)
1111
if content_exceeds_screen_height?(content)
12-
page do |io|
12+
page(**options) do |io|
1313
io.puts content
1414
end
1515
else
1616
$stdout.puts content
1717
end
1818
end
1919

20-
def page
21-
if IRB.conf[:USE_PAGER] && STDIN.tty? && pager = setup_pager
20+
def page(retain_content: false)
21+
if IRB.conf[:USE_PAGER] && STDIN.tty? && pager = setup_pager(retain_content: retain_content)
2222
begin
2323
pid = pager.pid
2424
yield pager
@@ -55,19 +55,20 @@ def content_exceeds_screen_height?(content)
5555
pageable_height * screen_width < Reline::Unicode.calculate_width(content, true)
5656
end
5757

58-
def setup_pager
58+
def setup_pager(retain_content:)
5959
require 'shellwords'
6060

61-
PAGE_COMMANDS.each do |pager|
62-
pager = Shellwords.split(pager)
63-
next if pager.empty?
61+
PAGE_COMMANDS.each do |pager_cmd|
62+
cmd = Shellwords.split(pager_cmd)
63+
next if cmd.empty?
6464

65-
if pager.first == 'less'
66-
pager << '-R' unless pager.include?('-R')
65+
if cmd.first == 'less'
66+
cmd << '-R' unless cmd.include?('-R')
67+
cmd << '-X' if retain_content && !cmd.include?('-X')
6768
end
6869

6970
begin
70-
io = IO.popen(pager, 'w')
71+
io = IO.popen(cmd, 'w')
7172
rescue
7273
next
7374
end

test/irb/helper.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def setup
9393
if ruby_core?
9494
omit "This test works only under ruby/irb"
9595
end
96+
97+
write_rc <<~RUBY
98+
IRB.conf[:USE_PAGER] = false
99+
RUBY
96100
end
97101

98102
def teardown
@@ -197,8 +201,14 @@ def write_ruby(program)
197201
end
198202

199203
def write_rc(content)
200-
@irbrc = Tempfile.new('irbrc')
201-
@tmpfiles << @irbrc
204+
# Append irbrc content if a tempfile for it already exists
205+
if @irbrc
206+
@irbrc = File.open(@irbrc, "a")
207+
else
208+
@irbrc = Tempfile.new('irbrc')
209+
@tmpfiles << @irbrc
210+
end
211+
202212
@irbrc.write(content)
203213
@irbrc.close
204214
@envs['IRBRC'] = @irbrc.path

test/irb/test_context.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def setup
1111
IRB.init_config(nil)
1212
IRB.conf[:USE_SINGLELINE] = false
1313
IRB.conf[:VERBOSE] = false
14+
IRB.conf[:USE_PAGER] = false
1415
workspace = IRB::WorkSpace.new(Object.new)
1516
@context = IRB::Context.new(nil, workspace, TestInputMethod.new)
1617

test/irb/test_debug_cmd.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,6 @@ def test_help_command_is_delegated_to_the_debugger
346346
end
347347

348348
def test_show_cmds_display_different_content_when_debugger_is_enabled
349-
write_rc <<~RUBY
350-
IRB.conf[:USE_PAGER] = false
351-
RUBY
352-
353349
write_ruby <<~'ruby'
354350
binding.irb
355351
ruby

test/irb/test_irb.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
module TestIRB
77
class InputTest < IntegrationTestCase
88
def test_symbol_aliases_are_handled_correctly
9-
write_rc <<~RUBY
10-
IRB.conf[:USE_PAGER] = false
11-
RUBY
12-
139
write_ruby <<~'RUBY'
1410
class Foo
1511
end
@@ -26,7 +22,6 @@ class Foo
2622

2723
def test_symbol_aliases_are_handled_correctly_with_singleline_mode
2824
write_rc <<~RUBY
29-
IRB.conf[:USE_PAGER] = false
3025
IRB.conf[:USE_SINGLELINE] = true
3126
RUBY
3227

test/irb/yamatanooroti/test_rendering.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
class IRB::RenderingTest < Yamatanooroti::TestCase
1212
def setup
13+
@original_term = ENV['TERM']
14+
ENV['TERM'] = "xterm-256color"
1315
@pwd = Dir.pwd
1416
suffix = '%010d' % Random.rand(0..65535)
1517
@tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
@@ -27,6 +29,7 @@ def setup
2729
def teardown
2830
FileUtils.rm_rf(@tmpdir)
2931
ENV['IRBRC'] = @irbrc_backup
32+
ENV['TERM'] = @original_term
3033
ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT']
3134
end
3235

@@ -377,6 +380,40 @@ def test_pager_page_content_doesnt_page_output_when_it_fits_in_the_screen
377380
assert_match(/foobar/, screen)
378381
end
379382

383+
def test_long_evaluation_output_is_paged
384+
write_irbrc <<~'LINES'
385+
puts 'start IRB'
386+
require "irb/pager"
387+
LINES
388+
start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
389+
write("'a' * 80 * 11\n")
390+
write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
391+
close
392+
393+
screen = result.join("\n").sub(/\n*\z/, "\n")
394+
assert_match(/(a{80}\n){8}/, screen)
395+
# because pager is invoked, foobar will not be evaluated
396+
assert_not_match(/foobar/, screen)
397+
end
398+
399+
def test_long_evaluation_output_is_preserved_after_paging
400+
write_irbrc <<~'LINES'
401+
puts 'start IRB'
402+
require "irb/pager"
403+
LINES
404+
start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
405+
write("'a' * 80 * 11\n")
406+
write("q") # quit pager
407+
write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
408+
close
409+
410+
screen = result.join("\n").sub(/\n*\z/, "\n")
411+
# confirm pager has exited
412+
assert_match(/foobar/, screen)
413+
# confirm output is preserved
414+
assert_match(/(a{80}\n){6}/, screen)
415+
end
416+
380417
def test_debug_integration_hints_debugger_commands
381418
write_irbrc <<~'LINES'
382419
IRB.conf[:USE_COLORIZE] = false

0 commit comments

Comments
 (0)