From 1c1d2b9c34f6adcaf25667ab51f7f3720b4d8abe Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 17 Dec 2003 05:43:17 +0000 Subject: [PATCH] Add pager support to ri, and start implementing command line options git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5205 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- MANIFEST | 1 + bin/ri | 64 ++++++++++++--- lib/rdoc/ri/ri_options.rb | 158 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 12 deletions(-) create mode 100644 lib/rdoc/ri/ri_options.rb diff --git a/MANIFEST b/MANIFEST index 32294deb09..153b205c33 100644 --- a/MANIFEST +++ b/MANIFEST @@ -262,6 +262,7 @@ lib/rdoc/parsers/parserfactory.rb lib/rdoc/ri/ri_cache.rb lib/rdoc/ri/ri_descriptions.rb lib/rdoc/ri/ri_formatter.rb +lib/rdoc/ri/ri_options.rb lib/rdoc/ri/ri_paths.rb lib/rdoc/ri/ri_reader.rb lib/rdoc/ri/ri_util.rb diff --git a/bin/ri b/bin/ri index c9c3abc4ad..4103f3a7a8 100755 --- a/bin/ri +++ b/bin/ri @@ -16,6 +16,7 @@ require 'rdoc/ri/ri_cache' require 'rdoc/ri/ri_util' require 'rdoc/ri/ri_reader' require 'rdoc/ri/ri_formatter' +require 'rdoc/ri/ri_options' ###################################################################### @@ -33,19 +34,50 @@ end class RiDisplay def initialize + @options = RI::Options.instance + @options.parse paths = RI::Paths::PATH if paths.empty? $stderr.puts "No ri documentation found in:" [ RI::Paths::SYSDIR, RI::Paths::SITEDIR, RI::Paths::HOMEDIR].each do |d| $stderr.puts " #{d}" end - $stderr.puts "\nIs ri correctly installed?" + $stderr.puts "\nWas rdoc run to create documentation?" exit 1 end @ri_reader = RI::RiReader.new(RI::RiCache.new(paths)) - @formatter = RI::RiFormatter.new(72, " ") + @formatter = RI::RiFormatter.new(@options.width, " ") end + + ###################################################################### + + def setup_pager + require 'tempfile' + + @save_stdout = STDOUT.clone + STDOUT.reopen(Tempfile.new("ri_")) + end + + ###################################################################### + + def page_output + path = STDOUT.path + STDOUT.reopen(@save_stdout) + @save_stdout = nil + paged = false + for pager in [ ENV['pager'], "less", "more <" ].compact.uniq + if system("#{pager} #{path}") + paged = true + break + end + end + if !paged + @options.use_stdout = true + puts File.read(path) + end + end + ###################################################################### def display_params(method) @@ -175,18 +207,26 @@ def display_info_for(arg) end end - if desc.method_name.nil? - report_class_stuff(namespaces) - else - methods = @ri_reader.find_methods(desc.method_name, - desc.is_class_method, - namespaces) - - if methods.empty? - raise RiError.new("Nothing known about #{arg}") + setup_pager unless @options.use_stdout + + begin + if desc.method_name.nil? + report_class_stuff(namespaces) else - report_method_stuff(desc.method_name, methods) + methods = @ri_reader.find_methods(desc.method_name, + desc.is_class_method, + namespaces) + + if methods.empty? + raise RiError.new("Nothing known about #{arg}") + else + report_method_stuff(desc.method_name, methods) + end end + + page_output unless @options.use_stdout + ensure + STDOUT.reopen(@save_stdout) if @save_stdout end end diff --git a/lib/rdoc/ri/ri_options.rb b/lib/rdoc/ri/ri_options.rb new file mode 100644 index 0000000000..3ec6d4657c --- /dev/null +++ b/lib/rdoc/ri/ri_options.rb @@ -0,0 +1,158 @@ +# We handle the parsing of options, and subsequently as a singleton +# object to be queried for option values + +module RI + + VERSION_STRING = "alpha 0.1" + + class Options + + require 'singleton' + require 'getoptlong' + + include Singleton + + # No not use a pager. Writable, because ri sets it if it + # can't find a pager + attr_accessor :use_stdout + + # The width of the output line + attr_reader :width + + module OptionList + + OPTION_LIST = [ + [ "--help", "-h", nil, + "you're looking at it" ], + + [ "--no-pager", "-T", nil, + "Send output directly to stdout." + ], + + [ "--width", "-w", "output width", + "set the width of the output" ], + + ] + + def OptionList.options + OPTION_LIST.map do |long, short, arg,| + [ long, + short, + arg ? GetoptLong::REQUIRED_ARGUMENT : GetoptLong::NO_ARGUMENT + ] + end + end + + + def OptionList.strip_output(text) + text =~ /^\s+/ + leading_spaces = $& + text.gsub!(/^#{leading_spaces}/, '') + $stdout.puts text + end + + + # Show an error and exit + + def OptionList.error(msg) + $stderr.puts + $stderr.puts msg + $stderr.puts "\nFor help on options, try 'ri --help'\n\n" + exit 1 + end + + # Show usage and exit + + def OptionList.usage + + puts + puts(RI::VERSION_STRING) + puts + + name = File.basename($0) + OptionList.strip_output(<<-EOT) + Usage: + + #{name} [options] [names...] + + Display information on Ruby classes, modules, and methods. + Give the names of classes or methods to see their documentation. + Partial names may be given: if the names match more than + one entity, a list will be shown, otherwise details on + that entity will be displayed. + + Nested classes and modules can be specified using the normal + Name::Name notation, and instance methods can be distinguished + from class methods using "." (or "#") instead of "::". + + For example: + + ri File + ri File.new + ri F.n + ri zip + + Note that shell quoting may be required for method names + containing puncuation: + + ri 'Array.[]' + ri compact\! + + Options: + + EOT + + OPTION_LIST.each do |long, short, arg, desc| + opt = sprintf("%20s", "#{long}, #{short}") + oparg = sprintf("%-7s", arg) + print "#{opt} #{oparg}" + desc = desc.split("\n") + if arg.nil? || arg.length < 7 + puts desc.shift + else + puts + end + desc.each do |line| + puts(" "*28 + line) + end + puts + end + + exit 0 + end + end + + # Parse command line options. + + def parse + + @use_stdout = !STDOUT.tty? + @width = 72 + + begin + + go = GetoptLong.new(*OptionList.options) + go.quiet = true + + go.each do |opt, arg| + case opt + when "--help" then OptionList.usage + when "--no-pager" then @use_stdout = true + when "--width" + begin + @width = Integer(arg) + rescue + $stderr.puts "Invalid width: '#{arg}'" + exit 1 + end + end + end + + rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error + OptionList.error(error.message) + + end + end + end +end +