mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
Implement cd -
command
The `cd -` command allows a user to toggle between last two scopes. Example: [1] pry(main)> cd :foo [2] pry(:foo):1> cd :bar [3] pry(:bar):2> cd - ^ save :bar and toggle [4] pry(:foo):1> cd 44 ^ we are back at foo [5] pry(44):2> cd .. [6] pry(:foo):1> cd .. [7] pry(main)> cd - ^ save main and toggle [8] pry(:foo):1> ^ we are back at foo Signed-off-by: Kyrylo Silin <kyrylosilin@gmail.com> Signed-off-by: Yorick Peterse <yorickpeterse@gmail.com>
This commit is contained in:
parent
77c96fd349
commit
3e56239d50
4 changed files with 401 additions and 13 deletions
|
@ -110,8 +110,9 @@ class Pry
|
||||||
_pry_.binding_stack.clear
|
_pry_.binding_stack.clear
|
||||||
throw(:breakout)
|
throw(:breakout)
|
||||||
else
|
else
|
||||||
# otherwise just pops a binding
|
# otherwise just pops a binding and stores it as old binding
|
||||||
_pry_.binding_stack.pop
|
_pry_.command_state["cd"].old_binding = _pry_.binding_stack.pop
|
||||||
|
_pry_.command_state["cd"].append = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,41 +9,64 @@ class Pry
|
||||||
Usage: cd [OPTIONS] [--help]
|
Usage: cd [OPTIONS] [--help]
|
||||||
|
|
||||||
Move into new context (object or scope). As in unix shells use
|
Move into new context (object or scope). As in unix shells use
|
||||||
`cd ..` to go back and `cd /` to return to Pry top-level).
|
`cd ..` to go back, `cd /` to return to Pry top-level and `cd -`
|
||||||
Complex syntax (e.g cd ../@x/y) also supported.
|
to toggle between last two scopes).
|
||||||
|
Complex syntax (e.g `cd ../@x/y`) also supported.
|
||||||
|
|
||||||
e.g: `cd @x`
|
e.g: `cd @x`
|
||||||
e.g: `cd ..
|
e.g: `cd ..`
|
||||||
e.g: `cd /`
|
e.g: `cd /`
|
||||||
|
e.g: `cd -`
|
||||||
|
|
||||||
https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope
|
https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope
|
||||||
BANNER
|
BANNER
|
||||||
|
|
||||||
def process
|
def process
|
||||||
path = arg_string.split(/\//)
|
# Extract command arguments. Delete blank arguments like " ", but
|
||||||
stack = _pry_.binding_stack.dup
|
# don't delete empty strings like "".
|
||||||
|
path = arg_string.split(/\//).delete_if { |a| a =~ /\A\s+\z/ }
|
||||||
|
stack = _pry_.binding_stack.dup
|
||||||
|
|
||||||
# special case when we only get a single "/", return to root
|
# Save current state values for the sake of restoring them them later
|
||||||
stack = [stack.first] if path.empty?
|
# (for example, when an exception raised).
|
||||||
|
old_binding = state.old_binding
|
||||||
|
append = state.append
|
||||||
|
|
||||||
|
# Special case when we only get a single "/", return to root.
|
||||||
|
if path.empty?
|
||||||
|
set_old_binding(stack.last, true) if old_binding
|
||||||
|
stack = [stack.first]
|
||||||
|
end
|
||||||
|
|
||||||
path.each do |context|
|
path.each do |context|
|
||||||
begin
|
begin
|
||||||
case context.chomp
|
case context.chomp
|
||||||
when ""
|
when ""
|
||||||
|
set_old_binding(stack.last, true)
|
||||||
stack = [stack.first]
|
stack = [stack.first]
|
||||||
when "::"
|
when "::"
|
||||||
|
set_old_binding(stack.last, false)
|
||||||
stack.push(TOPLEVEL_BINDING)
|
stack.push(TOPLEVEL_BINDING)
|
||||||
when "."
|
when "."
|
||||||
next
|
next
|
||||||
when ".."
|
when ".."
|
||||||
unless stack.size == 1
|
unless stack.size == 1
|
||||||
stack.pop
|
set_old_binding(stack.pop, true)
|
||||||
|
end
|
||||||
|
when "-"
|
||||||
|
if state.old_binding
|
||||||
|
toggle_old_binding(stack, old_binding, append)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
unless path.length > 1
|
||||||
|
set_old_binding(stack.last, false)
|
||||||
|
end
|
||||||
stack.push(Pry.binding_for(stack.last.eval(context)))
|
stack.push(Pry.binding_for(stack.last.eval(context)))
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue RescuableException => e
|
rescue RescuableException => e
|
||||||
|
set_old_binding(old_binding, append) # Restore previous values.
|
||||||
|
|
||||||
output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
|
output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
|
||||||
output.puts e.inspect
|
output.puts e.inspect
|
||||||
return
|
return
|
||||||
|
@ -52,6 +75,43 @@ class Pry
|
||||||
|
|
||||||
_pry_.binding_stack = stack
|
_pry_.binding_stack = stack
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Toggle old binding value by either appending it to the current stack
|
||||||
|
# (when `append` is `true`) or setting the new one (when `append` is
|
||||||
|
# `false`).
|
||||||
|
#
|
||||||
|
# @param [Array<Binding>] stack The current stack of bindings.
|
||||||
|
# @param [Binding] old_binding The old binding.
|
||||||
|
# @param [Boolean] append The adjunction flag.
|
||||||
|
#
|
||||||
|
# @return [Binding] The new old binding.
|
||||||
|
def toggle_old_binding(stack, old_binding, append)
|
||||||
|
if append
|
||||||
|
stack.push(old_binding)
|
||||||
|
old_binding = stack[-2]
|
||||||
|
else
|
||||||
|
old_binding = stack.pop
|
||||||
|
end
|
||||||
|
append = !append
|
||||||
|
|
||||||
|
set_old_binding(old_binding, append)
|
||||||
|
|
||||||
|
old_binding
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set new old binding and adjunction flag.
|
||||||
|
#
|
||||||
|
# @param [Binding] binding The old binding.
|
||||||
|
# @param [Boolean] append The adjunction flag.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
def set_old_binding(binding, append)
|
||||||
|
state.old_binding = binding
|
||||||
|
state.append = append
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -60,9 +60,9 @@ class Pry
|
||||||
def initialize(options={})
|
def initialize(options={})
|
||||||
refresh(options)
|
refresh(options)
|
||||||
|
|
||||||
@binding_stack = []
|
@binding_stack = []
|
||||||
@indent = Pry::Indent.new
|
@indent = Pry::Indent.new
|
||||||
@command_state = {}
|
@command_state = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Refresh the Pry instance settings from the Pry class.
|
# Refresh the Pry instance settings from the Pry class.
|
||||||
|
|
|
@ -5,6 +5,333 @@ describe 'Pry::DefaultCommands::Cd' do
|
||||||
$obj = nil
|
$obj = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'state' do
|
||||||
|
it 'should not to be set up in fresh instance' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.should == nil
|
||||||
|
instance.command_state["cd"].append.should == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'old binding toggling with `cd -`' do
|
||||||
|
describe 'when an error was raised' do
|
||||||
|
it 'should ensure cd @ raises SyntaxError' do
|
||||||
|
mock_pry("cd @").should =~ /SyntaxError/
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd @", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.should == nil
|
||||||
|
instance.command_state["cd"].append.should == nil
|
||||||
|
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd @", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd @", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :mon_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when using simple cd syntax' do
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with a single `cd -` call' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :mon_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with multple `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'series of cd calls' do
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd 42", "cd :john_dogg", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == 42
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with a single `cd -` call' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd 42", "cd :john_dogg", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with multple `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd 42", "cd :john_dogg", "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == 42
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with fuzzy `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd -", "cd 42", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == 42
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when using cd ..' do
|
||||||
|
before do
|
||||||
|
$obj = Object.new
|
||||||
|
$obj.instance_variable_set(:@x, 66)
|
||||||
|
$obj.instance_variable_set(:@y, 79)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ..", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd $obj/@x/../@y", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == 66
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with a single `cd -` call' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ..", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd $obj/@x/../@y", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == 79
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with multiple `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ..", "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd $obj/@x/../@y", "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == 66
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when using cd ::' do
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ::", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with a single `cd -` call' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ::", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with multiple `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ::", "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when using cd /' do
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd /", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with a single `cd -` call' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd /", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with multiple `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd /", "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :john_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when using ^D (Control-D) key press' do
|
||||||
|
before do
|
||||||
|
@control_d = "Pry::DEFAULT_CONTROL_D_HANDLER.call('', _pry_)"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should keep correct old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd :mon_dogg",
|
||||||
|
"cd :kyr_dogg", @control_d, "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :kyr_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with a single `cd -` call' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd :mon_dogg",
|
||||||
|
"cd :kyr_dogg", @control_d, "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :mon_dogg
|
||||||
|
instance.command_state["cd"].append.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should toggle with multiple `cd -` calls' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd :john_dogg", "cd :mon_dogg",
|
||||||
|
"cd :kyr_dogg", @control_d, "cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.eval("self").should == :kyr_dogg
|
||||||
|
instance.command_state["cd"].append.should == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not toggle when there is no old binding' do
|
||||||
|
instance = nil
|
||||||
|
redirect_pry_io(InputTester.new("cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.should == nil
|
||||||
|
instance.command_state["cd"].append.should == nil
|
||||||
|
|
||||||
|
redirect_pry_io(InputTester.new("cd -", "cd -", "exit-all")) do
|
||||||
|
instance = Pry.new
|
||||||
|
instance.repl
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.command_state["cd"].old_binding.should == nil
|
||||||
|
instance.command_state["cd"].append.should == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'should cd into simple input' do
|
it 'should cd into simple input' do
|
||||||
b = Pry.binding_for(Object.new)
|
b = Pry.binding_for(Object.new)
|
||||||
b.eval("x = :mon_ouie")
|
b.eval("x = :mon_ouie")
|
||||||
|
|
Loading…
Reference in a new issue