pry 0.9.8.2 → 0.9.8.3
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,127 @@
|
|
1
|
+
class Pry
|
2
|
+
module DefaultCommands
|
3
|
+
Help = Pry::CommandSet.new do
|
4
|
+
create_command "help" do |cmd|
|
5
|
+
description "Show a list of commands. Type `help <foo>` for information about <foo>."
|
6
|
+
|
7
|
+
banner <<-BANNER
|
8
|
+
Usage: help [ COMMAND ]
|
9
|
+
|
10
|
+
With no arguments, help lists all the available commands in the current
|
11
|
+
command-set along with their description.
|
12
|
+
|
13
|
+
When given a command name as an argument, shows the help for that command.
|
14
|
+
BANNER
|
15
|
+
|
16
|
+
# We only want to show commands that have descriptions, so that the
|
17
|
+
# easter eggs don't show up.
|
18
|
+
def visible_commands
|
19
|
+
visible = {}
|
20
|
+
commands.each do |key, command|
|
21
|
+
visible[key] = command if command.description && !command.description.empty?
|
22
|
+
end
|
23
|
+
visible
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get a hash of available commands grouped by the "group" name.
|
27
|
+
def command_groups
|
28
|
+
visible_commands.values.group_by(&:group)
|
29
|
+
end
|
30
|
+
|
31
|
+
def process
|
32
|
+
if args.empty?
|
33
|
+
display_index(command_groups)
|
34
|
+
else
|
35
|
+
display_search(args.first)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Display the index view, with headings and short descriptions per command.
|
40
|
+
#
|
41
|
+
# @param Hash[String => Array[Commands]]
|
42
|
+
def display_index(groups)
|
43
|
+
help_text = []
|
44
|
+
|
45
|
+
groups.keys.sort_by(&method(:group_sort_key)).each do |key|
|
46
|
+
commands = groups[key].sort_by{ |command| command.options[:listing].to_s }
|
47
|
+
|
48
|
+
unless commands.empty?
|
49
|
+
help_text << "#{text.bold(key)}\n" + commands.map do |command|
|
50
|
+
" #{command.options[:listing].to_s.ljust(18)} #{command.description}"
|
51
|
+
end.join("\n")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
stagger_output(help_text.join("\n\n"))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Display help for an individual command or group.
|
59
|
+
#
|
60
|
+
# @param String The string to search for.
|
61
|
+
def display_search(search)
|
62
|
+
if command = command_set.find_command_for_help(search)
|
63
|
+
display_command(command)
|
64
|
+
else
|
65
|
+
groups = search_hash(search, command_groups)
|
66
|
+
|
67
|
+
if groups.size > 0
|
68
|
+
display_index(groups)
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
filtered = search_hash(search, visible_commands)
|
73
|
+
raise CommandError, "No help found for '#{args.first}'" if filtered.empty?
|
74
|
+
|
75
|
+
if filtered.size == 1
|
76
|
+
display_command(filtered.values.first)
|
77
|
+
else
|
78
|
+
display_index({"'#{search}' commands" => filtered.values})
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Display help for an individual command.
|
84
|
+
#
|
85
|
+
# @param [Pry::Command]
|
86
|
+
def display_command(command)
|
87
|
+
stagger_output command.new.help
|
88
|
+
end
|
89
|
+
|
90
|
+
# Find a subset of a hash that matches the user's search term.
|
91
|
+
#
|
92
|
+
# If there's an exact match a Hash of one element will be returned,
|
93
|
+
# otherwise a sub-Hash with every key that matches the search will
|
94
|
+
# be returned.
|
95
|
+
#
|
96
|
+
# @param [String] the search term
|
97
|
+
# @param [Hash] the hash to search
|
98
|
+
def search_hash(search, hash)
|
99
|
+
matching = {}
|
100
|
+
|
101
|
+
hash.each_pair do |key, value|
|
102
|
+
next unless key.is_a?(String)
|
103
|
+
if normalize(key) == normalize(search)
|
104
|
+
return {key => value}
|
105
|
+
elsif normalize(key).start_with?(normalize(search))
|
106
|
+
matching[key] = value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
matching
|
111
|
+
end
|
112
|
+
|
113
|
+
# Clean search terms to make it easier to search group names
|
114
|
+
#
|
115
|
+
# @param String
|
116
|
+
# @return String
|
117
|
+
def normalize(key)
|
118
|
+
key.downcase.gsub(/pry\W+/, '')
|
119
|
+
end
|
120
|
+
|
121
|
+
def group_sort_key(group_name)
|
122
|
+
[%w(Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands).index(group_name.gsub(' ', '_')) || 99, group_name]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
class Pry
|
2
|
+
module DefaultCommands
|
3
|
+
Hist = Pry::CommandSet.new do
|
4
|
+
|
5
|
+
create_command "hist", "Show and replay Readline history. Aliases: history" do
|
6
|
+
group "Editing"
|
7
|
+
banner <<-USAGE
|
8
|
+
Usage: hist
|
9
|
+
hist --head N
|
10
|
+
hist --tail N
|
11
|
+
hist --show START..END
|
12
|
+
hist --grep PATTERN
|
13
|
+
hist --clear
|
14
|
+
hist --replay START..END
|
15
|
+
hist --save [START..END] FILE
|
16
|
+
USAGE
|
17
|
+
|
18
|
+
def options(opt)
|
19
|
+
opt.on :H, :head, "Display the first N items.", :optional => true, :as => Integer
|
20
|
+
opt.on :T, :tail, "Display the last N items.", :optional => true, :as => Integer
|
21
|
+
opt.on :s, :show, "Show the given range of lines.", :optional => true, :as => Range
|
22
|
+
opt.on :G, :grep, "Show lines matching the given pattern.", true, :as => String
|
23
|
+
opt.on :c, :clear, "Clear the current session's history."
|
24
|
+
opt.on :r, :replay, "Replay a line or range of lines.", true, :as => Range
|
25
|
+
opt.on :save, "Save history to a file.", true, :as => Range
|
26
|
+
|
27
|
+
opt.on :e, :'exclude-pry', "Exclude Pry commands from the history."
|
28
|
+
opt.on :n, :'no-numbers', "Omit line numbers."
|
29
|
+
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
30
|
+
end
|
31
|
+
|
32
|
+
def process
|
33
|
+
@history = Pry::Code(Pry.history.to_a)
|
34
|
+
|
35
|
+
@history = case
|
36
|
+
when opts.present?(:head)
|
37
|
+
@history.between(1, opts[:head] || 10)
|
38
|
+
when opts.present?(:tail)
|
39
|
+
@history.between(-(opts[:tail] || 10), -1)
|
40
|
+
when opts.present?(:show)
|
41
|
+
@history.between(opts[:show])
|
42
|
+
else
|
43
|
+
@history
|
44
|
+
end
|
45
|
+
|
46
|
+
if opts.present?(:grep)
|
47
|
+
@history = @history.grep(opts[:grep])
|
48
|
+
end
|
49
|
+
|
50
|
+
if opts.present?(:'exclude-pry')
|
51
|
+
@history = @history.select { |l, ln| !command_set.valid_command?(l) }
|
52
|
+
end
|
53
|
+
|
54
|
+
if opts.present?(:save)
|
55
|
+
process_save
|
56
|
+
elsif opts.present?(:clear)
|
57
|
+
process_clear
|
58
|
+
elsif opts.present?(:replay)
|
59
|
+
process_replay
|
60
|
+
else
|
61
|
+
process_display
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def process_display
|
66
|
+
unless opts.present?(:'no-numbers')
|
67
|
+
@history = @history.with_line_numbers
|
68
|
+
end
|
69
|
+
|
70
|
+
render_output(@history, opts)
|
71
|
+
end
|
72
|
+
|
73
|
+
def process_save
|
74
|
+
case opts[:save]
|
75
|
+
when Range
|
76
|
+
@history = @history.between(opts[:save])
|
77
|
+
|
78
|
+
unless args.first
|
79
|
+
raise CommandError, "Must provide a file name."
|
80
|
+
end
|
81
|
+
|
82
|
+
file_name = File.expand_path(args.first)
|
83
|
+
when String
|
84
|
+
file_name = File.expand_path(opts[:save])
|
85
|
+
end
|
86
|
+
|
87
|
+
output.puts "Saving history in #{file_name}..."
|
88
|
+
|
89
|
+
File.open(file_name, 'w') { |f| f.write(@history.to_s) }
|
90
|
+
|
91
|
+
output.puts "History saved."
|
92
|
+
end
|
93
|
+
|
94
|
+
def process_clear
|
95
|
+
Pry.history.clear
|
96
|
+
output.puts "History cleared."
|
97
|
+
end
|
98
|
+
|
99
|
+
def process_replay
|
100
|
+
@history = @history.between(opts[:r])
|
101
|
+
|
102
|
+
_pry_.input_stack.push _pry_.input
|
103
|
+
_pry_.input = StringIO.new(@history.raw)
|
104
|
+
# eval_string << "#{@history.raw}\n"
|
105
|
+
# run "show-input" unless _pry_.complete_expression?(eval_string)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
alias_command "history", "hist"
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
1
3
|
class Pry
|
2
4
|
module DefaultCommands
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
command(/\.(.*)/, "All text following a '.' is forwarded to the shell.", :listing => ".<shell command>", :use_prefix => false) do |cmd|
|
6
|
+
InputAndOutput = Pry::CommandSet.new do
|
7
|
+
command(/\.(.*)/, "All text following a '.' is forwarded to the shell.", :listing => ".<shell command>", :use_prefix => false, :takes_block => true) do |cmd|
|
7
8
|
if cmd =~ /^cd\s+(.+)/i
|
8
9
|
dest = $1
|
9
10
|
begin
|
@@ -12,7 +13,13 @@ class Pry
|
|
12
13
|
raise CommandError, "No such directory: #{dest}"
|
13
14
|
end
|
14
15
|
else
|
15
|
-
|
16
|
+
pass_block(cmd)
|
17
|
+
|
18
|
+
if command_block
|
19
|
+
command_block.call `#{cmd}`
|
20
|
+
else
|
21
|
+
Pry.config.system.call(output, cmd, _pry_)
|
22
|
+
end
|
16
23
|
end
|
17
24
|
end
|
18
25
|
|
@@ -30,6 +37,122 @@ class Pry
|
|
30
37
|
end
|
31
38
|
alias_command "file-mode", "shell-mode"
|
32
39
|
|
40
|
+
create_command "gist", "Gist a method or expression history to github.", :requires_gem => "gist", :shellwords => false do
|
41
|
+
banner <<-USAGE
|
42
|
+
Usage: gist [OPTIONS] [METH]
|
43
|
+
Gist method (doc or source) or input expression to github.
|
44
|
+
Ensure the `gist` gem is properly working before use. http://github.com/defunkt/gist for instructions.
|
45
|
+
e.g: gist -m my_method
|
46
|
+
e.g: gist -d my_method
|
47
|
+
e.g: gist -i 1..10
|
48
|
+
e.g: gist -c show-method
|
49
|
+
e.g: gist -m hello_world --lines 2..-2
|
50
|
+
USAGE
|
51
|
+
|
52
|
+
attr_accessor :content
|
53
|
+
attr_accessor :code_type
|
54
|
+
|
55
|
+
def setup
|
56
|
+
require 'gist'
|
57
|
+
self.content = ""
|
58
|
+
self.code_type = :ruby
|
59
|
+
end
|
60
|
+
|
61
|
+
def options(opt)
|
62
|
+
opt.on :m, :method, "Gist a method's source.", true do |meth_name|
|
63
|
+
meth = get_method_or_raise(meth_name, target, {})
|
64
|
+
self.content << meth.source
|
65
|
+
self.code_type = meth.source_type
|
66
|
+
end
|
67
|
+
opt.on :d, :doc, "Gist a method's documentation.", true do |meth_name|
|
68
|
+
meth = get_method_or_raise(meth_name, target, {})
|
69
|
+
text.no_color do
|
70
|
+
self.content << process_comment_markup(meth.doc, self.code_type)
|
71
|
+
end
|
72
|
+
self.code_type = :plain
|
73
|
+
end
|
74
|
+
opt.on :c, :command, "Gist a command's source.", true do |command_name|
|
75
|
+
command = find_command(command_name)
|
76
|
+
block = Pry::Method.new(find_command(command_name).block)
|
77
|
+
self.content << block.source
|
78
|
+
end
|
79
|
+
opt.on :f, :file, "Gist a file.", true do |file|
|
80
|
+
self.content << File.read(File.expand_path(file))
|
81
|
+
end
|
82
|
+
opt.on :p, :public, "Create a public gist (default: false)", :default => false
|
83
|
+
opt.on :l, :lines, "Only gist a subset of lines.", :optional => true, :as => Range, :default => 1..-1
|
84
|
+
opt.on :i, :in, "Gist entries from Pry's input expression history. Takes an index or range.", :optional => true,
|
85
|
+
:as => Range, :default => -5..-1 do |range|
|
86
|
+
range = convert_to_range(range)
|
87
|
+
input_expressions = _pry_.input_array[range] || []
|
88
|
+
Array(input_expressions).each_with_index do |code, index|
|
89
|
+
corrected_index = index + range.first
|
90
|
+
if code && code != ""
|
91
|
+
self.content << code
|
92
|
+
if code !~ /;\Z/
|
93
|
+
self.content << "#{comment_expression_result_for_gist(Pry.config.gist.inspecter.call(_pry_.output_array[corrected_index]))}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def process
|
101
|
+
perform_gist
|
102
|
+
end
|
103
|
+
|
104
|
+
def perform_gist
|
105
|
+
type_map = { :ruby => "rb", :c => "c", :plain => "plain" }
|
106
|
+
|
107
|
+
if self.content =~ /\A\s*\z/
|
108
|
+
raise CommandError, "Found no code to gist."
|
109
|
+
end
|
110
|
+
|
111
|
+
# prevent Gist from exiting the session on error
|
112
|
+
begin
|
113
|
+
extname = opts.present?(:file) ? ".#{gist_file_extension(opts[:f])}" : ".#{type_map[self.code_type]}"
|
114
|
+
|
115
|
+
if opts.present?(:lines)
|
116
|
+
self.content = restrict_to_lines(content, opts[:l])
|
117
|
+
end
|
118
|
+
|
119
|
+
link = Gist.write([:extension => extname,
|
120
|
+
:input => self.content],
|
121
|
+
!opts[:p])
|
122
|
+
rescue SystemExit
|
123
|
+
end
|
124
|
+
|
125
|
+
if link
|
126
|
+
Gist.copy(link)
|
127
|
+
output.puts "Gist created at #{link} and added to clipboard."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def gist_file_extension(file_name)
|
132
|
+
file_name.split(".").last
|
133
|
+
end
|
134
|
+
|
135
|
+
def convert_to_range(n)
|
136
|
+
if !n.is_a?(Range)
|
137
|
+
(n..n)
|
138
|
+
else
|
139
|
+
n
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def comment_expression_result_for_gist(result)
|
144
|
+
content = ""
|
145
|
+
result.lines.each_with_index do |line, index|
|
146
|
+
if index == 0
|
147
|
+
content << "# => #{line}"
|
148
|
+
else
|
149
|
+
content << "# #{line}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
content
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
33
156
|
create_command "save-file", "Export to a file using content from the REPL." do
|
34
157
|
banner <<-USAGE
|
35
158
|
Usage: save-file [OPTIONS] [FILE]
|
@@ -130,13 +253,13 @@ class Pry
|
|
130
253
|
|
131
254
|
def process
|
132
255
|
handler = case
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
256
|
+
when opts.present?(:ex)
|
257
|
+
method :process_ex
|
258
|
+
when opts.present?(:in)
|
259
|
+
method :process_in
|
260
|
+
else
|
261
|
+
method :process_file
|
262
|
+
end
|
140
263
|
|
141
264
|
output = handler.call do |code|
|
142
265
|
code.code_type = opts[:type] || :ruby
|
@@ -183,8 +306,8 @@ class Pry
|
|
183
306
|
HEADER
|
184
307
|
|
185
308
|
code = yield(Pry::Code.from_file(ex_file).
|
186
|
-
|
187
|
-
|
309
|
+
between(start_line, end_line).
|
310
|
+
with_marker(ex_line))
|
188
311
|
|
189
312
|
"#{header}#{code}"
|
190
313
|
end
|
@@ -233,8 +356,7 @@ class Pry
|
|
233
356
|
code
|
234
357
|
end
|
235
358
|
end
|
236
|
-
end
|
237
359
|
|
360
|
+
end
|
238
361
|
end
|
239
362
|
end
|
240
|
-
|
@@ -5,8 +5,80 @@ class Pry
|
|
5
5
|
|
6
6
|
Introspection = Pry::CommandSet.new do
|
7
7
|
|
8
|
+
create_command "show-doc", "Show the comments above METH. Aliases: \?", :shellwords => false do |*args|
|
9
|
+
banner <<-BANNER
|
10
|
+
Usage: show-doc [OPTIONS] [METH]
|
11
|
+
Show the comments above method METH. Tries instance methods first and then methods by default.
|
12
|
+
e.g show-doc hello_method
|
13
|
+
BANNER
|
14
|
+
|
15
|
+
def options(opt)
|
16
|
+
method_options(opt)
|
17
|
+
opt.on :l, "line-numbers", "Show line numbers."
|
18
|
+
opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)."
|
19
|
+
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
20
|
+
end
|
21
|
+
|
22
|
+
def process
|
23
|
+
meth = method_object
|
24
|
+
raise Pry::CommandError, "No documentation found." if meth.doc.nil? || meth.doc.empty?
|
25
|
+
|
26
|
+
doc = process_comment_markup(meth.doc, meth.source_type)
|
27
|
+
output.puts make_header(meth, doc)
|
28
|
+
output.puts "#{text.bold("Owner:")} #{meth.owner || "N/A"}"
|
29
|
+
output.puts "#{text.bold("Visibility:")} #{meth.visibility}"
|
30
|
+
output.puts "#{text.bold("Signature:")} #{meth.signature}"
|
31
|
+
output.puts
|
32
|
+
|
33
|
+
if opts.present?(:b) || opts.present?(:l)
|
34
|
+
doc = Code.new(doc, start_line, :text).
|
35
|
+
with_line_numbers(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
render_output(doc, opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def start_line
|
42
|
+
if opts.present?(:'base-one')
|
43
|
+
1
|
44
|
+
else
|
45
|
+
(method_object.source_line - method_object.doc.lines.count) || 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_command "?", "show-doc"
|
52
|
+
|
53
|
+
create_command "stat", "View method information and set _file_ and _dir_ locals.", :shellwords => false do |*args|
|
54
|
+
banner <<-BANNER
|
55
|
+
Usage: stat [OPTIONS] [METH]
|
56
|
+
Show method information for method METH and set _file_ and _dir_ locals.
|
57
|
+
e.g: stat hello_method
|
58
|
+
BANNER
|
59
|
+
|
60
|
+
def options(opt)
|
61
|
+
method_options(opt)
|
62
|
+
end
|
63
|
+
|
64
|
+
def process
|
65
|
+
meth = method_object
|
66
|
+
output.puts unindent <<-EOS
|
67
|
+
Method Information:
|
68
|
+
--
|
69
|
+
Name: #{meth.name}
|
70
|
+
Owner: #{meth.owner ? meth.owner : "Unknown"}
|
71
|
+
Visibility: #{meth.visibility}
|
72
|
+
Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"}
|
73
|
+
Arity: #{meth.arity}
|
74
|
+
Method Signature: #{meth.signature}
|
75
|
+
Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."}
|
76
|
+
EOS
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
8
80
|
create_command "show-method" do
|
9
|
-
description "Show the source for METH.
|
81
|
+
description "Show the source for METH. Aliases: $, show-source"
|
10
82
|
|
11
83
|
banner <<-BANNER
|
12
84
|
Usage: show-method [OPTIONS] [METH]
|
@@ -62,7 +134,7 @@ class Pry
|
|
62
134
|
alias_command "show-source", "show-method"
|
63
135
|
alias_command "$", "show-method"
|
64
136
|
|
65
|
-
command "show-command", "Show the source for CMD.
|
137
|
+
command "show-command", "Show the source for CMD." do |*args|
|
66
138
|
target = target()
|
67
139
|
|
68
140
|
opts = Slop.parse!(args) do |opt|
|
@@ -103,244 +175,19 @@ class Pry
|
|
103
175
|
end
|
104
176
|
end
|
105
177
|
|
106
|
-
create_command "
|
107
|
-
description "Invoke the default editor on a file. Type `edit --help` for more info"
|
108
|
-
|
178
|
+
create_command "ri", "View ri documentation. e.g `ri Array#each`" do
|
109
179
|
banner <<-BANNER
|
110
|
-
Usage:
|
111
|
-
|
112
|
-
Open a text editor. When no FILE is given, edits the pry input buffer.
|
113
|
-
Ensure Pry.config.editor is set to your editor of choice.
|
114
|
-
|
115
|
-
e.g: `edit sample.rb`
|
116
|
-
e.g: `edit sample.rb --line 105`
|
117
|
-
e.g: `edit --ex`
|
180
|
+
Usage: ri [spec]
|
181
|
+
e.g. ri Array#each
|
118
182
|
|
119
|
-
|
183
|
+
Relies on the ri executable being available. See also: show-doc.
|
120
184
|
BANNER
|
121
185
|
|
122
|
-
def options(opt)
|
123
|
-
opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)", :optional => true, :as => Integer
|
124
|
-
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
|
125
|
-
opt.on :t, :temp, "Open an empty temporary file"
|
126
|
-
opt.on :l, :line, "Jump to this line in the opened file", true, :as => Integer
|
127
|
-
opt.on :n, :"no-reload", "Don't automatically reload the edited code"
|
128
|
-
opt.on :c, :"current", "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)."
|
129
|
-
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
|
130
|
-
end
|
131
|
-
|
132
186
|
def process
|
133
|
-
|
134
|
-
raise CommandError, "Only one of --ex, --temp, --in and FILE may be specified."
|
135
|
-
end
|
136
|
-
|
137
|
-
if !opts.present?(:ex) && !opts.present?(:current) && args.empty?
|
138
|
-
# edit of local code, eval'd within pry.
|
139
|
-
process_local_edit
|
140
|
-
else
|
141
|
-
# edit of remote code, eval'd at top-level
|
142
|
-
process_remote_edit
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def process_i
|
147
|
-
case opts[:i]
|
148
|
-
when Range
|
149
|
-
(_pry_.input_array[opts[:i]] || []).join
|
150
|
-
when Fixnum
|
151
|
-
_pry_.input_array[opts[:i]] || ""
|
152
|
-
else
|
153
|
-
return output.puts "Not a valid range: #{opts[:i]}"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def process_local_edit
|
158
|
-
content = case
|
159
|
-
when opts.present?(:temp)
|
160
|
-
""
|
161
|
-
when opts.present?(:in)
|
162
|
-
process_i
|
163
|
-
when eval_string.strip != ""
|
164
|
-
eval_string
|
165
|
-
else
|
166
|
-
_pry_.input_array.reverse_each.find{ |x| x && x.strip != "" } || ""
|
167
|
-
end
|
168
|
-
|
169
|
-
line = content.lines.count
|
170
|
-
|
171
|
-
temp_file do |f|
|
172
|
-
f.puts(content)
|
173
|
-
f.flush
|
174
|
-
invoke_editor(f.path, line)
|
175
|
-
if !opts.present?(:'no-reload') && !Pry.config.disable_auto_reload
|
176
|
-
silence_warnings do
|
177
|
-
eval_string.replace(File.read(f.path))
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def process_remote_edit
|
184
|
-
if opts.present?(:ex)
|
185
|
-
if _pry_.last_exception.nil?
|
186
|
-
raise CommandError, "No exception found."
|
187
|
-
end
|
188
|
-
|
189
|
-
ex = _pry_.last_exception
|
190
|
-
bt_index = opts[:ex].to_i
|
191
|
-
|
192
|
-
ex_file, ex_line = ex.bt_source_location_for(bt_index)
|
193
|
-
if ex_file && RbxPath.is_core_path?(ex_file)
|
194
|
-
file_name = RbxPath.convert_path_to_full(ex_file)
|
195
|
-
else
|
196
|
-
file_name = ex_file
|
197
|
-
end
|
198
|
-
|
199
|
-
line = ex_line
|
200
|
-
|
201
|
-
if file_name.nil?
|
202
|
-
raise CommandError, "Exception has no associated file."
|
203
|
-
end
|
204
|
-
|
205
|
-
if Pry.eval_path == file_name
|
206
|
-
raise CommandError, "Cannot edit exceptions raised in REPL."
|
207
|
-
end
|
208
|
-
elsif opts.present?(:current)
|
209
|
-
file_name = target.eval("__FILE__")
|
210
|
-
line = target.eval("__LINE__")
|
211
|
-
else
|
212
|
-
|
213
|
-
# break up into file:line
|
214
|
-
file_name = File.expand_path(args.first)
|
215
|
-
line = file_name.sub!(/:(\d+)$/, "") ? $1.to_i : 1
|
216
|
-
end
|
217
|
-
|
218
|
-
if not_a_real_file?(file_name)
|
219
|
-
raise CommandError, "#{file_name} is not a valid file name, cannot edit!"
|
220
|
-
end
|
221
|
-
|
222
|
-
line = opts[:l].to_i if opts.present?(:line)
|
223
|
-
|
224
|
-
invoke_editor(file_name, line)
|
225
|
-
set_file_and_dir_locals(file_name)
|
226
|
-
|
227
|
-
if opts.present?(:reload) || ((opts.present?(:ex) || file_name.end_with?(".rb")) && !opts.present?(:'no-reload')) && !Pry.config.disable_auto_reload
|
228
|
-
silence_warnings do
|
229
|
-
TOPLEVEL_BINDING.eval(File.read(file_name), file_name)
|
230
|
-
end
|
231
|
-
end
|
187
|
+
run ".ri", *args
|
232
188
|
end
|
233
189
|
end
|
234
190
|
|
235
|
-
create_command "edit-method" do
|
236
|
-
description "Edit a method. Type `edit-method --help` for more info."
|
237
|
-
|
238
|
-
banner <<-BANNER
|
239
|
-
Usage: edit-method [OPTIONS] [METH]
|
240
|
-
|
241
|
-
Edit the method METH in an editor.
|
242
|
-
Ensure Pry.config.editor is set to your editor of choice.
|
243
|
-
|
244
|
-
e.g: `edit-method hello_method`
|
245
|
-
e.g: `edit-method Pry#rep`
|
246
|
-
e.g: `edit-method`
|
247
|
-
|
248
|
-
https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_method
|
249
|
-
BANNER
|
250
|
-
|
251
|
-
command_options :shellwords => false
|
252
|
-
|
253
|
-
def options(opt)
|
254
|
-
method_options(opt)
|
255
|
-
opt.on :n, "no-reload", "Do not automatically reload the method's file after editing."
|
256
|
-
opt.on "no-jump", "Do not fast forward editor to first line of method."
|
257
|
-
opt.on :p, :patch, "Instead of editing the method's file, try to edit in a tempfile and apply as a monkey patch."
|
258
|
-
end
|
259
|
-
|
260
|
-
def process
|
261
|
-
if !Pry.config.editor
|
262
|
-
raise CommandError, "No editor set!\nEnsure that #{text.bold("Pry.config.editor")} is set to your editor of choice."
|
263
|
-
end
|
264
|
-
|
265
|
-
begin
|
266
|
-
@method = method_object
|
267
|
-
rescue NonMethodContextError => err
|
268
|
-
end
|
269
|
-
|
270
|
-
if opts.present?(:patch) || (@method && @method.dynamically_defined?)
|
271
|
-
if err
|
272
|
-
raise err # can't patch a non-method
|
273
|
-
end
|
274
|
-
|
275
|
-
process_patch
|
276
|
-
else
|
277
|
-
if err && !File.exist?(target.eval('__FILE__'))
|
278
|
-
raise err # can't edit a non-file
|
279
|
-
end
|
280
|
-
|
281
|
-
process_file
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
def process_patch
|
286
|
-
lines = @method.source.lines.to_a
|
287
|
-
|
288
|
-
if ((original_name = @method.original_name) &&
|
289
|
-
lines[0] =~ /^def (?:.*?\.)?#{original_name}(?=[\(\s;]|$)/)
|
290
|
-
lines[0] = "def #{original_name}#{$'}"
|
291
|
-
else
|
292
|
-
raise CommandError, "Pry can only patch methods created with the `def` keyword."
|
293
|
-
end
|
294
|
-
|
295
|
-
temp_file do |f|
|
296
|
-
f.puts lines.join
|
297
|
-
f.flush
|
298
|
-
invoke_editor(f.path, 0)
|
299
|
-
|
300
|
-
if @method.alias?
|
301
|
-
with_method_transaction(original_name, @method.owner) do
|
302
|
-
Pry.new(:input => StringIO.new(File.read(f.path))).rep(@method.owner)
|
303
|
-
Pry.binding_for(@method.owner).eval("alias #{@method.name} #{original_name}")
|
304
|
-
end
|
305
|
-
else
|
306
|
-
Pry.new(:input => StringIO.new(File.read(f.path))).rep(@method.owner)
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
def process_file
|
312
|
-
file, line = extract_file_and_line
|
313
|
-
|
314
|
-
invoke_editor(file, opts["no-jump"] ? 0 : line)
|
315
|
-
silence_warnings do
|
316
|
-
load file unless opts.present?(:'no-reload') || Pry.config.disable_auto_reload
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
protected
|
321
|
-
def extract_file_and_line
|
322
|
-
if @method
|
323
|
-
if @method.source_type == :c
|
324
|
-
raise CommandError, "Can't edit a C method."
|
325
|
-
else
|
326
|
-
[@method.source_file, @method.source_line]
|
327
|
-
end
|
328
|
-
else
|
329
|
-
[target.eval('__FILE__'), target.eval('__LINE__')]
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
def with_method_transaction(meth_name, target=TOPLEVEL_BINDING)
|
334
|
-
target = Pry.binding_for(target)
|
335
|
-
temp_name = "__pry_#{meth_name}__"
|
336
|
-
|
337
|
-
target.eval("alias #{temp_name} #{meth_name}")
|
338
|
-
yield
|
339
|
-
target.eval("alias #{meth_name} #{temp_name}")
|
340
|
-
ensure
|
341
|
-
target.eval("undef #{temp_name}") rescue nil
|
342
|
-
end
|
343
|
-
end
|
344
191
|
end
|
345
192
|
end
|
346
193
|
end
|