pry 0.9.8.2-i386-mswin32 → 0.9.8.3-i386-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/README.markdown +20 -13
- data/Rakefile +1 -1
- data/lib/pry.rb +1 -2
- data/lib/pry/command.rb +88 -2
- data/lib/pry/command_set.rb +15 -85
- data/lib/pry/commands.rb +12 -8
- data/lib/pry/default_commands/cd.rb +58 -0
- data/lib/pry/default_commands/commands.rb +62 -0
- data/lib/pry/default_commands/context.rb +48 -165
- data/lib/pry/default_commands/editing.rb +385 -0
- data/lib/pry/default_commands/help.rb +127 -0
- data/lib/pry/default_commands/hist.rb +116 -0
- data/lib/pry/default_commands/{shell.rb → input_and_output.rb} +137 -15
- data/lib/pry/default_commands/introspection.rb +79 -232
- data/lib/pry/default_commands/ls.rb +4 -2
- data/lib/pry/default_commands/{basic.rb → misc.rb} +1 -14
- data/lib/pry/default_commands/navigating_pry.rb +114 -0
- data/lib/pry/helpers/base_helpers.rb +15 -3
- data/lib/pry/helpers/command_helpers.rb +16 -0
- data/lib/pry/history.rb +12 -4
- data/lib/pry/method.rb +2 -2
- data/lib/pry/pry_class.rb +7 -1
- data/lib/pry/pry_instance.rb +6 -0
- data/lib/pry/rbx_path.rb +6 -18
- data/lib/pry/version.rb +1 -1
- data/pry.gemspec +8 -8
- data/test/helper.rb +8 -0
- data/test/test_command.rb +256 -2
- data/test/test_command_integration.rb +2 -13
- data/test/test_command_set.rb +13 -23
- data/test/test_default_commands/test_help.rb +57 -0
- data/test/test_default_commands/test_introspection.rb +23 -0
- data/test/test_pry.rb +11 -0
- metadata +13 -9
- data/lib/pry/default_commands/documentation.rb +0 -209
- data/lib/pry/default_commands/input.rb +0 -247
- data/test/test_default_commands.rb +0 -58
@@ -0,0 +1,62 @@
|
|
1
|
+
class Pry
|
2
|
+
module DefaultCommands
|
3
|
+
Commands = Pry::CommandSet.new do
|
4
|
+
create_command "import-set", "Import a command set" do
|
5
|
+
group "Commands"
|
6
|
+
def process(command_set_name)
|
7
|
+
raise CommandError, "Provide a command set name" if command_set.nil?
|
8
|
+
|
9
|
+
set = target.eval(arg_string)
|
10
|
+
_pry_.commands.import set
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
create_command "install-command", "Install a disabled command." do |name|
|
15
|
+
group 'Commands'
|
16
|
+
|
17
|
+
banner <<-BANNER
|
18
|
+
Usage: install-command COMMAND
|
19
|
+
|
20
|
+
Installs the gems necessary to run the given COMMAND. You will generally not
|
21
|
+
need to run this unless told to by an error message.
|
22
|
+
BANNER
|
23
|
+
|
24
|
+
def process(name)
|
25
|
+
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
|
26
|
+
command = find_command(name)
|
27
|
+
|
28
|
+
if command_dependencies_met?(command.options)
|
29
|
+
output.puts "Dependencies for #{command.name} are met. Nothing to do."
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
output.puts "Attempting to install `#{name}` command..."
|
34
|
+
gems_to_install = Array(command.options[:requires_gem])
|
35
|
+
|
36
|
+
gems_to_install.each do |g|
|
37
|
+
next if gem_installed?(g)
|
38
|
+
output.puts "Installing `#{g}` gem..."
|
39
|
+
|
40
|
+
begin
|
41
|
+
Gem::DependencyInstaller.new.install(g)
|
42
|
+
rescue Gem::GemNotFoundException
|
43
|
+
raise CommandError, "Required Gem: `#{g}` not found. Aborting command installation."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Gem.refresh
|
48
|
+
gems_to_install.each do |g|
|
49
|
+
begin
|
50
|
+
require g
|
51
|
+
rescue LoadError
|
52
|
+
raise CommandError, "Required Gem: `#{g}` installed but not found?!. Aborting command installation."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
output.puts "Installation of `#{name}` successful! Type `help #{name}` for information"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -1,167 +1,30 @@
|
|
1
1
|
require "pry/default_commands/ls"
|
2
|
+
require "pry/default_commands/cd"
|
2
3
|
|
3
4
|
class Pry
|
4
5
|
module DefaultCommands
|
5
6
|
|
6
7
|
Context = Pry::CommandSet.new do
|
7
8
|
import Ls
|
9
|
+
import Cd
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
banner <<-BANNER
|
13
|
-
Usage: cd [OPTIONS] [--help]
|
14
|
-
|
15
|
-
Move into new context (object or scope). As in unix shells use
|
16
|
-
`cd ..` to go back and `cd /` to return to Pry top-level).
|
17
|
-
Complex syntax (e.g cd ../@x/y) also supported.
|
18
|
-
|
19
|
-
e.g: `cd @x`
|
20
|
-
e.g: `cd ..
|
21
|
-
e.g: `cd /`
|
22
|
-
|
23
|
-
https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope
|
24
|
-
BANNER
|
25
|
-
|
26
|
-
def process
|
27
|
-
path = arg_string.split(/\//)
|
28
|
-
stack = _pry_.binding_stack.dup
|
29
|
-
|
30
|
-
# special case when we only get a single "/", return to root
|
31
|
-
stack = [stack.first] if path.empty?
|
32
|
-
|
33
|
-
path.each do |context|
|
34
|
-
begin
|
35
|
-
case context.chomp
|
36
|
-
when ""
|
37
|
-
stack = [stack.first]
|
38
|
-
when "::"
|
39
|
-
stack.push(TOPLEVEL_BINDING)
|
40
|
-
when "."
|
41
|
-
next
|
42
|
-
when ".."
|
43
|
-
unless stack.size == 1
|
44
|
-
stack.pop
|
45
|
-
end
|
46
|
-
else
|
47
|
-
stack.push(Pry.binding_for(stack.last.eval(context)))
|
48
|
-
end
|
49
|
-
|
50
|
-
rescue RescuableException => e
|
51
|
-
output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
|
52
|
-
output.puts e.inspect
|
53
|
-
return
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
_pry_.binding_stack = stack
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
command "switch-to", "Start a new sub-session on a binding in the current stack (numbered by nesting)." do |selection|
|
62
|
-
selection = selection.to_i
|
63
|
-
|
64
|
-
if selection < 0 || selection > _pry_.binding_stack.size - 1
|
65
|
-
raise CommandError, "Invalid binding index #{selection} - use `nesting` command to view valid indices."
|
66
|
-
else
|
67
|
-
Pry.start(_pry_.binding_stack[selection])
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
command "nesting", "Show nesting information." do
|
72
|
-
output.puts "Nesting status:"
|
73
|
-
output.puts "--"
|
74
|
-
_pry_.binding_stack.each_with_index do |obj, level|
|
75
|
-
if level == 0
|
76
|
-
output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)"
|
77
|
-
else
|
78
|
-
output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}"
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
command "jump-to", "Jump to a binding further up the stack, popping all bindings below." do |break_level|
|
84
|
-
break_level = break_level.to_i
|
85
|
-
nesting_level = _pry_.binding_stack.size - 1
|
86
|
-
|
87
|
-
case break_level
|
88
|
-
when nesting_level
|
89
|
-
output.puts "Already at nesting level #{nesting_level}"
|
90
|
-
when (0...nesting_level)
|
91
|
-
_pry_.binding_stack.slice!(break_level + 1, _pry_.binding_stack.size)
|
92
|
-
|
93
|
-
else
|
94
|
-
max_nest_level = nesting_level - 1
|
95
|
-
output.puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}."
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
command "exit-all", "End the current Pry session (popping all bindings) and returning to caller. Accepts optional return value. Aliases: !!@" do
|
100
|
-
# clear the binding stack
|
101
|
-
_pry_.binding_stack.clear
|
102
|
-
|
103
|
-
# break out of the repl loop
|
104
|
-
throw(:breakout, target.eval(arg_string))
|
105
|
-
end
|
106
|
-
|
107
|
-
alias_command "!!@", "exit-all"
|
108
|
-
|
109
|
-
create_command "exit" do
|
110
|
-
description "Pop the previous binding (does NOT exit program). Type `exit --help` for more information. Aliases: quit"
|
111
|
-
|
112
|
-
banner <<-BANNER
|
113
|
-
Usage: exit [OPTIONS] [--help]
|
114
|
-
Aliases: quit
|
115
|
-
|
116
|
-
It can be useful to exit a context with a user-provided value. For
|
117
|
-
instance an exit value can be used to determine program flow.
|
118
|
-
|
119
|
-
e.g: `exit "pry this"`
|
120
|
-
e.g: `exit`
|
121
|
-
|
122
|
-
https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value
|
123
|
-
BANNER
|
124
|
-
|
125
|
-
command_options(
|
126
|
-
:keep_retval => true
|
127
|
-
)
|
128
|
-
|
129
|
-
def process
|
130
|
-
if _pry_.binding_stack.one?
|
131
|
-
# when breaking out of top-level then behave like `exit-all`
|
132
|
-
process_exit_all
|
133
|
-
else
|
134
|
-
# otherwise just pop a binding and return user supplied value
|
135
|
-
process_pop_and_return
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def process_exit_all
|
140
|
-
_pry_.binding_stack.clear
|
141
|
-
throw(:breakout, target.eval(arg_string))
|
142
|
-
end
|
143
|
-
|
144
|
-
def process_pop_and_return
|
145
|
-
popped_object = _pry_.binding_stack.pop.eval('self')
|
11
|
+
command "whereami", "Show the code context for the session. (whereami <n> shows <n> extra lines of code around the invocation line. Default: 5)" do |num|
|
12
|
+
file, line_num = file_and_line_from_binding(target)
|
13
|
+
i_num = num ? num.to_i : 5
|
146
14
|
|
147
|
-
|
148
|
-
|
149
|
-
popped_object
|
15
|
+
if file != Pry.eval_path && (file =~ /(\(.*\))|<.*>/ || file == "" || file == "-e")
|
16
|
+
raise CommandError, "Cannot find local context. Did you use binding.pry?"
|
150
17
|
end
|
151
|
-
end
|
152
|
-
|
153
|
-
alias_command "quit", "exit"
|
154
18
|
|
155
|
-
|
156
|
-
Pry.save_history if Pry.config.history.should_save
|
157
|
-
Kernel.exit target.eval(arg_string).to_i
|
158
|
-
end
|
19
|
+
set_file_and_dir_locals(file)
|
159
20
|
|
160
|
-
|
161
|
-
|
21
|
+
method = Pry::Method.from_binding(target)
|
22
|
+
method_description = method ? " in #{method.name_with_owner}" : ""
|
23
|
+
output.puts "\n#{text.bold('From:')} #{file} @ line #{line_num}#{method_description}:\n\n"
|
162
24
|
|
163
|
-
|
164
|
-
|
25
|
+
code = Pry::Code.from_file(file).around(line_num, i_num)
|
26
|
+
output.puts code.with_line_numbers.with_marker(line_num)
|
27
|
+
output.puts
|
165
28
|
end
|
166
29
|
|
167
30
|
create_command "pry-backtrace", "Show the backtrace for the Pry session." do
|
@@ -184,24 +47,44 @@ class Pry
|
|
184
47
|
end
|
185
48
|
end
|
186
49
|
|
187
|
-
command "
|
188
|
-
|
189
|
-
|
190
|
-
|
50
|
+
command "reset", "Reset the REPL to a clean state." do
|
51
|
+
output.puts "Pry reset."
|
52
|
+
exec "pry"
|
53
|
+
end
|
191
54
|
|
192
|
-
|
193
|
-
|
194
|
-
end
|
55
|
+
create_command /wtf([?!]*)/, "Show the backtrace of the most recent exception" do
|
56
|
+
options :listing => 'wtf?'
|
195
57
|
|
196
|
-
|
58
|
+
banner <<-BANNER
|
59
|
+
Show's a few lines of the backtrace of the most recent exception (also available
|
60
|
+
as _ex_.backtrace).
|
197
61
|
|
198
|
-
|
199
|
-
method_description = method ? " in #{method.name_with_owner}" : ""
|
200
|
-
output.puts "\n#{text.bold('From:')} #{file} @ line #{line_num}#{method_description}:\n\n"
|
62
|
+
If you want to see more lines, add more question marks or exclamation marks:
|
201
63
|
|
202
|
-
|
203
|
-
|
204
|
-
|
64
|
+
e.g.
|
65
|
+
pry(main)> wtf?
|
66
|
+
pry(main)> wtf?!???!?!?
|
67
|
+
|
68
|
+
To see the entire backtrace, pass the -v/--verbose flag:
|
69
|
+
|
70
|
+
e.g.
|
71
|
+
pry(main)> wtf -v
|
72
|
+
BANNER
|
73
|
+
|
74
|
+
def options(opt)
|
75
|
+
opt.on(:v, :verbose, "Show the full backtrace.")
|
76
|
+
end
|
77
|
+
|
78
|
+
def process
|
79
|
+
raise Pry::CommandError, "No most-recent exception" unless _pry_.last_exception
|
80
|
+
|
81
|
+
output.puts "#{text.bold('Exception:')} #{_pry_.last_exception.class}: #{_pry_.last_exception}\n--"
|
82
|
+
if opts.verbose?
|
83
|
+
output.puts Code.new(_pry_.last_exception.backtrace, 0, :text).with_line_numbers.to_s
|
84
|
+
else
|
85
|
+
output.puts Code.new(_pry_.last_exception.backtrace.first([captures[0].size, 0.5].max * 10), 0, :text).with_line_numbers.to_s
|
86
|
+
end
|
87
|
+
end
|
205
88
|
end
|
206
89
|
|
207
90
|
end
|
@@ -0,0 +1,385 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'pry/default_commands/hist'
|
3
|
+
|
4
|
+
class Pry
|
5
|
+
module DefaultCommands
|
6
|
+
|
7
|
+
Editing = Pry::CommandSet.new do
|
8
|
+
import Hist
|
9
|
+
|
10
|
+
create_command "!", "Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop.", :use_prefix => false do
|
11
|
+
def process
|
12
|
+
output.puts "Input buffer cleared!"
|
13
|
+
eval_string.replace("")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
create_command "show-input", "Show the contents of the input buffer for the current multi-line expression." do
|
18
|
+
def process
|
19
|
+
output.puts Code.new(eval_string).with_line_numbers
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
create_command "edit" do
|
24
|
+
description "Invoke the default editor on a file."
|
25
|
+
|
26
|
+
banner <<-BANNER
|
27
|
+
Usage: edit [--no-reload|--reload] [--line LINE] [--temp|--ex|FILE[:LINE]|--in N]
|
28
|
+
|
29
|
+
Open a text editor. When no FILE is given, edits the pry input buffer.
|
30
|
+
Ensure Pry.config.editor is set to your editor of choice.
|
31
|
+
|
32
|
+
e.g: `edit sample.rb`
|
33
|
+
e.g: `edit sample.rb --line 105`
|
34
|
+
e.g: `edit --ex`
|
35
|
+
|
36
|
+
https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command
|
37
|
+
BANNER
|
38
|
+
|
39
|
+
def options(opt)
|
40
|
+
opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)", :optional => true, :as => Integer
|
41
|
+
opt.on :i, :in, "Open a temporary file containing the Nth line of _in_. N may be a range.", :optional => true, :as => Range, :default => -1..-1
|
42
|
+
opt.on :t, :temp, "Open an empty temporary file"
|
43
|
+
opt.on :l, :line, "Jump to this line in the opened file", true, :as => Integer
|
44
|
+
opt.on :n, :"no-reload", "Don't automatically reload the edited code"
|
45
|
+
opt.on :c, :"current", "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)."
|
46
|
+
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
|
47
|
+
end
|
48
|
+
|
49
|
+
def process
|
50
|
+
if [opts.present?(:ex), opts.present?(:temp), opts.present?(:in), !args.empty?].count(true) > 1
|
51
|
+
raise CommandError, "Only one of --ex, --temp, --in and FILE may be specified."
|
52
|
+
end
|
53
|
+
|
54
|
+
if !opts.present?(:ex) && !opts.present?(:current) && args.empty?
|
55
|
+
# edit of local code, eval'd within pry.
|
56
|
+
process_local_edit
|
57
|
+
else
|
58
|
+
# edit of remote code, eval'd at top-level
|
59
|
+
process_remote_edit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_i
|
64
|
+
case opts[:i]
|
65
|
+
when Range
|
66
|
+
(_pry_.input_array[opts[:i]] || []).join
|
67
|
+
when Fixnum
|
68
|
+
_pry_.input_array[opts[:i]] || ""
|
69
|
+
else
|
70
|
+
return output.puts "Not a valid range: #{opts[:i]}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def process_local_edit
|
75
|
+
content = case
|
76
|
+
when opts.present?(:temp)
|
77
|
+
""
|
78
|
+
when opts.present?(:in)
|
79
|
+
process_i
|
80
|
+
when eval_string.strip != ""
|
81
|
+
eval_string
|
82
|
+
else
|
83
|
+
_pry_.input_array.reverse_each.find{ |x| x && x.strip != "" } || ""
|
84
|
+
end
|
85
|
+
|
86
|
+
line = content.lines.count
|
87
|
+
|
88
|
+
temp_file do |f|
|
89
|
+
f.puts(content)
|
90
|
+
f.flush
|
91
|
+
invoke_editor(f.path, line)
|
92
|
+
if !opts.present?(:'no-reload') && !Pry.config.disable_auto_reload
|
93
|
+
silence_warnings do
|
94
|
+
eval_string.replace(File.read(f.path))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def process_remote_edit
|
101
|
+
if opts.present?(:ex)
|
102
|
+
if _pry_.last_exception.nil?
|
103
|
+
raise CommandError, "No exception found."
|
104
|
+
end
|
105
|
+
|
106
|
+
ex = _pry_.last_exception
|
107
|
+
bt_index = opts[:ex].to_i
|
108
|
+
|
109
|
+
ex_file, ex_line = ex.bt_source_location_for(bt_index)
|
110
|
+
if ex_file && RbxPath.is_core_path?(ex_file)
|
111
|
+
file_name = RbxPath.convert_path_to_full(ex_file)
|
112
|
+
else
|
113
|
+
file_name = ex_file
|
114
|
+
end
|
115
|
+
|
116
|
+
line = ex_line
|
117
|
+
|
118
|
+
if file_name.nil?
|
119
|
+
raise CommandError, "Exception has no associated file."
|
120
|
+
end
|
121
|
+
|
122
|
+
if Pry.eval_path == file_name
|
123
|
+
raise CommandError, "Cannot edit exceptions raised in REPL."
|
124
|
+
end
|
125
|
+
elsif opts.present?(:current)
|
126
|
+
file_name = target.eval("__FILE__")
|
127
|
+
line = target.eval("__LINE__")
|
128
|
+
else
|
129
|
+
|
130
|
+
# break up into file:line
|
131
|
+
file_name = File.expand_path(args.first)
|
132
|
+
line = file_name.sub!(/:(\d+)$/, "") ? $1.to_i : 1
|
133
|
+
end
|
134
|
+
|
135
|
+
if not_a_real_file?(file_name)
|
136
|
+
raise CommandError, "#{file_name} is not a valid file name, cannot edit!"
|
137
|
+
end
|
138
|
+
|
139
|
+
line = opts[:l].to_i if opts.present?(:line)
|
140
|
+
|
141
|
+
invoke_editor(file_name, line)
|
142
|
+
set_file_and_dir_locals(file_name)
|
143
|
+
|
144
|
+
if opts.present?(:reload) || ((opts.present?(:ex) || file_name.end_with?(".rb")) && !opts.present?(:'no-reload')) && !Pry.config.disable_auto_reload
|
145
|
+
silence_warnings do
|
146
|
+
TOPLEVEL_BINDING.eval(File.read(file_name), file_name)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
create_command "edit-method" do
|
153
|
+
description "Edit the source code for a method."
|
154
|
+
|
155
|
+
banner <<-BANNER
|
156
|
+
Usage: edit-method [OPTIONS] [METH]
|
157
|
+
|
158
|
+
Edit the method METH in an editor.
|
159
|
+
Ensure Pry.config.editor is set to your editor of choice.
|
160
|
+
|
161
|
+
e.g: `edit-method hello_method`
|
162
|
+
e.g: `edit-method Pry#rep`
|
163
|
+
e.g: `edit-method`
|
164
|
+
|
165
|
+
https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_method
|
166
|
+
BANNER
|
167
|
+
|
168
|
+
command_options :shellwords => false
|
169
|
+
|
170
|
+
def options(opt)
|
171
|
+
method_options(opt)
|
172
|
+
opt.on :n, "no-reload", "Do not automatically reload the method's file after editing."
|
173
|
+
opt.on "no-jump", "Do not fast forward editor to first line of method."
|
174
|
+
opt.on :p, :patch, "Instead of editing the method's file, try to edit in a tempfile and apply as a monkey patch."
|
175
|
+
end
|
176
|
+
|
177
|
+
def process
|
178
|
+
if !Pry.config.editor
|
179
|
+
raise CommandError, "No editor set!\nEnsure that #{text.bold("Pry.config.editor")} is set to your editor of choice."
|
180
|
+
end
|
181
|
+
|
182
|
+
begin
|
183
|
+
@method = method_object
|
184
|
+
rescue NonMethodContextError => err
|
185
|
+
end
|
186
|
+
|
187
|
+
if opts.present?(:patch) || (@method && @method.dynamically_defined?)
|
188
|
+
if err
|
189
|
+
raise err # can't patch a non-method
|
190
|
+
end
|
191
|
+
|
192
|
+
process_patch
|
193
|
+
else
|
194
|
+
if err && !File.exist?(target.eval('__FILE__'))
|
195
|
+
raise err # can't edit a non-file
|
196
|
+
end
|
197
|
+
|
198
|
+
process_file
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def process_patch
|
203
|
+
lines = @method.source.lines.to_a
|
204
|
+
|
205
|
+
if ((original_name = @method.original_name) &&
|
206
|
+
lines[0] =~ /^def (?:.*?\.)?#{original_name}(?=[\(\s;]|$)/)
|
207
|
+
lines[0] = "def #{original_name}#{$'}"
|
208
|
+
else
|
209
|
+
raise CommandError, "Pry can only patch methods created with the `def` keyword."
|
210
|
+
end
|
211
|
+
|
212
|
+
temp_file do |f|
|
213
|
+
f.puts lines.join
|
214
|
+
f.flush
|
215
|
+
invoke_editor(f.path, 0)
|
216
|
+
|
217
|
+
if @method.alias?
|
218
|
+
with_method_transaction(original_name, @method.owner) do
|
219
|
+
Pry.new(:input => StringIO.new(File.read(f.path))).rep(@method.owner)
|
220
|
+
Pry.binding_for(@method.owner).eval("alias #{@method.name} #{original_name}")
|
221
|
+
end
|
222
|
+
else
|
223
|
+
Pry.new(:input => StringIO.new(File.read(f.path))).rep(@method.owner)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def process_file
|
229
|
+
file, line = extract_file_and_line
|
230
|
+
|
231
|
+
invoke_editor(file, opts["no-jump"] ? 0 : line)
|
232
|
+
silence_warnings do
|
233
|
+
load file unless opts.present?(:'no-reload') || Pry.config.disable_auto_reload
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
protected
|
238
|
+
def extract_file_and_line
|
239
|
+
if @method
|
240
|
+
if @method.source_type == :c
|
241
|
+
raise CommandError, "Can't edit a C method."
|
242
|
+
else
|
243
|
+
[@method.source_file, @method.source_line]
|
244
|
+
end
|
245
|
+
else
|
246
|
+
[target.eval('__FILE__'), target.eval('__LINE__')]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def with_method_transaction(meth_name, target=TOPLEVEL_BINDING)
|
251
|
+
target = Pry.binding_for(target)
|
252
|
+
temp_name = "__pry_#{meth_name}__"
|
253
|
+
|
254
|
+
target.eval("alias #{temp_name} #{meth_name}")
|
255
|
+
yield
|
256
|
+
target.eval("alias #{meth_name} #{temp_name}")
|
257
|
+
ensure
|
258
|
+
target.eval("undef #{temp_name}") rescue nil
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
create_command(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/) do
|
263
|
+
description "Amend a line of input in multi-line mode."
|
264
|
+
command_options :interpolate => false, :listing => "amend-line"
|
265
|
+
|
266
|
+
banner <<-'BANNER'
|
267
|
+
Amend a line of input in multi-line mode. `amend-line N`, where the N in `amend-line N` represents line to replace.
|
268
|
+
|
269
|
+
Can also specify a range of lines using `amend-line N..M` syntax. Passing '!' as replacement content deletes the line(s) instead.
|
270
|
+
e.g amend-line 1 puts 'hello world! # replace line 1'
|
271
|
+
e.g amend-line 1..4 ! # delete lines 1..4
|
272
|
+
e.g amend-line 3 >puts 'goodbye' # insert before line 3
|
273
|
+
e.g amend-line puts 'hello again' # no line number modifies immediately preceding line
|
274
|
+
BANNER
|
275
|
+
|
276
|
+
def process
|
277
|
+
start_line_number, end_line_number, replacement_line = *args
|
278
|
+
|
279
|
+
if eval_string.empty?
|
280
|
+
raise CommandError, "No input to amend."
|
281
|
+
end
|
282
|
+
|
283
|
+
replacement_line = "" if !replacement_line
|
284
|
+
input_array = eval_string.each_line.to_a
|
285
|
+
|
286
|
+
end_line_number = start_line_number.to_i if !end_line_number
|
287
|
+
line_range = start_line_number ? (one_index_number(start_line_number.to_i)..one_index_number(end_line_number.to_i)) : input_array.size - 1
|
288
|
+
|
289
|
+
# delete selected lines if replacement line is '!'
|
290
|
+
if arg_string == "!"
|
291
|
+
input_array.slice!(line_range)
|
292
|
+
elsif arg_string.start_with?(">")
|
293
|
+
insert_slot = Array(line_range).first
|
294
|
+
input_array.insert(insert_slot, arg_string[1..-1] + "\n")
|
295
|
+
else
|
296
|
+
input_array[line_range] = arg_string + "\n"
|
297
|
+
end
|
298
|
+
eval_string.replace input_array.join
|
299
|
+
run "show-input"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
create_command "play" do
|
304
|
+
description "Play back a string variable or a method or a file as input."
|
305
|
+
|
306
|
+
banner <<-BANNER
|
307
|
+
Usage: play [OPTIONS] [--help]
|
308
|
+
|
309
|
+
The play command enables you to replay code from files and methods as
|
310
|
+
if they were entered directly in the Pry REPL. Default action (no
|
311
|
+
options) is to play the provided string variable
|
312
|
+
|
313
|
+
e.g: `play -i 20 --lines 1..3`
|
314
|
+
e.g: `play -m Pry#repl --lines 1..-1`
|
315
|
+
e.g: `play -f Rakefile --lines 5`
|
316
|
+
|
317
|
+
https://github.com/pry/pry/wiki/User-Input#wiki-Play
|
318
|
+
BANNER
|
319
|
+
|
320
|
+
attr_accessor :content
|
321
|
+
|
322
|
+
def setup
|
323
|
+
self.content = ""
|
324
|
+
end
|
325
|
+
|
326
|
+
def options(opt)
|
327
|
+
opt.on :m, :method, "Play a method's source.", true do |meth_name|
|
328
|
+
meth = get_method_or_raise(meth_name, target, {})
|
329
|
+
self.content << meth.source
|
330
|
+
end
|
331
|
+
opt.on :d, :doc, "Play a method's documentation.", true do |meth_name|
|
332
|
+
meth = get_method_or_raise(meth_name, target, {})
|
333
|
+
text.no_color do
|
334
|
+
self.content << process_comment_markup(meth.doc, :ruby)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
opt.on :c, :command, "Play a command's source.", true do |command_name|
|
338
|
+
command = find_command(command_name)
|
339
|
+
block = Pry::Method.new(find_command(command_name).block)
|
340
|
+
self.content << block.source
|
341
|
+
end
|
342
|
+
opt.on :f, :file, "Play a file.", true do |file|
|
343
|
+
self.content << File.read(File.expand_path(file))
|
344
|
+
end
|
345
|
+
opt.on :l, :lines, "Only play a subset of lines.", :optional => true, :as => Range, :default => 1..-1
|
346
|
+
opt.on :i, :in, "Play entries from Pry's input expression history. Takes an index or range.", :optional => true,
|
347
|
+
:as => Range, :default => -5..-1 do |range|
|
348
|
+
input_expressions = _pry_.input_array[range] || []
|
349
|
+
Array(input_expressions).each { |v| self.content << v }
|
350
|
+
end
|
351
|
+
opt.on :o, "open", 'When used with the -m switch, it plays the entire method except the last line, leaving the method definition "open". `amend-line` can then be used to modify the method.'
|
352
|
+
end
|
353
|
+
|
354
|
+
def process
|
355
|
+
perform_play
|
356
|
+
run "show-input" unless _pry_.complete_expression?(eval_string)
|
357
|
+
end
|
358
|
+
|
359
|
+
def process_non_opt
|
360
|
+
args.each do |arg|
|
361
|
+
begin
|
362
|
+
self.content << target.eval(arg)
|
363
|
+
rescue Pry::RescuableException
|
364
|
+
raise CommandError, "Prblem when evaling #{arg}."
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def perform_play
|
370
|
+
process_non_opt
|
371
|
+
|
372
|
+
if opts.present?(:lines)
|
373
|
+
self.content = restrict_to_lines(self.content, opts[:l])
|
374
|
+
end
|
375
|
+
|
376
|
+
if opts.present?(:open)
|
377
|
+
self.content = restrict_to_lines(self.content, 1..-2)
|
378
|
+
end
|
379
|
+
|
380
|
+
eval_string << self.content
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|