#!/usr/bin/env ruby require 'rdoc/ri/ri_driver' class RiDriver # This patched version of the method allows the user to choose # a matching method from an interactive prompt if multiple methods # are matched. def report_method_stuff(requested_method_name, methods) if methods.size == 1 method = @ri_reader.get_method(methods[0]) @display.display_method_info(method) else entries = methods.find_all {|m| m.name == requested_method_name} if entries.empty? unless methods.empty? # There are no exact matches, but partial matches # Print approximate matches. STDOUT.puts "There are no exact matches, but #{methods.size} partial matches." STDOUT.puts "Type the number of the method you want, or press Return to cancel:\n\n" entries = methods end else # There are multiple exact matches STDOUT.puts "More than one method matched your request. Type the number" STDOUT.puts "of the method you want, or press Return to cancel:\n\n" end if entries.size == 1 method = @ri_reader.get_method(entries[0]) @display.display_method_info(method) else entries.each_with_index do |m, i| # Show version numbers if there is more than one exact match. # Assume that a gem version always appears like "-2.0.2" in the path. # Thanks to Leslie Viljoen for suggesting this version-numbering # feature on comp.lang.ruby. version_string = nil if entries.select {|e| e.full_name == m.full_name}.size > 1 match_data = /-(\d+\.\d+\.\d+)/.match(m.path_name) version_string = match_data ? " (#{match_data[1]})" : nil end STDOUT.puts "%2d %s%s" % [i+1, m.full_name, version_string] end STDOUT.print ">> " choice = STDIN.gets.chomp.to_i if choice == 0 exit end method = @ri_reader.get_method(entries[choice-1]) @display.display_method_info(method) end end end # This patch enables the user to get all methods on a class as a numbered # menu if the user enters something like "ri String*" def get_info_for(arg) # The patch: show_method_menu = false if arg =~ /\w\*/ filter = [] case arg.split('*')[1] when /^i/ filter << :instance_methods when /^c/ filter << :class_methods else filter = [:class_methods, :instance_methods] end arg = klass = arg.split('*')[0] show_method_menu = true end 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 # at this point, if we have multiple possible namespaces, but one # is an exact match for our requested class, prune down to just it full_class_name = desc.full_class_name entries = namespaces.find_all {|m| m.full_name == full_class_name} namespaces = entries if entries.size == 1 if desc.method_name.nil? # Another patch here: Show an interactive menu of methods for the class if show_method_menu klass = @ri_reader.get_class(namespaces[0]) index = 1 menu_methods = [] use_readline = false if require('readline') && require('abbrev') use_readline = true end options = RI::Options.instance formatter = RI::TextFormatter.new(options, " ") superclass = klass.superclass_string if superclass superclass = " < " + superclass else superclass = "" end formatter.draw_line(klass.display_name + ": " + klass.full_name + superclass) filter.each do |x| formatter.display_heading(x.to_s.gsub('_', ' ').capitalize + ":", 2, "") methods_to_display = klass.send(x).collect do |m| menu_methods << m index += 1 m.name end if methods_to_display.empty? STDOUT.puts " No methods" end formatter.wrap( methods_to_display.join(", ") ) end # prepare abbreviations if use_readline abbreviations = menu_methods.map {|m| m.name}.abbrev Readline.completion_proc = proc do |string| abbreviations.values.uniq.grep /^#{string}/ end end if index == 1 # no results STDOUT.puts "No results." exit end if use_readline STDOUT.puts "\nEnter the method name you want. You can use tab to autocomplete." STDOUT.puts "Enter a blank line to exit." else STDOUT.puts "\nEnter the method name you want. Enter a blank line to exit." STDOUT.print ">> " end loop do if use_readline choice_string = Readline.readline(">> ").chomp else choice_string = STDIN.gets.chomp end if choice_string == '' exit end choice = menu_methods.detect {|m| m.name == choice_string} if choice.nil? STDOUT.puts "No method matched '#{choice_string}'." exit end methods = @ri_reader.find_methods(choice.name, desc.is_class_method, namespaces).select {|m| m.name == choice.name} method = @ri_reader.get_method(methods[0]) @display.display_method_info(method) end else # The original code report_class_stuff(namespaces) end 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 end end ri = RiDriver.new ri.process_args