pry 0.9.8.4 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG +26 -0
  3. data/README.markdown +3 -3
  4. data/lib/pry.rb +9 -1
  5. data/lib/pry/code.rb +93 -0
  6. data/lib/pry/command.rb +35 -22
  7. data/lib/pry/command_set.rb +97 -67
  8. data/lib/pry/config.rb +63 -10
  9. data/lib/pry/core_extensions.rb +24 -18
  10. data/lib/pry/default_commands/context.rb +72 -12
  11. data/lib/pry/default_commands/easter_eggs.rb +4 -0
  12. data/lib/pry/default_commands/editing.rb +43 -15
  13. data/lib/pry/default_commands/find_method.rb +171 -0
  14. data/lib/pry/default_commands/hist.rb +10 -6
  15. data/lib/pry/default_commands/introspection.rb +183 -30
  16. data/lib/pry/default_commands/ls.rb +77 -7
  17. data/lib/pry/default_commands/misc.rb +1 -0
  18. data/lib/pry/default_commands/navigating_pry.rb +1 -8
  19. data/lib/pry/helpers/base_helpers.rb +10 -2
  20. data/lib/pry/helpers/command_helpers.rb +23 -40
  21. data/lib/pry/helpers/documentation_helpers.rb +65 -0
  22. data/lib/pry/indent.rb +17 -4
  23. data/lib/pry/method.rb +61 -45
  24. data/lib/pry/pry_class.rb +9 -3
  25. data/lib/pry/pry_instance.rb +99 -65
  26. data/lib/pry/rbx_method.rb +2 -9
  27. data/lib/pry/version.rb +1 -1
  28. data/lib/pry/wrapped_module.rb +236 -1
  29. data/pry.gemspec +5 -5
  30. data/test/helper.rb +22 -0
  31. data/test/test_command.rb +29 -0
  32. data/test/test_command_integration.rb +193 -10
  33. data/test/test_command_set.rb +82 -17
  34. data/test/test_default_commands/test_cd.rb +16 -0
  35. data/test/test_default_commands/test_context.rb +61 -0
  36. data/test/test_default_commands/test_documentation.rb +163 -43
  37. data/test/test_default_commands/test_find_method.rb +42 -0
  38. data/test/test_default_commands/test_input.rb +32 -0
  39. data/test/test_default_commands/test_introspection.rb +50 -197
  40. data/test/test_default_commands/test_ls.rb +22 -0
  41. data/test/test_default_commands/test_show_source.rb +306 -0
  42. data/test/test_pry.rb +3 -3
  43. data/test/test_pry_defaults.rb +21 -0
  44. data/test/test_sticky_locals.rb +81 -1
  45. data/test/test_syntax_checking.rb +7 -6
  46. metadata +22 -14
@@ -32,21 +32,25 @@ class Pry
32
32
  def process
33
33
  @history = Pry::Code(Pry.history.to_a)
34
34
 
35
+ if opts.present?(:show)
36
+ @history = @history.between(opts[:show])
37
+ end
38
+
39
+ if opts.present?(:grep)
40
+ @history = @history.grep(opts[:grep])
41
+ end
42
+
35
43
  @history = case
36
44
  when opts.present?(:head)
37
- @history.between(1, opts[:head] || 10)
45
+ @history.take_lines(1, opts[:head] || 10)
38
46
  when opts.present?(:tail)
39
- @history.between(-(opts[:tail] || 10), -1)
47
+ @history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10)
40
48
  when opts.present?(:show)
41
49
  @history.between(opts[:show])
42
50
  else
43
51
  @history
44
52
  end
45
53
 
46
- if opts.present?(:grep)
47
- @history = @history.grep(opts[:grep])
48
- end
49
-
50
54
  if opts.present?(:'exclude-pry')
51
55
  @history = @history.select { |l, ln| !command_set.valid_command?(l) }
52
56
  end
@@ -3,23 +3,127 @@ require 'tempfile'
3
3
  class Pry
4
4
  module DefaultCommands
5
5
 
6
+ # For show-doc and show-source
7
+ module ModuleIntrospectionHelpers
8
+ attr_accessor :module_object
9
+
10
+ def module?(name)
11
+ self.module_object = Pry::WrappedModule.from_str(name, target)
12
+ end
13
+
14
+ def method?
15
+ !!method_object
16
+ rescue CommandError
17
+ false
18
+ end
19
+
20
+ def process(name)
21
+ if module?(name)
22
+ code_or_doc = process_module
23
+ else method?
24
+ code_or_doc = process_method
25
+ end
26
+
27
+ render_output(code_or_doc, opts)
28
+ end
29
+
30
+ def module_start_line(mod, candidate=0)
31
+ if opts.present?(:'base-one')
32
+ 1
33
+ else
34
+ mod.source_line_for_candidate(candidate)
35
+ end
36
+ end
37
+
38
+ def use_line_numbers?
39
+ opts.present?(:b) || opts.present?(:l)
40
+ end
41
+ end
42
+
6
43
  Introspection = Pry::CommandSet.new do
7
44
 
8
- create_command "show-doc", "Show the comments above METH. Aliases: \?", :shellwords => false do |*args|
45
+ create_command "show-doc", "Show the comments above METH. Aliases: \?", :shellwords => false do
46
+ include ModuleIntrospectionHelpers
47
+ include Helpers::DocumentationHelpers
48
+ extend Helpers::BaseHelpers
49
+
9
50
  banner <<-BANNER
10
51
  Usage: show-doc [OPTIONS] [METH]
52
+ Aliases: ?
53
+
11
54
  Show the comments above method METH. Tries instance methods first and then methods by default.
12
- e.g show-doc hello_method
55
+ e.g show-doc hello_method # docs for hello_method
56
+ e.g show-doc Pry # docs for Pry class
57
+ e.g show-doc Pry -a # docs for all definitions of Pry class (all monkey patches)
13
58
  BANNER
14
59
 
60
+ options :requires_gem => "ruby18_source_location" if mri_18?
61
+
62
+ def setup
63
+ require 'ruby18_source_location' if mri_18?
64
+ end
65
+
15
66
  def options(opt)
16
67
  method_options(opt)
17
68
  opt.on :l, "line-numbers", "Show line numbers."
18
69
  opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)."
19
70
  opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
71
+ opt.on :a, :all, "Show docs for all definitions and monkeypatches of the module (modules only)"
20
72
  end
21
73
 
22
- def process
74
+ def process_module
75
+ if opts.present?(:all)
76
+ all_modules
77
+ else
78
+ normal_module
79
+ end
80
+ end
81
+
82
+ def normal_module
83
+ mod = module_object
84
+
85
+ # source_file reveals the underlying .c file in case of core
86
+ # classes on MRI. This is different to source_location, which
87
+ # will return nil.
88
+ if mod.yard_docs?
89
+ file_name, line = mod.source_file, nil
90
+ else
91
+ file_name, line = mod.source_location
92
+ end
93
+
94
+ if mod.doc.empty?
95
+ output.puts "No documentation found."
96
+ ""
97
+ else
98
+ set_file_and_dir_locals(file_name) if !mod.yard_docs?
99
+ doc = ""
100
+ doc << mod.doc
101
+
102
+ doc = Code.new(doc, module_start_line(mod), :text).
103
+ with_line_numbers(use_line_numbers?).to_s
104
+
105
+ doc.insert(0, "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line ? line : "N/A"}:\n\n")
106
+ end
107
+ end
108
+
109
+ def all_modules
110
+ mod = module_object
111
+
112
+ doc = ""
113
+ doc << "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n"
114
+ mod.number_of_candidates.times do |v|
115
+ begin
116
+ doc << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{mod.source_file_for_candidate(v)} @ #{mod.source_line_for_candidate(v)}:\n\n"
117
+ dc = mod.doc_for_candidate(v)
118
+ doc << (dc.empty? ? "No documentation found.\n" : dc)
119
+ rescue Pry::RescuableException
120
+ next
121
+ end
122
+ end
123
+ doc
124
+ end
125
+
126
+ def process_method
23
127
  meth = method_object
24
128
  raise Pry::CommandError, "No documentation found." if meth.doc.nil? || meth.doc.empty?
25
129
 
@@ -30,12 +134,20 @@ class Pry
30
134
  output.puts "#{text.bold("Signature:")} #{meth.signature}"
31
135
  output.puts
32
136
 
33
- if opts.present?(:b) || opts.present?(:l)
137
+ if use_line_numbers?
34
138
  doc = Code.new(doc, start_line, :text).
35
- with_line_numbers(true)
139
+ with_line_numbers(true).to_s
36
140
  end
37
141
 
38
- render_output(doc, opts)
142
+ doc
143
+ end
144
+
145
+ def module_start_line(mod, candidate=0)
146
+ if opts.present?(:'base-one')
147
+ 1
148
+ else
149
+ mod.source_line_for_candidate(candidate) - mod.doc_for_candidate(candidate).lines.count
150
+ end
39
151
  end
40
152
 
41
153
  def start_line
@@ -45,12 +157,11 @@ class Pry
45
157
  (method_object.source_line - method_object.doc.lines.count) || 1
46
158
  end
47
159
  end
48
-
49
160
  end
50
161
 
51
162
  alias_command "?", "show-doc"
52
163
 
53
- create_command "stat", "View method information and set _file_ and _dir_ locals.", :shellwords => false do |*args|
164
+ create_command "stat", "View method information and set _file_ and _dir_ locals.", :shellwords => false do
54
165
  banner <<-BANNER
55
166
  Usage: stat [OPTIONS] [METH]
56
167
  Show method information for method METH and set _file_ and _dir_ locals.
@@ -77,45 +188,87 @@ class Pry
77
188
  end
78
189
  end
79
190
 
80
- create_command "show-method" do
81
- description "Show the source for METH. Aliases: $, show-source"
191
+ create_command "show-source" do
192
+ include ModuleIntrospectionHelpers
193
+ extend Helpers::BaseHelpers
194
+
195
+ description "Show the source for METH or CLASS. Aliases: $, show-method"
82
196
 
83
197
  banner <<-BANNER
84
- Usage: show-method [OPTIONS] [METH]
85
- Aliases: $, show-source
198
+ Usage: show-source [OPTIONS] [METH|CLASS]
199
+ Aliases: $, show-method
86
200
 
87
- Show the source for method METH. Tries instance methods first and then methods by default.
201
+ Show the source for method METH or CLASS. Tries instance methods first and then methods by default.
88
202
 
89
- e.g: `show-method hello_method`
90
- e.g: `show-method -m hello_method`
91
- e.g: `show-method Pry#rep`
203
+ e.g: `show-source hello_method`
204
+ e.g: `show-source -m hello_method`
205
+ e.g: `show-source Pry#rep` # source for Pry#rep method
206
+ e.g: `show-source Pry` # source for Pry class
207
+ e.g: `show-source Pry -a` # source for all Pry class definitions (all monkey patches)
92
208
 
93
209
  https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method
94
210
  BANNER
95
211
 
96
- command_options(
97
- :shellwords => false
98
- )
212
+ options :shellwords => false
213
+ options :requires_gem => "ruby18_source_location" if mri_18?
214
+
215
+ def setup
216
+ require 'ruby18_source_location' if mri_18?
217
+ end
99
218
 
100
219
  def options(opt)
101
220
  method_options(opt)
102
221
  opt.on :l, "line-numbers", "Show line numbers."
103
222
  opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)."
104
223
  opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
224
+ opt.on :a, :all, "Show source for all definitions and monkeypatches of the module (modules only)"
105
225
  end
106
226
 
107
- def process
227
+ def process_method
108
228
  raise CommandError, "Could not find method source" unless method_object.source
109
229
 
110
- output.puts make_header(method_object)
111
- output.puts "#{text.bold("Owner:")} #{method_object.owner || "N/A"}"
112
- output.puts "#{text.bold("Visibility:")} #{method_object.visibility}"
113
- output.puts
230
+ code = ""
231
+ code << make_header(method_object)
232
+ code << "#{text.bold("Owner:")} #{method_object.owner || "N/A"}\n"
233
+ code << "#{text.bold("Visibility:")} #{method_object.visibility}\n"
234
+ code << "\n"
114
235
 
115
- code = Code.from_method(method_object, start_line).
116
- with_line_numbers(use_line_numbers?)
236
+ code << Code.from_method(method_object, start_line).
237
+ with_line_numbers(use_line_numbers?).to_s
238
+ end
117
239
 
118
- render_output(code, opts)
240
+ def process_module
241
+ if opts.present?(:all)
242
+ all_modules
243
+ else
244
+ normal_module
245
+ end
246
+ end
247
+
248
+ def normal_module
249
+ mod = module_object
250
+
251
+ file_name, line = mod.source_location
252
+ set_file_and_dir_locals(file_name)
253
+ code = ""
254
+ code << "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line}:\n\n"
255
+ code << Code.from_module(mod, module_start_line(mod)).with_line_numbers(use_line_numbers?).to_s
256
+ end
257
+
258
+ def all_modules
259
+ mod = module_object
260
+
261
+ code = ""
262
+ code << "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n"
263
+ mod.number_of_candidates.times do |v|
264
+ begin
265
+ code << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{mod.source_file_for_candidate(v)} @ line #{mod.source_line_for_candidate(v)}:\n\n"
266
+ code << Code.new(mod.source_for_candidate(v), module_start_line(mod, v)).with_line_numbers(use_line_numbers?).to_s
267
+ rescue Pry::RescuableException
268
+ next
269
+ end
270
+ end
271
+ code
119
272
  end
120
273
 
121
274
  def use_line_numbers?
@@ -131,8 +284,8 @@ class Pry
131
284
  end
132
285
  end
133
286
 
134
- alias_command "show-source", "show-method"
135
- alias_command "$", "show-method"
287
+ alias_command "show-method", "show-source"
288
+ alias_command "$", "show-source"
136
289
 
137
290
  command "show-command", "Show the source for CMD." do |*args|
138
291
  target = target()
@@ -167,7 +320,7 @@ class Pry
167
320
  output.puts make_header(block)
168
321
  output.puts
169
322
 
170
- code = Code.from_method(block).with_line_numbers(opts.present?(:'line-numbers'))
323
+ code = Code.from_method(block).with_line_numbers(opts.present?(:'line-numbers')).to_s
171
324
 
172
325
  render_output(code, opts)
173
326
  else
@@ -30,11 +30,15 @@ class Pry
30
30
  opt.on :g, "globals", "Show global variables, including those builtin to Ruby (in cyan)"
31
31
  opt.on :l, "locals", "Show locals, including those provided by Pry (in red)"
32
32
 
33
- opt.on :c, "constants", "Show constants, highlighting classes (in blue), and exceptions (in purple)"
33
+ opt.on :c, "constants", "Show constants, highlighting classes (in blue), and exceptions (in purple).\n" +
34
+ " " * 32 + "Constants that are pending autoload? are also shown (in yellow)."
34
35
 
35
36
  opt.on :i, "ivars", "Show instance variables (in blue) and class variables (in bright blue)"
36
37
 
37
38
  opt.on :G, "grep", "Filter output by regular expression", :optional => false
39
+ if jruby?
40
+ opt.on :J, "all-java", "Show all the aliases for methods from java (default is to show only prettiest)"
41
+ end
38
42
  end
39
43
 
40
44
  def process
@@ -46,6 +50,7 @@ class Pry
46
50
  opts.present?(:ivars))
47
51
 
48
52
  show_methods = opts.present?(:methods) || opts.present?(:'instance-methods') || opts.present?(:ppp) || !has_opts
53
+ show_self_methods = (!has_opts && Module === obj)
49
54
  show_constants = opts.present?(:constants) || (!has_opts && Module === obj)
50
55
  show_ivars = opts.present?(:ivars) || !has_opts
51
56
  show_locals = opts.present?(:locals) || (!has_opts && args.empty?)
@@ -72,7 +77,7 @@ class Pry
72
77
 
73
78
  if show_methods
74
79
  # methods is a hash {Module/Class => [Pry::Methods]}
75
- methods = all_methods(obj).select{ |method| opts.present?(:ppp) || method.visibility == :public }.group_by(&:owner)
80
+ methods = all_methods(obj).group_by(&:owner)
76
81
 
77
82
  # reverse the resolution order so that the most useful information appears right by the prompt
78
83
  resolution_order(obj).take_while(&below_ceiling(obj)).reverse.each do |klass|
@@ -81,10 +86,17 @@ class Pry
81
86
  end
82
87
  end
83
88
 
89
+ if show_self_methods
90
+ methods = all_methods(obj, true).select{ |m| m.owner == obj && m.name =~ grep_regex }
91
+ output_section "#{Pry::WrappedModule.new(obj).method_prefix}methods", format_methods(methods)
92
+ end
93
+
84
94
  if show_ivars
85
95
  klass = (Module === obj ? obj : obj.class)
86
- output_section("instance variables", format_variables(:instance_var, grep[obj.__send__(:instance_variables)]))
87
- output_section("class variables", format_variables(:class_var, grep[klass.__send__(:class_variables)]))
96
+ ivars = Pry::Method.safe_send(obj, :instance_variables)
97
+ kvars = Pry::Method.safe_send(klass, :class_variables)
98
+ output_section("instance variables", format_variables(:instance_var, ivars))
99
+ output_section("class variables", format_variables(:class_var, kvars))
88
100
  end
89
101
 
90
102
  if show_locals
@@ -109,8 +121,64 @@ class Pry
109
121
  $LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH)
110
122
 
111
123
  # Get all the methods that we'll want to output
112
- def all_methods(obj)
113
- opts.present?(:'instance-methods') ? Pry::Method.all_from_class(obj) : Pry::Method.all_from_obj(obj)
124
+ def all_methods(obj, instance_methods=false)
125
+ methods = if instance_methods || opts.present?(:'instance-methods')
126
+ Pry::Method.all_from_class(obj)
127
+ else
128
+ Pry::Method.all_from_obj(obj)
129
+ end
130
+
131
+ if jruby? && !opts.present?(:J)
132
+ methods = trim_jruby_aliases(methods)
133
+ end
134
+
135
+ methods.select{ |method| opts.present?(:ppp) || method.visibility == :public }
136
+ end
137
+
138
+ # JRuby creates lots of aliases for methods imported from java in an attempt to
139
+ # make life easier for ruby programmers.
140
+ # (e.g. getFooBar becomes get_foo_bar and foo_bar, and maybe foo_bar? if it
141
+ # returns a Boolean).
142
+ # The full transformations are in the assignAliases method of:
143
+ # https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java
144
+ #
145
+ # This has the unfortunate side-effect of making the output of ls even more
146
+ # incredibly verbose than it normally would be for these objects; and so we filter
147
+ # out all but the nicest of these aliases here.
148
+ #
149
+ # TODO: This is a little bit vague, better heuristics could be used.
150
+ # JRuby also has a lot of scala-specific logic, which we don't copy.
151
+ #
152
+ def trim_jruby_aliases(methods)
153
+ grouped = methods.group_by do |m|
154
+ m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase
155
+ end
156
+
157
+ grouped.map do |key, values|
158
+ values = values.sort_by do |m|
159
+ rubbishness(m.name)
160
+ end
161
+
162
+ found = []
163
+ values.select do |x|
164
+ (!found.any?{ |y| x == y }) && found << x
165
+ end
166
+ end.flatten(1)
167
+ end
168
+
169
+ # When removing jruby aliases, we want to keep the alias that is "least rubbish"
170
+ # according to this metric.
171
+ def rubbishness(name)
172
+ name.each_char.map{ |x|
173
+ case x
174
+ when /[A-Z]/
175
+ 1
176
+ when '?', '=', '!'
177
+ -2
178
+ else
179
+ 0
180
+ end
181
+ }.inject(&:+) + (name.size / 100.0)
114
182
  end
115
183
 
116
184
  def resolution_order(obj)
@@ -152,7 +220,7 @@ class Pry
152
220
 
153
221
  def format_constants(mod, constants)
154
222
  constants.sort_by(&:downcase).map do |name|
155
- if const = (!mod.autoload?(name) && mod.const_get(name) rescue nil)
223
+ if const = (!mod.autoload?(name) && (mod.const_get(name) || true) rescue nil)
156
224
  if (const < Exception rescue false)
157
225
  color(:exception_constant, name)
158
226
  elsif (Module === mod.const_get(name) rescue false)
@@ -160,6 +228,8 @@ class Pry
160
228
  else
161
229
  color(:constant, name)
162
230
  end
231
+ else
232
+ color(:unloaded_constant, name)
163
233
  end
164
234
  end
165
235
  end