#!/usr/bin/env ruby # usage: # # ri name... # # where name can be # # Class | Class::method | Class#method | Class.method | method # # All names may be abbreviated to their minimum unbiguous form. If a name # _is_ ambiguous, all valid options will be listed. # require 'rdoc/ri/ri_paths' 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' ###################################################################### def display_usage File.open(__FILE__) do |f| f.gets puts $1 while (f.gets =~ /^# ?(.*)/) end exit 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 "\nWas rdoc run to create documentation?" exit 1 end @ri_reader = RI::RiReader.new(RI::RiCache.new(paths)) @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 <", 'pager' ].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) params = method.params if params[0,1] == "(" if method.is_singleton params = method.full_name + params else params = method.name + params end end params.split(/\n/).each {|p| @formatter.wrap(p) } end ###################################################################### def display_flow(flow) if !flow || flow.empty? @formatter.wrap("(no description...)") else @formatter.display_flow(flow) end end ###################################################################### def display_method_info(method_entry) method = @ri_reader.get_method(method_entry) @formatter.draw_line(method.full_name) display_params(method) @formatter.draw_line display_flow(method.comment) if method.aliases && !method.aliases.empty? @formatter.blankline aka = "(also known as " aka << method.aliases.map {|a| a.name }.join(", ") aka << ")" @formatter.wrap(aka) end end ###################################################################### def display_class_info(class_entry) klass = @ri_reader.get_class(class_entry) @formatter.draw_line("Class: " + klass.full_name) display_flow(klass.comment) @formatter.draw_line unless klass.includes.empty? @formatter.blankline @formatter.wrap("Includes:", "") incs = [] klass.includes.each do |inc| inc_desc = @ri_reader.find_class_by_name(inc.name) if inc_desc str = inc.name + "(" str << inc_desc.instance_methods.map{|m| m.name}.join(", ") str << ")" incs << str else incs << inc.name end end @formatter.wrap(incs.sort.join(', ')) end unless klass.constants.empty? @formatter.blankline @formatter.wrap("Constants:", "") len = 0 klass.constants.each { |c| len = c.name.length if c.name.length > len } len += 2 klass.constants.each do |c| @formatter.wrap(c.value, @formatter.indent+((c.name+":").ljust(len))) end end unless klass.class_methods.empty? @formatter.blankline @formatter.wrap("Class methods:", "") @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', ')) end unless klass.instance_methods.empty? @formatter.blankline @formatter.wrap("Instance methods:", "") @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', ')) end unless klass.attributes.empty? @formatter.blankline @formatter.wrap("Attributes:", "") @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', ')) end end ###################################################################### # If the list of matching methods contains exactly one entry, or # if it contains an entry that exactly matches the requested method, # then display that entry, otherwise display the list of # matching method names def report_method_stuff(requested_method_name, methods) if methods.size == 1 display_method_info(methods[0]) elsif (entry = methods.find {|m| m.name == requested_method_name}) display_method_info(entry) else puts "More than one method matched your request. You can refine" puts "your search by asking for information on one of:\n\n" @formatter.wrap(methods.map {|m| m.full_name} .join(", ")) end end ###################################################################### def report_class_stuff(namespaces) if namespaces.size > 1 puts "More than one class or module matched your request. You can refine" puts "your search by asking for information on one of:\n\n" puts @formatter.wrap("", namespaces.map {|m| m.full_name} .join(", ")) else class_desc = @ri_reader.get_class(namespaces[0]) display_class_info(namespaces[0]) end end ###################################################################### def display_info_for(arg) desc = NameDescriptor.new(arg) namespaces = @ri_reader.top_level_namespace for class_name in desc.class_names namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces) if namespaces.empty? raise RiError.new("Nothing known about #{arg}") end end setup_pager unless @options.use_stdout begin 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}") 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 end ###################################################################### if ARGV.size.zero? display_usage else ri = RiDisplay.new begin ARGV.each do |arg| ri.display_info_for(arg) end rescue RiError => e $stderr.puts(e.message) exit(1) end end