1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
class Pry
class Command::Edit < Pry::ClassCommand
require 'pry/commands/edit/exception_patcher'
require 'pry/commands/edit/file_and_line_locator'
match 'edit'
group 'Editing'
description 'Invoke the default editor on a file.'
banner <<-'BANNER'
Usage: edit [--no-reload|--reload|--patch] [--line LINE] [--temp|--ex|FILE[:LINE]|OBJECT|--in N]
Open a text editor. When no FILE is given, edits the pry input buffer.
When a method/module/command is given, the code is opened in an editor.
Ensure `Pry.config.editor` or `_pry_.config.editor` is set to your editor of choice.
edit sample.rb edit -p MyClass#my_method
edit sample.rb --line 105 edit MyClass
edit MyClass#my_method edit --ex
edit --method edit --ex -p
https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command
BANNER
def options(opt)
opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)",
optional_argument: true, as: Integer
opt.on :i, :in, "Open a temporary file containing the Nth input expression. N may be a range",
optional_argument: true, as: Range, default: -1..-1
opt.on :t, :temp, "Open an empty temporary file"
opt.on :l, :line, "Jump to this line in the opened file",
argument: true, as: Integer
opt.on :n, :"no-reload", "Don't automatically reload the edited file"
opt.on :c, :current, "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)"
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
opt.on :p, :patch, "Instead of editing the object's file, try to edit in a tempfile and apply as a monkey patch"
opt.on :m, :method, "Explicitly edit the _current_ method (when inside a method context)."
end
def process
if bad_option_combination?
raise CommandError, "Only one of --ex, --temp, --in, --method and FILE may be specified."
end
if repl_edit?
# code defined in pry, eval'd within pry.
repl_edit
elsif runtime_patch?
# patch code without persisting changes, implies future changes are patches
apply_runtime_patch
else
# code stored in actual files, eval'd at top-level
file_edit
end
end
def repl_edit?
!opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) &&
filename_argument.empty?
end
def repl_edit
content = Pry::Editor.new(_pry_).edit_tempfile_with_content(initial_temp_file_content,
initial_temp_file_content.lines.count)
silence_warnings do
eval_string.replace content
end
Pry.history.push(content)
end
def file_based_exception?
opts.present?(:ex) && !opts.present?(:patch)
end
def runtime_patch?
!file_based_exception? &&
(opts.present?(:patch) ||
previously_patched?(code_object) ||
pry_method?(code_object))
end
def apply_runtime_patch
if patch_exception?
ExceptionPatcher.new(_pry_, state, file_and_line_for_current_exception).perform_patch
else
if code_object.is_a?(Pry::Method)
code_object.redefine Pry::Editor.new(_pry_).edit_tempfile_with_content(code_object.source)
else
raise NotImplementedError, "Cannot yet patch #{code_object} objects!"
end
end
end
def ensure_file_name_is_valid(file_name)
raise CommandError, "Cannot find a valid file for #{filename_argument}" if !file_name
raise CommandError, "#{file_name} is not a valid file name, cannot edit!" if not_a_real_file?(file_name)
end
def file_and_line_for_current_exception
FileAndLineLocator.from_exception(_pry_.last_exception, opts[:ex].to_i)
end
def file_and_line
file_name, line = if opts.present?(:current)
FileAndLineLocator.from_binding(target)
elsif opts.present?(:ex)
file_and_line_for_current_exception
elsif code_object
FileAndLineLocator.from_code_object(code_object, filename_argument)
else
# when file and line are passed as a single arg, e.g my_file.rb:30
FileAndLineLocator.from_filename_argument(filename_argument)
end
[file_name, opts.present?(:line) ? opts[:l].to_i : line]
end
def file_edit
file_name, line = file_and_line
ensure_file_name_is_valid(file_name)
Pry::Editor.new(_pry_).invoke_editor(file_name, line, reload?(file_name))
set_file_and_dir_locals(file_name)
if reload?(file_name)
silence_warnings do
load file_name
end
end
end
def filename_argument
args.join(' ')
end
def code_object
@code_object ||=
!probably_a_file?(filename_argument) &&
Pry::CodeObject.lookup(filename_argument, _pry_)
end
def pry_method?(code_object)
code_object.is_a?(Pry::Method) &&
code_object.pry_method?
end
def previously_patched?(code_object)
code_object.is_a?(Pry::Method) && Pry::Method::Patcher.code_for(code_object.source_location.first)
end
def patch_exception?
opts.present?(:ex) && opts.present?(:patch)
end
def bad_option_combination?
[opts.present?(:ex), opts.present?(:temp),
opts.present?(:in), opts.present?(:method), !filename_argument.empty?].count(true) > 1
end
def input_expression
case opts[:i]
when Range
(_pry_.input_ring[opts[:i]] || []).join
when Integer
_pry_.input_ring[opts[:i]] || ""
else
raise Pry::CommandError, "Not a valid range: #{opts[:i]}"
end
end
def reloadable?
opts.present?(:reload) || opts.present?(:ex)
end
def never_reload?
opts.present?(:'no-reload') || _pry_.config.disable_auto_reload
end
def reload?(file_name = "")
(reloadable? || file_name.end_with?(".rb")) && !never_reload?
end
def initial_temp_file_content
case
when opts.present?(:temp)
""
when opts.present?(:in)
input_expression
when eval_string.strip != ""
eval_string
else
_pry_.input_ring.to_a.reverse_each.find { |x| x && x.strip != "" } || ""
end
end
def probably_a_file?(str)
[".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) ||
str =~ /\/|\\/
end
end
Pry::Commands.add_command(Pry::Command::Edit)
end
|