pry 0.12.0 → 0.14.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +162 -1
- data/LICENSE +1 -1
- data/README.md +331 -269
- data/bin/pry +5 -0
- data/lib/pry.rb +132 -119
- data/lib/pry/basic_object.rb +8 -4
- data/lib/pry/block_command.rb +22 -0
- data/lib/pry/class_command.rb +194 -0
- data/lib/pry/cli.rb +43 -51
- data/lib/pry/code.rb +40 -28
- data/lib/pry/code/code_file.rb +28 -24
- data/lib/pry/code/code_range.rb +4 -2
- data/lib/pry/code/loc.rb +15 -8
- data/lib/pry/code_object.rb +40 -38
- data/lib/pry/color_printer.rb +47 -46
- data/lib/pry/command.rb +166 -369
- data/lib/pry/command_set.rb +76 -73
- data/lib/pry/command_state.rb +31 -0
- data/lib/pry/commands/amend_line.rb +86 -81
- data/lib/pry/commands/bang.rb +18 -14
- data/lib/pry/commands/bang_pry.rb +15 -11
- data/lib/pry/commands/cat.rb +61 -54
- data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
- data/lib/pry/commands/cat/exception_formatter.rb +71 -60
- data/lib/pry/commands/cat/file_formatter.rb +55 -49
- data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
- data/lib/pry/commands/cd.rb +40 -35
- data/lib/pry/commands/change_inspector.rb +29 -22
- data/lib/pry/commands/change_prompt.rb +44 -39
- data/lib/pry/commands/clear_screen.rb +16 -10
- data/lib/pry/commands/code_collector.rb +148 -133
- data/lib/pry/commands/disable_pry.rb +23 -19
- data/lib/pry/commands/easter_eggs.rb +19 -30
- data/lib/pry/commands/edit.rb +184 -161
- data/lib/pry/commands/edit/exception_patcher.rb +21 -17
- data/lib/pry/commands/edit/file_and_line_locator.rb +34 -23
- data/lib/pry/commands/exit.rb +39 -35
- data/lib/pry/commands/exit_all.rb +24 -20
- data/lib/pry/commands/exit_program.rb +20 -16
- data/lib/pry/commands/find_method.rb +168 -160
- data/lib/pry/commands/fix_indent.rb +16 -12
- data/lib/pry/commands/help.rb +140 -133
- data/lib/pry/commands/hist.rb +151 -150
- data/lib/pry/commands/import_set.rb +20 -16
- data/lib/pry/commands/jump_to.rb +25 -21
- data/lib/pry/commands/list_inspectors.rb +35 -28
- data/lib/pry/commands/ls.rb +124 -102
- data/lib/pry/commands/ls/constants.rb +59 -42
- data/lib/pry/commands/ls/formatter.rb +50 -46
- data/lib/pry/commands/ls/globals.rb +38 -34
- data/lib/pry/commands/ls/grep.rb +17 -13
- data/lib/pry/commands/ls/instance_vars.rb +29 -27
- data/lib/pry/commands/ls/interrogatable.rb +18 -12
- data/lib/pry/commands/ls/jruby_hacks.rb +47 -41
- data/lib/pry/commands/ls/local_names.rb +26 -22
- data/lib/pry/commands/ls/local_vars.rb +38 -28
- data/lib/pry/commands/ls/ls_entity.rb +47 -51
- data/lib/pry/commands/ls/methods.rb +44 -43
- data/lib/pry/commands/ls/methods_helper.rb +46 -42
- data/lib/pry/commands/ls/self_methods.rb +23 -22
- data/lib/pry/commands/nesting.rb +21 -17
- data/lib/pry/commands/play.rb +93 -82
- data/lib/pry/commands/pry_backtrace.rb +22 -17
- data/lib/pry/commands/pry_version.rb +15 -11
- data/lib/pry/commands/raise_up.rb +27 -22
- data/lib/pry/commands/reload_code.rb +60 -48
- data/lib/pry/commands/reset.rb +16 -12
- data/lib/pry/commands/ri.rb +55 -45
- data/lib/pry/commands/save_file.rb +45 -43
- data/lib/pry/commands/shell_command.rb +51 -51
- data/lib/pry/commands/shell_mode.rb +21 -17
- data/lib/pry/commands/show_doc.rb +80 -68
- data/lib/pry/commands/show_info.rb +189 -171
- data/lib/pry/commands/show_input.rb +16 -11
- data/lib/pry/commands/show_source.rb +110 -45
- data/lib/pry/commands/stat.rb +35 -31
- data/lib/pry/commands/switch_to.rb +21 -15
- data/lib/pry/commands/toggle_color.rb +20 -16
- data/lib/pry/commands/watch_expression.rb +89 -86
- data/lib/pry/commands/watch_expression/expression.rb +32 -27
- data/lib/pry/commands/whereami.rb +156 -148
- data/lib/pry/commands/wtf.rb +75 -50
- data/lib/pry/config.rb +307 -25
- data/lib/pry/config/attributable.rb +22 -0
- data/lib/pry/config/lazy_value.rb +29 -0
- data/lib/pry/config/memoized_value.rb +34 -0
- data/lib/pry/config/value.rb +24 -0
- data/lib/pry/control_d_handler.rb +28 -0
- data/lib/pry/core_extensions.rb +9 -7
- data/lib/pry/editor.rb +48 -21
- data/lib/pry/env.rb +18 -0
- data/lib/pry/exception_handler.rb +43 -0
- data/lib/pry/exceptions.rb +13 -16
- data/lib/pry/forwardable.rb +5 -1
- data/lib/pry/helpers.rb +2 -0
- data/lib/pry/helpers/base_helpers.rb +68 -197
- data/lib/pry/helpers/command_helpers.rb +50 -61
- data/lib/pry/helpers/documentation_helpers.rb +20 -13
- data/lib/pry/helpers/options_helpers.rb +14 -7
- data/lib/pry/helpers/platform.rb +7 -5
- data/lib/pry/helpers/table.rb +33 -26
- data/lib/pry/helpers/text.rb +17 -14
- data/lib/pry/history.rb +48 -56
- data/lib/pry/hooks.rb +21 -12
- data/lib/pry/indent.rb +54 -50
- data/lib/pry/input_completer.rb +248 -230
- data/lib/pry/input_lock.rb +8 -9
- data/lib/pry/inspector.rb +36 -24
- data/lib/pry/last_exception.rb +45 -45
- data/lib/pry/method.rb +141 -94
- data/lib/pry/method/disowned.rb +16 -4
- data/lib/pry/method/patcher.rb +12 -3
- data/lib/pry/method/weird_method_locator.rb +68 -44
- data/lib/pry/object_path.rb +33 -25
- data/lib/pry/output.rb +121 -35
- data/lib/pry/pager.rb +186 -180
- data/lib/pry/prompt.rb +123 -54
- data/lib/pry/pry_class.rb +61 -103
- data/lib/pry/pry_instance.rb +217 -215
- data/lib/pry/repl.rb +18 -22
- data/lib/pry/repl_file_loader.rb +27 -21
- data/lib/pry/ring.rb +11 -6
- data/lib/pry/slop.rb +574 -563
- data/lib/pry/slop/commands.rb +164 -169
- data/lib/pry/slop/option.rb +172 -168
- data/lib/pry/syntax_highlighter.rb +26 -0
- data/lib/pry/system_command_handler.rb +17 -0
- data/lib/pry/testable.rb +59 -61
- data/lib/pry/testable/evalable.rb +21 -12
- data/lib/pry/testable/mockable.rb +18 -10
- data/lib/pry/testable/pry_tester.rb +71 -56
- data/lib/pry/testable/utility.rb +29 -21
- data/lib/pry/testable/variables.rb +49 -43
- data/lib/pry/version.rb +3 -1
- data/lib/pry/warning.rb +27 -0
- data/lib/pry/wrapped_module.rb +51 -42
- data/lib/pry/wrapped_module/candidate.rb +21 -14
- metadata +35 -35
- data/lib/pry/commands.rb +0 -6
- data/lib/pry/commands/disabled_commands.rb +0 -2
- data/lib/pry/commands/gem_cd.rb +0 -26
- data/lib/pry/commands/gem_install.rb +0 -32
- data/lib/pry/commands/gem_list.rb +0 -33
- data/lib/pry/commands/gem_open.rb +0 -29
- data/lib/pry/commands/gem_readme.rb +0 -25
- data/lib/pry/commands/gem_search.rb +0 -40
- data/lib/pry/commands/gem_stats.rb +0 -83
- data/lib/pry/commands/gist.rb +0 -102
- data/lib/pry/commands/install_command.rb +0 -54
- data/lib/pry/config/behavior.rb +0 -255
- data/lib/pry/config/convenience.rb +0 -28
- data/lib/pry/config/default.rb +0 -159
- data/lib/pry/config/memoization.rb +0 -48
- data/lib/pry/platform.rb +0 -91
- data/lib/pry/plugins.rb +0 -122
- data/lib/pry/rubygem.rb +0 -84
- data/lib/pry/terminal.rb +0 -91
data/lib/pry/hooks.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Pry
|
2
4
|
# Implements a hooks system for Pry. A hook is a callable that is associated
|
3
5
|
# with an event. A number of events are currently provided by Pry, these
|
@@ -10,6 +12,16 @@ class Pry
|
|
10
12
|
# puts "hello"
|
11
13
|
# end
|
12
14
|
class Hooks
|
15
|
+
def self.default
|
16
|
+
hooks = new
|
17
|
+
hooks.add_hook(:before_session, :default) do |_out, _target, pry_instance|
|
18
|
+
next if pry_instance.quiet?
|
19
|
+
|
20
|
+
pry_instance.run_command('whereami --quiet')
|
21
|
+
end
|
22
|
+
hooks
|
23
|
+
end
|
24
|
+
|
13
25
|
def initialize
|
14
26
|
@hooks = Hash.new { |h, k| h[k] = [] }
|
15
27
|
end
|
@@ -34,8 +46,9 @@ class Pry
|
|
34
46
|
# @return [Pry:Hooks] The receiver.
|
35
47
|
# @see #merge
|
36
48
|
def merge!(other)
|
37
|
-
@hooks.merge!(other.dup.hooks) do |
|
38
|
-
temp_hash
|
49
|
+
@hooks.merge!(other.dup.hooks) do |_key, array, other_array|
|
50
|
+
temp_hash = {}
|
51
|
+
output = []
|
39
52
|
|
40
53
|
(array + other_array).reverse_each do |pair|
|
41
54
|
temp_hash[pair.first] ||= output.unshift(pair)
|
@@ -54,7 +67,7 @@ class Pry
|
|
54
67
|
# @return [Pry::Hooks] a new `Pry::Hooks` instance containing a merge of the
|
55
68
|
# contents of two `Pry:Hooks` instances.
|
56
69
|
def merge(other)
|
57
|
-
|
70
|
+
dup.tap do |v|
|
58
71
|
v.merge!(other)
|
59
72
|
end
|
60
73
|
end
|
@@ -74,12 +87,10 @@ class Pry
|
|
74
87
|
raise ArgumentError, "Hook with name '#{hook_name}' already defined!"
|
75
88
|
end
|
76
89
|
|
77
|
-
if !block && !callable
|
78
|
-
raise ArgumentError, "Must provide a block or callable."
|
79
|
-
end
|
90
|
+
raise ArgumentError, "Must provide a block or callable." if !block && !callable
|
80
91
|
|
81
92
|
# ensure we only have one anonymous hook
|
82
|
-
@hooks[event_name].delete_if { |h,
|
93
|
+
@hooks[event_name].delete_if { |h, _k| h.nil? } if hook_name.nil?
|
83
94
|
|
84
95
|
if block
|
85
96
|
@hooks[event_name] << [hook_name, block]
|
@@ -95,7 +106,7 @@ class Pry
|
|
95
106
|
# @param [Array] args The arguments to pass to each hook function.
|
96
107
|
# @return [Object] The return value of the last executed hook.
|
97
108
|
def exec_hook(event_name, *args, &block)
|
98
|
-
@hooks[event_name.to_s].map do |
|
109
|
+
@hooks[event_name.to_s].map do |_hook_name, callable|
|
99
110
|
begin
|
100
111
|
callable.call(*args, &block)
|
101
112
|
rescue RescuableException => e
|
@@ -115,7 +126,7 @@ class Pry
|
|
115
126
|
# @param [Symbol] hook_name The name of the hook
|
116
127
|
# @return [#call] a specific hook for a given event.
|
117
128
|
def get_hook(event_name, hook_name)
|
118
|
-
hook = @hooks[event_name.to_s].find do |current_hook_name,
|
129
|
+
hook = @hooks[event_name.to_s].find do |current_hook_name, _callable|
|
119
130
|
current_hook_name == hook_name
|
120
131
|
end
|
121
132
|
hook.last if hook
|
@@ -164,8 +175,6 @@ class Pry
|
|
164
175
|
|
165
176
|
protected
|
166
177
|
|
167
|
-
|
168
|
-
@hooks
|
169
|
-
end
|
178
|
+
attr_reader :hooks
|
170
179
|
end
|
171
180
|
end
|
data/lib/pry/indent.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Pry
|
4
4
|
##
|
@@ -21,35 +21,35 @@ class Pry
|
|
21
21
|
attr_reader :stack
|
22
22
|
|
23
23
|
# The amount of spaces to insert for each indent level.
|
24
|
-
SPACES = ' '
|
24
|
+
SPACES = ' '.freeze
|
25
25
|
|
26
26
|
# Hash containing all the tokens that should increase the indentation
|
27
27
|
# level. The keys of this hash are open tokens, the values the matching
|
28
28
|
# tokens that should prevent a line from being indented if they appear on
|
29
29
|
# the same line.
|
30
30
|
OPEN_TOKENS = {
|
31
|
-
'def'
|
32
|
-
'class'
|
31
|
+
'def' => 'end',
|
32
|
+
'class' => 'end',
|
33
33
|
'module' => 'end',
|
34
|
-
'do'
|
35
|
-
'if'
|
34
|
+
'do' => 'end',
|
35
|
+
'if' => 'end',
|
36
36
|
'unless' => 'end',
|
37
|
-
'while'
|
38
|
-
'until'
|
39
|
-
'for'
|
40
|
-
'case'
|
41
|
-
'begin'
|
42
|
-
'['
|
43
|
-
'{'
|
44
|
-
'('
|
45
|
-
}
|
37
|
+
'while' => 'end',
|
38
|
+
'until' => 'end',
|
39
|
+
'for' => 'end',
|
40
|
+
'case' => 'end',
|
41
|
+
'begin' => 'end',
|
42
|
+
'[' => ']',
|
43
|
+
'{' => '}',
|
44
|
+
'(' => ')'
|
45
|
+
}.freeze
|
46
46
|
|
47
47
|
# Which tokens can either be open tokens, or appear as modifiers on
|
48
48
|
# a single-line.
|
49
|
-
SINGLELINE_TOKENS = %w
|
49
|
+
SINGLELINE_TOKENS = %w[if while until unless rescue].freeze
|
50
50
|
|
51
51
|
# Which tokens can be followed by an optional "do" keyword.
|
52
|
-
OPTIONAL_DO_TOKENS = %w
|
52
|
+
OPTIONAL_DO_TOKENS = %w[for while until].freeze
|
53
53
|
|
54
54
|
# Collection of token types that should be ignored. Without this list
|
55
55
|
# keywords such as "class" inside strings would cause the code to be
|
@@ -58,7 +58,7 @@ class Pry
|
|
58
58
|
# :pre_constant and :preserved_constant are the CodeRay 0.9.8 and 1.0.0
|
59
59
|
# classifications of "true", "false", and "nil".
|
60
60
|
IGNORE_TOKENS = [:space, :content, :string, :method, :ident,
|
61
|
-
:constant, :pre_constant, :predefined_constant]
|
61
|
+
:constant, :pre_constant, :predefined_constant].freeze
|
62
62
|
|
63
63
|
# Tokens that indicate the end of a statement (i.e. that, if they appear
|
64
64
|
# directly before an "if" indicates that that if applies to the same line,
|
@@ -73,7 +73,7 @@ class Pry
|
|
73
73
|
|
74
74
|
# Collection of tokens that should appear dedented even though they
|
75
75
|
# don't affect the surrounding code.
|
76
|
-
MIDWAY_TOKENS = %w
|
76
|
+
MIDWAY_TOKENS = %w[when else elsif ensure rescue].freeze
|
77
77
|
|
78
78
|
# Clean the indentation of a fragment of ruby.
|
79
79
|
#
|
@@ -101,7 +101,8 @@ class Pry
|
|
101
101
|
indent.module_nesting
|
102
102
|
end
|
103
103
|
|
104
|
-
def initialize
|
104
|
+
def initialize(pry_instance = Pry.new)
|
105
|
+
@pry_instance = pry_instance
|
105
106
|
reset
|
106
107
|
end
|
107
108
|
|
@@ -145,7 +146,9 @@ class Pry
|
|
145
146
|
input.lines.each do |line|
|
146
147
|
if in_string?
|
147
148
|
tokens = tokenize("#{open_delimiters_line}\n#{line}")
|
148
|
-
tokens = tokens.drop_while
|
149
|
+
tokens = tokens.drop_while do |token, _type|
|
150
|
+
!(token.is_a?(String) && token.include?("\n"))
|
151
|
+
end
|
149
152
|
previously_in_string = true
|
150
153
|
else
|
151
154
|
tokens = tokenize(line)
|
@@ -166,7 +169,7 @@ class Pry
|
|
166
169
|
|
167
170
|
@indent_level = prefix
|
168
171
|
|
169
|
-
|
172
|
+
output
|
170
173
|
end
|
171
174
|
|
172
175
|
# Get the indentation for the start of the next line.
|
@@ -193,7 +196,6 @@ class Pry
|
|
193
196
|
# @return [Array[Integer]]
|
194
197
|
#
|
195
198
|
def indentation_delta(tokens)
|
196
|
-
|
197
199
|
# We need to keep track of whether we've seen a "for" on this line because
|
198
200
|
# if the line ends with "do" then that "do" should be discounted (i.e. we're
|
199
201
|
# only opening one level not two) To do this robustly we want to keep track
|
@@ -204,20 +206,26 @@ class Pry
|
|
204
206
|
# When deciding whether an "if" token is the start of a multiline statement,
|
205
207
|
# or just the middle of a single-line if statement, we just look at the
|
206
208
|
# preceding token, which is tracked here.
|
207
|
-
last_token
|
209
|
+
last_token = nil
|
210
|
+
last_kind = nil
|
208
211
|
|
209
212
|
# delta keeps track of the total difference from the start of each line after
|
210
213
|
# the given token, 0 is just the level at which the current line started for
|
211
214
|
# reference.
|
212
|
-
remove_before
|
215
|
+
remove_before = 0
|
216
|
+
add_after = 0
|
213
217
|
|
214
218
|
# If the list of tokens contains a matching closing token the line should
|
215
219
|
# not be indented (and thus we should return true).
|
216
220
|
tokens.each do |token, kind|
|
217
|
-
is_singleline_if =
|
221
|
+
is_singleline_if =
|
222
|
+
SINGLELINE_TOKENS.include?(token) && end_of_statement?(last_token, last_kind)
|
218
223
|
is_optional_do = (token == "do" && seen_for_at.include?(add_after - 1))
|
219
224
|
|
220
|
-
|
225
|
+
unless kind == :space
|
226
|
+
last_token = token
|
227
|
+
last_kind = kind
|
228
|
+
end
|
221
229
|
next if IGNORE_TOKENS.include?(kind)
|
222
230
|
|
223
231
|
track_module_nesting(token, kind)
|
@@ -228,7 +236,7 @@ class Pry
|
|
228
236
|
|
229
237
|
if kind == :delimiter
|
230
238
|
track_delimiter(token)
|
231
|
-
elsif OPEN_TOKENS.
|
239
|
+
elsif OPEN_TOKENS.key?(token) && !is_optional_do && !is_singleline_if
|
232
240
|
@stack << token
|
233
241
|
add_after += 1
|
234
242
|
elsif token == OPEN_TOKENS[@stack.last]
|
@@ -247,20 +255,21 @@ class Pry
|
|
247
255
|
end
|
248
256
|
end
|
249
257
|
|
250
|
-
|
258
|
+
[remove_before, add_after]
|
251
259
|
end
|
252
260
|
|
253
|
-
# If the code just before an "if" or "while" token on a line looks like the
|
254
|
-
# then we want to treat that "if" as a singleline, not
|
261
|
+
# If the code just before an "if" or "while" token on a line looks like the
|
262
|
+
# end of a statement, then we want to treat that "if" as a singleline, not
|
263
|
+
# multiline statement.
|
255
264
|
def end_of_statement?(last_token, last_kind)
|
256
|
-
(last_token =~
|
265
|
+
(last_token =~ %r{^[)\]\}/]$} || STATEMENT_END_TOKENS.include?(last_kind))
|
257
266
|
end
|
258
267
|
|
259
268
|
# Are we currently in the middle of a string literal.
|
260
269
|
#
|
261
|
-
# This is used to determine whether to re-indent a given line, we mustn't
|
262
|
-
# within string literals because to do so would actually change
|
263
|
-
# String!
|
270
|
+
# This is used to determine whether to re-indent a given line, we mustn't
|
271
|
+
# re-indent within string literals because to do so would actually change
|
272
|
+
# the value of the String!
|
264
273
|
#
|
265
274
|
# @return Boolean
|
266
275
|
def in_string?
|
@@ -272,16 +281,17 @@ class Pry
|
|
272
281
|
# @param [String] string The Ruby to lex
|
273
282
|
# @return [Array] An Array of pairs of [token_value, token_type]
|
274
283
|
def tokenize(string)
|
275
|
-
tokens =
|
284
|
+
tokens = SyntaxHighlighter.tokenize(string)
|
276
285
|
tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens) # Coderay 1.0.0
|
277
286
|
tokens.to_a
|
278
287
|
end
|
279
288
|
|
280
289
|
# Update the internal state about what kind of strings are open.
|
281
290
|
#
|
282
|
-
# Most of the complication here comes from the fact that HEREDOCs can be
|
283
|
-
# normal strings (which can't be nested) we assume that CodeRay
|
284
|
-
# open-and-close delimiters so we don't bother checking what
|
291
|
+
# Most of the complication here comes from the fact that HEREDOCs can be
|
292
|
+
# nested. For normal strings (which can't be nested) we assume that CodeRay
|
293
|
+
# correctly pairs open-and-close delimiters so we don't bother checking what
|
294
|
+
# they are.
|
285
295
|
#
|
286
296
|
# @param [String] token The token (of type :delimiter)
|
287
297
|
def track_delimiter(token)
|
@@ -292,11 +302,7 @@ class Pry
|
|
292
302
|
when @close_heredocs[@heredoc_queue.first]
|
293
303
|
@heredoc_queue.shift
|
294
304
|
else
|
295
|
-
|
296
|
-
@string_start = nil
|
297
|
-
else
|
298
|
-
@string_start = token
|
299
|
-
end
|
305
|
+
@string_start = @string_start ? nil : token
|
300
306
|
end
|
301
307
|
end
|
302
308
|
|
@@ -312,7 +318,7 @@ class Pry
|
|
312
318
|
#
|
313
319
|
# @return String
|
314
320
|
def open_delimiters_line
|
315
|
-
"puts #{open_delimiters.join(
|
321
|
+
"puts #{open_delimiters.join(', ')}"
|
316
322
|
end
|
317
323
|
|
318
324
|
# Update the internal state relating to module nesting.
|
@@ -333,7 +339,7 @@ class Pry
|
|
333
339
|
# @param [String] token a token from Coderay
|
334
340
|
# @param [Symbol] kind the kind of that token
|
335
341
|
def track_module_nesting(token, kind)
|
336
|
-
if kind == :keyword &&
|
342
|
+
if kind == :keyword && %w[class module].include?(token)
|
337
343
|
@module_nesting << [token, nil]
|
338
344
|
@awaiting_class = true
|
339
345
|
elsif @awaiting_class
|
@@ -358,9 +364,7 @@ class Pry
|
|
358
364
|
# @param [String] token a token from Coderay
|
359
365
|
# @param [Symbol] kind the kind of that token
|
360
366
|
def track_module_nesting_end(token, kind = :keyword)
|
361
|
-
if kind == :keyword &&
|
362
|
-
@module_nesting.pop
|
363
|
-
end
|
367
|
+
@module_nesting.pop if kind == :keyword && %w[class module].include?(token)
|
364
368
|
end
|
365
369
|
|
366
370
|
# Return a list of strings which can be used to re-construct the Module.nesting at
|
@@ -391,7 +395,7 @@ class Pry
|
|
391
395
|
line_to_measure = Pry::Helpers::Text.strip_color(prompt) << code
|
392
396
|
whitespace = ' ' * overhang
|
393
397
|
|
394
|
-
cols =
|
398
|
+
cols = @pry_instance.output.width
|
395
399
|
lines = cols == 0 ? 1 : (line_to_measure.length / cols + 1).to_i
|
396
400
|
|
397
401
|
if Helpers::Platform.windows_ansi?
|
data/lib/pry/input_completer.rb
CHANGED
@@ -1,265 +1,283 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# taken from irb
|
2
4
|
# Implements tab completion for Readline in Pry
|
3
|
-
class Pry
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
ReservedWords = [
|
18
|
-
"BEGIN", "END",
|
19
|
-
"alias", "and",
|
20
|
-
"begin", "break",
|
21
|
-
"case", "class",
|
22
|
-
"def", "defined", "do",
|
23
|
-
"else", "elsif", "end", "ensure",
|
24
|
-
"false", "for",
|
25
|
-
"if", "in",
|
26
|
-
"module",
|
27
|
-
"next", "nil", "not",
|
28
|
-
"or",
|
29
|
-
"redo", "rescue", "retry", "return",
|
30
|
-
"self", "super",
|
31
|
-
"then", "true",
|
32
|
-
"undef", "unless", "until",
|
33
|
-
"when", "while",
|
34
|
-
"yield" ]
|
5
|
+
class Pry
|
6
|
+
class InputCompleter
|
7
|
+
NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/.freeze
|
8
|
+
ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/.freeze
|
9
|
+
SYMBOL_REGEXP = /^(:[^:.]*)$/.freeze
|
10
|
+
SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/.freeze
|
11
|
+
REGEX_REGEXP = %r{^(/[^/]*/)\.([^.]*)$}.freeze
|
12
|
+
PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/.freeze
|
13
|
+
TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/.freeze
|
14
|
+
CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/.freeze
|
15
|
+
CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/.freeze
|
16
|
+
HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/.freeze
|
17
|
+
GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/.freeze
|
18
|
+
VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/.freeze
|
35
19
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
20
|
+
RESERVED_WORDS = %w[
|
21
|
+
BEGIN END
|
22
|
+
alias and
|
23
|
+
begin break
|
24
|
+
case class
|
25
|
+
def defined do
|
26
|
+
else elsif end ensure
|
27
|
+
false for
|
28
|
+
if in
|
29
|
+
module
|
30
|
+
next nil not
|
31
|
+
or
|
32
|
+
redo rescue retry return
|
33
|
+
self super
|
34
|
+
then true
|
35
|
+
undef unless until
|
36
|
+
when while
|
37
|
+
yield
|
38
|
+
].freeze
|
41
39
|
|
42
|
-
|
40
|
+
WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{(".freeze
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
def initialize(input, pry = nil)
|
43
|
+
@pry = pry
|
44
|
+
@input = input
|
45
|
+
if @input.respond_to?(:basic_word_break_characters=)
|
46
|
+
@input.basic_word_break_characters = WORD_ESCAPE_STR
|
47
|
+
end
|
50
48
|
|
51
|
-
|
52
|
-
# Return a new completion proc for use by Readline.
|
53
|
-
#
|
54
|
-
def call(str, options = {})
|
55
|
-
custom_completions = options[:custom_completions] || []
|
56
|
-
# if there are multiple contexts e.g. cd 1/2/3
|
57
|
-
# get new target for 1/2 and find candidates for 3
|
58
|
-
path, input = build_path(str)
|
49
|
+
return unless @input.respond_to?(:completion_append_character=)
|
59
50
|
|
60
|
-
|
61
|
-
target = options[:target]
|
62
|
-
else
|
63
|
-
# Assume the user is tab-completing the 'cd' command
|
64
|
-
begin
|
65
|
-
target = Pry::ObjectPath.new(path.call, @pry.binding_stack).resolve.last
|
66
|
-
# but if that doesn't work, assume they're doing division with no spaces
|
67
|
-
rescue Pry::CommandError
|
68
|
-
target = options[:target]
|
69
|
-
end
|
51
|
+
@input.completion_append_character = nil
|
70
52
|
end
|
71
53
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
candidates = Array.instance_methods.collect(&:to_s)
|
85
|
-
select_message(path, receiver, message, candidates)
|
86
|
-
when PROC_OR_HASH_REGEXP # Proc or Hash
|
87
|
-
receiver = $1
|
88
|
-
message = Regexp.quote($2)
|
89
|
-
candidates = Proc.instance_methods.collect(&:to_s)
|
90
|
-
candidates |= Hash.instance_methods.collect(&:to_s)
|
91
|
-
select_message(path, receiver, message, candidates)
|
92
|
-
when SYMBOL_REGEXP # Symbol
|
93
|
-
if Symbol.respond_to?(:all_symbols)
|
94
|
-
sym = Regexp.quote($1)
|
95
|
-
candidates = Symbol.all_symbols.collect { |s| ":" << s.id2name }
|
96
|
-
candidates.grep(/^#{sym}/)
|
97
|
-
else
|
98
|
-
[]
|
99
|
-
end
|
100
|
-
when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods
|
101
|
-
receiver = $1
|
102
|
-
candidates = Object.constants.collect(&:to_s)
|
103
|
-
candidates.grep(/^#{receiver}/).collect { |e| "::" << e }
|
104
|
-
when CONSTANT_REGEXP # Constant
|
105
|
-
message = $1
|
106
|
-
begin
|
107
|
-
context = target.eval("self")
|
108
|
-
context = context.class unless context.respond_to? :constants
|
109
|
-
candidates = context.constants.collect(&:to_s)
|
110
|
-
rescue
|
111
|
-
candidates = []
|
112
|
-
end
|
113
|
-
candidates = candidates.grep(/^#{message}/).collect(&path)
|
114
|
-
when CONSTANT_OR_METHOD_REGEXP # Constant or class methods
|
115
|
-
receiver = $1
|
116
|
-
message = Regexp.quote($2)
|
117
|
-
begin
|
118
|
-
candidates = eval("#{receiver}.constants.collect(&:to_s)", bind)
|
119
|
-
candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind)
|
120
|
-
rescue Pry::RescuableException
|
121
|
-
candidates = []
|
122
|
-
end
|
123
|
-
candidates.grep(/^#{message}/).collect { |e| receiver + "::" + e }
|
124
|
-
when SYMBOL_METHOD_CALL_REGEXP # method call on a Symbol
|
125
|
-
receiver = $1
|
126
|
-
message = Regexp.quote($2)
|
127
|
-
candidates = Symbol.instance_methods.collect(&:to_s)
|
128
|
-
select_message(path, receiver, message, candidates)
|
129
|
-
when NUMERIC_REGEXP
|
130
|
-
# Numeric
|
131
|
-
receiver = $1
|
132
|
-
message = Regexp.quote($5)
|
133
|
-
begin
|
134
|
-
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
135
|
-
rescue Pry::RescuableException
|
136
|
-
candidates = []
|
137
|
-
end
|
138
|
-
select_message(path, receiver, message, candidates)
|
139
|
-
when HEX_REGEXP
|
140
|
-
# Numeric(0xFFFF)
|
141
|
-
receiver = $1
|
142
|
-
message = Regexp.quote($2)
|
54
|
+
# Return a new completion proc for use by Readline.
|
55
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
56
|
+
def call(str, options = {})
|
57
|
+
custom_completions = options[:custom_completions] || []
|
58
|
+
# if there are multiple contexts e.g. cd 1/2/3
|
59
|
+
# get new target for 1/2 and find candidates for 3
|
60
|
+
path, input = build_path(str)
|
61
|
+
|
62
|
+
if path.call.empty?
|
63
|
+
target = options[:target]
|
64
|
+
else
|
65
|
+
# Assume the user is tab-completing the 'cd' command
|
143
66
|
begin
|
144
|
-
|
145
|
-
|
146
|
-
|
67
|
+
target = Pry::ObjectPath.new(path.call, @pry.binding_stack).resolve.last
|
68
|
+
# but if that doesn't work, assume they're doing division with no spaces
|
69
|
+
rescue Pry::CommandError
|
70
|
+
target = options[:target]
|
147
71
|
end
|
148
|
-
|
149
|
-
when GLOBALVARIABLE_REGEXP # global
|
150
|
-
regmessage = Regexp.new(Regexp.quote($1))
|
151
|
-
candidates = global_variables.collect(&:to_s).grep(regmessage)
|
152
|
-
when VARIABLE_REGEXP # variable
|
153
|
-
receiver = $1
|
154
|
-
message = Regexp.quote($2)
|
155
|
-
|
156
|
-
gv = eval("global_variables", bind).collect(&:to_s)
|
157
|
-
lv = eval("local_variables", bind).collect(&:to_s)
|
158
|
-
cv = eval("self.class.constants", bind).collect(&:to_s)
|
72
|
+
end
|
159
73
|
|
160
|
-
|
161
|
-
|
162
|
-
|
74
|
+
begin
|
75
|
+
bind = target
|
76
|
+
# Complete stdlib symbols
|
77
|
+
case input
|
78
|
+
when REGEX_REGEXP # Regexp
|
79
|
+
receiver = Regexp.last_match(1)
|
80
|
+
message = Regexp.quote(Regexp.last_match(2))
|
81
|
+
candidates = Regexp.instance_methods.collect(&:to_s)
|
82
|
+
select_message(path, receiver, message, candidates)
|
83
|
+
when ARRAY_REGEXP # Array
|
84
|
+
receiver = Regexp.last_match(1)
|
85
|
+
message = Regexp.quote(Regexp.last_match(2))
|
86
|
+
candidates = Array.instance_methods.collect(&:to_s)
|
87
|
+
select_message(path, receiver, message, candidates)
|
88
|
+
when PROC_OR_HASH_REGEXP # Proc or Hash
|
89
|
+
receiver = Regexp.last_match(1)
|
90
|
+
message = Regexp.quote(Regexp.last_match(2))
|
91
|
+
candidates = Proc.instance_methods.collect(&:to_s)
|
92
|
+
candidates |= Hash.instance_methods.collect(&:to_s)
|
93
|
+
select_message(path, receiver, message, candidates)
|
94
|
+
when SYMBOL_REGEXP # Symbol
|
95
|
+
if Symbol.respond_to?(:all_symbols)
|
96
|
+
sym = Regexp.quote(Regexp.last_match(1))
|
97
|
+
candidates = Symbol.all_symbols.collect { |s| ":" + s.id2name }
|
98
|
+
candidates.grep(/^#{sym}/)
|
99
|
+
else
|
100
|
+
[]
|
101
|
+
end
|
102
|
+
when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods
|
103
|
+
receiver = Regexp.last_match(1)
|
104
|
+
candidates = Object.constants.collect(&:to_s)
|
105
|
+
candidates.grep(/^#{receiver}/).collect { |e| "::" + e }
|
106
|
+
when CONSTANT_REGEXP # Constant
|
107
|
+
message = Regexp.last_match(1)
|
108
|
+
begin
|
109
|
+
context = target.eval("self")
|
110
|
+
context = context.class unless context.respond_to? :constants
|
111
|
+
candidates = context.constants.collect(&:to_s)
|
112
|
+
rescue StandardError
|
113
|
+
candidates = []
|
114
|
+
end
|
115
|
+
candidates = candidates.grep(/^#{message}/).collect(&path)
|
116
|
+
when CONSTANT_OR_METHOD_REGEXP # Constant or class methods
|
117
|
+
receiver = Regexp.last_match(1)
|
118
|
+
message = Regexp.quote(Regexp.last_match(2))
|
163
119
|
begin
|
164
|
-
candidates = eval(
|
120
|
+
candidates = eval( # rubocop:disable Security/Eval
|
121
|
+
"#{receiver}.constants.collect(&:to_s)", bind, __FILE__, __LINE__
|
122
|
+
)
|
123
|
+
candidates |= eval( # rubocop:disable Security/Eval
|
124
|
+
"#{receiver}.methods.collect(&:to_s)", bind, __FILE__, __LINE__
|
125
|
+
)
|
165
126
|
rescue Pry::RescuableException
|
166
127
|
candidates = []
|
167
128
|
end
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
129
|
+
candidates.grep(/^#{message}/).collect { |e| receiver + "::" + e }
|
130
|
+
when SYMBOL_METHOD_CALL_REGEXP # method call on a Symbol
|
131
|
+
receiver = Regexp.last_match(1)
|
132
|
+
message = Regexp.quote(Regexp.last_match(2))
|
133
|
+
candidates = Symbol.instance_methods.collect(&:to_s)
|
134
|
+
select_message(path, receiver, message, candidates)
|
135
|
+
when NUMERIC_REGEXP
|
136
|
+
# Numeric
|
137
|
+
receiver = Regexp.last_match(1)
|
138
|
+
message = Regexp.quote(Regexp.last_match(5))
|
139
|
+
begin
|
140
|
+
# rubocop:disable Security/Eval
|
141
|
+
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
142
|
+
# rubocop:enable Security/Eval
|
143
|
+
rescue Pry::RescuableException
|
144
|
+
candidates = []
|
145
|
+
end
|
146
|
+
select_message(path, receiver, message, candidates)
|
147
|
+
when HEX_REGEXP
|
148
|
+
# Numeric(0xFFFF)
|
149
|
+
receiver = Regexp.last_match(1)
|
150
|
+
message = Regexp.quote(Regexp.last_match(2))
|
151
|
+
begin
|
152
|
+
# rubocop:disable Security/Eval
|
153
|
+
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
154
|
+
# rubocop:enable Security/Eval
|
155
|
+
rescue Pry::RescuableException
|
156
|
+
candidates = []
|
157
|
+
end
|
158
|
+
select_message(path, receiver, message, candidates)
|
159
|
+
when GLOBALVARIABLE_REGEXP # global
|
160
|
+
regmessage = Regexp.new(Regexp.quote(Regexp.last_match(1)))
|
161
|
+
candidates = global_variables.collect(&:to_s).grep(regmessage)
|
162
|
+
when VARIABLE_REGEXP # variable
|
163
|
+
receiver = Regexp.last_match(1)
|
164
|
+
message = Regexp.quote(Regexp.last_match(2))
|
175
165
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
166
|
+
gv = eval("global_variables", bind, __FILE__, __LINE__).collect(&:to_s)
|
167
|
+
lv = eval("local_variables", bind, __FILE__, __LINE__).collect(&:to_s)
|
168
|
+
cv = eval("self.class.constants", bind, __FILE__, __LINE__).collect(&:to_s)
|
169
|
+
|
170
|
+
if (gv | lv | cv).include?(receiver) || /^[A-Z]/ =~ receiver && /\./ !~ receiver
|
171
|
+
# foo.func and foo is local var. OR
|
172
|
+
# Foo::Bar.func
|
173
|
+
begin
|
174
|
+
candidates = eval( # rubocop:disable Security/Eval
|
175
|
+
"#{receiver}.methods", bind, __FILE__, __LINE__
|
176
|
+
).collect(&:to_s)
|
177
|
+
rescue Pry::RescuableException
|
178
|
+
candidates = []
|
180
179
|
end
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
180
|
+
else
|
181
|
+
# func1.func2
|
182
|
+
require 'set'
|
183
|
+
candidates = Set.new
|
184
|
+
to_ignore = ignored_modules
|
185
|
+
ObjectSpace.each_object(Module) do |m|
|
186
|
+
next if begin
|
187
|
+
to_ignore.include?(m)
|
188
|
+
rescue StandardError
|
189
|
+
true
|
190
|
+
end
|
191
|
+
|
192
|
+
# jruby doesn't always provide #instance_methods() on each
|
193
|
+
# object.
|
194
|
+
if m.respond_to?(:instance_methods)
|
195
|
+
candidates.merge m.instance_methods(false).collect(&:to_s)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
select_message(path, receiver, message, candidates.sort)
|
200
|
+
when /^\.([^.]*)$/
|
201
|
+
# Unknown(maybe String)
|
202
|
+
receiver = ""
|
203
|
+
message = Regexp.quote(Regexp.last_match(1))
|
204
|
+
candidates = String.instance_methods(true).collect(&:to_s)
|
205
|
+
select_message(path, receiver, message, candidates)
|
206
|
+
else
|
207
|
+
candidates = eval(
|
208
|
+
"methods | private_methods | local_variables | " \
|
209
|
+
"self.class.constants | instance_variables",
|
210
|
+
bind, __FILE__, __LINE__ - 2
|
211
|
+
).collect(&:to_s)
|
196
212
|
|
197
|
-
|
198
|
-
|
213
|
+
if eval("respond_to?(:class_variables)", bind, __FILE__, __LINE__)
|
214
|
+
candidates += eval(
|
215
|
+
"class_variables", bind, __FILE__, __LINE__
|
216
|
+
).collect(&:to_s)
|
217
|
+
end
|
218
|
+
candidates =
|
219
|
+
(candidates | RESERVED_WORDS | custom_completions)
|
220
|
+
.grep(/^#{Regexp.quote(input)}/)
|
221
|
+
candidates.collect(&path)
|
199
222
|
end
|
200
|
-
|
201
|
-
|
223
|
+
rescue Pry::RescuableException
|
224
|
+
[]
|
202
225
|
end
|
203
|
-
rescue Pry::RescuableException
|
204
|
-
[]
|
205
226
|
end
|
206
|
-
|
227
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
207
228
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
when /^[a-zA-Z_]/
|
212
|
-
path.call(receiver + "." << e)
|
213
|
-
when /^[0-9]/
|
214
|
-
when *Operators
|
215
|
-
#receiver + " " << e
|
216
|
-
end
|
217
|
-
}.compact
|
218
|
-
end
|
229
|
+
def select_message(path, receiver, message, candidates)
|
230
|
+
candidates.grep(/^#{message}/).collect do |e|
|
231
|
+
next unless e =~ /^[a-zA-Z_]/
|
219
232
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
#
|
225
|
-
|
233
|
+
path.call(receiver + "." + e)
|
234
|
+
end.compact
|
235
|
+
end
|
236
|
+
|
237
|
+
# build_path seperates the input into two parts: path and input.
|
238
|
+
# input is the partial string that should be completed
|
239
|
+
# path is a proc that takes an input and builds a full path.
|
240
|
+
def build_path(input)
|
241
|
+
# check to see if the input is a regex
|
242
|
+
return proc { |i| i.to_s }, input if input[%r{/\.}]
|
226
243
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
244
|
+
trailing_slash = input.end_with?('/')
|
245
|
+
contexts = input.chomp('/').split(%r{/})
|
246
|
+
input = contexts[-1]
|
247
|
+
path = proc do |i|
|
248
|
+
p = contexts[0..-2].push(i).join('/')
|
249
|
+
p += '/' if trailing_slash && !i.nil?
|
250
|
+
p
|
251
|
+
end
|
252
|
+
[path, input]
|
234
253
|
end
|
235
|
-
return path, input
|
236
|
-
end
|
237
254
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
255
|
+
def ignored_modules
|
256
|
+
# We could cache the result, but IRB is not loaded by default.
|
257
|
+
# And this is very fast anyway.
|
258
|
+
# By using this approach, we avoid Module#name calls, which are
|
259
|
+
# relatively slow when there are a lot of anonymous modules defined.
|
260
|
+
s = Set.new
|
244
261
|
|
245
|
-
|
246
|
-
|
262
|
+
scanner = lambda do |m|
|
263
|
+
next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses.
|
247
264
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
265
|
+
s << m
|
266
|
+
m.constants(false).each do |c|
|
267
|
+
value = m.const_get(c)
|
268
|
+
scanner.call(value) if value.is_a?(Module)
|
269
|
+
end
|
252
270
|
end
|
253
|
-
end
|
254
271
|
|
255
|
-
|
256
|
-
|
257
|
-
|
272
|
+
# FIXME: Add Pry here as well?
|
273
|
+
[:IRB, :SLex, :RubyLex, :RubyToken].each do |module_name|
|
274
|
+
next unless Object.const_defined?(module_name)
|
258
275
|
|
259
|
-
|
260
|
-
|
276
|
+
scanner.call(Object.const_get(module_name))
|
277
|
+
end
|
261
278
|
|
262
|
-
|
263
|
-
|
279
|
+
s.delete(IRB::Context) if defined?(IRB::Context)
|
280
|
+
s
|
281
|
+
end
|
264
282
|
end
|
265
283
|
end
|