pry 0.12.2 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +110 -1
  3. data/LICENSE +1 -1
  4. data/README.md +331 -269
  5. data/bin/pry +5 -0
  6. data/lib/pry.rb +133 -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 +40 -31
  11. data/lib/pry/code.rb +39 -27
  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 +24 -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 +81 -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 +109 -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 +311 -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 +41 -42
  118. data/lib/pry/plugins.rb +25 -8
  119. data/lib/pry/prompt.rb +123 -54
  120. data/lib/pry/pry_class.rb +61 -98
  121. data/lib/pry/pry_instance.rb +217 -215
  122. data/lib/pry/repl.rb +18 -22
  123. data/lib/pry/repl_file_loader.rb +27 -21
  124. data/lib/pry/ring.rb +11 -6
  125. data/lib/pry/slop.rb +574 -563
  126. data/lib/pry/slop/commands.rb +164 -169
  127. data/lib/pry/slop/option.rb +172 -168
  128. data/lib/pry/syntax_highlighter.rb +26 -0
  129. data/lib/pry/system_command_handler.rb +17 -0
  130. data/lib/pry/testable.rb +59 -61
  131. data/lib/pry/testable/evalable.rb +21 -12
  132. data/lib/pry/testable/mockable.rb +18 -10
  133. data/lib/pry/testable/pry_tester.rb +71 -56
  134. data/lib/pry/testable/utility.rb +29 -21
  135. data/lib/pry/testable/variables.rb +49 -43
  136. data/lib/pry/version.rb +3 -1
  137. data/lib/pry/warning.rb +27 -0
  138. data/lib/pry/wrapped_module.rb +51 -42
  139. data/lib/pry/wrapped_module/candidate.rb +21 -14
  140. metadata +31 -30
  141. data/lib/pry/commands.rb +0 -6
  142. data/lib/pry/commands/disabled_commands.rb +0 -2
  143. data/lib/pry/commands/gem_cd.rb +0 -26
  144. data/lib/pry/commands/gem_install.rb +0 -32
  145. data/lib/pry/commands/gem_list.rb +0 -33
  146. data/lib/pry/commands/gem_open.rb +0 -29
  147. data/lib/pry/commands/gem_readme.rb +0 -25
  148. data/lib/pry/commands/gem_search.rb +0 -40
  149. data/lib/pry/commands/gem_stats.rb +0 -83
  150. data/lib/pry/commands/gist.rb +0 -102
  151. data/lib/pry/commands/install_command.rb +0 -54
  152. data/lib/pry/config/behavior.rb +0 -255
  153. data/lib/pry/config/convenience.rb +0 -28
  154. data/lib/pry/config/default.rb +0 -159
  155. data/lib/pry/config/memoization.rb +0 -48
  156. data/lib/pry/platform.rb +0 -91
  157. data/lib/pry/rubygem.rb +0 -84
  158. data/lib/pry/terminal.rb +0 -91
@@ -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
@@ -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