pry 0.9.6.2-i386-mingw32 → 0.9.7-i386-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/CHANGELOG +19 -1
- data/CONTRIBUTORS +22 -16
- data/Rakefile +12 -6
- data/bin/pry +15 -12
- data/lib/pry.rb +39 -28
- data/lib/pry/command_context.rb +1 -0
- data/lib/pry/command_processor.rb +9 -2
- data/lib/pry/command_set.rb +7 -0
- data/lib/pry/config.rb +8 -0
- data/lib/pry/default_commands/basic.rb +7 -10
- data/lib/pry/default_commands/context.rb +36 -26
- data/lib/pry/default_commands/documentation.rb +31 -123
- data/lib/pry/default_commands/gems.rb +9 -4
- data/lib/pry/default_commands/input.rb +21 -11
- data/lib/pry/default_commands/introspection.rb +79 -88
- data/lib/pry/default_commands/ls.rb +165 -176
- data/lib/pry/default_commands/shell.rb +8 -8
- data/lib/pry/extended_commands/experimental.rb +1 -5
- data/lib/pry/extended_commands/user_command_api.rb +17 -5
- data/lib/pry/helpers.rb +1 -0
- data/lib/pry/helpers/base_helpers.rb +5 -1
- data/lib/pry/helpers/command_helpers.rb +22 -241
- data/lib/pry/helpers/options_helpers.rb +58 -0
- data/lib/pry/helpers/text.rb +10 -4
- data/lib/pry/history.rb +1 -1
- data/lib/pry/indent.rb +205 -0
- data/lib/pry/method.rb +412 -0
- data/lib/pry/pry_class.rb +56 -15
- data/lib/pry/pry_instance.rb +63 -16
- data/lib/pry/rbx_method.rb +20 -0
- data/lib/pry/rbx_path.rb +34 -0
- data/lib/pry/version.rb +1 -1
- data/pry.gemspec +16 -16
- data/test/helper.rb +8 -4
- data/test/test_command_helpers.rb +1 -69
- data/test/test_command_set.rb +29 -28
- data/test/test_default_commands/test_documentation.rb +23 -10
- data/test/test_default_commands/test_introspection.rb +58 -13
- data/test/test_default_commands/test_ls.rb +148 -0
- data/test/test_indent.rb +234 -0
- data/test/test_input_stack.rb +13 -0
- data/test/test_method.rb +291 -0
- data/test/test_pry.rb +10 -1
- metadata +88 -71
data/lib/pry/history.rb
CHANGED
@@ -34,7 +34,7 @@ class Pry
|
|
34
34
|
# @param [String] line
|
35
35
|
# @return [String] The same line that was passed in
|
36
36
|
def push(line)
|
37
|
-
unless line.empty? || (@history.last && line
|
37
|
+
unless line.empty? || (@history.last && line == @history.last)
|
38
38
|
Readline::HISTORY << line
|
39
39
|
@history << line
|
40
40
|
end
|
data/lib/pry/indent.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'coderay'
|
2
|
+
|
3
|
+
class Pry
|
4
|
+
##
|
5
|
+
# Pry::Indent is a class that can be used to indent a number of lines
|
6
|
+
# containing Ruby code similar as to how IRB does it (but better). The class
|
7
|
+
# works by tokenizing a string using CodeRay and then looping over those
|
8
|
+
# tokens. Based on the tokens in a line of code that line (or the next one)
|
9
|
+
# will be indented or un-indented by correctly.
|
10
|
+
#
|
11
|
+
class Indent
|
12
|
+
# String containing the spaces to be inserted before the next line.
|
13
|
+
attr_reader :indent_level
|
14
|
+
|
15
|
+
# The amount of spaces to insert for each indent level.
|
16
|
+
SPACES = ' '
|
17
|
+
|
18
|
+
# Hash containing all the tokens that should increase the indentation
|
19
|
+
# level. The keys of this hash are open tokens, the values the matching
|
20
|
+
# tokens that should prevent a line from being indented if they appear on
|
21
|
+
# the same line.
|
22
|
+
OPEN_TOKENS = {
|
23
|
+
'def' => 'end',
|
24
|
+
'class' => 'end',
|
25
|
+
'module' => 'end',
|
26
|
+
'do' => 'end',
|
27
|
+
'if' => 'end',
|
28
|
+
'unless' => 'end',
|
29
|
+
'while' => 'end',
|
30
|
+
'until' => 'end',
|
31
|
+
'for' => 'end',
|
32
|
+
'case' => 'end',
|
33
|
+
'begin' => 'end',
|
34
|
+
'[' => ']',
|
35
|
+
'{' => '}',
|
36
|
+
'(' => ')'
|
37
|
+
}
|
38
|
+
|
39
|
+
# Which tokens can either be open tokens, or appear as modifiers on
|
40
|
+
# a single-line.
|
41
|
+
SINGLELINE_TOKENS = %w(if while until unless rescue)
|
42
|
+
|
43
|
+
# Collection of token types that should be ignored. Without this list
|
44
|
+
# keywords such as "class" inside strings would cause the code to be
|
45
|
+
# indented incorrectly.
|
46
|
+
IGNORE_TOKENS = [:space, :content, :string, :delimiter, :method, :ident]
|
47
|
+
|
48
|
+
# Tokens that indicate the end of a statement (i.e. that, if they appear
|
49
|
+
# directly before an "if" indicates that that if applies to the same line,
|
50
|
+
# not the next line)
|
51
|
+
STATEMENT_END_TOKENS = IGNORE_TOKENS + [:regexp, :integer, :float]
|
52
|
+
|
53
|
+
# Collection of tokens that should appear dedented even though they
|
54
|
+
# don't affect the surrounding code.
|
55
|
+
MIDWAY_TOKENS = %w(when else elsif rescue)
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
reset
|
59
|
+
end
|
60
|
+
|
61
|
+
# reset internal state
|
62
|
+
def reset
|
63
|
+
@stack = []
|
64
|
+
@indent_level = ''
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Indents a string and returns it. This string can either be a single line
|
69
|
+
# or multiple ones.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# str = <<TXT
|
73
|
+
# class User
|
74
|
+
# attr_accessor :name
|
75
|
+
# end
|
76
|
+
# TXT
|
77
|
+
#
|
78
|
+
# # This would result in the following being displayed:
|
79
|
+
# #
|
80
|
+
# # class User
|
81
|
+
# # attr_accessor :name
|
82
|
+
# # end
|
83
|
+
# #
|
84
|
+
# puts Pry::Indent.new.indent(str)
|
85
|
+
#
|
86
|
+
# @param [String] input The input string to indent.
|
87
|
+
# @return [String] The indented version of +input+.
|
88
|
+
#
|
89
|
+
def indent(input)
|
90
|
+
output = ''
|
91
|
+
open_tokens = OPEN_TOKENS.keys
|
92
|
+
prefix = indent_level
|
93
|
+
|
94
|
+
input.lines.each do |line|
|
95
|
+
tokens = CodeRay.scan(line, :ruby)
|
96
|
+
tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens) # Coderay 1.0.0
|
97
|
+
|
98
|
+
before, after = indentation_delta(tokens)
|
99
|
+
|
100
|
+
before.times{ prefix.sub! SPACES, '' }
|
101
|
+
output += prefix + line.strip + "\n"
|
102
|
+
prefix += SPACES * after
|
103
|
+
end
|
104
|
+
|
105
|
+
@indent_level = prefix
|
106
|
+
|
107
|
+
return output.gsub(/\s+$/, '')
|
108
|
+
end
|
109
|
+
|
110
|
+
# Get the change in indentation indicated by the line.
|
111
|
+
#
|
112
|
+
# By convention, you remove indent from the line containing end tokens,
|
113
|
+
# but add indent to the line *after* that which contains the start tokens.
|
114
|
+
#
|
115
|
+
# This method returns a pair, where the first number is the number of closings
|
116
|
+
# on this line (i.e. the number of indents to remove before the line) and the
|
117
|
+
# second is the number of openings (i.e. the number of indents to add after
|
118
|
+
# this line)
|
119
|
+
#
|
120
|
+
# @param [Array] tokens A list of tokens to scan.
|
121
|
+
# @return [Array[Integer]]
|
122
|
+
#
|
123
|
+
def indentation_delta(tokens)
|
124
|
+
|
125
|
+
# We need to keep track of whether we've seen a "for" on this line because
|
126
|
+
# if the line ends with "do" then that "do" should be discounted (i.e. we're
|
127
|
+
# only opening one level not two) To do this robustly we want to keep track
|
128
|
+
# of the indent level at which we saw the for, so we can differentiate
|
129
|
+
# between "for x in [1,2,3] do" and "for x in ([1,2,3].map do" properly
|
130
|
+
seen_for_at = []
|
131
|
+
|
132
|
+
# When deciding whether an "if" token is the start of a multiline statement,
|
133
|
+
# or just the middle of a single-line if statement, we just look at the
|
134
|
+
# preceding token, which is tracked here.
|
135
|
+
last_token, last_kind = [nil, nil]
|
136
|
+
|
137
|
+
# delta keeps track of the total difference from the start of each line after
|
138
|
+
# the given token, 0 is just the level at which the current line started for
|
139
|
+
# reference.
|
140
|
+
remove_before, add_after = [0, 0]
|
141
|
+
|
142
|
+
# If the list of tokens contains a matching closing token the line should
|
143
|
+
# not be indented (and thus we should return true).
|
144
|
+
tokens.each do |token, kind|
|
145
|
+
is_singleline_if = (SINGLELINE_TOKENS.include?(token)) && end_of_statement?(last_token, last_kind)
|
146
|
+
is_optional_do = (token == "do" && seen_for_at.include?(add_after - 1))
|
147
|
+
|
148
|
+
last_token, last_kind = token, kind unless kind == :space
|
149
|
+
next if IGNORE_TOKENS.include?(kind)
|
150
|
+
|
151
|
+
seen_for_at << add_after if token == "for"
|
152
|
+
|
153
|
+
if OPEN_TOKENS.keys.include?(token) && !is_optional_do && !is_singleline_if
|
154
|
+
@stack << token
|
155
|
+
add_after += 1
|
156
|
+
elsif token == OPEN_TOKENS[@stack.last]
|
157
|
+
@stack.pop
|
158
|
+
if add_after == 0
|
159
|
+
remove_before += 1
|
160
|
+
else
|
161
|
+
add_after -= 1
|
162
|
+
end
|
163
|
+
elsif MIDWAY_TOKENS.include?(token)
|
164
|
+
if add_after == 0
|
165
|
+
remove_before += 1
|
166
|
+
add_after += 1
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
return [remove_before, add_after]
|
172
|
+
end
|
173
|
+
|
174
|
+
# If the code just before an "if" or "while" token on a line looks like the end of a statement,
|
175
|
+
# then we want to treat that "if" as a singleline, not multiline statement.
|
176
|
+
def end_of_statement?(last_token, last_kind)
|
177
|
+
(last_token =~ /^[)\]}\/]$/ || STATEMENT_END_TOKENS.include?(last_kind))
|
178
|
+
end
|
179
|
+
|
180
|
+
# Return a string which, when printed, will rewrite the previous line with
|
181
|
+
# the correct indentation. Mostly useful for fixing 'end'.
|
182
|
+
#
|
183
|
+
# @param [String] full_line The full line of input, including the prompt.
|
184
|
+
# @param [Fixnum] overhang (0) The number of chars to erase afterwards (i.e.,
|
185
|
+
# the difference in length between the old line and the new one).
|
186
|
+
# @return [String]
|
187
|
+
def correct_indentation(full_line, overhang=0)
|
188
|
+
if Readline.respond_to?(:get_screen_size)
|
189
|
+
_, cols = Readline.get_screen_size
|
190
|
+
lines = full_line.length / cols + 1
|
191
|
+
elsif ENV['COLUMNS'] && ENV['COLUMNS'] != ''
|
192
|
+
cols = ENV['COLUMNS'].to_i
|
193
|
+
lines = full_line.length / cols + 1
|
194
|
+
else
|
195
|
+
lines = 1
|
196
|
+
end
|
197
|
+
|
198
|
+
move_up = "\e[#{lines}F"
|
199
|
+
whitespace = ' ' * overhang
|
200
|
+
move_down = "\e[#{lines}E"
|
201
|
+
|
202
|
+
"#{move_up}#{full_line}#{whitespace}#{move_down}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
data/lib/pry/method.rb
ADDED
@@ -0,0 +1,412 @@
|
|
1
|
+
class Pry
|
2
|
+
class Method
|
3
|
+
include RbxMethod if Helpers::BaseHelpers.rbx?
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# Given a string representing a method name and optionally a binding to
|
7
|
+
# search in, find and return the requested method wrapped in a `Pry::Method`
|
8
|
+
# instance.
|
9
|
+
#
|
10
|
+
# @param [String, nil] name The name of the method to retrieve, or `nil` to
|
11
|
+
# delegate to `from_binding` instead.
|
12
|
+
# @param [Binding] target The context in which to search for the method.
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [Boolean] :instance Look for an instance method if `name` doesn't
|
15
|
+
# contain any context.
|
16
|
+
# @option options [Boolean] :methods Look for a bound/singleton method if `name` doesn't
|
17
|
+
# contain any context.
|
18
|
+
# @return [Pry::Method, nil] A `Pry::Method` instance containing the requested
|
19
|
+
# method, or `nil` if no method could be located matching the parameters.
|
20
|
+
def from_str(name, target=TOPLEVEL_BINDING, options={})
|
21
|
+
if name.nil?
|
22
|
+
from_binding(target)
|
23
|
+
elsif name.to_s =~ /(.+)\#(\S+)\Z/
|
24
|
+
context, meth_name = $1, $2
|
25
|
+
from_module(target.eval(context), meth_name)
|
26
|
+
elsif name.to_s =~ /(.+)\.(\S+)\Z/
|
27
|
+
context, meth_name = $1, $2
|
28
|
+
from_obj(target.eval(context), meth_name)
|
29
|
+
elsif options[:instance]
|
30
|
+
from_module(target.eval("self"), name)
|
31
|
+
elsif options[:methods]
|
32
|
+
from_obj(target.eval("self"), name)
|
33
|
+
else
|
34
|
+
from_str(name, target, :instance => true) or
|
35
|
+
from_str(name, target, :methods => true)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Given a `Binding`, try to extract the `::Method` it originated from and
|
40
|
+
# use it to instantiate a `Pry::Method`. Return `nil` if this isn't
|
41
|
+
# possible.
|
42
|
+
#
|
43
|
+
# @param [Binding] b
|
44
|
+
# @return [Pry::Method, nil]
|
45
|
+
#
|
46
|
+
def from_binding(b)
|
47
|
+
meth_name = b.eval('__method__')
|
48
|
+
if [:__script__, nil, :__binding__, :__binding_impl__].include?(meth_name)
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
new(b.eval("method(:#{meth_name})"))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Given a `Class` or `Module` and the name of a method, try to
|
56
|
+
# instantiate a `Pry::Method` containing the instance method of
|
57
|
+
# that name. Return `nil` if no such method exists.
|
58
|
+
#
|
59
|
+
# @param [Class, Module] klass
|
60
|
+
# @param [String] name
|
61
|
+
# @return [Pry::Method, nil]
|
62
|
+
def from_class(klass, name)
|
63
|
+
new(klass.instance_method(name)) rescue nil
|
64
|
+
end
|
65
|
+
alias from_module from_class
|
66
|
+
|
67
|
+
# Given an object and the name of a method, try to instantiate
|
68
|
+
# a `Pry::Method` containing the method of that name bound to
|
69
|
+
# that object. Return `nil` if no such method exists.
|
70
|
+
#
|
71
|
+
# @param [Object] obj
|
72
|
+
# @param [String] name
|
73
|
+
# @return [Pry::Method, nil]
|
74
|
+
def from_obj(obj, name)
|
75
|
+
new(obj.method(name)) rescue nil
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get all of the instance methods of a `Class` or `Module`
|
79
|
+
# @param [Class,Module] klass
|
80
|
+
# @return [Array[Pry::Method]]
|
81
|
+
def all_from_class(klass)
|
82
|
+
all_from_common(klass, :instance_method)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get all of the methods on an `Object`
|
86
|
+
# @param [Object] obj
|
87
|
+
# @return [Array[Pry::Method]]
|
88
|
+
def all_from_obj(obj)
|
89
|
+
all_from_common(obj, :method)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get every `Class` and `Module`, in order, that will be checked when looking
|
93
|
+
# for an instance method to call on this object.
|
94
|
+
# @param [Object] obj
|
95
|
+
# @return [Array[Class, Module]]
|
96
|
+
def resolution_order(obj)
|
97
|
+
if Class === obj
|
98
|
+
singleton_class_resolution_order(obj) + instance_resolution_order(Class)
|
99
|
+
else
|
100
|
+
klass = singleton_class(obj) rescue obj.class
|
101
|
+
instance_resolution_order(klass)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get every `Class` and `Module`, in order, that will be checked when looking
|
106
|
+
# for methods on instances of the given `Class` or `Module`.
|
107
|
+
# This does not treat singleton classes of classes specially.
|
108
|
+
# @param [Class, Module] klass
|
109
|
+
# @return [Array[Class, Module]]
|
110
|
+
def instance_resolution_order(klass)
|
111
|
+
# include klass in case it is a singleton class,
|
112
|
+
([klass] + klass.ancestors).uniq
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# See all_from_class and all_from_obj.
|
118
|
+
# If method_type is :instance_method, obj must be a `Class` or a `Module`
|
119
|
+
# If method_type is :method, obj can be any `Object`
|
120
|
+
#
|
121
|
+
# N.B. we pre-cache the visibility here to avoid O(N²) behaviour in "ls".
|
122
|
+
def all_from_common(obj, method_type)
|
123
|
+
%w(public protected private).map do |visibility|
|
124
|
+
safe_send(obj, :"#{visibility}_#{method_type}s").map do |method_name|
|
125
|
+
new(safe_send(obj, method_type, method_name), :visibility => visibility.to_sym)
|
126
|
+
end
|
127
|
+
end.flatten(1)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Acts like send but ignores any methods defined below Object or Class in the
|
131
|
+
# inheritance heirarchy.
|
132
|
+
# This is required to introspect methods on objects like Net::HTTP::Get that
|
133
|
+
# have overridden the `method` method.
|
134
|
+
def safe_send(obj, method, *args, &block)
|
135
|
+
(Class === obj ? Class : Object).instance_method(method).bind(obj).call(*args, &block)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get the singleton classes of superclasses that could define methods on
|
139
|
+
# the given class object, and any modules they include.
|
140
|
+
# If a module is included at multiple points in the ancestry, only
|
141
|
+
# the lowest copy will be returned.
|
142
|
+
def singleton_class_resolution_order(klass)
|
143
|
+
resolution_order = klass.ancestors.map do |anc|
|
144
|
+
[singleton_class(anc)] + singleton_class(anc).included_modules if anc.is_a?(Class)
|
145
|
+
end.compact.flatten(1)
|
146
|
+
|
147
|
+
resolution_order.reverse.uniq.reverse - Class.included_modules
|
148
|
+
end
|
149
|
+
|
150
|
+
def singleton_class(obj); class << obj; self; end end
|
151
|
+
end
|
152
|
+
|
153
|
+
# A new instance of `Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.
|
154
|
+
#
|
155
|
+
# @param [::Method, UnboundMethod, Proc] method
|
156
|
+
# @param [Hash] known_info, can be used to pre-cache expensive to compute stuff.
|
157
|
+
# @return [Pry::Method]
|
158
|
+
def initialize(method, known_info={})
|
159
|
+
@method = method
|
160
|
+
@visibility = known_info[:visibility]
|
161
|
+
end
|
162
|
+
|
163
|
+
# Get the name of the method as a String, regardless of the underlying Method#name type.
|
164
|
+
# @return [String]
|
165
|
+
def name
|
166
|
+
@method.name.to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
# @return [String, nil] The source code of the method, or `nil` if it's unavailable.
|
170
|
+
def source
|
171
|
+
@source ||= case source_type
|
172
|
+
when :c
|
173
|
+
info = pry_doc_info
|
174
|
+
if info and info.source
|
175
|
+
code = strip_comments_from_c_code(info.source)
|
176
|
+
end
|
177
|
+
when :rbx
|
178
|
+
strip_leading_whitespace(core_code)
|
179
|
+
when :ruby
|
180
|
+
if pry_method?
|
181
|
+
code = Pry.new(:input => StringIO.new(Pry.line_buffer[source_line..-1].join), :prompt => proc {""}, :hooks => {}).r
|
182
|
+
else
|
183
|
+
code = @method.source
|
184
|
+
end
|
185
|
+
strip_leading_whitespace(code)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# @return [String, nil] The documentation for the method, or `nil` if it's
|
190
|
+
# unavailable.
|
191
|
+
# @raise [CommandError] Raises when the method was defined in the REPL.
|
192
|
+
def doc
|
193
|
+
@doc ||= case source_type
|
194
|
+
when :c
|
195
|
+
info = pry_doc_info
|
196
|
+
info.docstring if info
|
197
|
+
when :rbx
|
198
|
+
strip_leading_hash_and_whitespace_from_ruby_comments(core_doc)
|
199
|
+
when :ruby
|
200
|
+
if pry_method?
|
201
|
+
raise CommandError, "Can't view doc for a REPL-defined method."
|
202
|
+
else
|
203
|
+
strip_leading_hash_and_whitespace_from_ruby_comments(@method.comment)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# @return [Symbol] The source type of the method. The options are
|
209
|
+
# `:ruby` for ordinary Ruby methods, `:c` for methods written in
|
210
|
+
# C, or `:rbx` for Rubinius core methods written in Ruby.
|
211
|
+
def source_type
|
212
|
+
if Helpers::BaseHelpers.rbx?
|
213
|
+
if core? then :rbx else :ruby end
|
214
|
+
else
|
215
|
+
if source_location.nil? then :c else :ruby end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# @return [String, nil] The name of the file the method is defined in, or
|
220
|
+
# `nil` if the filename is unavailable.
|
221
|
+
def source_file
|
222
|
+
if source_location.nil?
|
223
|
+
if !Helpers::BaseHelpers.rbx? and source_type == :c
|
224
|
+
info = pry_doc_info
|
225
|
+
info.file if info
|
226
|
+
end
|
227
|
+
else
|
228
|
+
source_location.first
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# @return [Fixnum, nil] The line of code in `source_file` which begins
|
233
|
+
# the method's definition, or `nil` if that information is unavailable.
|
234
|
+
def source_line
|
235
|
+
source_location.nil? ? nil : source_location.last
|
236
|
+
end
|
237
|
+
|
238
|
+
# @return [Symbol] The visibility of the method. May be `:public`,
|
239
|
+
# `:protected`, or `:private`.
|
240
|
+
def visibility
|
241
|
+
@visibility ||= if owner.public_instance_methods.any? { |m| m.to_s == name }
|
242
|
+
:public
|
243
|
+
elsif owner.protected_instance_methods.any? { |m| m.to_s == name }
|
244
|
+
:protected
|
245
|
+
elsif owner.private_instance_methods.any? { |m| m.to_s == name }
|
246
|
+
:private
|
247
|
+
else
|
248
|
+
:none
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# @return [String] A representation of the method's signature, including its
|
253
|
+
# name and parameters. Optional and "rest" parameters are marked with `*`
|
254
|
+
# and block parameters with `&`. If the parameter names are unavailable,
|
255
|
+
# they're given numbered names instead.
|
256
|
+
# Paraphrased from `awesome_print` gem.
|
257
|
+
def signature
|
258
|
+
if respond_to?(:parameters)
|
259
|
+
args = parameters.inject([]) do |arr, (type, name)|
|
260
|
+
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
|
261
|
+
arr << case type
|
262
|
+
when :req then name.to_s
|
263
|
+
when :opt, :rest then "*#{name}"
|
264
|
+
when :block then "&#{name}"
|
265
|
+
else '?'
|
266
|
+
end
|
267
|
+
end
|
268
|
+
else
|
269
|
+
args = (1..arity.abs).map { |i| "arg#{i}" }
|
270
|
+
args[-1] = "*#{args[-1]}" if arity < 0
|
271
|
+
end
|
272
|
+
|
273
|
+
"#{name}(#{args.join(', ')})"
|
274
|
+
end
|
275
|
+
|
276
|
+
# @return [Pry::Method, nil] The wrapped method that is called when you
|
277
|
+
# use "super" in the body of this method.
|
278
|
+
def super(times=1)
|
279
|
+
if respond_to?(:receiver)
|
280
|
+
sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times)
|
281
|
+
sup &&= sup.bind(receiver)
|
282
|
+
else
|
283
|
+
sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times)
|
284
|
+
end
|
285
|
+
Pry::Method.new(sup) if sup
|
286
|
+
end
|
287
|
+
|
288
|
+
# @return [Symbol, nil] The original name the method was defined under,
|
289
|
+
# before any aliasing, or `nil` if it can't be determined.
|
290
|
+
def original_name
|
291
|
+
return nil if source_type != :ruby
|
292
|
+
|
293
|
+
first_line = source.lines.first
|
294
|
+
return nil if first_line.strip !~ /^def /
|
295
|
+
|
296
|
+
if RUBY_VERSION =~ /^1\.9/ && RUBY_ENGINE == "ruby"
|
297
|
+
require 'ripper'
|
298
|
+
|
299
|
+
# Ripper is ok with an extraneous end, so we don't need to worry about
|
300
|
+
# whether it's a one-liner.
|
301
|
+
tree = Ripper::SexpBuilder.new(first_line + ";end").parse
|
302
|
+
|
303
|
+
name = tree.flatten(2).each do |lst|
|
304
|
+
break lst[1] if lst[0] == :@ident
|
305
|
+
end
|
306
|
+
|
307
|
+
name.is_a?(String) ? name : nil
|
308
|
+
else
|
309
|
+
require 'ruby_parser'
|
310
|
+
|
311
|
+
# RubyParser breaks if there's an extra end, so we'll just rescue
|
312
|
+
# and try again.
|
313
|
+
tree = begin
|
314
|
+
RubyParser.new.parse(first_line + ";end")
|
315
|
+
rescue Racc::ParseError
|
316
|
+
RubyParser.new.parse(first_line)
|
317
|
+
end
|
318
|
+
|
319
|
+
name = tree.each_cons(2) do |a, b|
|
320
|
+
break a if b.is_a?(Array) && b.first == :args
|
321
|
+
end
|
322
|
+
|
323
|
+
name.is_a?(Symbol) ? name.to_s : nil
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# @return [Boolean] Was the method defined outside a source file?
|
328
|
+
def dynamically_defined?
|
329
|
+
!!(source_file and source_file =~ /(\(.*\))|<.*>/)
|
330
|
+
end
|
331
|
+
|
332
|
+
# @return [Boolean] Was the method defined within the Pry REPL?
|
333
|
+
def pry_method?
|
334
|
+
source_file == Pry.eval_path
|
335
|
+
end
|
336
|
+
|
337
|
+
# @return [Boolean] Is the method definitely an alias?
|
338
|
+
def alias?
|
339
|
+
name != original_name
|
340
|
+
end
|
341
|
+
|
342
|
+
# @return [Boolean]
|
343
|
+
def ==(obj)
|
344
|
+
if obj.is_a? Pry::Method
|
345
|
+
super
|
346
|
+
else
|
347
|
+
@method == obj
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# @param [Class] klass
|
352
|
+
# @return [Boolean]
|
353
|
+
def is_a?(klass)
|
354
|
+
klass == Pry::Method or @method.is_a?(klass)
|
355
|
+
end
|
356
|
+
alias kind_of? is_a?
|
357
|
+
|
358
|
+
# @param [String, Symbol] method_name
|
359
|
+
# @return [Boolean]
|
360
|
+
def respond_to?(method_name)
|
361
|
+
super or @method.respond_to?(method_name)
|
362
|
+
end
|
363
|
+
|
364
|
+
# Delegate any unknown calls to the wrapped method.
|
365
|
+
def method_missing(method_name, *args, &block)
|
366
|
+
@method.send(method_name, *args, &block)
|
367
|
+
end
|
368
|
+
|
369
|
+
private
|
370
|
+
# @return [YARD::CodeObjects::MethodObject]
|
371
|
+
# @raise [CommandError] Raises when the method can't be found or `pry-doc` isn't installed.
|
372
|
+
def pry_doc_info
|
373
|
+
if Pry.config.has_pry_doc
|
374
|
+
Pry::MethodInfo.info_for(@method) or raise CommandError, "Cannot locate this method: #{name}."
|
375
|
+
else
|
376
|
+
raise CommandError, "Cannot locate this method: #{name}. Try `gem install pry-doc` to get access to Ruby Core documentation."
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
# @param [String] code
|
381
|
+
# @return [String]
|
382
|
+
def strip_comments_from_c_code(code)
|
383
|
+
code.sub(/\A\s*\/\*.*?\*\/\s*/m, '')
|
384
|
+
end
|
385
|
+
|
386
|
+
# @param [String] comment
|
387
|
+
# @return [String]
|
388
|
+
def strip_leading_hash_and_whitespace_from_ruby_comments(comment)
|
389
|
+
comment = comment.dup
|
390
|
+
comment.gsub!(/\A\#+?$/, '')
|
391
|
+
comment.gsub!(/^\s*#/, '')
|
392
|
+
strip_leading_whitespace(comment)
|
393
|
+
end
|
394
|
+
|
395
|
+
# @param [String] text
|
396
|
+
# @return [String]
|
397
|
+
def strip_leading_whitespace(text)
|
398
|
+
Pry::Helpers::CommandHelpers.unindent(text)
|
399
|
+
end
|
400
|
+
|
401
|
+
# @param [Class,Module] the ancestors to investigate
|
402
|
+
# @return [Method] the unwrapped super-method
|
403
|
+
def super_using_ancestors(ancestors, times=1)
|
404
|
+
next_owner = self.owner
|
405
|
+
times.times do
|
406
|
+
next_owner = ancestors[ancestors.index(next_owner) + 1]
|
407
|
+
return nil unless next_owner
|
408
|
+
end
|
409
|
+
next_owner.instance_method(name) rescue nil
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|