diff options
| author | Stan Lo <stan001212@gmail.com> | 2023-07-25 19:33:18 +0100 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2023-07-25 18:33:23 +0000 |
| commit | d8cee5507361b2cd3f3baccb8fda45bad8389a70 (patch) | |
| tree | 90550675d7f90c2fab62701d6135e3355e9e5987 /lib | |
| parent | e1104017e3080fd432c0b5fdc3ae6e004ffd0834 (diff) | |
[ruby/irb] Display `show_cmds`'s output in a pager when in TTY
environment
(https://github.com/ruby/irb/pull/647)
This can:
- Make it easier to scroll up and down the commands list
- Avoid pushing up users' previous output
- Allow users to do basic search with `/<word>`
https://github.com/ruby/irb/commit/f94e8a66dd
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/irb/cmd/show_cmds.rb | 7 | ||||
| -rw-r--r-- | lib/irb/pager.rb | 61 |
2 files changed, 65 insertions, 3 deletions
diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb index 490561825e..1c9f1ea19a 100644 --- a/lib/irb/cmd/show_cmds.rb +++ b/lib/irb/cmd/show_cmds.rb @@ -2,6 +2,7 @@ require "stringio" require_relative "nop" +require_relative "../pager" module IRB # :stopdoc: @@ -28,9 +29,9 @@ module IRB output.puts end - puts output.string - - nil + Pager.page do |io| + io.puts output.string + end end end end diff --git a/lib/irb/pager.rb b/lib/irb/pager.rb new file mode 100644 index 0000000000..16066cf273 --- /dev/null +++ b/lib/irb/pager.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module IRB + # The implementation of this class is borrowed from RDoc's lib/rdoc/ri/driver.rb. + # Please do NOT use this class directly outside of IRB. + class Pager + PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq + + class << self + def page + if STDIN.tty? && pager = setup_pager + begin + pid = pager.pid + yield pager + ensure + pager.close + end + else + yield $stdout + end + # When user presses Ctrl-C, IRB would raise `IRB::Abort` + # But since Pager is implemented by running paging commands like `less` in another process with `IO.popen`, + # the `IRB::Abort` exception only interrupts IRB's execution but doesn't affect the pager + # So to properly terminate the pager with Ctrl-C, we need to catch `IRB::Abort` and kill the pager process + rescue IRB::Abort + Process.kill("TERM", pid) if pid + nil + rescue Errno::EPIPE + end + + private + + def setup_pager + require 'shellwords' + + PAGE_COMMANDS.each do |pager| + pager = Shellwords.split(pager) + next if pager.empty? + + if pager.first == 'less' || pager.first == 'more' + pager << '-R' unless pager.include?('-R') + end + + begin + io = IO.popen(pager, 'w') + rescue + next + end + + if $? && $?.pid == io.pid && $?.exited? # pager didn't work + next + end + + return io + end + + nil + end + end + end +end |
