From 77dccce37cb9fd1b8fbc52160d4103d1c4579669 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 1 Mar 2024 01:24:05 +0900 Subject: [PATCH] Invoke pager for `--help` --- lib/optparse.rb | 23 +++++++++++++++++-- test/optparse/test_optparse.rb | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/optparse.rb b/lib/optparse.rb index 51dbfd0..76ff38a 100644 --- a/lib/optparse.rb +++ b/lib/optparse.rb @@ -1050,6 +1050,26 @@ def compsys(to, name = File.basename($0)) # :nodoc: to << " '*:file:_files' && return 0\n" end + def help_exit + if STDOUT.tty? && (pager = ENV.values_at(*%w[RUBY_PAGER PAGER]).find {|e| e && !e.empty?}) + less = ENV["LESS"] + args = [{"LESS" => "#{!less || less.empty? ? '-' : less}Fe"}, pager, "w"] + print = proc do |f| + f.puts help + rescue Errno::EPIPE + # pager terminated + end + if Process.respond_to?(:fork) and false + IO.popen("-") {|f| f ? Process.exec(*args, in: f) : print.call(STDOUT)} + # unreachable + end + IO.popen(*args, &print) + else + puts help + end + exit + end + # # Default options for ARGV, which never appear in option summary. # @@ -1061,8 +1081,7 @@ def compsys(to, name = File.basename($0)) # :nodoc: # Officious['help'] = proc do |parser| Switch::NoArgument.new do |arg| - puts parser.help - exit + parser.help_exit end end diff --git a/test/optparse/test_optparse.rb b/test/optparse/test_optparse.rb index 393b033..8d09e0f 100644 --- a/test/optparse/test_optparse.rb +++ b/test/optparse/test_optparse.rb @@ -164,4 +164,44 @@ def test_nonopt_pattern e = assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-t))} assert_equal(["-t"], e.args) end + + def test_help_pager + require 'tmpdir' + Dir.mktmpdir do |dir| + File.open(File.join(dir, "options.rb"), "w") do |f| + f.puts "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + stdout = STDOUT.dup + def stdout.tty?; true; end + Object.__send__(:remove_const, :STDOUT) + STDOUT = stdout + ARGV.options do |opt| + end; + 100.times {|i| f.puts " opt.on('--opt-#{i}') {}"} + f.puts "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + opt.parse! + end + end; + end + + optparse = $".find {|path| path.end_with?("/optparse.rb")} + args = ["-r#{optparse}", "options.rb", "--help"] + cmd = File.join(dir, "pager.cmd") + if RbConfig::CONFIG["EXECUTABLE_EXTS"]&.include?(".cmd") + command = "@echo off" + else # if File.executable?("/bin/sh") + # TruffleRuby just calls `posix_spawnp` and no fallback to `/bin/sh`. + command = "#!/bin/sh\n" + end + + [ + [{"RUBY_PAGER"=>cmd, "PAGER"=>"echo ng"}, "Executing RUBY_PAGER"], + [{"RUBY_PAGER"=>nil, "PAGER"=>cmd}, "Executing PAGER"], + ].each do |env, expected| + File.write(cmd, "#{command}\n" "echo #{expected}\n", perm: 0o700) + assert_in_out_err([env, *args], "", [expected], chdir: dir) + end + end + end end