Patches: Browse | Submit New | Admin

[#20655] a patch to improve how ri handles ambiguous method lookups

Date:
2008-06-13 14:19
Priority:
3
Submitted By:
Daniel Choi (danchoi)
Assigned To:
Eric Hodel (drbrain)
Category:
Documentation, ri, and rdoc
State:
Open
Summary:
a patch to improve how ri handles ambiguous method lookups

Detailed description
This patch to ri makes it go into a convenient interactive menu mode when the user looks up an ambiguous method. 

Example:

$ ri ActiveRecord::Base.find
More than one method matched your request. Type the number
of the method you want, or press Return to cancel:

 1 ActiveRecord::Base::find (1.15.6)
 2 ActiveRecord::Base::find (2.0.2)
 3 ActiveRecord::Base::find (2.1.0)
>>  [type a number and hit return]

More detailed info is on my blog:

http://danielchoi.com/software/ri-enhanced.html

Here is the patch:

#!/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 number of matches is less than MAX_ITEMS, just show all
    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.size == 1
        method = @ri_reader.get_method(entries[0])
        @display.display_method_info(method)
      else
        # This is the patch that lets you choose an item
        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"
        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 
end

ri = RiDriver.new
ri.process_args

Add A Comment: Notepad

Please login


Followup

Message
Date: 2008-12-11 14:52
Sender: Roger Pack

feel free to submit a patch to the new bug tracker
[http://redmine.ruby-lang.org/] if this patch still applies.
Thanks!
Date: 2008-06-13 23:35
Sender: Daniel Choi

The lines in that pasted patch are probably messed up, so
I'm attaching a file.
Date: 2008-06-13 23:31
Sender: Daniel Choi

Here is a patch file for rdoc 1. I also adapted the code to
rdoc 2 and submitted those patches in the RDoc project area.

--- ri_driver-orig.rb	2008-06-13 19:29:04.000000000 -0400
+++ ri_driver.rb	2008-06-13 19:30:13.000000000 -0400
@@ -45,22 +45,53 @@
   # if it contains an entry that exactly matches the
requested method,
   # then display that entry, otherwise display the list of
   # matching method names
-  
+  #
+  # 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
-        @display.display_method_list(methods)
-      end
-    end
-  end
-  
+        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  
  
#################################################################
#####
   
   def report_class_stuff(namespaces)
@@ -79,11 +110,29 @@
   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)
-    desc = NameDescriptor.new(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
@@ -101,7 +150,82 @@
     namespaces = entries if entries.size == 1
 
     if desc.method_name.nil?
-      report_class_stuff(namespaces)
+      # 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
+
+        loop do 
+          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."
+            choice_string = Readline.readline(">>
").chomp
+          else
+            STDOUT.puts "\nEnter the method name you want.
Enter a blank line to exit."
+            STDOUT.print ">> "
+            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,
Date: 2008-06-13 16:50
Sender: Daniel Choi

I just added more to my patch (which I should have said
overrides methods in rdoc/ri/ri_driver.rb) to provide a menu
interface to class and instance methods on a class. 

Please see the bottom of 

http://danielchoi.com/software/ri-enhanced.html

for details and link to the patch.
Date: 2008-06-13 14:52
Sender: Daniel Choi

I made some fixes and enhancements to my patch to work with
partial method name matches. Attached is the patch in a
file. It's also linked from the blog url I gave above. 

Attached Files:

Name Description Download
ri new patch Download
patchfile.txt patch file Download

Changes:

Field Old Value Date By
File Added3824: patchfile.txt2008-06-13 23:35danchoi
File Added3821: ri2008-06-13 14:52danchoi