1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00
pry--pry/lib/pry/commands/find_method.rb
Kyrylo Silin 256f35422a Prettify command descriptions, switches and stuff
Wrap command descriptions to 80 characters. Convert some string options
to symbols (where possible). Align options in code. Remove dots in the
end of switch descriptions.

Signed-off-by: Kyrylo Silin <kyrylosilin@gmail.com>
2013-01-09 22:23:19 +02:00

199 lines
5.5 KiB
Ruby

class Pry
class Command::FindMethod < Pry::ClassCommand
extend Pry::Helpers::BaseHelpers
match 'find-method'
group 'Context'
description 'Recursively search for a method within a Class/Module or the current namespace.'
command_options :shellwords => false
command_options :requires_gem => 'ruby18_source_location' if mri_18?
banner <<-'BANNER'
Usage: find-method [-n|-c] METHOD [NAMESPACE]
Recursively search for a method within a Class/Module or the current namespace.
Use the `-n` switch (the default) to search for methods whose name matches the
given regex. Use the `-c` switch to search for methods that contain the given
code.
# Find all methods whose name match /re/ inside
# the Pry namespace. Matches Pry#repl, etc.
find-method re Pry
# Find all methods that contain the code:
# output.puts inside the Pry namepsace.
find-method -c 'output.puts' Pry
BANNER
def setup
require 'ruby18_source_location' if mri_18?
end
def options(opti)
opti.on :n, :name, "Search for a method by name"
opti.on :c, :content, "Search for a method based on content in Regex form"
end
def process
return if args.size < 1
klass = search_class
matches = if opts.content?
content_search(klass)
else
name_search(klass)
end
show_search_results(matches)
end
private
# @return [Regexp] The pattern to search for.
def pattern
@pattern ||= ::Regexp.new args[0]
end
# Output the result of the search.
#
# @param [Array] matches
def show_search_results(matches)
if matches.empty?
output.puts text.bold("No Methods Matched")
else
print_matches(matches)
end
end
# The class to search for methods.
# We only search classes, so if the search object is an
# instance, return its class. If no search object is given
# search `target_self`.
def search_class
klass = if args[1]
target.eval(args[1])
else
target_self
end
klass.is_a?(Module) ? klass : klass.class
end
# pretty-print a list of matching methods.
#
# @param Array[Method]
def print_matches(matches)
grouped = matches.group_by(&:owner)
order = grouped.keys.sort_by{ |x| x.name || x.to_s }
order.each do |klass|
print_matches_for_class(klass, grouped)
end
end
# Print matched methods for a class
def print_matches_for_class(klass, grouped)
output.puts text.bold(klass.name)
grouped[klass].each do |method|
header = method.name_with_owner
output.puts header + additional_info(header, method)
end
end
# Return the matched lines of method source if `-c` is given or ""
# if `-c` was not given
def additional_info(header, method)
if opts.content?
": " + colorize_code(matched_method_lines(header, method))
else
""
end
end
def matched_method_lines(header, method)
method.source.split(/\n/).select {|x| x =~ pattern }.join("\n#{' ' * header.length}")
end
# Run the given block against every constant in the provided namespace.
#
# @param Module The namespace in which to start the search.
# @param Hash[Module,Boolean] The namespaces we've already visited (private)
# @yieldparam klazz Each class/module in the namespace.
#
def recurse_namespace(klass, done={}, &block)
return if !(Module === klass) || done[klass]
done[klass] = true
yield klass
klass.constants.each do |name|
next if klass.autoload?(name)
begin
const = klass.const_get(name)
rescue RescuableException
# constant loading is an inexact science at the best of times,
# this often happens when a constant was .autoload? but someone
# tried to load it. It's now not .autoload? but will still raise
# a NameError when you access it.
else
recurse_namespace(const, done, &block)
end
end
end
# Gather all the methods in a namespace that pass the given block.
#
# @param Module The namespace in which to search.
# @yieldparam Method The method to test
# @yieldreturn Boolean
# @return Array[Method]
#
def search_all_methods(namespace)
done = Hash.new{ |h,k| h[k] = {} }
matches = []
recurse_namespace(namespace) do |klass|
(Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method|
next if done[method.owner][method.name]
done[method.owner][method.name] = true
matches << method if yield method
end
end
matches
end
# Search for all methods with a name that matches the given regex
# within a namespace.
#
# @param Module The namespace to search
# @return Array[Method]
#
def name_search(namespace)
search_all_methods(namespace) do |meth|
meth.name =~ pattern
end
end
# Search for all methods who's implementation matches the given regex
# within a namespace.
#
# @param Module The namespace to search
# @return Array[Method]
#
def content_search(namespace)
search_all_methods(namespace) do |meth|
begin
meth.source =~ pattern
rescue RescuableException
false
end
end
end
end
Pry::Commands.add_command(Pry::Command::FindMethod)
end