pry 0.9.8.2-i386-mswin32 → 0.9.8.3-i386-mswin32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|