pry 0.7.7.2-i386-mswin32 → 0.8.0-i386-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +393 -195
- data/Rakefile +5 -4
- data/bin/pry +31 -39
- data/lib/pry.rb +18 -11
- data/lib/pry/command_base.rb +74 -21
- data/lib/pry/command_base_helpers.rb +241 -0
- data/lib/pry/command_helpers.rb +303 -0
- data/lib/pry/command_processor.rb +174 -0
- data/lib/pry/commands.rb +479 -260
- data/lib/pry/completion.rb +1 -1
- data/lib/pry/custom_completions.rb +6 -0
- data/lib/pry/hooks.rb +3 -7
- data/lib/pry/print.rb +10 -13
- data/lib/pry/prompts.rb +7 -2
- data/lib/pry/pry_class.rb +34 -11
- data/lib/pry/pry_instance.rb +83 -106
- data/lib/pry/version.rb +1 -1
- data/test/test.rb +126 -59
- data/test/test_helper.rb +6 -0
- metadata +112 -109
- data/lib/pry.rbc +0 -541
- data/lib/pry/#commands.rb# +0 -718
- data/lib/pry/command_base.rbc +0 -1795
- data/lib/pry/commands.rbc +0 -14430
- data/lib/pry/completion.rbc +0 -4485
- data/lib/pry/core_extensions.rbc +0 -592
- data/lib/pry/hooks.rbc +0 -649
- data/lib/pry/print.rbc +0 -400
- data/lib/pry/prompts.rbc +0 -574
- data/lib/pry/pry_class.rbc +0 -2376
- data/lib/pry/pry_instance.rbc +0 -4633
- data/lib/pry/version.rbc +0 -131
@@ -0,0 +1,303 @@
|
|
1
|
+
class Pry
|
2
|
+
class Commands < CommandBase
|
3
|
+
module CommandHelpers
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def try_to_load_pry_doc
|
8
|
+
|
9
|
+
# YARD crashes on rbx, so do not require it
|
10
|
+
if !Object.const_defined?(:RUBY_ENGINE) || RUBY_ENGINE !~ /rbx/
|
11
|
+
require "pry-doc"
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
end
|
15
|
+
|
16
|
+
def meth_name_from_binding(b)
|
17
|
+
meth_name = b.eval('__method__')
|
18
|
+
if [:__script__, nil, :__binding__, :__binding_impl__].include?(meth_name)
|
19
|
+
nil
|
20
|
+
else
|
21
|
+
meth_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_file_and_dir_locals(file_name)
|
26
|
+
return if !target
|
27
|
+
$_file_temp = File.expand_path(file_name)
|
28
|
+
$_dir_temp = File.dirname($_file_temp)
|
29
|
+
target.eval("_file_ = $_file_temp")
|
30
|
+
target.eval("_dir_ = $_dir_temp")
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_line_numbers(lines, start_line)
|
34
|
+
line_array = lines.each_line.to_a
|
35
|
+
line_array.each_with_index.map do |line, idx|
|
36
|
+
adjusted_index = idx + start_line
|
37
|
+
if Pry.color
|
38
|
+
cindex = CodeRay.scan("#{adjusted_index}", :ruby).term
|
39
|
+
"#{cindex}: #{line}"
|
40
|
+
else
|
41
|
+
"#{idx}: #{line}"
|
42
|
+
end
|
43
|
+
end.join
|
44
|
+
end
|
45
|
+
|
46
|
+
# if start_line is not false then add line numbers starting with start_line
|
47
|
+
def render_output(should_flood, start_line, doc)
|
48
|
+
if start_line
|
49
|
+
doc = add_line_numbers(doc, start_line)
|
50
|
+
end
|
51
|
+
|
52
|
+
if should_flood
|
53
|
+
output.puts doc
|
54
|
+
else
|
55
|
+
stagger_output(doc)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_for_dynamically_defined_method(meth)
|
60
|
+
file, _ = meth.source_location
|
61
|
+
if file =~ /(\(.*\))|<.*>/
|
62
|
+
raise "Cannot retrieve source for dynamically defined method."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_first_word(text)
|
67
|
+
text.split.drop(1).join(' ')
|
68
|
+
end
|
69
|
+
|
70
|
+
# turn off color for duration of block
|
71
|
+
def no_color(&block)
|
72
|
+
old_color_state = Pry.color
|
73
|
+
Pry.color = false
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
Pry.color = old_color_state
|
77
|
+
end
|
78
|
+
|
79
|
+
def code_and_code_type_for(meth)
|
80
|
+
case code_type = code_type_for(meth)
|
81
|
+
when nil
|
82
|
+
return nil
|
83
|
+
when :c
|
84
|
+
code = Pry::MethodInfo.info_for(meth).source
|
85
|
+
code = strip_comments_from_c_code(code)
|
86
|
+
when :ruby
|
87
|
+
code = strip_leading_whitespace(meth.source)
|
88
|
+
set_file_and_dir_locals(meth.source_location.first)
|
89
|
+
end
|
90
|
+
|
91
|
+
[code, code_type]
|
92
|
+
end
|
93
|
+
|
94
|
+
def doc_and_code_type_for(meth)
|
95
|
+
case code_type = code_type_for(meth)
|
96
|
+
when nil
|
97
|
+
return nil
|
98
|
+
when :c
|
99
|
+
doc = Pry::MethodInfo.info_for(meth).docstring
|
100
|
+
when :ruby
|
101
|
+
doc = meth.comment
|
102
|
+
doc = strip_leading_hash_and_whitespace_from_ruby_comments(doc)
|
103
|
+
set_file_and_dir_locals(meth.source_location.first)
|
104
|
+
end
|
105
|
+
|
106
|
+
[doc, code_type]
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_method_object(meth_name, target, options)
|
110
|
+
if !meth_name
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
|
114
|
+
if options[:M]
|
115
|
+
target.eval("instance_method(:#{meth_name})")
|
116
|
+
elsif options[:m]
|
117
|
+
target.eval("method(:#{meth_name})")
|
118
|
+
else
|
119
|
+
begin
|
120
|
+
target.eval("instance_method(:#{meth_name})")
|
121
|
+
rescue
|
122
|
+
begin
|
123
|
+
target.eval("method(:#{meth_name})")
|
124
|
+
rescue
|
125
|
+
return nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def make_header(meth, code_type, content)
|
132
|
+
num_lines = "Number of lines: #{bold(content.each_line.count.to_s)}"
|
133
|
+
case code_type
|
134
|
+
when :ruby
|
135
|
+
file, line = meth.source_location
|
136
|
+
"\n#{bold('From:')} #{file} @ line #{line}:\n#{num_lines}\n\n"
|
137
|
+
else
|
138
|
+
file = Pry::MethodInfo.info_for(meth).file
|
139
|
+
"\n#{bold('From:')} #{file} in Ruby Core (C Method):\n#{num_lines}\n\n"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def is_a_c_method?(meth)
|
144
|
+
meth.source_location.nil?
|
145
|
+
end
|
146
|
+
|
147
|
+
def should_use_pry_doc?(meth)
|
148
|
+
Pry.has_pry_doc && is_a_c_method?(meth)
|
149
|
+
end
|
150
|
+
|
151
|
+
def code_type_for(meth)
|
152
|
+
# only C methods
|
153
|
+
if should_use_pry_doc?(meth)
|
154
|
+
info = Pry::MethodInfo.info_for(meth)
|
155
|
+
if info && info.source
|
156
|
+
code_type = :c
|
157
|
+
else
|
158
|
+
output.puts "Cannot find C method: #{meth.name}"
|
159
|
+
code_type = nil
|
160
|
+
end
|
161
|
+
else
|
162
|
+
if is_a_c_method?(meth)
|
163
|
+
output.puts "Cannot locate this method: #{meth.name}. Try `gem install pry-doc` to get access to Ruby Core documentation."
|
164
|
+
code_type = nil
|
165
|
+
else
|
166
|
+
check_for_dynamically_defined_method(meth)
|
167
|
+
code_type = :ruby
|
168
|
+
end
|
169
|
+
end
|
170
|
+
code_type
|
171
|
+
end
|
172
|
+
|
173
|
+
def file_map
|
174
|
+
{
|
175
|
+
[".c", ".h"] => :c,
|
176
|
+
[".cpp", ".hpp", ".cc", ".h", "cxx"] => :cpp,
|
177
|
+
[".rb", "Rakefile", ".irbrc", ".gemspec", ".pryrc"] => :ruby,
|
178
|
+
".py" => :python,
|
179
|
+
".diff" => :diff,
|
180
|
+
".css" => :css,
|
181
|
+
".html" => :html,
|
182
|
+
[".yaml", ".yml"] => :yaml,
|
183
|
+
".xml" => :xml,
|
184
|
+
".php" => :php,
|
185
|
+
".js" => :javascript,
|
186
|
+
".java" => :java,
|
187
|
+
".rhtml" => :rhtml,
|
188
|
+
".json" => :json
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
def syntax_highlight_by_file_type_or_specified(contents, file_name, file_type)
|
193
|
+
_, language_detected = file_map.find do |k, v|
|
194
|
+
Array(k).any? do |matcher|
|
195
|
+
matcher == File.extname(file_name) || matcher == File.basename(file_name)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
language_detected = file_type if file_type
|
200
|
+
CodeRay.scan(contents, language_detected).term
|
201
|
+
end
|
202
|
+
|
203
|
+
# convert negative line numbers to positive by wrapping around
|
204
|
+
# last line (as per array indexing with negative numbers)
|
205
|
+
def normalized_line_number(line_number, total_lines)
|
206
|
+
line_number < 0 ? line_number + total_lines : line_number
|
207
|
+
end
|
208
|
+
|
209
|
+
# returns the file content between the lines and the normalized
|
210
|
+
# start and end line numbers.
|
211
|
+
def read_between_the_lines(file_name, start_line, end_line)
|
212
|
+
content = File.read(File.expand_path(file_name))
|
213
|
+
lines_array = content.each_line.to_a
|
214
|
+
|
215
|
+
[lines_array[start_line..end_line].join, normalized_line_number(start_line, lines_array.size),
|
216
|
+
normalized_line_number(end_line, lines_array.size)]
|
217
|
+
end
|
218
|
+
|
219
|
+
# documentation related helpers
|
220
|
+
def strip_color_codes(str)
|
221
|
+
str.gsub(/\e\[.*?(\d)+m/, '')
|
222
|
+
end
|
223
|
+
|
224
|
+
def process_rdoc(comment, code_type)
|
225
|
+
comment = comment.dup
|
226
|
+
comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
|
227
|
+
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
|
228
|
+
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { Pry.color ? "\e[34m#{$1}\e[0m" : $1 }.
|
229
|
+
gsub(/\B\+(\w*?)\+\B/) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
|
230
|
+
gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
|
231
|
+
gsub(/`(?:\s*\n)?(.*?)\s*`/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }
|
232
|
+
end
|
233
|
+
|
234
|
+
def process_yardoc_tag(comment, tag)
|
235
|
+
in_tag_block = nil
|
236
|
+
output = comment.lines.map do |v|
|
237
|
+
if in_tag_block && v !~ /^\S/
|
238
|
+
strip_color_codes(strip_color_codes(v))
|
239
|
+
elsif in_tag_block
|
240
|
+
in_tag_block = false
|
241
|
+
v
|
242
|
+
else
|
243
|
+
in_tag_block = true if v =~ /^@#{tag}/
|
244
|
+
v
|
245
|
+
end
|
246
|
+
end.join
|
247
|
+
end
|
248
|
+
|
249
|
+
def process_yardoc(comment)
|
250
|
+
yard_tags = ["param", "return", "option", "yield", "attr", "attr_reader", "attr_writer",
|
251
|
+
"deprecate", "example"]
|
252
|
+
(yard_tags - ["example"]).inject(comment) { |a, v| process_yardoc_tag(a, v) }.
|
253
|
+
gsub(/^@(#{yard_tags.join("|")})/) { Pry.color ? "\e[33m#{$1}\e[0m": $1 }
|
254
|
+
end
|
255
|
+
|
256
|
+
def process_comment_markup(comment, code_type)
|
257
|
+
process_yardoc process_rdoc(comment, code_type)
|
258
|
+
end
|
259
|
+
|
260
|
+
# strip leading whitespace but preserve indentation
|
261
|
+
def strip_leading_whitespace(text)
|
262
|
+
return text if text.empty?
|
263
|
+
leading_spaces = text.lines.first[/^(\s+)/, 1]
|
264
|
+
text.gsub(/^#{leading_spaces}/, '')
|
265
|
+
end
|
266
|
+
|
267
|
+
def strip_leading_hash_and_whitespace_from_ruby_comments(comment)
|
268
|
+
comment = comment.dup
|
269
|
+
comment.gsub!(/\A\#+?$/, '')
|
270
|
+
comment.gsub!(/^\s*#/, '')
|
271
|
+
strip_leading_whitespace(comment)
|
272
|
+
end
|
273
|
+
|
274
|
+
def strip_comments_from_c_code(code)
|
275
|
+
code.sub /\A\s*\/\*.*?\*\/\s*/m, ''
|
276
|
+
end
|
277
|
+
|
278
|
+
def prompt(message, options="Yn")
|
279
|
+
opts = options.scan(/./)
|
280
|
+
optstring = opts.join("/") # case maintained
|
281
|
+
defaults = opts.select{|o| o.upcase == o }
|
282
|
+
opts = opts.map{|o| o.downcase}
|
283
|
+
|
284
|
+
raise "Error: Too many default values for the prompt: #{default.inspect}" if defaults.size > 1
|
285
|
+
|
286
|
+
default = defaults.first
|
287
|
+
|
288
|
+
loop do
|
289
|
+
response = Pry.input.readline("#{message} (#{optstring}) ").downcase
|
290
|
+
case response
|
291
|
+
when *opts
|
292
|
+
return response
|
293
|
+
when ""
|
294
|
+
return default.downcase
|
295
|
+
else
|
296
|
+
output.puts " |_ Invalid option: #{response.inspect}. Try again."
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class Pry
|
4
|
+
class CommandProcessor
|
5
|
+
SYSTEM_COMMAND_DELIMITER = "."
|
6
|
+
SYSTEM_COMMAND_REGEX = /^#{Regexp.escape(SYSTEM_COMMAND_DELIMITER)}(.*)/
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
attr_accessor :pry_instance
|
11
|
+
|
12
|
+
def initialize(pry_instance)
|
13
|
+
@pry_instance = pry_instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def_delegators :@pry_instance, :commands, :nesting, :output
|
17
|
+
|
18
|
+
# Is the string a command valid?
|
19
|
+
# @param [String] val The string passed in from the Pry prompt.
|
20
|
+
# @return [Boolean] Whether the string is a valid command.
|
21
|
+
def valid_command?(val)
|
22
|
+
system_command?(val) || pry_command?(val)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Is the string a valid system command?
|
26
|
+
# @param [String] val The string passed in from the Pry prompt.
|
27
|
+
# @return [Boolean] Whether the string is a valid system command.
|
28
|
+
def system_command?(val)
|
29
|
+
!!(SYSTEM_COMMAND_REGEX =~ val)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Is the string a valid pry command?
|
33
|
+
# A Pry command is a command that is not a system command.
|
34
|
+
# @param [String] val The string passed in from the Pry prompt.
|
35
|
+
# @return [Boolean] Whether the string is a valid Pry command.
|
36
|
+
def pry_command?(val)
|
37
|
+
!!command_matched(val).first
|
38
|
+
end
|
39
|
+
|
40
|
+
# Revaluate the string (str) and perform interpolation.
|
41
|
+
# @param [String] str The string to reevaluate with interpolation.
|
42
|
+
# @param [Binding] target The context where the string should be
|
43
|
+
# reevaluated in.
|
44
|
+
# @return [String] The reevaluated string with interpolations
|
45
|
+
# applied (if any).
|
46
|
+
def interpolate_string(str, target)
|
47
|
+
dumped_str = str.dump
|
48
|
+
dumped_str.gsub!(/\\\#\{/, '#{')
|
49
|
+
target.eval(dumped_str)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Execute a given system command.
|
53
|
+
# The commands first have interpolation applied against the
|
54
|
+
# `target` context.
|
55
|
+
# All system command are forwarded to a shell. Note that the `cd`
|
56
|
+
# command is special-cased and is converted internallly to a `Dir.chdir`
|
57
|
+
# @param [String] val The system command to execute.
|
58
|
+
# @param [Binding] target The context in which to perform string interpolation.
|
59
|
+
def execute_system_command(val, target)
|
60
|
+
SYSTEM_COMMAND_REGEX =~ val
|
61
|
+
cmd = interpolate_string($1, target)
|
62
|
+
|
63
|
+
if cmd =~ /^cd\s+(.+)/i
|
64
|
+
begin
|
65
|
+
@@cd_history ||= []
|
66
|
+
if $1 == "-"
|
67
|
+
dest = @@cd_history.pop || Dir.pwd
|
68
|
+
else
|
69
|
+
dest = File.expand_path($1)
|
70
|
+
end
|
71
|
+
|
72
|
+
@@cd_history << Dir.pwd
|
73
|
+
Dir.chdir(dest)
|
74
|
+
rescue Errno::ENOENT
|
75
|
+
output.puts "No such directory: #{dest}"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
if !system(cmd)
|
79
|
+
output.puts "Error: there was a problem executing system command: #{cmd}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Tick, tock, im getting rid of this shit soon.
|
84
|
+
val.replace("")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Determine whether a Pry command was matched and return command data
|
88
|
+
# and argument string.
|
89
|
+
# This method should not need to be invoked directly.
|
90
|
+
# @param [String] val The line of input.
|
91
|
+
# @return [Array] The command data and arg string pair
|
92
|
+
def command_matched(val)
|
93
|
+
_, cmd_data = commands.commands.find do |name, cmd_data|
|
94
|
+
/^#{Regexp.escape(name)}(?!\S)(?:\s+(.+))?/ =~ val
|
95
|
+
end
|
96
|
+
|
97
|
+
[cmd_data, $1]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Process Pry commands. Pry commands are not Ruby methods and are evaluated
|
101
|
+
# prior to Ruby expressions.
|
102
|
+
# Commands can be modified/configured by the user: see `Pry::Commands`
|
103
|
+
# This method should not need to be invoked directly - it is called
|
104
|
+
# by `Pry#r`.
|
105
|
+
# @param [String] val The current line of input.
|
106
|
+
# @param [String] eval_string The cumulative lines of input for
|
107
|
+
# multi-line input.
|
108
|
+
# @param [Binding] target The receiver of the commands.
|
109
|
+
def process_commands(val, eval_string, target)
|
110
|
+
def val.clear() replace("") end
|
111
|
+
def eval_string.clear() replace("") end
|
112
|
+
|
113
|
+
if system_command?(val)
|
114
|
+
execute_system_command(val, target)
|
115
|
+
return
|
116
|
+
end
|
117
|
+
|
118
|
+
# no command was matched, so return to caller
|
119
|
+
return if !pry_command?(val)
|
120
|
+
|
121
|
+
val.replace interpolate_string(val, target)
|
122
|
+
cmd_data, args_string = command_matched(val)
|
123
|
+
|
124
|
+
args = args_string ? Shellwords.shellwords(args_string) : []
|
125
|
+
action = cmd_data[:action]
|
126
|
+
keep_retval = cmd_data[:keep_retval]
|
127
|
+
|
128
|
+
options = {
|
129
|
+
:val => val,
|
130
|
+
:eval_string => eval_string,
|
131
|
+
:nesting => nesting,
|
132
|
+
:commands => commands.commands
|
133
|
+
}
|
134
|
+
|
135
|
+
ret_value = execute_command(target, action, options, *args)
|
136
|
+
|
137
|
+
# return value of block only if :keep_retval is true
|
138
|
+
ret_value if keep_retval
|
139
|
+
end
|
140
|
+
|
141
|
+
# Execute a Pry command.
|
142
|
+
# This method should not need to be invoked directly.
|
143
|
+
# @param [Binding] target The target of the Pry session.
|
144
|
+
# @param [Proc] action The proc that implements the command.
|
145
|
+
# @param [Hash] options The options to set on the Commands object.
|
146
|
+
# @param [Array] args The command arguments.
|
147
|
+
def execute_command(target, action, options, *args)
|
148
|
+
|
149
|
+
# set some useful methods to be used by the action blocks
|
150
|
+
commands.opts = options
|
151
|
+
commands.target = target
|
152
|
+
commands.output = output
|
153
|
+
|
154
|
+
case action.arity <=> 0
|
155
|
+
when -1
|
156
|
+
|
157
|
+
# Use instance_exec() to make the `opts` method, etc available
|
158
|
+
ret_val = commands.instance_exec(*args, &action)
|
159
|
+
when 1, 0
|
160
|
+
|
161
|
+
# ensure that we get the right number of parameters
|
162
|
+
# since 1.8.7 complains about incorrect arity (1.9.2
|
163
|
+
# doesn't care)
|
164
|
+
args_with_corrected_arity = args.values_at *0..(action.arity - 1)
|
165
|
+
ret_val = commands.instance_exec(*args_with_corrected_arity, &action)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Tick, tock, im getting rid of this shit soon.
|
169
|
+
options[:val].clear
|
170
|
+
|
171
|
+
ret_val
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|