pry 0.9.8.2-i386-mingw32 → 0.9.8.3-i386-mingw32
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,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
|