pry 0.9.11.4 → 0.9.12pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -0
- data/Rakefile +4 -0
- data/lib/pry.rb +1 -1
- data/lib/pry/cli.rb +14 -8
- data/lib/pry/code.rb +3 -3
- data/lib/pry/command.rb +20 -5
- data/lib/pry/command_set.rb +3 -3
- data/lib/pry/commands.rb +1 -1
- data/lib/pry/commands/disabled_commands.rb +2 -0
- data/lib/pry/commands/ls.rb +1 -2
- data/lib/pry/commands/reload_code.rb +8 -1
- data/lib/pry/commands/show_info.rb +27 -4
- data/lib/pry/commands/show_source.rb +2 -1
- data/lib/pry/commands/whereami.rb +87 -19
- data/lib/pry/completion.rb +7 -4
- data/lib/pry/helpers/base_helpers.rb +5 -2
- data/lib/pry/helpers/command_helpers.rb +3 -1
- data/lib/pry/helpers/documentation_helpers.rb +18 -7
- data/lib/pry/helpers/table.rb +4 -4
- data/lib/pry/indent.rb +2 -7
- data/lib/pry/method.rb +89 -129
- data/lib/pry/method/disowned.rb +53 -0
- data/lib/pry/method/weird_method_locator.rb +186 -0
- data/lib/pry/module_candidate.rb +3 -3
- data/lib/pry/pager.rb +13 -11
- data/lib/pry/pry_class.rb +10 -3
- data/lib/pry/terminal.rb +73 -0
- data/lib/pry/version.rb +1 -1
- data/lib/pry/wrapped_module.rb +51 -1
- data/spec/command_helpers_spec.rb +21 -1
- data/spec/commands/ls_spec.rb +4 -0
- data/spec/commands/show_doc_spec.rb +28 -28
- data/spec/commands/show_source_spec.rb +70 -22
- data/spec/commands/whereami_spec.rb +60 -11
- data/spec/documentation_helper_spec.rb +73 -0
- data/spec/fixtures/whereami_helper.rb +6 -0
- data/spec/helpers/table_spec.rb +19 -0
- data/spec/method_spec.rb +24 -7
- metadata +13 -7
- data/lib/pry/commands/deprecated_commands.rb +0 -2
- data/lib/pry/terminal_info.rb +0 -48
@@ -0,0 +1,186 @@
|
|
1
|
+
class Pry
|
2
|
+
class Method
|
3
|
+
|
4
|
+
# This class is responsible for locating the *real* `Pry::Method`
|
5
|
+
# object captured by a binding.
|
6
|
+
#
|
7
|
+
# Given a `Binding` from inside a method and a 'seed' Pry::Method object,
|
8
|
+
# there are primarily two situations where the seed method doesn't match
|
9
|
+
# the Binding:
|
10
|
+
# 1. The Pry::Method is from a subclass 2. The Pry::Method represents a method of the same name
|
11
|
+
# while the original was renamed to something else. For 1. we
|
12
|
+
# search vertically up the inheritance chain,
|
13
|
+
# and for 2. we search laterally along the object's method table.
|
14
|
+
#
|
15
|
+
# When we locate the method that matches the Binding we wrap it in
|
16
|
+
# Pry::Method and return it, or return nil if we fail.
|
17
|
+
class WeirdMethodLocator
|
18
|
+
class << self
|
19
|
+
|
20
|
+
# Whether the given method object matches the associated binding.
|
21
|
+
# If the method object does not match the binding, then it's
|
22
|
+
# most likely not the method captured by the binding, and we
|
23
|
+
# must commence a search.
|
24
|
+
#
|
25
|
+
# @param [Pry::Method] method
|
26
|
+
# @param [Binding] b
|
27
|
+
# @return [Boolean]
|
28
|
+
def normal_method?(method, b)
|
29
|
+
method && (method.source_file && method.source_range rescue false) &&
|
30
|
+
File.expand_path(method.source_file) == File.expand_path(b.eval('__FILE__')) &&
|
31
|
+
method.source_range.include?(b.eval('__LINE__'))
|
32
|
+
end
|
33
|
+
|
34
|
+
def weird_method?(method, b)
|
35
|
+
!normal_method?(method, b)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_accessor :method
|
40
|
+
attr_accessor :target
|
41
|
+
|
42
|
+
# @param [Pry::Method] method The seed method.
|
43
|
+
# @param [Binding] target The Binding that captures the method
|
44
|
+
# we want to locate.
|
45
|
+
def initialize(method, target)
|
46
|
+
@method, @target = method, target
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Pry::Method, nil] The Pry::Method that matches the
|
50
|
+
# given binding.
|
51
|
+
def get_method
|
52
|
+
find_method_in_superclass || find_renamed_method
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Boolean] Whether the Pry::Method is unrecoverable
|
56
|
+
# This usually happens when the method captured by the Binding
|
57
|
+
# has been subsequently deleted.
|
58
|
+
def lost_method?
|
59
|
+
!!(get_method.nil? && renamed_method_source_location)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def normal_method?(method)
|
65
|
+
self.class.normal_method?(method, target)
|
66
|
+
end
|
67
|
+
|
68
|
+
def target_self
|
69
|
+
target.eval('self')
|
70
|
+
end
|
71
|
+
|
72
|
+
def target_file
|
73
|
+
pry_file? ? target.eval('__FILE__') : File.expand_path(target.eval('__FILE__'))
|
74
|
+
end
|
75
|
+
|
76
|
+
def target_line
|
77
|
+
target.eval('__LINE__')
|
78
|
+
end
|
79
|
+
|
80
|
+
def pry_file?
|
81
|
+
Pry.eval_path == target.eval('__FILE__')
|
82
|
+
end
|
83
|
+
|
84
|
+
# it's possible in some cases that the method we find by this approach is a sub-method of
|
85
|
+
# the one we're currently in, consider:
|
86
|
+
#
|
87
|
+
# class A; def b; binding.pry; end; end
|
88
|
+
# class B < A; def b; super; end; end
|
89
|
+
#
|
90
|
+
# Given that we can normally find the source_range of methods, and that we know which
|
91
|
+
# __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
|
92
|
+
#
|
93
|
+
# This obviously won't work if the source is unavaiable for some reason, or if both
|
94
|
+
# methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
|
95
|
+
# is broken.
|
96
|
+
#
|
97
|
+
# @return [Pry::Method, nil] The Pry::Method representing the
|
98
|
+
# superclass method.
|
99
|
+
def find_method_in_superclass
|
100
|
+
guess = method
|
101
|
+
|
102
|
+
while guess
|
103
|
+
# needs rescue if this is a Disowned method or a C method or something...
|
104
|
+
# TODO: Fix up the exception handling so we don't need a bare rescue
|
105
|
+
if normal_method?(guess)
|
106
|
+
return guess
|
107
|
+
else
|
108
|
+
guess = guess.super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
|
113
|
+
# This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
|
114
|
+
# or other unknown circumstances (TODO: we should warn the user when this happens)
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
# This is the case where the name of a method has changed
|
119
|
+
# (via alias_method) so we locate the Method object for the
|
120
|
+
# renamed method.
|
121
|
+
#
|
122
|
+
# @return [Pry::Method, nil] The Pry::Method representing the
|
123
|
+
# renamed method
|
124
|
+
def find_renamed_method
|
125
|
+
return if !valid_file?(target_file)
|
126
|
+
alias_name = all_methods_for(target_self).find do |v|
|
127
|
+
expanded_source_location(target_self.method(v).source_location) == renamed_method_source_location
|
128
|
+
end
|
129
|
+
|
130
|
+
alias_name && Pry::Method(target_self.method(alias_name))
|
131
|
+
end
|
132
|
+
|
133
|
+
def expanded_source_location(sl)
|
134
|
+
return if !sl
|
135
|
+
|
136
|
+
if pry_file?
|
137
|
+
sl
|
138
|
+
else
|
139
|
+
[File.expand_path(sl.first), sl.last]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Use static analysis to locate the start of the method definition.
|
144
|
+
# We have the `__FILE__` and `__LINE__` from the binding and the
|
145
|
+
# original name of the method so we search up until we find a
|
146
|
+
# def/define_method, etc defining a method of the appropriate name.
|
147
|
+
#
|
148
|
+
# @return [Array<String, Fixnum>] The `source_location` of the
|
149
|
+
# renamed method
|
150
|
+
def renamed_method_source_location
|
151
|
+
return @original_method_source_location if defined?(@original_method_source_location)
|
152
|
+
|
153
|
+
source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v|
|
154
|
+
Pry::Method.method_definition?(method.name, v)
|
155
|
+
end
|
156
|
+
|
157
|
+
@original_method_source_location = source_index &&
|
158
|
+
[target_file, index_to_line_number(source_index)]
|
159
|
+
end
|
160
|
+
|
161
|
+
def index_to_line_number(index)
|
162
|
+
# Pry.line_buffer is 0-indexed
|
163
|
+
pry_file? ? index : index + 1
|
164
|
+
end
|
165
|
+
|
166
|
+
def valid_file?(file)
|
167
|
+
File.exists?(file) || Pry.eval_path == file
|
168
|
+
end
|
169
|
+
|
170
|
+
def lines_for_file(file)
|
171
|
+
@lines_for_file ||= {}
|
172
|
+
@lines_for_file[file] ||= if Pry.eval_path == file
|
173
|
+
Pry.line_buffer
|
174
|
+
else
|
175
|
+
File.readlines(file)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def all_methods_for(obj)
|
180
|
+
obj.public_methods(false) +
|
181
|
+
obj.private_methods(false) +
|
182
|
+
obj.protected_methods(false)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/lib/pry/module_candidate.rb
CHANGED
@@ -52,8 +52,8 @@ class Pry
|
|
52
52
|
# @return [String] The source for the candidate, i.e the
|
53
53
|
# complete module/class definition.
|
54
54
|
def source
|
55
|
+
return nil if file.nil?
|
55
56
|
return @source if @source
|
56
|
-
raise CommandError, "Could not locate source for #{wrapped}!" if file.nil?
|
57
57
|
|
58
58
|
@source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line, number_of_lines_in_first_chunk))
|
59
59
|
end
|
@@ -61,10 +61,10 @@ class Pry
|
|
61
61
|
# @raise [Pry::CommandError] If documentation cannot be found.
|
62
62
|
# @return [String] The documentation for the candidate.
|
63
63
|
def doc
|
64
|
+
return nil if file.nil?
|
64
65
|
return @doc if @doc
|
65
|
-
raise CommandError, "Could not locate doc for #{wrapped}!" if file.nil?
|
66
66
|
|
67
|
-
@doc =
|
67
|
+
@doc = get_comment_content(Pry::Code.from_file(file).comment_describing(line))
|
68
68
|
end
|
69
69
|
|
70
70
|
# @return [Array, nil] A `[String, Fixnum]` pair representing the
|
data/lib/pry/pager.rb
CHANGED
@@ -12,8 +12,11 @@ class Pry::Pager
|
|
12
12
|
case pager
|
13
13
|
when nil
|
14
14
|
no_pager = !SystemPager.available?
|
15
|
-
|
16
|
-
|
15
|
+
if no_pager || Pry::Helpers::BaseHelpers.jruby?
|
16
|
+
SimplePager.new(text).page
|
17
|
+
else
|
18
|
+
SystemPager.new(text).page
|
19
|
+
end
|
17
20
|
when :simple
|
18
21
|
SimplePager.new(text).page
|
19
22
|
when :system
|
@@ -24,12 +27,8 @@ class Pry::Pager
|
|
24
27
|
end
|
25
28
|
|
26
29
|
def self.page_size
|
27
|
-
|
28
|
-
|
29
|
-
$stdout.winsize.first
|
30
|
-
rescue LoadError
|
31
|
-
27
|
32
|
-
end
|
30
|
+
rows = Pry::Terminal.screen_size
|
31
|
+
@page_size ||= (rows && rows.first || 27)
|
33
32
|
end
|
34
33
|
|
35
34
|
def initialize(text)
|
@@ -38,11 +37,14 @@ class Pry::Pager
|
|
38
37
|
|
39
38
|
class SimplePager < Pry::Pager
|
40
39
|
def page
|
40
|
+
# The pager size minus the number of lines used by the simple pager info bar.
|
41
|
+
page_size = Pry::Pager.page_size - 3
|
41
42
|
text_array = @text.lines.to_a
|
42
|
-
|
43
|
+
|
44
|
+
text_array.each_slice(page_size) do |chunk|
|
43
45
|
puts chunk.join
|
44
|
-
break if chunk.size <
|
45
|
-
if text_array.size >
|
46
|
+
break if chunk.size < page_size
|
47
|
+
if text_array.size > page_size
|
46
48
|
puts "\n<page break> --- Press enter to continue ( q<enter> to break ) --- <page break>"
|
47
49
|
break if $stdin.gets.chomp == "q"
|
48
50
|
end
|
data/lib/pry/pry_class.rb
CHANGED
@@ -263,9 +263,16 @@ class Pry
|
|
263
263
|
|
264
264
|
def self.auto_resize!
|
265
265
|
trap :WINCH do
|
266
|
-
|
267
|
-
|
268
|
-
|
266
|
+
begin
|
267
|
+
Readline.set_screen_size *Terminal.size!
|
268
|
+
rescue => e
|
269
|
+
warn "\nPry.auto_resize!'s Readline.set_screen_size failed: #{e}"
|
270
|
+
end
|
271
|
+
begin
|
272
|
+
Readline.refresh_line
|
273
|
+
rescue => e
|
274
|
+
warn "\nPry.auto_resize!'s Readline.refresh_line failed: #{e}"
|
275
|
+
end
|
269
276
|
end
|
270
277
|
end
|
271
278
|
|
data/lib/pry/terminal.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
class Pry::Terminal
|
2
|
+
class << self
|
3
|
+
# Return a pair of [rows, columns] which gives the size of the window.
|
4
|
+
#
|
5
|
+
# If the window size cannot be determined, return nil.
|
6
|
+
def screen_size
|
7
|
+
rows, cols = actual_screen_size
|
8
|
+
if rows && cols
|
9
|
+
[rows.to_i, cols.to_i]
|
10
|
+
else
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return a screen size or a default if that fails.
|
16
|
+
def size! default = [25, 80]
|
17
|
+
screen_size || default
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return a screen width or the default if that fails.
|
21
|
+
def width! default = 80
|
22
|
+
size![1]
|
23
|
+
end
|
24
|
+
|
25
|
+
def actual_screen_size
|
26
|
+
# The best way, if possible (requires non-jruby ≥1.9 or io-console gem)
|
27
|
+
screen_size_according_to_io_console or
|
28
|
+
# Fall back to the old standby, though it might be stale:
|
29
|
+
screen_size_according_to_env or
|
30
|
+
# Fall further back, though this one is also out of date without something
|
31
|
+
# calling Readline.set_screen_size
|
32
|
+
screen_size_according_to_readline or
|
33
|
+
# Windows users can otherwise run ansicon and get a decent answer:
|
34
|
+
screen_size_according_to_ansicon_env
|
35
|
+
end
|
36
|
+
|
37
|
+
def screen_size_according_to_io_console
|
38
|
+
return if Pry::Helpers::BaseHelpers.jruby?
|
39
|
+
require 'io/console'
|
40
|
+
$stdout.winsize if $stdout.tty? and $stdout.respond_to?(:winsize)
|
41
|
+
rescue LoadError
|
42
|
+
# They're probably on 1.8 without the io-console gem. We'll keep trying.
|
43
|
+
end
|
44
|
+
|
45
|
+
def screen_size_according_to_env
|
46
|
+
size = [ENV['LINES'] || ENV['ROWS'], ENV['COLUMNS']]
|
47
|
+
size if nonzero_column?(size)
|
48
|
+
end
|
49
|
+
|
50
|
+
def screen_size_according_to_readline
|
51
|
+
if Readline.respond_to?(:get_screen_size)
|
52
|
+
size = Readline.get_screen_size
|
53
|
+
size if nonzero_column?(size)
|
54
|
+
end
|
55
|
+
rescue Java::JavaLang::NullPointerException
|
56
|
+
# This rescue won't happen on jrubies later than:
|
57
|
+
# https://github.com/jruby/jruby/pull/436
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def screen_size_according_to_ansicon_env
|
62
|
+
return unless ENV['ANSICON'] =~ /\((.*)x(.*)\)/
|
63
|
+
size = [$2, $1]
|
64
|
+
size if nonzero_column?(size)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def nonzero_column?(size)
|
70
|
+
size[1].to_i > 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/pry/version.rb
CHANGED
data/lib/pry/wrapped_module.rb
CHANGED
@@ -65,6 +65,30 @@ class Pry
|
|
65
65
|
@doc = nil
|
66
66
|
end
|
67
67
|
|
68
|
+
# Returns an array of the names of the constants accessible in the wrapped
|
69
|
+
# module. This provides a consistent interface between 1.8 and 1.9 and also
|
70
|
+
# avoids the problem of accidentally calling the singleton method
|
71
|
+
# `Module.constants`.
|
72
|
+
# @param [Boolean] inherit (true) Include the names of constants from
|
73
|
+
# included modules?
|
74
|
+
def constants(inherit = true)
|
75
|
+
method = Module.instance_method(:constants).bind(@wrapped)
|
76
|
+
|
77
|
+
# If we're on 1.8, we have to manually remove ancestors' constants. If
|
78
|
+
# we're on 1.9, though, it's better to use the built-in `inherit` param,
|
79
|
+
# since it doesn't do things like incorrectly remove Pry::Config.
|
80
|
+
if method.arity == 0
|
81
|
+
consts = method.call
|
82
|
+
if !inherit
|
83
|
+
consts -= (@wrapped.ancestors - [@wrapped]).map(&:constants).flatten
|
84
|
+
end
|
85
|
+
else
|
86
|
+
consts = method.call(inherit)
|
87
|
+
end
|
88
|
+
|
89
|
+
consts
|
90
|
+
end
|
91
|
+
|
68
92
|
# The prefix that would appear before methods defined on this class.
|
69
93
|
#
|
70
94
|
# i.e. the "String." or "String#" in String.new and String#initialize.
|
@@ -205,13 +229,27 @@ class Pry
|
|
205
229
|
@memoized_candidates[rank] ||= Candidate.new(self, rank)
|
206
230
|
end
|
207
231
|
|
208
|
-
|
209
232
|
# @return [Fixnum] The number of candidate definitions for the
|
210
233
|
# current module.
|
211
234
|
def number_of_candidates
|
212
235
|
method_candidates.count
|
213
236
|
end
|
214
237
|
|
238
|
+
# @note On JRuby 1.9 and higher, in certain conditions, this method chucks
|
239
|
+
# away its ability to be quick (when there are lots of monkey patches,
|
240
|
+
# like in Rails). However, it should be efficient enough on other rubies.
|
241
|
+
# @see https://github.com/jruby/jruby/issues/525
|
242
|
+
# @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on
|
243
|
+
# other rubies returns Enumerator
|
244
|
+
def candidates
|
245
|
+
enum = generator.new do |y|
|
246
|
+
(0...number_of_candidates).each do |num|
|
247
|
+
y.yield candidate(num)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
Pry::Helpers::BaseHelpers.jruby_19? ? enum.to_a : enum
|
251
|
+
end
|
252
|
+
|
215
253
|
# @return [Boolean] Whether YARD docs are available for this module.
|
216
254
|
def yard_docs?
|
217
255
|
!!(defined?(YARD) && YARD::Registry.at(name))
|
@@ -237,6 +275,18 @@ class Pry
|
|
237
275
|
|
238
276
|
private
|
239
277
|
|
278
|
+
# Ruby 1.8 doesn't support `Enumerator` (it's called Generator instead)
|
279
|
+
#
|
280
|
+
# @return [Object] Return the appropriate generator class.
|
281
|
+
def generator
|
282
|
+
@generator ||= if defined?(Enumerator)
|
283
|
+
Enumerator
|
284
|
+
else
|
285
|
+
require 'generator'
|
286
|
+
Generator
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
240
290
|
# @return [Pry::WrappedModule::Candidate] The candidate of rank 0,
|
241
291
|
# that is the 'monkey patch' of this module with the highest
|
242
292
|
# number of methods. It is considered the 'canonical' definition
|
@@ -5,5 +5,25 @@ describe Pry::Helpers::CommandHelpers do
|
|
5
5
|
@helper = Pry::Helpers::CommandHelpers
|
6
6
|
end
|
7
7
|
|
8
|
-
|
8
|
+
describe "unindent" do
|
9
|
+
it "should remove the same prefix from all lines" do
|
10
|
+
@helper.unindent(" one\n two\n").should == "one\ntwo\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should not be phased by empty lines" do
|
14
|
+
@helper.unindent(" one\n\n two\n").should == "one\n\ntwo\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should only remove a common prefix" do
|
18
|
+
@helper.unindent(" one\n two\n").should == " one\ntwo\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should also remove tabs if present" do
|
22
|
+
@helper.unindent("\tone\n\ttwo\n").should == "one\ntwo\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should ignore lines starting with --" do
|
26
|
+
@helper.unindent(" one\n--\n two\n").should == "one\n--\ntwo\n"
|
27
|
+
end
|
28
|
+
end
|
9
29
|
end
|