mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
[#408] migrate play, cat, edit & cd commands to command_class
This commit is contained in:
parent
76924d98b3
commit
99a98ef8a9
5 changed files with 220 additions and 202 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ coverage/
|
|||
.rbx/
|
||||
.rvmrc
|
||||
Gemfile.lock
|
||||
*.swp
|
||||
|
|
|
@ -6,42 +6,44 @@ class Pry
|
|||
Context = Pry::CommandSet.new do
|
||||
import Ls
|
||||
|
||||
command "cd", "Move into a new context (use `cd ..` to go back and `cd /` to return to Pry top-level). Complex syntax (e.g cd ../@x/y) also supported." do |obj|
|
||||
path = arg_string.split(/\//)
|
||||
stack = _pry_.binding_stack.dup
|
||||
command_class "cd", "Move into a new context (use `cd ..` to go back and `cd /` to return to Pry top-level). Complex syntax (e.g cd ../@x/y) also supported." do
|
||||
def process
|
||||
path = arg_string.split(/\//)
|
||||
stack = _pry_.binding_stack.dup
|
||||
|
||||
# special case when we only get a single "/", return to root
|
||||
stack = [stack.first] if path.empty?
|
||||
# special case when we only get a single "/", return to root
|
||||
stack = [stack.first] if path.empty?
|
||||
|
||||
resolve_failure = false
|
||||
path.each do |context|
|
||||
begin
|
||||
case context.chomp
|
||||
when ""
|
||||
stack = [stack.first]
|
||||
when "::"
|
||||
stack.push(TOPLEVEL_BINDING)
|
||||
when "."
|
||||
next
|
||||
when ".."
|
||||
unless stack.size == 1
|
||||
stack.pop
|
||||
resolve_failure = false
|
||||
path.each do |context|
|
||||
begin
|
||||
case context.chomp
|
||||
when ""
|
||||
stack = [stack.first]
|
||||
when "::"
|
||||
stack.push(TOPLEVEL_BINDING)
|
||||
when "."
|
||||
next
|
||||
when ".."
|
||||
unless stack.size == 1
|
||||
stack.pop
|
||||
end
|
||||
else
|
||||
stack.push(Pry.binding_for(stack.last.eval(context)))
|
||||
end
|
||||
else
|
||||
stack.push(Pry.binding_for(stack.last.eval(context)))
|
||||
|
||||
rescue RescuableException => e
|
||||
output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
|
||||
output.puts e.inspect
|
||||
resolve_failure = true
|
||||
break
|
||||
end
|
||||
|
||||
rescue RescuableException => e
|
||||
output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
|
||||
output.puts e.inspect
|
||||
resolve_failure = true
|
||||
break
|
||||
end
|
||||
|
||||
return if resolve_failure
|
||||
|
||||
_pry_.binding_stack = stack
|
||||
end
|
||||
|
||||
next if resolve_failure
|
||||
|
||||
_pry_.binding_stack = stack
|
||||
end
|
||||
|
||||
command "switch-to", "Start a new sub-session on a binding in the current stack (numbered by nesting)." do |selection|
|
||||
|
@ -92,20 +94,22 @@ class Pry
|
|||
|
||||
alias_command "!!@", "exit-all"
|
||||
|
||||
command "exit", "Pop the current binding and return to the one immediately prior. Note this does NOT exit the program. Aliases: quit", :keep_retval => true do
|
||||
if _pry_.binding_stack.one?
|
||||
# when breaking out of top-level then behave like `exit-all`
|
||||
_pry_.binding_stack.clear
|
||||
throw(:breakout, target.eval(arg_string))
|
||||
else
|
||||
# otherwise just pop a binding
|
||||
popped_object = _pry_.binding_stack.pop.eval('self')
|
||||
|
||||
# return a user-specified value if given
|
||||
if !arg_string.empty?
|
||||
target.eval(arg_string)
|
||||
command_class "exit", "Pop the current binding and return to the one immediately prior. Note this does NOT exit the program. Aliases: quit", :keep_retval => true do
|
||||
def process
|
||||
if _pry_.binding_stack.one?
|
||||
# when breaking out of top-level then behave like `exit-all`
|
||||
_pry_.binding_stack.clear
|
||||
throw(:breakout, target.eval(arg_string))
|
||||
else
|
||||
popped_object
|
||||
# otherwise just pop a binding
|
||||
popped_object = _pry_.binding_stack.pop.eval('self')
|
||||
|
||||
# return a user-specified value if given
|
||||
if !arg_string.empty?
|
||||
target.eval(arg_string)
|
||||
else
|
||||
popped_object
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,8 +59,9 @@ class Pry
|
|||
|
||||
alias_command(/%.?(-?\d+)?(?:\.\.(-?\d+))?/, "amend-line")
|
||||
|
||||
command "play", "Play back a string variable or a method or a file as input. Type `play --help` for more information." do |*args|
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
command_class "play", "Play back a string variable or a method or a file as input. Type `play --help` for more information." do
|
||||
|
||||
def options(opt)
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: play [OPTIONS] [--help]
|
||||
Default action (no options) is to play the provided string variable
|
||||
|
@ -73,47 +74,47 @@ class Pry
|
|||
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.help
|
||||
end
|
||||
end
|
||||
|
||||
if opts.present?(:method)
|
||||
meth_name = opts[:m]
|
||||
meth = get_method_or_raise(meth_name, target, {}, :omit_help)
|
||||
next unless meth.source
|
||||
def process
|
||||
|
||||
range = opts.present?(:lines) ? one_index_range_or_number(opts[:l]) : (0..-1)
|
||||
range = (0..-2) if opts.present?(:open)
|
||||
if opts.present?(:method)
|
||||
meth_name = opts[:m]
|
||||
meth = get_method_or_raise(meth_name, target, {}, :omit_help)
|
||||
return unless meth.source
|
||||
|
||||
eval_string << Array(meth.source.each_line.to_a[range]).join
|
||||
elsif opts.present?(:file)
|
||||
file_name = File.expand_path(opts[:f])
|
||||
range = opts.present?(:lines) ? one_index_range_or_number(opts[:l]) : (0..-1)
|
||||
range = (0..-2) if opts.present?(:open)
|
||||
|
||||
if !File.exists?(file_name)
|
||||
raise CommandError, "No such file: #{opts[:f]}"
|
||||
eval_string << Array(meth.source.each_line.to_a[range]).join
|
||||
elsif opts.present?(:file)
|
||||
file_name = File.expand_path(opts[:f])
|
||||
|
||||
if !File.exists?(file_name)
|
||||
raise CommandError, "No such file: #{opts[:f]}"
|
||||
end
|
||||
|
||||
text_array = File.readlines(file_name)
|
||||
range = opts.present?(:lines) ? one_index_range_or_number(opts[:l]) : (0..-1)
|
||||
range = (0..-2) if opts.present?(:open)
|
||||
|
||||
_pry_.input_stack << _pry_.input
|
||||
_pry_.input = StringIO.new(Array(text_array[range]).join)
|
||||
else
|
||||
if !args.first
|
||||
raise CommandError, "No input to play command."
|
||||
end
|
||||
|
||||
code = target.eval(args.first)
|
||||
|
||||
range = opts.present?(:lines) ? one_index_range_or_number(opts[:l]) : (0..-1)
|
||||
range = (0..-2) if opts.present?(:open)
|
||||
|
||||
eval_string << Array(code.each_line.to_a[range]).join
|
||||
end
|
||||
|
||||
text_array = File.readlines(file_name)
|
||||
range = opts.present?(:lines) ? one_index_range_or_number(opts[:l]) : (0..-1)
|
||||
range = (0..-2) if opts.present?(:open)
|
||||
|
||||
_pry_.input_stack << _pry_.input
|
||||
_pry_.input = StringIO.new(Array(text_array[range]).join)
|
||||
else
|
||||
if !args.first
|
||||
raise CommandError, "No input to play command."
|
||||
end
|
||||
|
||||
code = target.eval(args.first)
|
||||
|
||||
range = opts.present?(:lines) ? one_index_range_or_number(opts[:l]) : (0..-1)
|
||||
range = (0..-2) if opts.present?(:open)
|
||||
|
||||
eval_string << Array(code.each_line.to_a[range]).join
|
||||
run "show-input" if !_pry_.complete_expression?(eval_string)
|
||||
end
|
||||
|
||||
run "show-input" if !_pry_.complete_expression?(eval_string)
|
||||
end
|
||||
|
||||
command "hist", "Show and replay Readline history. Type `hist --help` for more info. Aliases: history" do |*args|
|
||||
|
|
|
@ -99,8 +99,10 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
command "edit", "Invoke the default editor on a file. Type `edit --help` for more info" do |*args|
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
command_class "edit", "Invoke the default editor on a file. Type `edit --help` for more info" do
|
||||
attr_accessor :content
|
||||
|
||||
def options(opt)
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: edit [--no-reload|--reload] [--line LINE] [--temp|--ex|FILE[:LINE]|--in N]
|
||||
Open a text editor. When no FILE is given, edits the pry input buffer.
|
||||
|
@ -114,40 +116,45 @@ class Pry
|
|||
opt.on :l, :line, "Jump to this line in the opened file", true, :as => Integer
|
||||
opt.on :n, :"no-reload", "Don't automatically reload the edited code"
|
||||
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
|
||||
opt.on :h, :help, "This message." do
|
||||
output.puts opt.help
|
||||
end
|
||||
|
||||
def process
|
||||
self.content = ""
|
||||
|
||||
if [opts.present?(:ex), opts.present?(:temp), opts.present?(:in), !args.empty?].count(true) > 1
|
||||
raise CommandError, "Only one of --ex, --temp, --in and FILE may be specified."
|
||||
end
|
||||
|
||||
if !opts.present?(:ex) && args.empty?
|
||||
local_edit
|
||||
else
|
||||
remote_edit
|
||||
end
|
||||
end
|
||||
next if opts.present?(:help)
|
||||
|
||||
if [opts.present?(:ex), opts.present?(:temp), opts.present?(:in), !args.empty?].count(true) > 1
|
||||
raise CommandError, "Only one of --ex, --temp, --in and FILE may be specified."
|
||||
end
|
||||
def local_edit
|
||||
# edit of local code, eval'd within pry.
|
||||
self.content = if opts.present?(:temp)
|
||||
""
|
||||
elsif opts.present?(:in)
|
||||
case opts[:i]
|
||||
when Range
|
||||
(_pry_.input_array[opts[:i]] || []).join
|
||||
when Fixnum
|
||||
_pry_.input_array[opts[:i]] || ""
|
||||
else
|
||||
return output.puts "Not a valid range: #{opts[:i]}"
|
||||
end
|
||||
elsif eval_string.strip != ""
|
||||
eval_string
|
||||
else
|
||||
_pry_.input_array.reverse_each.find{ |x| x && x.strip != "" } || ""
|
||||
end
|
||||
|
||||
# edit of local code, eval'd within pry.
|
||||
if !opts.present?(:ex) && args.empty?
|
||||
|
||||
content = if opts.present?(:temp)
|
||||
""
|
||||
elsif opts.present?(:in)
|
||||
case opts[:i]
|
||||
when Range
|
||||
(_pry_.input_array[opts[:i]] || []).join
|
||||
when Fixnum
|
||||
_pry_.input_array[opts[:i]] || ""
|
||||
else
|
||||
next output.puts "Not a valid range: #{opts[:i]}"
|
||||
end
|
||||
elsif eval_string.strip != ""
|
||||
eval_string
|
||||
else
|
||||
_pry_.input_array.reverse_each.find{ |x| x && x.strip != "" } || ""
|
||||
end
|
||||
|
||||
line = content.lines.count
|
||||
line = self.content.lines.count
|
||||
|
||||
temp_file do |f|
|
||||
f.puts(content)
|
||||
f.puts(self.content)
|
||||
f.flush
|
||||
invoke_editor(f.path, line)
|
||||
if !opts.present?(:'no-reload')
|
||||
|
@ -156,9 +163,10 @@ class Pry
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# edit of remote code, eval'd at top-level
|
||||
else
|
||||
def remote_edit
|
||||
# edit of remote code, eval'd at top-level
|
||||
if opts.present?(:ex)
|
||||
if _pry_.last_exception.nil?
|
||||
raise CommandError, "No exception found."
|
||||
|
|
|
@ -31,40 +31,46 @@ class Pry
|
|||
|
||||
alias_command "file-mode", "shell-mode"
|
||||
|
||||
command "cat", "Show code from a file or Pry's input buffer. Type `cat --help` for more information." do |*args|
|
||||
start_line = 0
|
||||
end_line = -1
|
||||
file_name = nil
|
||||
bt_index = 0
|
||||
command_class "cat", "Show code from a file or Pry's input buffer. Type `cat --help` for more information." do
|
||||
attr_accessor :start_line
|
||||
attr_accessor :end_line
|
||||
attr_accessor :file_name
|
||||
attr_accessor :bt_index
|
||||
attr_accessor :contents
|
||||
|
||||
def options(opt)
|
||||
self.start_line = 0
|
||||
self.end_line = -1
|
||||
self.file_name = nil
|
||||
self.bt_index = 0
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
opt.on :s, :start, "Start line (defaults to start of file)Line 1 is the first line.", true, :as => Integer do |line|
|
||||
start_line = line - 1
|
||||
self.start_line = line - 1
|
||||
end
|
||||
|
||||
opt.on :e, :end, "End line (defaults to end of file). Line -1 is the last line", true, :as => Integer do |line|
|
||||
end_line = line - 1
|
||||
self.end_line = line - 1
|
||||
end
|
||||
|
||||
opt.on :ex, "Show a window of N lines either side of the last exception (defaults to 5).", :optional => true, :as => Integer do |bt_index_arg|
|
||||
window_size = Pry.config.exception_window_size || 5
|
||||
ex = _pry_.last_exception
|
||||
next if !ex
|
||||
return if !ex
|
||||
if bt_index_arg
|
||||
bt_index = bt_index_arg
|
||||
self.bt_index = bt_index_arg
|
||||
else
|
||||
bt_index = ex.bt_index
|
||||
self.bt_index = ex.bt_index
|
||||
end
|
||||
ex.bt_index = (bt_index + 1) % ex.backtrace.size
|
||||
ex.bt_index = (self.bt_index + 1) % ex.backtrace.size
|
||||
|
||||
ex_file, ex_line = ex.bt_source_location_for(bt_index)
|
||||
start_line = (ex_line - 1) - window_size
|
||||
start_line = start_line < 0 ? 0 : start_line
|
||||
end_line = (ex_line - 1) + window_size
|
||||
ex_file, ex_line = ex.bt_source_location_for(self.bt_index)
|
||||
self.start_line = (ex_line - 1) - window_size
|
||||
self.start_line = self.start_line < 0 ? 0 : self.start_line
|
||||
self.end_line = (ex_line - 1) + window_size
|
||||
if ex_file && RbxPath.is_core_path?(ex_file)
|
||||
file_name = RbxPath.convert_path_to_full(ex_file)
|
||||
self.file_name = RbxPath.convert_path_to_full(ex_file)
|
||||
else
|
||||
file_name = ex_file
|
||||
self.file_name = ex_file
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -73,95 +79,93 @@ class Pry
|
|||
opt.on :l, "line-numbers", "Show line numbers."
|
||||
opt.on :t, :type, "The specific file type for syntax higlighting (e.g ruby, python)", true, :as => Symbol
|
||||
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
||||
opt.on :h, :help, "This message." do
|
||||
output.puts opt.help
|
||||
end
|
||||
end
|
||||
|
||||
next if opts.present?(:help)
|
||||
def process
|
||||
self.contents = ""
|
||||
|
||||
if opts.present?(:ex)
|
||||
if file_name.nil?
|
||||
raise CommandError, "No Exception or Exception has no associated file."
|
||||
end
|
||||
else
|
||||
file_name = args.shift
|
||||
end
|
||||
|
||||
if opts.present?(:in)
|
||||
normalized_range = absolute_index_range(opts[:i], _pry_.input_array.length)
|
||||
input_items = _pry_.input_array[normalized_range] || []
|
||||
|
||||
zipped_items = normalized_range.zip(input_items).reject { |_, s| s.nil? || s == "" }
|
||||
|
||||
unless zipped_items.length > 0
|
||||
raise CommandError, "No expressions found."
|
||||
end
|
||||
|
||||
if opts[:i].is_a?(Range)
|
||||
contents = ""
|
||||
|
||||
zipped_items.each do |i, s|
|
||||
contents << "#{text.bold(i.to_s)}:\n"
|
||||
|
||||
code = syntax_highlight_by_file_type_or_specified(s, nil, :ruby)
|
||||
|
||||
if opts.present?(:'line-numbers')
|
||||
contents << text.indent(text.with_line_numbers(code, 1), 2)
|
||||
else
|
||||
contents << text.indent(code, 2)
|
||||
end
|
||||
if opts.present?(:ex)
|
||||
if self.file_name.nil?
|
||||
raise CommandError, "No Exception or Exception has no associated file."
|
||||
end
|
||||
else
|
||||
contents = syntax_highlight_by_file_type_or_specified(zipped_items.first.last, nil, :ruby)
|
||||
end
|
||||
else
|
||||
unless file_name
|
||||
raise CommandError, "Must provide a file name."
|
||||
self.file_name = args.shift
|
||||
end
|
||||
|
||||
begin
|
||||
contents, _, _ = read_between_the_lines(file_name, start_line, end_line)
|
||||
rescue Errno::ENOENT
|
||||
raise CommandError, "Could not find file: #{file_name}"
|
||||
end
|
||||
if opts.present?(:in)
|
||||
normalized_range = absolute_index_range(opts[:i], _pry_.input_array.length)
|
||||
input_items = _pry_.input_array[normalized_range] || []
|
||||
|
||||
contents = syntax_highlight_by_file_type_or_specified(contents, file_name, opts[:type])
|
||||
zipped_items = normalized_range.zip(input_items).reject { |_, s| s.nil? || s == "" }
|
||||
|
||||
if opts.present?(:'line-numbers')
|
||||
contents = text.with_line_numbers contents, start_line + 1
|
||||
end
|
||||
end
|
||||
|
||||
# add the arrow pointing to line that caused the exception
|
||||
if opts.present?(:ex)
|
||||
ex_file, ex_line = _pry_.last_exception.bt_source_location_for(bt_index)
|
||||
contents = text.with_line_numbers contents, start_line + 1, :bright_red
|
||||
|
||||
contents = contents.lines.each_with_index.map do |line, idx|
|
||||
l = idx + start_line
|
||||
if l == (ex_line - 1)
|
||||
" =>#{line}"
|
||||
else
|
||||
" #{line}"
|
||||
unless zipped_items.length > 0
|
||||
raise CommandError, "No expressions found."
|
||||
end
|
||||
end.join
|
||||
|
||||
# header for exceptions
|
||||
output.puts "\n#{Pry::Helpers::Text.bold('Exception:')} #{_pry_.last_exception.class}: #{_pry_.last_exception.message}\n--"
|
||||
output.puts "#{Pry::Helpers::Text.bold('From:')} #{ex_file} @ line #{ex_line} @ #{text.bold('level: ')} #{bt_index} of backtrace (of #{_pry_.last_exception.backtrace.size - 1}).\n\n"
|
||||
end
|
||||
if opts[:i].is_a?(Range)
|
||||
self.contents = ""
|
||||
|
||||
set_file_and_dir_locals(file_name)
|
||||
zipped_items.each do |i, s|
|
||||
self.contents << "#{text.bold(i.to_s)}:\n"
|
||||
|
||||
if opts.present?(:flood)
|
||||
output.puts contents
|
||||
else
|
||||
stagger_output(contents)
|
||||
code = syntax_highlight_by_file_type_or_specified(s, nil, :ruby)
|
||||
|
||||
if opts.present?(:'line-numbers')
|
||||
self.contents << text.indent(text.with_line_numbers(code, 1), 2)
|
||||
else
|
||||
self.contents << text.indent(code, 2)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.contents = syntax_highlight_by_file_type_or_specified(zipped_items.first.last, nil, :ruby)
|
||||
end
|
||||
else
|
||||
unless self.file_name
|
||||
raise CommandError, "Must provide a file name."
|
||||
end
|
||||
|
||||
begin
|
||||
self.contents, _, _ = read_between_the_lines(self.file_name, self.start_line, self.end_line)
|
||||
rescue Errno::ENOENT
|
||||
raise CommandError, "Could not find file: #{self.file_name}"
|
||||
end
|
||||
|
||||
self.contents = syntax_highlight_by_file_type_or_specified(self.contents, self.file_name, opts[:type])
|
||||
|
||||
if opts.present?(:'line-numbers')
|
||||
self.contents = text.with_line_numbers self.contents, self.start_line + 1
|
||||
end
|
||||
end
|
||||
|
||||
# add the arrow pointing to line that caused the exception
|
||||
if opts.present?(:ex)
|
||||
ex_file, ex_line = _pry_.last_exception.bt_source_location_for(self.bt_index)
|
||||
self.contents = text.with_line_numbers self.contents, self.start_line + 1, :bright_red
|
||||
|
||||
self.contents = self.contents.lines.each_with_index.map do |line, idx|
|
||||
l = idx + self.start_line
|
||||
if l == (ex_line - 1)
|
||||
" =>#{line}"
|
||||
else
|
||||
" #{line}"
|
||||
end
|
||||
end.join
|
||||
|
||||
# header for exceptions
|
||||
output.puts "\n#{Pry::Helpers::Text.bold('Exception:')} #{_pry_.last_exception.class}: #{_pry_.last_exception.message}\n--"
|
||||
output.puts "#{Pry::Helpers::Text.bold('From:')} #{ex_file} @ line #{ex_line} @ #{text.bold('level: ')} #{self.bt_index} of backtrace (of #{_pry_.last_exception.backtrace.size - 1}).\n\n"
|
||||
end
|
||||
|
||||
set_file_and_dir_locals(self.file_name)
|
||||
|
||||
if opts.present?(:flood)
|
||||
output.puts self.contents
|
||||
else
|
||||
stagger_output(self.contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue