forked from pry/pry
/
input.rb
248 lines (198 loc) · 9.02 KB
/
input.rb
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class Pry
module DefaultCommands
Input = Pry::CommandSet.new do
command "!", "Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop.", :use_prefix => false do
output.puts "Input buffer cleared!"
eval_string.replace("")
end
command "show-input", "Show the contents of the input buffer for the current multi-line expression." do
render_output(false, 1, Pry.color ? CodeRay.scan(eval_string, :ruby).term : eval_string)
end
command(/amend-line.?(-?\d+)?(?:\.\.(-?\d+))?/, "Amend a line of input in multi-line mode. Type `amend-line --help` for more information. Aliases %",
:interpolate => false, :listing => "amend-line") do |*args|
start_line_number, end_line_number, replacement_line = *args
opts = Slop.parse!(args.compact) do |opt|
opt.banner %{Amend a line of input in multi-line mode. `amend-line N`, where the N in `amend-line N` represents line to replace.
Can also specify a range of lines using `amend-line N..M` syntax. Passing '!' as replacement content deletes the line(s) instead. Aliases: %N
e.g amend-line 1 puts 'hello world! # replace line 1'
e.g amend-line 1..4 ! # delete lines 1..4
e.g amend-line 3 >puts 'goodbye' # insert before line 3
e.g amend-line puts 'hello again' # no line number modifies immediately preceding line
}
opt.on :h, :help, "This message." do
output.puts opt
end
end
next if opts.h?
next output.puts "No input to amend." if eval_string.empty?
replacement_line = "" if !replacement_line
input_array = eval_string.each_line.to_a
end_line_number = start_line_number.to_i if !end_line_number
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
# delete selected lines if replacement line is '!'
if arg_string == "!"
input_array.slice!(line_range)
elsif arg_string.start_with?(">")
insert_slot = Array(line_range).first
input_array.insert(insert_slot, arg_string[1..-1] + "\n")
else
input_array[line_range] = arg_string + "\n"
end
eval_string.replace input_array.join
run "show-input"
end
alias_command(/%.?(-?\d+)?(?:\.\.(-?\d+))?/, /amend-line.?(-?\d+)?(?:\.\.(-?\d+))?/, "")
command "play", "Play back a string or a method or a file as input. Type `play --help` for more information." do |*args|
opts = Slop.parse!(args) do |opt|
opt.banner "Usage: play [OPTIONS] [--help]\nDefault action (no options) is to play the provided string\ne.g `play puts 'hello world'` #=> \"hello world\"\ne.g `play -m Pry#repl --lines 1..-1`\ne.g `play -f Rakefile --lines 5`"
opt.on :l, :lines, 'The line (or range of lines) to replay.', true, :as => Range
opt.on :m, :method, 'Play a method.', true
opt.on :f, "file", 'The file to replay in context.', true
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.'
opt.on :h, :help, "This message." do
output.puts opt
end
opt.on_noopts { _pry_.input = StringIO.new(arg_string) }
end
if opts.m?
meth_name = opts[:m]
if (meth = get_method_object(meth_name, target, {})).nil?
output.puts "Invalid method name: #{meth_name}."
next
end
code, _ = code_and_code_type_for(meth)
next if !code
range = opts.l? ? one_index_range_or_number(opts[:l]) : (0..-1)
range = (0..-2) if opts.o?
_pry_.input = StringIO.new(Array(code.each_line.to_a[range]).join)
end
if opts.f?
file_name = File.expand_path(opts[:f])
next output.puts "No such file: #{opts[:f]}" if !File.exists?(file_name)
text_array = File.readlines(file_name)
range = opts.l? ? one_index_range_or_number(opts[:l]) : (0..-1)
range = (0..-2) if opts.o?
_pry_.input = StringIO.new(Array(text_array[range]).join)
end
end
command "hist", "Show and replay Readline history. Type `hist --help` for more info. Aliases: history" do |*args|
# exclude the current command from history.
history = Pry.history.to_a[0..-2]
opts = Slop.parse!(args) do |opt|
opt.banner "Usage: hist [--replay START..END] [--clear] [--grep PATTERN] [--head N] [--tail N] [--help] [--save [START..END] file.txt]\n"
opt.on :g, :grep, 'A pattern to match against the history.', true do |pattern|
pattern = Regexp.new arg_string.strip.split(/ /, 2).last.strip
history.pop
history.map!.with_index do |element, index|
if element =~ pattern
"#{text.blue index}: #{element}"
end
end
stagger_output history.compact.join "\n"
end
opt.on :head, 'Display the first N items of history',
:optional => true,
:as => Integer,
:unless => :grep do |limit|
limit ||= 10
list = history.first limit
lines = text.with_line_numbers list.join("\n"), 0
stagger_output lines
end
opt.on :t, :tail, 'Display the last N items of history',
:optional => true,
:as => Integer,
:unless => :grep do |limit|
limit ||= 10
offset = history.size - limit
offset = offset < 0 ? 0 : offset
list = history.last limit
lines = text.with_line_numbers list.join("\n"), offset
stagger_output lines
end
opt.on :s, :show, 'Show the history corresponding to the history line (or range of lines).',
true,
:as => Range,
:unless => :grep do |range|
start_line = range.is_a?(Range) ? range.first : range
lines = text.with_line_numbers Array(history[range]).join("\n"), start_line
stagger_output lines
end
opt.on :e, :exclude, 'Exclude pry commands from the history.', :unless => :grep do
history.map!.with_index do |element, index|
unless command_processor.valid_command? element
"#{text.blue index}: #{element}"
end
end
stagger_output history.compact.join "\n"
end
opt.on :r, :replay, 'The line (or range of lines) to replay.',
true,
:as => Range,
:unless => :grep do |range|
actions = Array(history[range]).join("\n") + "\n"
_pry_.input = StringIO.new(actions)
end
opt.on "save", "Save history to a file. --save [start..end] output.txt. Pry commands are excluded from saved history.", true, :as => Range
opt.on :c, :clear, 'Clear the history', :unless => :grep do
Pry.history.clear
output.puts 'History cleared.'
end
opt.on :h, :help, 'Show this message.', :tail => true, :unless => :grep do
output.puts opt.help
end
opt.on_empty do
lines = text.with_line_numbers history.join("\n"), 0
stagger_output lines
end
end
# FIXME: hack to save history (this must be refactored)
if opts["save"]
file_name = nil
hist_array = nil
case opts["save"]
when Range
hist_array = Array(history[opts["save"]])
next output.puts "Must provide a file name." if !args.first
file_name = File.expand_path(args.first)
when String
hist_array = history
file_name = File.expand_path(opts["save"])
end
output.puts "Saving history in #{file_name} ..."
# exclude pry commands
hist_array.reject! do |element|
command_processor.valid_command?(element)
end
File.open(file_name, 'w') do |f|
f.write hist_array.join("\n")
end
output.puts "... history saved."
end
end
alias_command "history", "hist", ""
helpers do
def one_index_number(line_number)
if line_number > 0
line_number - 1
elsif line_number < 0
line_number
else
line_number
end
end
def one_index_range(range)
Range.new(one_index_number(range.begin), one_index_number(range.end))
end
def one_index_range_or_number(range_or_number)
case range_or_number
when Range
one_index_range(range_or_number)
else
one_index_number(range_or_number)
end
end
end
end
end
end