1
0
Fork 0
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:
Bram Swenson 2012-01-14 01:01:51 -08:00 committed by John Mair
parent 76924d98b3
commit 99a98ef8a9
5 changed files with 220 additions and 202 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ coverage/
.rbx/
.rvmrc
Gemfile.lock
*.swp

View file

@ -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

View file

@ -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|

View file

@ -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."

View file

@ -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