From 21aea8f316662beb943c1054df773e9297eef90c Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Mon, 28 Oct 2013 21:25:13 -0700 Subject: [PATCH 01/14] Refactor Pager and subclasses to support streaming --- lib/pry/helpers/base_helpers.rb | 7 +-- lib/pry/pager.rb | 100 ++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 29 deletions(-) diff --git a/lib/pry/helpers/base_helpers.rb b/lib/pry/helpers/base_helpers.rb index 9c1e793b..886a295c 100644 --- a/lib/pry/helpers/base_helpers.rb +++ b/lib/pry/helpers/base_helpers.rb @@ -114,14 +114,13 @@ class Pry $stdout end - if text.lines.count < Pry::Pager.page_size || !Pry.pager - out.puts text - else + if Pry.pager Pry::Pager.page(text) + else + out.puts text end rescue Errno::ENOENT Pry::Pager.page(text, :simple) - rescue Errno::EPIPE end # @param [String] arg_string The object path expressed as a string. diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 4bce42ae..9d3d0794 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -1,52 +1,98 @@ require 'pry/terminal' + +# A Pry::Pager is an IO-like object that accepts text and either prints it +# immediately, prints it one page at a time, or streams it to an external +# program to print one page at a time. class Pry::Pager + class StopPaging < StandardError + end + # @param [String] text # A piece of text to run through a pager. - # @param [Symbol?] pager + # @param [Symbol?] pager_type # `:simple` -- Use the pure ruby pager. # `:system` -- Use the system pager (less) or the environment variable # $PAGER if set. # `nil` -- Infer what pager to use from the environment. What this # really means is that JRuby and systems that do not have # access to 'less' will run through the pure ruby pager. - def self.page(text, pager = nil) - case pager + def self.page(text, pager_type = nil) + pager = best_available($stdout, pager_type) + pager << text + ensure + pager.close if pager + end + + def self.best_available(output, pager_type = nil) + case pager_type when nil no_pager = !SystemPager.available? if no_pager || Pry::Helpers::BaseHelpers.jruby? - SimplePager.new(text).page + SimplePager.new(output) else - SystemPager.new(text).page + SystemPager.new(output) end when :simple - SimplePager.new(text).page + SimplePager.new(output) when :system - SystemPager.new(text).page + SystemPager.new(output) else raise "'#{pager}' is not a recognized pager." end end def self.page_size - @page_size ||= Pry::Terminal.height! + Pry::Terminal.height! end - def initialize(text) - @text = text + def initialize(out) + @out = out + end + + def page_size + @page_size ||= self.class.page_size + end + + def puts(str) + print "#{str.chomp}\n" + end + + def write(str) + @out.write str + end + + def print(str) + write str + end + alias << print + + def close + # no-op for base pager, but important for subclasses end class SimplePager < Pry::Pager - def page - # The pager size minus the number of lines used by the simple pager info bar. - page_size = Pry::Pager.page_size - 3 - text_array = @text.lines.to_a + # Window height minus the number of lines used by the info bar. + def self.page_size + super - 3 + end - text_array.each_slice(page_size) do |chunk| - puts chunk.join - break if chunk.size < page_size - if text_array.size > page_size - puts "\n --- Press enter to continue ( q to break ) --- " - break if $stdin.gets.chomp == "q" + def initialize(*) + super + @lines_printed = 0 + end + + def write(str) + page_size = self.class.page_size + + str.lines.each do |line| + @out.write line + @lines_printed += 1 if line.end_with?("\n") + + if @lines_printed >= page_size + @out.puts "\n --- Press enter to continue " \ + "( q to break ) --- " + raise StopPaging if $stdin.gets.chomp == "q" + @lines_printed = 0 end end end @@ -79,13 +125,17 @@ class Pry::Pager def initialize(*) super - @pager = SystemPager.default_pager + @pager = IO.popen(SystemPager.default_pager, 'w') end - def page - IO.popen(@pager, 'w') do |io| - io.write @text - end + def write(str) + @pager.write str + rescue Errno::EPIPE + # Don't worry about ^C. + end + + def close + @pager.close if @pager end end end From 54e47297272d64820f30101e306e67289a326252 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Mon, 28 Oct 2013 21:36:00 -0700 Subject: [PATCH 02/14] Stream colorized inspect output into pagers --- lib/pry.rb | 38 +++++++------------------------------- lib/pry/color_printer.rb | 33 +++++++++++++++++++++++++++++++++ lib/pry/pager.rb | 1 - spec/pry_output_spec.rb | 3 ++- 4 files changed, 42 insertions(+), 33 deletions(-) create mode 100644 lib/pry/color_printer.rb diff --git a/lib/pry.rb b/lib/pry.rb index 93725652..3732fb86 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -24,37 +24,12 @@ class Pry end def self.output_with_default_format(output, value, options = {}) - stringified = begin - value.pretty_inspect - rescue RescuableException - nil - end - - unless String === stringified - # Read the class name off of the singleton class to provide a default - # inspect. - eig = class << value; self; end - klass = Pry::Method.safe_send(eig, :ancestors).first - id = value.__id__.to_s(16) rescue 0 - stringified = "#<#{klass}:0x#{id}>" - end - - nonce = SecureRandom.hex(4) - - stringified.gsub!(/# #{result}" if options[:hashrocket] - Helpers::BaseHelpers.stagger_output(result, output) + pager = Pry::Pager.best_available(output) + pager.print "=> " if options[:hashrocket] + Pry::ColorPrinter.pp(value, pager) + rescue Pry::Pager::StopPaging + ensure + pager.close if pager end # may be convenient when working with enormous objects and @@ -225,6 +200,7 @@ require 'pry/core_extensions' require 'pry/pry_class' require 'pry/pry_instance' require 'pry/cli' +require 'pry/color_printer' require 'pry/pager' require 'pry/terminal' require 'pry/editor' diff --git a/lib/pry/color_printer.rb b/lib/pry/color_printer.rb new file mode 100644 index 00000000..cf9f915c --- /dev/null +++ b/lib/pry/color_printer.rb @@ -0,0 +1,33 @@ +# PP subclass for streaming inspect output in color. +class Pry + class ColorPrinter < ::PP + OBJ_COLOR = begin + code = CodeRay::Encoders::Terminal::TOKEN_COLORS[:keyword] + if code.start_with? "\e" + code + else + "\e[0m\e[0;#{code}m" + end + end + + def self.pp(obj, out = $>, width = 79) + q = ColorPrinter.new(out, width) + q.guard_inspect_key { q.pp obj } + q.flush + out << "\n" + end + + def text(str, width = str.length) + super *if !Pry.color + [str, width] + # Don't recolorize output with color [Issue #751] + elsif str.include?("\e[") + ["#{str}\e[0m", width] + elsif str.start_with?('#<') || str == '=' || str == '>' + ["#{OBJ_COLOR}#{str}\e[0m", width] + else + [CodeRay.scan(str, :ruby).term, width] + end + end + end +end diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 9d3d0794..cf681f42 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -131,7 +131,6 @@ class Pry::Pager def write(str) @pager.write str rescue Errno::EPIPE - # Don't worry about ^C. end def close diff --git a/spec/pry_output_spec.rb b/spec/pry_output_spec.rb index d90f77a2..5fe2d22f 100644 --- a/spec/pry_output_spec.rb +++ b/spec/pry_output_spec.rb @@ -60,8 +60,9 @@ describe Pry do it "should colorize strings as though they were ruby" do accumulator = StringIO.new + colorized = CodeRay.scan("[1]", :ruby).term Pry.config.print.call(accumulator, [1]) - accumulator.string.should == "=> [\e[1;34m1\e[0m]\e[0m\n" + accumulator.string.should == "=> #{colorized}\n" end it "should not colorize strings that already include color" do From 52c18a576c5e45457b95664f1a0b8094828e9025 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 14:14:51 -0700 Subject: [PATCH 03/14] Track output size more accurately --- lib/pry/pager.rb | 120 +++++++++++++++++++++++++++++++++++---------- spec/pager_spec.rb | 67 +++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 27 deletions(-) create mode 100644 spec/pager_spec.rb diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index cf681f42..85c74776 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -41,69 +41,68 @@ class Pry::Pager end end - def self.page_size - Pry::Terminal.height! - end - def initialize(out) @out = out end - def page_size - @page_size ||= self.class.page_size - end - def puts(str) print "#{str.chomp}\n" end - def write(str) - @out.write str - end - def print(str) write str end alias << print + def write(str) + @out.write str + end + def close # no-op for base pager, but important for subclasses end - class SimplePager < Pry::Pager - # Window height minus the number of lines used by the info bar. - def self.page_size - super - 3 - end + private + def height + @height ||= Pry::Terminal.height! + end + + def width + @width ||= Pry::Terminal.width! + end + + class SimplePager < Pry::Pager def initialize(*) super - @lines_printed = 0 + @tracker = PageTracker.new(height - 3, width) end def write(str) - page_size = self.class.page_size - str.lines.each do |line| @out.write line - @lines_printed += 1 if line.end_with?("\n") + @tracker.record line - if @lines_printed >= page_size + if @tracker.page? @out.puts "\n --- Press enter to continue " \ "( q to break ) --- " raise StopPaging if $stdin.gets.chomp == "q" - @lines_printed = 0 + @tracker.reset end end end end + # SystemPager buffers output until we're pretty sure it's at least a + # page long, then invokes an external pager and starts streaming + # output to it. If close is called before then, it just prints out + # the buffered content. class SystemPager < Pry::Pager def self.default_pager pager = ENV["PAGER"] || "" # Default to less, and make sure less is being passed the correct options - if pager.strip.empty? or pager =~ /^less\s*/ + if pager.strip.empty? or pager =~ /^less\b/ pager = "less -R -S -F -X" end @@ -125,16 +124,83 @@ class Pry::Pager def initialize(*) super - @pager = IO.popen(SystemPager.default_pager, 'w') + @tracker = PageTracker.new(height, width) + @buffer = "" end def write(str) - @pager.write str + if invoked_pager? + pager.write str + else + @tracker.record str + @buffer << str + + if @tracker.page? + pager.write @buffer + end + end rescue Errno::EPIPE end def close - @pager.close if @pager + if invoked_pager? + pager.close + else + @out.puts @buffer + end + end + + private + + def invoked_pager? + @pager + end + + def pager + @pager ||= IO.popen(self.class.default_pager, 'w') + end + end + + # PageTracker tracks output to determine whether it's likely to take up a + # whole page. This doesn't need to be super precise, but we can use it for + # SimplePager and to avoid invoking the system pager unnecessarily. + # + # One simplifying assumption is that we don't need page? to return true on + # the basis of an incomplete line. Long lines should be counted as multiple + # lines, but we don't have to transition from false to true until we see a + # newline. + class PageTracker + def initialize(rows, cols) + @rows, @cols = rows, cols + reset + end + + def record(str) + str.lines.each do |line| + if line.end_with? "\n" + @row += ((@col + line_length(line) - 1) / @cols) + 1 + @col = 0 + else + @col += line.length + end + end + end + + def page? + @row >= @rows + end + + def reset + @row = 0 + @col = 0 + end + + private + + # Approximation of the printable length of a given line, without the + # newline and without ANSI color codes. + def line_length(line) + line.chomp.gsub(/\e\[[\d;]*m/, '').length end end end diff --git a/spec/pager_spec.rb b/spec/pager_spec.rb new file mode 100644 index 00000000..2221c621 --- /dev/null +++ b/spec/pager_spec.rb @@ -0,0 +1,67 @@ +require "helper" + +describe "Pry::Pager" do + describe "PageTracker" do + before do + @pt = Pry::Pager::PageTracker.new(10, 10) + end + + def record_short_line + @pt.record "012345678\n" + end + + def record_long_line + @pt.record "0123456789012\n" + end + + def record_multiline + @pt.record "0123456789012\n01\n" + end + + def record_string_without_newline + @pt.record "0123456789" + end + + def record_string_with_color_codes + @pt.record(CodeRay.scan("0123456789", :ruby).term + "\n") + end + + it "records short lines that don't add up to a page" do + 9.times { record_short_line } + @pt.page?.should.be.false + end + + it "records short lines that do add up to a page" do + 10.times { record_short_line } + @pt.page?.should.be.true + end + + it "treats a long line as taking up more than one row" do + 4.times { record_long_line } + @pt.page?.should.be.false + record_long_line + @pt.page?.should.be.true + end + + it "records a string with an embedded newline" do + 3.times { record_multiline } + @pt.page?.should.be.false + record_short_line + @pt.page?.should.be.true + end + + it "doesn't count a line until it ends" do + 12.times { record_string_without_newline } + @pt.page?.should.be.false + record_short_line + @pt.page?.should.be.true + end + + it "doesn't count ansi color codes towards length" do + 9.times { record_string_with_color_codes } + @pt.page?.should.be.false + record_string_with_color_codes + @pt.page?.should.be.true + end + end +end From d703320e38397c1b3bf9cb3fc4389f9c741c448b Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 15:09:39 -0700 Subject: [PATCH 04/14] Fix inspect fallback for weird objects --- lib/pry/color_printer.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/pry/color_printer.rb b/lib/pry/color_printer.rb index cf9f915c..e0852b28 100644 --- a/lib/pry/color_printer.rb +++ b/lib/pry/color_printer.rb @@ -24,10 +24,29 @@ class Pry elsif str.include?("\e[") ["#{str}\e[0m", width] elsif str.start_with?('#<') || str == '=' || str == '>' - ["#{OBJ_COLOR}#{str}\e[0m", width] + [highlight_object_literal(str), width] else [CodeRay.scan(str, :ruby).term, width] end end + + def pp(obj) + super + rescue + # Read the class name off of the singleton class to provide a default + # inspect. + eig = class << obj; self; end + klass = Pry::Method.safe_send(eig, :ancestors).first + obj_id = obj.__id__.to_s(16) rescue 0 + str = "#<#{klass}:0x#{obj_id}>" + + text(Pry.color ? highlight_object_literal(str) : str) + end + + private + + def highlight_object_literal(object_literal) + "#{OBJ_COLOR}#{object_literal}\e[0m" + end end end From d0f198bd649baddcea739620f1e7901eb0894ea0 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 15:46:38 -0700 Subject: [PATCH 05/14] Fix a couple of SimplePager bugs * Exiting with q was broken by d703320e38397c1b3bf9cb3fc4389f9c741c448b. * We were only removing color codes in the case where the input string ends with a newline, which means we were overestimating the length of strings that don't end with newlines. After fixing that, SimplePager can accurately page through the inspect output for _pry_. --- lib/pry/color_printer.rb | 4 +++- lib/pry/pager.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pry/color_printer.rb b/lib/pry/color_printer.rb index e0852b28..f7c33e95 100644 --- a/lib/pry/color_printer.rb +++ b/lib/pry/color_printer.rb @@ -32,7 +32,9 @@ class Pry def pp(obj) super - rescue + rescue => e + raise if e.is_a? Pry::Pager::StopPaging + # Read the class name off of the singleton class to provide a default # inspect. eig = class << obj; self; end diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 85c74776..a5239b37 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -181,7 +181,7 @@ class Pry::Pager @row += ((@col + line_length(line) - 1) / @cols) + 1 @col = 0 else - @col += line.length + @col += line_length(line) end end end From 702f0be0a3bcc5c4f3a0b53839beed880522eedd Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 15:54:21 -0700 Subject: [PATCH 06/14] Raise StopPaging when user quits pager --- lib/pry/pager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index a5239b37..431958aa 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -140,6 +140,7 @@ class Pry::Pager end end rescue Errno::EPIPE + raise StopPaging end def close From 394dd52609fd463447ad560baec8a45b0e02cf52 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 18:40:56 -0700 Subject: [PATCH 07/14] Simplify pager selection, make base class a null pager --- lib/pry/helpers/base_helpers.rb | 13 +--- lib/pry/pager.rb | 109 ++++++++++++++++---------------- 2 files changed, 56 insertions(+), 66 deletions(-) diff --git a/lib/pry/helpers/base_helpers.rb b/lib/pry/helpers/base_helpers.rb index 886a295c..143bd404 100644 --- a/lib/pry/helpers/base_helpers.rb +++ b/lib/pry/helpers/base_helpers.rb @@ -98,9 +98,8 @@ class Pry mri? && RUBY_VERSION =~ /1.9/ end - - # Try to use `less` for paging, if it fails then use - # simple_pager. Also do not page if Pry.pager is falsey + # Send the given text through the best available pager (if Pry.pager is + # enabled). Infers where to send the output if used as a mixin. def stagger_output(text, out = nil) out ||= case when respond_to?(:output) @@ -114,13 +113,7 @@ class Pry $stdout end - if Pry.pager - Pry::Pager.page(text) - else - out.puts text - end - rescue Errno::ENOENT - Pry::Pager.page(text, :simple) + Pry::Pager.page(text, out) end # @param [String] arg_string The object path expressed as a string. diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 431958aa..1d98326a 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -7,72 +7,69 @@ class Pry::Pager class StopPaging < StandardError end - # @param [String] text - # A piece of text to run through a pager. - # @param [Symbol?] pager_type - # `:simple` -- Use the pure ruby pager. - # `:system` -- Use the system pager (less) or the environment variable - # $PAGER if set. - # `nil` -- Infer what pager to use from the environment. What this - # really means is that JRuby and systems that do not have - # access to 'less' will run through the pure ruby pager. - def self.page(text, pager_type = nil) - pager = best_available($stdout, pager_type) + # Send the given text through the best available pager (if Pry.pager is + # enabled). + # @param [String] text A piece of text to run through a pager. + # @param [#<<] output ($stdout) An object to send output to. + def self.page(text, out = $stdout) + pager = best_available(out) pager << text ensure pager.close if pager end - def self.best_available(output, pager_type = nil) - case pager_type - when nil - no_pager = !SystemPager.available? - if no_pager || Pry::Helpers::BaseHelpers.jruby? - SimplePager.new(output) - else - SystemPager.new(output) - end - when :simple + # Return an instance of the "best" available pager class -- SystemPager if + # possible, SimplePager if SystemPager isn't available, and NullPager if the + # user has disabled paging. All pagers accept output with #puts, #print, + # #write, and #<<. You must call #close when you're done writing output to a + # pager. + def self.best_available(output) + if !Pry.pager + NullPager.new(output) + elsif !SystemPager.available? || Pry::Helpers::BaseHelpers.jruby? SimplePager.new(output) - when :system - SystemPager.new(output) else - raise "'#{pager}' is not a recognized pager." + SystemPager.new(output) end end - def initialize(out) - @out = out + # A "pager" that actually just prints all output as it comes in. Used when + # Pry.pager is false. + class NullPager + def initialize(out) + @out = out + end + + def puts(str) + print "#{str.chomp}\n" + end + + def print(str) + write str + end + alias << print + + def write(str) + @out.write str + end + + def close + end + + private + + def height + @height ||= Pry::Terminal.height! + end + + def width + @width ||= Pry::Terminal.width! + end end - def puts(str) - print "#{str.chomp}\n" - end - - def print(str) - write str - end - alias << print - - def write(str) - @out.write str - end - - def close - # no-op for base pager, but important for subclasses - end - - private - - def height - @height ||= Pry::Terminal.height! - end - - def width - @width ||= Pry::Terminal.width! - end - - class SimplePager < Pry::Pager + # SimplePager is a very simple pure-Ruby implementation of paging. We use it + # on JRuby and when we can't find an external pager to use. + class SimplePager < NullPager def initialize(*) super @tracker = PageTracker.new(height - 3, width) @@ -80,7 +77,7 @@ class Pry::Pager def write(str) str.lines.each do |line| - @out.write line + @out.puts line @tracker.record line if @tracker.page? @@ -97,7 +94,7 @@ class Pry::Pager # page long, then invokes an external pager and starts streaming # output to it. If close is called before then, it just prints out # the buffered content. - class SystemPager < Pry::Pager + class SystemPager < NullPager def self.default_pager pager = ENV["PAGER"] || "" From 8b0fe8930f574907414c6272dd6f55263f3ffa33 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 18:46:01 -0700 Subject: [PATCH 08/14] Make Pry::Pager a module --- lib/pry/pager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 1d98326a..4c1f8b4a 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -3,7 +3,7 @@ require 'pry/terminal' # A Pry::Pager is an IO-like object that accepts text and either prints it # immediately, prints it one page at a time, or streams it to an external # program to print one page at a time. -class Pry::Pager +module Pry::Pager class StopPaging < StandardError end From 476435cf9ad67adc1788770eb1a71f0a8f979bc5 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 2 Nov 2013 18:50:04 -0700 Subject: [PATCH 09/14] Rescue StopPaging in Pry.page --- lib/pry/pager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 4c1f8b4a..e1cfbf8b 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -14,6 +14,7 @@ module Pry::Pager def self.page(text, out = $stdout) pager = best_available(out) pager << text + rescue StopPaging ensure pager.close if pager end From 659e4ca1b286b73cf34117ae0b35da19c39d3625 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sun, 3 Nov 2013 16:35:33 -0800 Subject: [PATCH 10/14] Add a block-based paging API on top of `best_available` --- lib/pry.rb | 14 +++------- lib/pry/commands/ls.rb | 6 ++--- lib/pry/pager.rb | 60 ++++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/lib/pry.rb b/lib/pry.rb index 3732fb86..f9589cfc 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -20,16 +20,10 @@ class Pry # The default print DEFAULT_PRINT = proc do |output, value| - output_with_default_format(output, value, :hashrocket => true) - end - - def self.output_with_default_format(output, value, options = {}) - pager = Pry::Pager.best_available(output) - pager.print "=> " if options[:hashrocket] - Pry::ColorPrinter.pp(value, pager) - rescue Pry::Pager::StopPaging - ensure - pager.close if pager + Pry::Pager.with_pager(output) do |pager| + pager.print "=> " + Pry::ColorPrinter.pp(value, pager) + end end # may be convenient when working with enormous objects and diff --git a/lib/pry/commands/ls.rb b/lib/pry/commands/ls.rb index 1f4190b8..4f53dd14 100644 --- a/lib/pry/commands/ls.rb +++ b/lib/pry/commands/ls.rb @@ -327,9 +327,9 @@ class Pry end def format_value(value) - accumulator = StringIO.new - Pry.output_with_default_format(accumulator, value, :hashrocket => false) - accumulator.string + str = "" + Pry::ColorPrinter.pp(value, str) + str end # Add a new section to the output. Outputs nothing if the section would be empty. diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index e1cfbf8b..1dbabb50 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -1,29 +1,40 @@ require 'pry/terminal' -# A Pry::Pager is an IO-like object that accepts text and either prints it +# A pager is an `IO`-like object that accepts text and either prints it # immediately, prints it one page at a time, or streams it to an external # program to print one page at a time. module Pry::Pager class StopPaging < StandardError end - # Send the given text through the best available pager (if Pry.pager is + # Send the given text through the best available pager (if `Pry.pager` is # enabled). # @param [String] text A piece of text to run through a pager. - # @param [#<<] output ($stdout) An object to send output to. - def self.page(text, out = $stdout) - pager = best_available(out) - pager << text + # @param [IO] output (`$stdout`) An object to send output to. + def self.page(text, output = $stdout) + with_pager(output) do |pager| + pager << text + end + end + + # Yields a pager object (`NullPager`, `SimplePager`, or `SystemPager`). All + # pagers accept output with `#puts`, `#print`, `#write`, and `#<<`. + # @param [IO] output (`$stdout`) An object to send output to. + def self.with_pager(output = $stdout) + pager = best_available(output) + yield pager rescue StopPaging ensure pager.close if pager end - # Return an instance of the "best" available pager class -- SystemPager if - # possible, SimplePager if SystemPager isn't available, and NullPager if the - # user has disabled paging. All pagers accept output with #puts, #print, - # #write, and #<<. You must call #close when you're done writing output to a - # pager. + # Return an instance of the "best" available pager class -- `SystemPager` if + # possible, `SimplePager` if `SystemPager` isn't available, and `NullPager` + # if the user has disabled paging. All pagers accept output with `#puts`, + # `#print`, `#write`, and `#<<`. You must call `#close` when you're done + # writing output to a pager, and you must rescue `Pry::Pager::StopPaging`. + # These requirements can be avoided by using `.with_pager` instead. + # @param [#<<] output ($stdout) An object to send output to. def self.best_available(output) if !Pry.pager NullPager.new(output) @@ -34,8 +45,8 @@ module Pry::Pager end end - # A "pager" that actually just prints all output as it comes in. Used when - # Pry.pager is false. + # `NullPager` is a "pager" that actually just prints all output as it comes + # in. Used when `Pry.pager` is false. class NullPager def initialize(out) @out = out @@ -68,8 +79,8 @@ module Pry::Pager end end - # SimplePager is a very simple pure-Ruby implementation of paging. We use it - # on JRuby and when we can't find an external pager to use. + # `SimplePager` is a straightforward pure-Ruby pager. We use it on JRuby and + # when we can't find a usable external pager. class SimplePager < NullPager def initialize(*) super @@ -91,10 +102,9 @@ module Pry::Pager end end - # SystemPager buffers output until we're pretty sure it's at least a - # page long, then invokes an external pager and starts streaming - # output to it. If close is called before then, it just prints out - # the buffered content. + # `SystemPager` buffers output until we're pretty sure it's at least a page + # long, then invokes an external pager and starts streaming output to it. If + # `#close` is called before then, it just prints out the buffered content. class SystemPager < NullPager def self.default_pager pager = ENV["PAGER"] || "" @@ -160,14 +170,14 @@ module Pry::Pager end end - # PageTracker tracks output to determine whether it's likely to take up a + # `PageTracker` tracks output to determine whether it's likely to take up a # whole page. This doesn't need to be super precise, but we can use it for - # SimplePager and to avoid invoking the system pager unnecessarily. + # `SimplePager` and to avoid invoking the system pager unnecessarily. # - # One simplifying assumption is that we don't need page? to return true on - # the basis of an incomplete line. Long lines should be counted as multiple - # lines, but we don't have to transition from false to true until we see a - # newline. + # One simplifying assumption is that we don't need `#page?` to return `true` + # on the basis of an incomplete line. Long lines should be counted as + # multiple lines, but we don't have to transition from `false` to `true` + # until we see a newline. class PageTracker def initialize(rows, cols) @rows, @cols = rows, cols From 2596fd3f3c164788429bf8aa626a5f025ffa3936 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sun, 3 Nov 2013 16:49:07 -0800 Subject: [PATCH 11/14] Fix SimplePager newline bug --- lib/pry/pager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index 1dbabb50..edcf4b30 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -89,7 +89,7 @@ module Pry::Pager def write(str) str.lines.each do |line| - @out.puts line + @out.print line @tracker.record line if @tracker.page? From a6cc93ead0de353054b1703d51bf7639bc61689a Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sun, 3 Nov 2013 18:36:19 -0800 Subject: [PATCH 12/14] Simplify usage of ColorPrinter in ls -l --- lib/pry/commands/ls.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pry/commands/ls.rb b/lib/pry/commands/ls.rb index 4f53dd14..441138d8 100644 --- a/lib/pry/commands/ls.rb +++ b/lib/pry/commands/ls.rb @@ -327,9 +327,7 @@ class Pry end def format_value(value) - str = "" - Pry::ColorPrinter.pp(value, str) - str + Pry::ColorPrinter.pp(value, "") end # Add a new section to the output. Outputs nothing if the section would be empty. From edc8ff6966b5f1bc46c4a170c1e67197b16b396c Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 9 Nov 2013 12:49:27 -0800 Subject: [PATCH 13/14] Remove -S option from less; wrap long lines by default --- lib/pry/pager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pry/pager.rb b/lib/pry/pager.rb index edcf4b30..4a77b28e 100644 --- a/lib/pry/pager.rb +++ b/lib/pry/pager.rb @@ -111,7 +111,7 @@ module Pry::Pager # Default to less, and make sure less is being passed the correct options if pager.strip.empty? or pager =~ /^less\b/ - pager = "less -R -S -F -X" + pager = "less -R -F -X" end pager From 1836a025725e378cb980d561a497461ca3d46b85 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Sat, 9 Nov 2013 12:53:39 -0800 Subject: [PATCH 14/14] Pass actual terminal width into pp --- lib/pry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pry.rb b/lib/pry.rb index f9589cfc..d4cd712a 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -22,7 +22,7 @@ class Pry DEFAULT_PRINT = proc do |output, value| Pry::Pager.with_pager(output) do |pager| pager.print "=> " - Pry::ColorPrinter.pp(value, pager) + Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1) end end