Skip to content

Commit

Permalink
Page ls's output (#657)
Browse files Browse the repository at this point in the history
* Page ls command's output

* Use Pager.page_content in show_cmds too
  • Loading branch information
st0012 authored Jul 26, 2023
1 parent f94e8a6 commit 82d1687
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 8 deletions.
17 changes: 12 additions & 5 deletions lib/irb/cmd/ls.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# frozen_string_literal: true

require "reline"
require "stringio"
require_relative "nop"
require_relative "../pager"
require_relative "../color"

module IRB
Expand Down Expand Up @@ -33,7 +35,7 @@ def execute(*arg, grep: nil)
o.dump("instance variables", obj.instance_variables)
o.dump("class variables", klass.class_variables)
o.dump("locals", locals)
nil
o.print_result
end

def dump_methods(o, klass, obj)
Expand Down Expand Up @@ -77,6 +79,11 @@ class Output
def initialize(grep: nil)
@grep = grep
@line_width = screen_width - MARGIN.length # right padding
@io = StringIO.new
end

def print_result
Pager.page_content(@io.string)
end

def dump(name, strs)
Expand All @@ -85,12 +92,12 @@ def dump(name, strs)
return if strs.empty?

# Attempt a single line
print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
@io.print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
puts strs.join(MARGIN)
@io.puts strs.join(MARGIN)
return
end
puts
@io.puts

# Dump with the largest # of columns that fits on a line
cols = strs.size
Expand All @@ -99,7 +106,7 @@ def dump(name, strs)
end
widths = col_widths(strs, cols: cols)
strs.each_slice(cols) do |ss|
puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
@io.puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
end
end

Expand Down
4 changes: 1 addition & 3 deletions lib/irb/cmd/show_cmds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def execute(*args)
output.puts
end

Pager.page do |io|
io.puts output.string
end
Pager.page_content(output.string)
end
end
end
Expand Down
25 changes: 25 additions & 0 deletions lib/irb/pager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ class Pager
PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq

class << self
def page_content(content)
if content_exceeds_screen_height?(content)
page do |io|
io.puts content
end
else
$stdout.puts content
end
end

def page
if STDIN.tty? && pager = setup_pager
begin
Expand All @@ -30,6 +40,21 @@ def page

private

def content_exceeds_screen_height?(content)
screen_height, screen_width = begin
Reline.get_screen_size
rescue Errno::EINVAL
[24, 80]
end

pageable_height = screen_height - 3 # leave some space for previous and the current prompt

# If the content has more lines than the pageable height
content.lines.count > pageable_height ||
# Or if the content is a few long lines
pageable_height * screen_width < Reline::Unicode.calculate_width(content, true)
end

def setup_pager
require 'shellwords'

Expand Down
10 changes: 10 additions & 0 deletions test/irb/test_cmd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,16 @@ def test_show_cmds
end

class LsTest < CommandTestCase
def setup
STDIN.singleton_class.define_method :tty? do
false
end
end

def teardown
STDIN.singleton_class.remove_method :tty?
end

def test_ls
out, err = execute_lines(
"class P\n",
Expand Down
48 changes: 48 additions & 0 deletions test/irb/yamatanooroti/test_rendering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,54 @@ def test_show_cmds_with_pager_can_quit_with_ctrl_c
assert_match(/foobar/, screen)
end

def test_pager_page_content_pages_output_when_it_does_not_fit_in_the_screen_because_of_total_length
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("IRB::Pager.page_content('a' * (80 * 8))\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}/, screen)
# because pager is invoked, foobar will not be evaluated
assert_not_match(/foobar/, screen)
end

def test_pager_page_content_pages_output_when_it_does_not_fit_in_the_screen_because_of_screen_height
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("IRB::Pager.page_content('a\n' * 8)\n")
write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
close

screen = result.join("\n").sub(/\n*\z/, "\n")
assert_match(/(a\n){8}/, screen)
# because pager is invoked, foobar will not be evaluated
assert_not_match(/foobar/, screen)
end

def test_pager_page_content_doesnt_page_output_when_it_fits_in_the_screen
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("IRB::Pager.page_content('a' * (80 * 7))\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}/, screen)
# because pager is not invoked, foobar will be evaluated
assert_match(/foobar/, screen)
end

private

def write_irbrc(content)
Expand Down

0 comments on commit 82d1687

Please sign in to comment.