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.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +162 -1
  3. data/LICENSE +1 -1
  4. data/README.md +331 -269
  5. data/bin/pry +5 -0
  6. data/lib/pry.rb +132 -119
  7. data/lib/pry/basic_object.rb +8 -4
  8. data/lib/pry/block_command.rb +22 -0
  9. data/lib/pry/class_command.rb +194 -0
  10. data/lib/pry/cli.rb +43 -51
  11. data/lib/pry/code.rb +40 -28
  12. data/lib/pry/code/code_file.rb +28 -24
  13. data/lib/pry/code/code_range.rb +4 -2
  14. data/lib/pry/code/loc.rb +15 -8
  15. data/lib/pry/code_object.rb +40 -38
  16. data/lib/pry/color_printer.rb +47 -46
  17. data/lib/pry/command.rb +166 -369
  18. data/lib/pry/command_set.rb +76 -73
  19. data/lib/pry/command_state.rb +31 -0
  20. data/lib/pry/commands/amend_line.rb +86 -81
  21. data/lib/pry/commands/bang.rb +18 -14
  22. data/lib/pry/commands/bang_pry.rb +15 -11
  23. data/lib/pry/commands/cat.rb +61 -54
  24. data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
  25. data/lib/pry/commands/cat/exception_formatter.rb +71 -60
  26. data/lib/pry/commands/cat/file_formatter.rb +55 -49
  27. data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
  28. data/lib/pry/commands/cd.rb +40 -35
  29. data/lib/pry/commands/change_inspector.rb +29 -22
  30. data/lib/pry/commands/change_prompt.rb +44 -39
  31. data/lib/pry/commands/clear_screen.rb +16 -10
  32. data/lib/pry/commands/code_collector.rb +148 -133
  33. data/lib/pry/commands/disable_pry.rb +23 -19
  34. data/lib/pry/commands/easter_eggs.rb +19 -30
  35. data/lib/pry/commands/edit.rb +184 -161
  36. data/lib/pry/commands/edit/exception_patcher.rb +21 -17
  37. data/lib/pry/commands/edit/file_and_line_locator.rb +34 -23
  38. data/lib/pry/commands/exit.rb +39 -35
  39. data/lib/pry/commands/exit_all.rb +24 -20
  40. data/lib/pry/commands/exit_program.rb +20 -16
  41. data/lib/pry/commands/find_method.rb +168 -160
  42. data/lib/pry/commands/fix_indent.rb +16 -12
  43. data/lib/pry/commands/help.rb +140 -133
  44. data/lib/pry/commands/hist.rb +151 -150
  45. data/lib/pry/commands/import_set.rb +20 -16
  46. data/lib/pry/commands/jump_to.rb +25 -21
  47. data/lib/pry/commands/list_inspectors.rb +35 -28
  48. data/lib/pry/commands/ls.rb +124 -102
  49. data/lib/pry/commands/ls/constants.rb +59 -42
  50. data/lib/pry/commands/ls/formatter.rb +50 -46
  51. data/lib/pry/commands/ls/globals.rb +38 -34
  52. data/lib/pry/commands/ls/grep.rb +17 -13
  53. data/lib/pry/commands/ls/instance_vars.rb +29 -27
  54. data/lib/pry/commands/ls/interrogatable.rb +18 -12
  55. data/lib/pry/commands/ls/jruby_hacks.rb +47 -41
  56. data/lib/pry/commands/ls/local_names.rb +26 -22
  57. data/lib/pry/commands/ls/local_vars.rb +38 -28
  58. data/lib/pry/commands/ls/ls_entity.rb +47 -51
  59. data/lib/pry/commands/ls/methods.rb +44 -43
  60. data/lib/pry/commands/ls/methods_helper.rb +46 -42
  61. data/lib/pry/commands/ls/self_methods.rb +23 -22
  62. data/lib/pry/commands/nesting.rb +21 -17
  63. data/lib/pry/commands/play.rb +93 -82
  64. data/lib/pry/commands/pry_backtrace.rb +22 -17
  65. data/lib/pry/commands/pry_version.rb +15 -11
  66. data/lib/pry/commands/raise_up.rb +27 -22
  67. data/lib/pry/commands/reload_code.rb +60 -48
  68. data/lib/pry/commands/reset.rb +16 -12
  69. data/lib/pry/commands/ri.rb +55 -45
  70. data/lib/pry/commands/save_file.rb +45 -43
  71. data/lib/pry/commands/shell_command.rb +51 -51
  72. data/lib/pry/commands/shell_mode.rb +21 -17
  73. data/lib/pry/commands/show_doc.rb +80 -68
  74. data/lib/pry/commands/show_info.rb +189 -171
  75. data/lib/pry/commands/show_input.rb +16 -11
  76. data/lib/pry/commands/show_source.rb +110 -45
  77. data/lib/pry/commands/stat.rb +35 -31
  78. data/lib/pry/commands/switch_to.rb +21 -15
  79. data/lib/pry/commands/toggle_color.rb +20 -16
  80. data/lib/pry/commands/watch_expression.rb +89 -86
  81. data/lib/pry/commands/watch_expression/expression.rb +32 -27
  82. data/lib/pry/commands/whereami.rb +156 -148
  83. data/lib/pry/commands/wtf.rb +75 -50
  84. data/lib/pry/config.rb +307 -25
  85. data/lib/pry/config/attributable.rb +22 -0
  86. data/lib/pry/config/lazy_value.rb +29 -0
  87. data/lib/pry/config/memoized_value.rb +34 -0
  88. data/lib/pry/config/value.rb +24 -0
  89. data/lib/pry/control_d_handler.rb +28 -0
  90. data/lib/pry/core_extensions.rb +9 -7
  91. data/lib/pry/editor.rb +48 -21
  92. data/lib/pry/env.rb +18 -0
  93. data/lib/pry/exception_handler.rb +43 -0
  94. data/lib/pry/exceptions.rb +13 -16
  95. data/lib/pry/forwardable.rb +5 -1
  96. data/lib/pry/helpers.rb +2 -0
  97. data/lib/pry/helpers/base_helpers.rb +68 -197
  98. data/lib/pry/helpers/command_helpers.rb +50 -61
  99. data/lib/pry/helpers/documentation_helpers.rb +20 -13
  100. data/lib/pry/helpers/options_helpers.rb +14 -7
  101. data/lib/pry/helpers/platform.rb +7 -5
  102. data/lib/pry/helpers/table.rb +33 -26
  103. data/lib/pry/helpers/text.rb +17 -14
  104. data/lib/pry/history.rb +48 -56
  105. data/lib/pry/hooks.rb +21 -12
  106. data/lib/pry/indent.rb +54 -50
  107. data/lib/pry/input_completer.rb +248 -230
  108. data/lib/pry/input_lock.rb +8 -9
  109. data/lib/pry/inspector.rb +36 -24
  110. data/lib/pry/last_exception.rb +45 -45
  111. data/lib/pry/method.rb +141 -94
  112. data/lib/pry/method/disowned.rb +16 -4
  113. data/lib/pry/method/patcher.rb +12 -3
  114. data/lib/pry/method/weird_method_locator.rb +68 -44
  115. data/lib/pry/object_path.rb +33 -25
  116. data/lib/pry/output.rb +121 -35
  117. data/lib/pry/pager.rb +186 -180
  118. data/lib/pry/prompt.rb +123 -54
  119. data/lib/pry/pry_class.rb +61 -103
  120. data/lib/pry/pry_instance.rb +217 -215
  121. data/lib/pry/repl.rb +18 -22
  122. data/lib/pry/repl_file_loader.rb +27 -21
  123. data/lib/pry/ring.rb +11 -6
  124. data/lib/pry/slop.rb +574 -563
  125. data/lib/pry/slop/commands.rb +164 -169
  126. data/lib/pry/slop/option.rb +172 -168
  127. data/lib/pry/syntax_highlighter.rb +26 -0
  128. data/lib/pry/system_command_handler.rb +17 -0
  129. data/lib/pry/testable.rb +59 -61
  130. data/lib/pry/testable/evalable.rb +21 -12
  131. data/lib/pry/testable/mockable.rb +18 -10
  132. data/lib/pry/testable/pry_tester.rb +71 -56
  133. data/lib/pry/testable/utility.rb +29 -21
  134. data/lib/pry/testable/variables.rb +49 -43
  135. data/lib/pry/version.rb +3 -1
  136. data/lib/pry/warning.rb +27 -0
  137. data/lib/pry/wrapped_module.rb +51 -42
  138. data/lib/pry/wrapped_module/candidate.rb +21 -14
  139. metadata +35 -35
  140. data/lib/pry/commands.rb +0 -6
  141. data/lib/pry/commands/disabled_commands.rb +0 -2
  142. data/lib/pry/commands/gem_cd.rb +0 -26
  143. data/lib/pry/commands/gem_install.rb +0 -32
  144. data/lib/pry/commands/gem_list.rb +0 -33
  145. data/lib/pry/commands/gem_open.rb +0 -29
  146. data/lib/pry/commands/gem_readme.rb +0 -25
  147. data/lib/pry/commands/gem_search.rb +0 -40
  148. data/lib/pry/commands/gem_stats.rb +0 -83
  149. data/lib/pry/commands/gist.rb +0 -102
  150. data/lib/pry/commands/install_command.rb +0 -54
  151. data/lib/pry/config/behavior.rb +0 -255
  152. data/lib/pry/config/convenience.rb +0 -28
  153. data/lib/pry/config/default.rb +0 -159
  154. data/lib/pry/config/memoization.rb +0 -48
  155. data/lib/pry/platform.rb +0 -91
  156. data/lib/pry/plugins.rb +0 -122
  157. data/lib/pry/rubygem.rb +0 -84
  158. 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 |key, array, other_array|
38
- temp_hash, output = {}, []
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
- self.dup.tap do |v|
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, k| h.nil? } if hook_name.nil?
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 |hook_name, callable|
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, callable|
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
- def hooks
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
- require 'coderay'
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' => 'end',
32
- 'class' => 'end',
31
+ 'def' => 'end',
32
+ 'class' => 'end',
33
33
  'module' => 'end',
34
- 'do' => 'end',
35
- 'if' => 'end',
34
+ 'do' => 'end',
35
+ 'if' => 'end',
36
36
  'unless' => 'end',
37
- 'while' => 'end',
38
- 'until' => 'end',
39
- 'for' => 'end',
40
- 'case' => 'end',
41
- 'begin' => 'end',
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(if while until unless rescue)
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(for while until)
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(when else elsif ensure rescue)
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 { |token, type| !(String === token && token.include?("\n")) }
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
- return output
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, last_kind = [nil, nil]
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, add_after = [0, 0]
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 = (SINGLELINE_TOKENS.include?(token)) && end_of_statement?(last_token, last_kind)
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
- last_token, last_kind = token, kind unless kind == :space
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.keys.include?(token) && !is_optional_do && !is_singleline_if
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
- return [remove_before, add_after]
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 end of a statement,
254
- # then we want to treat that "if" as a singleline, not multiline statement.
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 =~ /^[)\]}\/]$/ || STATEMENT_END_TOKENS.include?(last_kind))
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 re-indent
262
- # within string literals because to do so would actually change the value of the
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 = CodeRay.scan(string, :ruby)
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 nested. For
283
- # normal strings (which can't be nested) we assume that CodeRay correctly pairs
284
- # open-and-close delimiters so we don't bother checking what they are.
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
- if @string_start
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 && (token == "class" || token == "module")
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 && (token == "class" || token == "module")
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 = Terminal.width!
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?
@@ -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::InputCompleter
4
- NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
5
- ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/
6
- SYMBOL_REGEXP = /^(:[^:.]*)$/
7
- SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/
8
- REGEX_REGEXP = /^(\/[^\/]*\/)\.([^.]*)$/
9
- PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/
10
- TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/
11
- CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/
12
- CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/
13
- HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/
14
- GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/
15
- VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/
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
- Operators = [
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
- WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{("
40
+ WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{(".freeze
43
41
 
44
- def initialize(input, pry = nil)
45
- @pry = pry
46
- @input = input
47
- @input.basic_word_break_characters = WORD_ESCAPE_STR if @input.respond_to?(:basic_word_break_characters=)
48
- @input.completion_append_character = nil if @input.respond_to?(:completion_append_character=)
49
- end
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
- if path.call.empty?
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
- begin
73
- bind = target
74
- # Complete stdlib symbols
75
- case input
76
- when REGEX_REGEXP # Regexp
77
- receiver = $1
78
- message = Regexp.quote($2)
79
- candidates = Regexp.instance_methods.collect(&:to_s)
80
- select_message(path, receiver, message, candidates)
81
- when ARRAY_REGEXP # Array
82
- receiver = $1
83
- message = Regexp.quote($2)
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
- candidates = eval(receiver, bind).methods.collect(&:to_s)
145
- rescue Pry::RescuableException
146
- candidates = []
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
- select_message(path, receiver, message, candidates)
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
- if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
161
- # foo.func and foo is local var. OR
162
- # Foo::Bar.func
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("#{receiver}.methods", bind).collect(&:to_s)
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
- else
169
- # func1.func2
170
- require 'set'
171
- candidates = Set.new
172
- to_ignore = ignored_modules
173
- ObjectSpace.each_object(Module) { |m|
174
- next if (to_ignore.include?(m) rescue true)
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
- # jruby doesn't always provide #instance_methods() on each
177
- # object.
178
- if m.respond_to?(:instance_methods)
179
- candidates.merge m.instance_methods(false).collect(&:to_s)
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
- end
183
- select_message(path, receiver, message, candidates.sort)
184
- when /^\.([^.]*)$/
185
- # Unknown(maybe String)
186
- receiver = ""
187
- message = Regexp.quote($1)
188
- candidates = String.instance_methods(true).collect(&:to_s)
189
- select_message(path, receiver, message, candidates)
190
- else
191
- candidates = eval(
192
- "methods | private_methods | local_variables | " \
193
- "self.class.constants | instance_variables",
194
- bind
195
- ).collect(&:to_s)
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
- if eval("respond_to?(:class_variables)", bind)
198
- candidates += eval("class_variables", bind).collect(&:to_s)
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
- candidates = (candidates | ReservedWords | custom_completions).grep(/^#{Regexp.quote(input)}/)
201
- candidates.collect(&path)
223
+ rescue Pry::RescuableException
224
+ []
202
225
  end
203
- rescue Pry::RescuableException
204
- []
205
226
  end
206
- end
227
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
207
228
 
208
- def select_message(path, receiver, message, candidates)
209
- candidates.grep(/^#{message}/).collect { |e|
210
- case e
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
- # build_path seperates the input into two parts: path and input.
221
- # input is the partial string that should be completed
222
- # path is a proc that takes an input and builds a full path.
223
- def build_path(input)
224
- # check to see if the input is a regex
225
- return proc { |i| i.to_s }, input if input[/\/\./]
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
- trailing_slash = input.end_with?('/')
228
- contexts = input.chomp('/').split(/\//)
229
- input = contexts[-1]
230
- path = proc do |i|
231
- p = contexts[0..-2].push(i).join('/')
232
- p += '/' if trailing_slash && !i.nil?
233
- p
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
- def ignored_modules
239
- # We could cache the result, but IRB is not loaded by default.
240
- # And this is very fast anyway.
241
- # By using this approach, we avoid Module#name calls, which are
242
- # relatively slow when there are a lot of anonymous modules defined.
243
- s = Set.new
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
- scanner = lambda do |m|
246
- next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses.
262
+ scanner = lambda do |m|
263
+ next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses.
247
264
 
248
- s << m
249
- m.constants(false).each do |c|
250
- value = m.const_get(c)
251
- scanner.call(value) if value.is_a?(Module)
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
- # FIXME: Add Pry here as well?
256
- [:IRB, :SLex, :RubyLex, :RubyToken].each do |module_name|
257
- next unless Object.const_defined?(module_name)
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
- scanner.call(Object.const_get(module_name))
260
- end
276
+ scanner.call(Object.const_get(module_name))
277
+ end
261
278
 
262
- s.delete(IRB::Context) if defined?(IRB::Context)
263
- s
279
+ s.delete(IRB::Context) if defined?(IRB::Context)
280
+ s
281
+ end
264
282
  end
265
283
  end