pry 0.9.12.6 → 0.10.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +702 -0
  3. data/LICENSE +2 -2
  4. data/{README.markdown → README.md} +37 -31
  5. data/lib/pry.rb +38 -151
  6. data/lib/pry/cli.rb +35 -17
  7. data/lib/pry/code.rb +24 -63
  8. data/lib/pry/code/code_file.rb +103 -0
  9. data/lib/pry/code/code_range.rb +2 -1
  10. data/lib/pry/code/loc.rb +2 -2
  11. data/lib/pry/code_object.rb +40 -21
  12. data/lib/pry/color_printer.rb +55 -0
  13. data/lib/pry/command.rb +12 -9
  14. data/lib/pry/command_set.rb +81 -38
  15. data/lib/pry/commands.rb +1 -1
  16. data/lib/pry/commands/amend_line.rb +2 -2
  17. data/lib/pry/commands/bang.rb +1 -1
  18. data/lib/pry/commands/cat.rb +11 -2
  19. data/lib/pry/commands/cat/exception_formatter.rb +5 -6
  20. data/lib/pry/commands/cat/file_formatter.rb +15 -32
  21. data/lib/pry/commands/cd.rb +14 -3
  22. data/lib/pry/commands/change_inspector.rb +27 -0
  23. data/lib/pry/commands/change_prompt.rb +26 -0
  24. data/lib/pry/commands/code_collector.rb +4 -4
  25. data/lib/pry/commands/easter_eggs.rb +3 -3
  26. data/lib/pry/commands/edit.rb +10 -22
  27. data/lib/pry/commands/edit/exception_patcher.rb +2 -2
  28. data/lib/pry/commands/edit/file_and_line_locator.rb +0 -2
  29. data/lib/pry/commands/exit_program.rb +0 -1
  30. data/lib/pry/commands/find_method.rb +16 -22
  31. data/lib/pry/commands/gem_install.rb +5 -2
  32. data/lib/pry/commands/gem_open.rb +1 -1
  33. data/lib/pry/commands/gist.rb +10 -11
  34. data/lib/pry/commands/help.rb +14 -14
  35. data/lib/pry/commands/hist.rb +27 -8
  36. data/lib/pry/commands/install_command.rb +14 -12
  37. data/lib/pry/commands/list_inspectors.rb +35 -0
  38. data/lib/pry/commands/list_prompts.rb +35 -0
  39. data/lib/pry/commands/ls.rb +72 -296
  40. data/lib/pry/commands/ls/constants.rb +47 -0
  41. data/lib/pry/commands/ls/formatter.rb +49 -0
  42. data/lib/pry/commands/ls/globals.rb +48 -0
  43. data/lib/pry/commands/ls/grep.rb +21 -0
  44. data/lib/pry/commands/ls/instance_vars.rb +39 -0
  45. data/lib/pry/commands/ls/interrogatable.rb +18 -0
  46. data/lib/pry/commands/ls/jruby_hacks.rb +49 -0
  47. data/lib/pry/commands/ls/local_names.rb +35 -0
  48. data/lib/pry/commands/ls/local_vars.rb +39 -0
  49. data/lib/pry/commands/ls/ls_entity.rb +70 -0
  50. data/lib/pry/commands/ls/methods.rb +57 -0
  51. data/lib/pry/commands/ls/methods_helper.rb +46 -0
  52. data/lib/pry/commands/ls/self_methods.rb +32 -0
  53. data/lib/pry/commands/play.rb +44 -10
  54. data/lib/pry/commands/pry_backtrace.rb +1 -2
  55. data/lib/pry/commands/raise_up.rb +2 -2
  56. data/lib/pry/commands/reload_code.rb +16 -19
  57. data/lib/pry/commands/ri.rb +7 -3
  58. data/lib/pry/commands/shell_command.rb +18 -13
  59. data/lib/pry/commands/shell_mode.rb +2 -4
  60. data/lib/pry/commands/show_doc.rb +5 -0
  61. data/lib/pry/commands/show_info.rb +8 -13
  62. data/lib/pry/commands/show_source.rb +15 -3
  63. data/lib/pry/commands/simple_prompt.rb +1 -1
  64. data/lib/pry/commands/toggle_color.rb +8 -4
  65. data/lib/pry/commands/watch_expression.rb +105 -0
  66. data/lib/pry/commands/watch_expression/expression.rb +38 -0
  67. data/lib/pry/commands/whereami.rb +18 -10
  68. data/lib/pry/commands/wtf.rb +3 -3
  69. data/lib/pry/config.rb +20 -254
  70. data/lib/pry/config/behavior.rb +139 -0
  71. data/lib/pry/config/convenience.rb +26 -0
  72. data/lib/pry/config/default.rb +165 -0
  73. data/lib/pry/core_extensions.rb +31 -21
  74. data/lib/pry/editor.rb +107 -103
  75. data/lib/pry/exceptions.rb +77 -0
  76. data/lib/pry/helpers/base_helpers.rb +22 -109
  77. data/lib/pry/helpers/command_helpers.rb +10 -8
  78. data/lib/pry/helpers/documentation_helpers.rb +1 -2
  79. data/lib/pry/helpers/text.rb +4 -5
  80. data/lib/pry/history.rb +46 -45
  81. data/lib/pry/history_array.rb +6 -1
  82. data/lib/pry/hooks.rb +9 -29
  83. data/lib/pry/indent.rb +6 -6
  84. data/lib/pry/input_completer.rb +242 -0
  85. data/lib/pry/input_lock.rb +132 -0
  86. data/lib/pry/inspector.rb +27 -0
  87. data/lib/pry/last_exception.rb +61 -0
  88. data/lib/pry/method.rb +82 -87
  89. data/lib/pry/{commands/edit/method_patcher.rb → method/patcher.rb} +41 -38
  90. data/lib/pry/module_candidate.rb +4 -14
  91. data/lib/pry/object_path.rb +82 -0
  92. data/lib/pry/output.rb +50 -0
  93. data/lib/pry/pager.rb +191 -47
  94. data/lib/pry/plugins.rb +1 -1
  95. data/lib/pry/prompt.rb +26 -0
  96. data/lib/pry/pry_class.rb +149 -230
  97. data/lib/pry/pry_instance.rb +302 -413
  98. data/lib/pry/rbx_path.rb +1 -1
  99. data/lib/pry/repl.rb +202 -0
  100. data/lib/pry/repl_file_loader.rb +20 -26
  101. data/lib/pry/rubygem.rb +13 -5
  102. data/lib/pry/terminal.rb +2 -1
  103. data/lib/pry/test/helper.rb +26 -41
  104. data/lib/pry/version.rb +1 -1
  105. data/lib/pry/wrapped_module.rb +45 -59
  106. metadata +61 -224
  107. data/.document +0 -2
  108. data/.gitignore +0 -16
  109. data/.travis.yml +0 -25
  110. data/.yardopts +0 -1
  111. data/CHANGELOG +0 -534
  112. data/CONTRIBUTORS +0 -55
  113. data/Gemfile +0 -12
  114. data/Rakefile +0 -140
  115. data/TODO +0 -117
  116. data/lib/pry/completion.rb +0 -321
  117. data/lib/pry/custom_completions.rb +0 -6
  118. data/lib/pry/rbx_method.rb +0 -13
  119. data/man/pry.1 +0 -195
  120. data/man/pry.1.html +0 -204
  121. data/man/pry.1.ronn +0 -141
  122. data/pry.gemspec +0 -29
  123. data/spec/Procfile +0 -3
  124. data/spec/cli_spec.rb +0 -78
  125. data/spec/code_object_spec.rb +0 -277
  126. data/spec/code_spec.rb +0 -219
  127. data/spec/command_helpers_spec.rb +0 -29
  128. data/spec/command_integration_spec.rb +0 -644
  129. data/spec/command_set_spec.rb +0 -627
  130. data/spec/command_spec.rb +0 -821
  131. data/spec/commands/amend_line_spec.rb +0 -247
  132. data/spec/commands/bang_spec.rb +0 -19
  133. data/spec/commands/cat_spec.rb +0 -164
  134. data/spec/commands/cd_spec.rb +0 -250
  135. data/spec/commands/disable_pry_spec.rb +0 -25
  136. data/spec/commands/edit_spec.rb +0 -727
  137. data/spec/commands/exit_all_spec.rb +0 -34
  138. data/spec/commands/exit_program_spec.rb +0 -19
  139. data/spec/commands/exit_spec.rb +0 -34
  140. data/spec/commands/find_method_spec.rb +0 -70
  141. data/spec/commands/gem_list_spec.rb +0 -26
  142. data/spec/commands/gist_spec.rb +0 -79
  143. data/spec/commands/help_spec.rb +0 -56
  144. data/spec/commands/hist_spec.rb +0 -181
  145. data/spec/commands/jump_to_spec.rb +0 -15
  146. data/spec/commands/ls_spec.rb +0 -181
  147. data/spec/commands/play_spec.rb +0 -140
  148. data/spec/commands/raise_up_spec.rb +0 -56
  149. data/spec/commands/save_file_spec.rb +0 -177
  150. data/spec/commands/show_doc_spec.rb +0 -510
  151. data/spec/commands/show_input_spec.rb +0 -17
  152. data/spec/commands/show_source_spec.rb +0 -782
  153. data/spec/commands/whereami_spec.rb +0 -203
  154. data/spec/completion_spec.rb +0 -241
  155. data/spec/control_d_handler_spec.rb +0 -58
  156. data/spec/documentation_helper_spec.rb +0 -73
  157. data/spec/editor_spec.rb +0 -79
  158. data/spec/exception_whitelist_spec.rb +0 -21
  159. data/spec/fixtures/candidate_helper1.rb +0 -11
  160. data/spec/fixtures/candidate_helper2.rb +0 -8
  161. data/spec/fixtures/example.erb +0 -5
  162. data/spec/fixtures/example_nesting.rb +0 -33
  163. data/spec/fixtures/show_source_doc_examples.rb +0 -15
  164. data/spec/fixtures/testrc +0 -2
  165. data/spec/fixtures/testrcbad +0 -2
  166. data/spec/fixtures/whereami_helper.rb +0 -6
  167. data/spec/helper.rb +0 -34
  168. data/spec/helpers/bacon.rb +0 -86
  169. data/spec/helpers/mock_pry.rb +0 -43
  170. data/spec/helpers/table_spec.rb +0 -105
  171. data/spec/history_array_spec.rb +0 -67
  172. data/spec/hooks_spec.rb +0 -522
  173. data/spec/indent_spec.rb +0 -301
  174. data/spec/input_stack_spec.rb +0 -90
  175. data/spec/method_spec.rb +0 -482
  176. data/spec/prompt_spec.rb +0 -60
  177. data/spec/pry_defaults_spec.rb +0 -419
  178. data/spec/pry_history_spec.rb +0 -99
  179. data/spec/pry_output_spec.rb +0 -95
  180. data/spec/pry_spec.rb +0 -515
  181. data/spec/run_command_spec.rb +0 -25
  182. data/spec/sticky_locals_spec.rb +0 -157
  183. data/spec/syntax_checking_spec.rb +0 -81
  184. data/spec/wrapped_module_spec.rb +0 -261
  185. data/wiki/Customizing-pry.md +0 -397
  186. data/wiki/Home.md +0 -4
@@ -1,6 +1,6 @@
1
1
  class Pry
2
2
  # A history array is an array to which you can only add elements. Older
3
- # entries are removed progressively, so that the aray never contains more than
3
+ # entries are removed progressively, so that the array never contains more than
4
4
  # N elements.
5
5
  #
6
6
  # History arrays are used by Pry to store the output of the last commands.
@@ -89,6 +89,11 @@ class Pry
89
89
  ((@count - size)...@count).map { |n| @hash[n] }
90
90
  end
91
91
 
92
+ # Returns [Hash] copy of the internal @hash history
93
+ def to_h
94
+ @hash.dup
95
+ end
96
+
92
97
  def pop!
93
98
  @hash.delete @count - 1
94
99
  @count -= 1
@@ -18,11 +18,11 @@ class Pry
18
18
  # @param [Hash] hash The hash to convert to `Pry::Hooks`.
19
19
  # @return [Pry::Hooks] The resulting `Pry::Hooks` instance.
20
20
  def self.from_hash(hash)
21
+ return hash if hash.instance_of?(self)
21
22
  instance = new
22
23
  hash.each do |k, v|
23
24
  instance.add_hook(k, nil, v)
24
25
  end
25
-
26
26
  instance
27
27
  end
28
28
 
@@ -49,22 +49,6 @@ class Pry
49
49
  @errors ||= []
50
50
  end
51
51
 
52
- # FIXME:
53
- # This is a hack to alert people of the new API.
54
- def [](event_name)
55
- warn "`Pry.hooks[]` is deprecated! Please use the new `Pry::Hooks` API! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
56
-
57
- get_hook(event_name, nil)
58
- end
59
-
60
- # FIXME:
61
- # This is a hack to alert people of the new API.
62
- def []=(event_name, callable)
63
- warn "`Pry.hooks[]=` is deprecated! Please use the new `Pry::Hooks` API! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
64
-
65
- add_hook(event_name, nil, callable)
66
- end
67
-
68
52
  # Destructively merge the contents of two `Pry:Hooks` instances.
69
53
  # @param [Pry::Hooks] other The `Pry::Hooks` instance to merge
70
54
  # @return [Pry:Hooks] Returns the receiver.
@@ -146,18 +130,14 @@ class Pry
146
130
  def exec_hook(event_name, *args, &block)
147
131
  @hooks[event_name] ||= []
148
132
 
149
- # silence warnings to get rid of 1.8's "warning: multiple values
150
- # for a block parameter" warnings
151
- Pry::Helpers::BaseHelpers.silence_warnings do
152
- @hooks[event_name].map do |hook_name, callable|
153
- begin
154
- callable.call(*args, &block)
155
- rescue RescuableException => e
156
- errors << e
157
- e
158
- end
159
- end.last
160
- end
133
+ @hooks[event_name].map do |hook_name, callable|
134
+ begin
135
+ callable.call(*args, &block)
136
+ rescue RescuableException => e
137
+ errors << e
138
+ e
139
+ end
140
+ end.last
161
141
  end
162
142
 
163
143
  # Return the number of hook functions registered for the `event_name` event.
@@ -94,8 +94,8 @@ class Pry
94
94
  indent = new
95
95
  lines = str.split("\n")
96
96
  n = line_number - 1
97
- to_indent = lines[0...n] + (lines[n] || "").split("def").first(1)
98
- indent.indent(to_indent.join("\n") + "\n")
97
+ to_indent = lines[0...n] << (lines[n] || "").split("def").first(1)
98
+ indent.indent(to_indent.join("\n") << "\n")
99
99
  indent.module_nesting
100
100
  end
101
101
 
@@ -320,7 +320,7 @@ class Pry
320
320
  # [ ["class", "Foo"], ["module", "Bar::Baz"], ["class <<", "self"] ]
321
321
  #
322
322
  # A nil value in the @module_nesting array happens in two places: either
323
- # when @awaiting_token is true and we're still waiting for the string to
323
+ # when @awaiting_class is true and we're still waiting for the string to
324
324
  # fill that space, or when a parse was rejected.
325
325
  #
326
326
  # At the moment this function is quite restricted about what formats it will
@@ -341,7 +341,7 @@ class Pry
341
341
  @module_nesting.last[1] = token if kind == :class
342
342
  @awaiting_class = false
343
343
  else
344
- # leave @nesting[-1][
344
+ # leave @module_nesting[-1]
345
345
  @awaiting_class = false
346
346
  end
347
347
  end
@@ -384,13 +384,13 @@ class Pry
384
384
  # @return [String]
385
385
  def correct_indentation(prompt, code, overhang=0)
386
386
  prompt = prompt.delete("\001\002")
387
- full_line = prompt + code
387
+ line_to_measure = Pry::Helpers::Text.strip_color(prompt) << code
388
388
  whitespace = ' ' * overhang
389
389
 
390
390
  _, cols = Terminal.screen_size
391
391
 
392
392
  cols = cols.to_i
393
- lines = cols != 0 ? (full_line.length / cols + 1) : 1
393
+ lines = (cols != 0 ? (line_to_measure.length / cols + 1) : 1).to_i
394
394
 
395
395
  if Pry::Helpers::BaseHelpers.windows_ansi?
396
396
  move_up = "\e[#{lines}F"
@@ -0,0 +1,242 @@
1
+ # taken from irb
2
+ # 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" ]
35
+
36
+ Operators = [
37
+ "%", "&", "*", "**", "+", "-", "/",
38
+ "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
39
+ "[]", "[]=", "^", "!", "!=", "!~"
40
+ ]
41
+
42
+ WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{("
43
+
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
50
+
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)
59
+
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
70
+ end
71
+
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)
143
+ begin
144
+ candidates = eval(receiver, bind).methods.collect(&:to_s)
145
+ rescue Pry::RescuableException
146
+ candidates = []
147
+ 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)
159
+
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
163
+ begin
164
+ candidates = eval("#{receiver}.methods", bind).collect(&:to_s)
165
+ rescue Pry::RescuableException
166
+ candidates = []
167
+ end
168
+ else
169
+ # func1.func2
170
+ candidates = []
171
+ ObjectSpace.each_object(Module){|m|
172
+ begin
173
+ name = m.name.to_s
174
+ rescue Pry::RescuableException
175
+ name = ""
176
+ end
177
+ next if name != "IRB::Context" and
178
+ /^(IRB|SLex|RubyLex|RubyToken)/ =~ name
179
+
180
+ # jruby doesn't always provide #instance_methods() on each
181
+ # object.
182
+ if m.respond_to?(:instance_methods)
183
+ candidates.concat m.instance_methods(false).collect(&:to_s)
184
+ end
185
+ }
186
+ candidates.sort!
187
+ candidates.uniq!
188
+ end
189
+ select_message(path, receiver, message, candidates)
190
+ when /^\.([^.]*)$/
191
+ # Unknown(maybe String)
192
+ receiver = ""
193
+ message = Regexp.quote($1)
194
+ candidates = String.instance_methods(true).collect(&:to_s)
195
+ select_message(path, receiver, message, candidates)
196
+ else
197
+ candidates = eval(
198
+ "methods | private_methods | local_variables | " \
199
+ "self.class.constants | instance_variables",
200
+ bind
201
+ ).collect(&:to_s)
202
+
203
+ if eval("respond_to?(:class_variables)", bind)
204
+ candidates += eval("class_variables", bind).collect(&:to_s)
205
+ end
206
+ candidates = (candidates|ReservedWords|custom_completions).grep(/^#{Regexp.quote(input)}/)
207
+ candidates.collect(&path)
208
+ end
209
+ rescue Pry::RescuableException
210
+ []
211
+ end
212
+ end
213
+
214
+ def select_message(path, receiver, message, candidates)
215
+ candidates.grep(/^#{message}/).collect { |e|
216
+ case e
217
+ when /^[a-zA-Z_]/
218
+ path.call(receiver + "." << e)
219
+ when /^[0-9]/
220
+ when *Operators
221
+ #receiver + " " << e
222
+ end
223
+ }.compact
224
+ end
225
+
226
+ # build_path seperates the input into two parts: path and input.
227
+ # input is the partial string that should be completed
228
+ # path is a proc that takes an input and builds a full path.
229
+ def build_path(input)
230
+ # check to see if the input is a regex
231
+ return proc {|i| i.to_s }, input if input[/\/\./]
232
+ trailing_slash = input.end_with?('/')
233
+ contexts = input.chomp('/').split(/\//)
234
+ input = contexts[-1]
235
+ path = proc do |i|
236
+ p = contexts[0..-2].push(i).join('/')
237
+ p += '/' if trailing_slash && !i.nil?
238
+ p
239
+ end
240
+ return path, input
241
+ end
242
+ end
@@ -0,0 +1,132 @@
1
+ require 'thread'
2
+
3
+ class Pry
4
+ # There is one InputLock per input (such as STDIN) as two REPLs on the same
5
+ # input makes things delirious. InputLock serializes accesses to the input so
6
+ # that threads to not conflict with each other. The latest thread to request
7
+ # ownership of the input wins.
8
+ class InputLock
9
+ class Interrupt < Exception; end
10
+
11
+ class << self
12
+ attr_accessor :input_locks
13
+ attr_accessor :global_lock
14
+ end
15
+
16
+ self.input_locks = {}
17
+ self.global_lock = Mutex.new
18
+
19
+ def self.for(input)
20
+ # XXX This method leaks memory, as we never unregister an input once we
21
+ # are done with it. Fortunately, the leak is tiny (or so we hope). In
22
+ # usual scenarios, we would leak the StringIO that is passed to be
23
+ # evaluated from the command line.
24
+ global_lock.synchronize do
25
+ input_locks[input] ||= Pry::InputLock.new
26
+ end
27
+ end
28
+
29
+ def initialize
30
+ @mutex = Mutex.new
31
+ @cond = ConditionVariable.new
32
+ @owners = []
33
+ @interruptible = false
34
+ end
35
+
36
+ # Adds ourselves to the ownership list. The last one in the list may access
37
+ # the input through interruptible_region().
38
+ def __with_ownership(&block)
39
+ @mutex.synchronize do
40
+ # Three cases:
41
+ # 1) There are no owners, in this case we are good to go.
42
+ # 2) The current owner of the input is not reading the input (it might
43
+ # just be evaluating some ruby that the user typed).
44
+ # The current owner will figure out that it cannot go back to reading
45
+ # the input since we are adding ourselves to the @owners list, which
46
+ # in turns makes us the current owner.
47
+ # 3) The owner of the input is in the interruptible region, reading from
48
+ # the input. It's safe to send an Interrupt exception to interrupt
49
+ # the owner. It will then proceed like in case 2).
50
+ # We wait until the owner sets the interruptible flag back
51
+ # to false, meaning that he's out of the interruptible region.
52
+ # Note that the owner may receive multiple interrupts since, but that
53
+ # should be okay (and trying to avoid it is futile anyway).
54
+ while @interruptible
55
+ @owners.last.raise Interrupt
56
+ @cond.wait(@mutex)
57
+ end
58
+ @owners << Thread.current
59
+ end
60
+
61
+ block.call
62
+
63
+ ensure
64
+ @mutex.synchronize do
65
+ # We are releasing any desire to have the input ownership by removing
66
+ # ourselves from the list.
67
+ @owners.delete(Thread.current)
68
+
69
+ # We need to wake up the thread at the end of the @owners list, but
70
+ # sadly Ruby doesn't allow us to choose which one we wake up, so we wake
71
+ # them all up.
72
+ @cond.broadcast
73
+ end
74
+ end
75
+
76
+ def with_ownership(&block)
77
+ # If we are in a nested with_ownership() call (nested pry context), we do nothing.
78
+ nested = @mutex.synchronize { @owners.include?(Thread.current) }
79
+ nested ? block.call : __with_ownership(&block)
80
+ end
81
+
82
+ def enter_interruptible_region
83
+ @mutex.synchronize do
84
+ # We patiently wait until we are the owner. This may happen as another
85
+ # thread calls with_ownership() because of a binding.pry happening in
86
+ # another thread.
87
+ @cond.wait(@mutex) until @owners.last == Thread.current
88
+
89
+ # We are the legitimate owner of the input. We mark ourselves as
90
+ # interruptible, so other threads can send us an Interrupt exception
91
+ # while we are blocking from reading the input.
92
+ @interruptible = true
93
+ end
94
+ end
95
+
96
+ def leave_interruptible_region
97
+ @mutex.synchronize do
98
+ # We check if we are still the owner, because we could have received an
99
+ # Interrupt right after the following @cond.broadcast, making us retry.
100
+ @interruptible = false if @owners.last == Thread.current
101
+ @cond.broadcast
102
+ end
103
+ rescue Interrupt
104
+ # We need to guard against a spurious interrupt delivered while we are
105
+ # trying to acquire the lock (the rescue block is no longer in our scope).
106
+ retry
107
+ end
108
+
109
+ def interruptible_region(&block)
110
+ enter_interruptible_region
111
+
112
+ # XXX Note that there is a chance that we get the interrupt right after
113
+ # the readline call succeeded, but we'll never know, and we will retry the
114
+ # call, discarding that piece of input.
115
+ block.call
116
+
117
+ rescue Interrupt
118
+ # We were asked to back off. The one requesting the interrupt will be
119
+ # waiting on the conditional for the interruptible flag to change to false.
120
+ # Note that there can be some inefficiency, as we could immediately
121
+ # succeed in enter_interruptible_region(), even before the one requesting
122
+ # the ownership has the chance to register itself as an owner.
123
+ # To mitigate the issue, we sleep a little bit.
124
+ leave_interruptible_region
125
+ sleep 0.01
126
+ retry
127
+
128
+ ensure
129
+ leave_interruptible_region
130
+ end
131
+ end
132
+ end