diff --git a/CHANGELOG.md b/CHANGELOG.md index a54cc98..5f707c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.1.0 (2015-04-14) + +- Improvements: + * Add frame nav commands up, down and frame. + ## 3.0.1 (2015-04-02) - Improvements: diff --git a/README.md b/README.md index a3c2bc3..089400c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,14 @@ optional numeric argument to step multiple lines. **continue:** Continue program execution and end the Pry session. +**up:** Moves the stack frame up. Takes an optional numeric argument to move +multiple frames. + +**down:** Moves the stack frame down. Takes an optional numeric argument to move +multiple frames. + +**frame:** Moves to a specific frame. Called without arguments will show the +current frame. ## Matching Byebug Behaviour diff --git a/lib/byebug/processors/pry_processor.rb b/lib/byebug/processors/pry_processor.rb index fcea124..604190f 100644 --- a/lib/byebug/processors/pry_processor.rb +++ b/lib/byebug/processors/pry_processor.rb @@ -6,6 +6,7 @@ module Byebug # class PryProcessor < Processor attr_accessor :pry + attr_reader :state extend Forwardable def_delegators :@pry, :output @@ -15,6 +16,7 @@ def initialize(interface = LocalInterface.new) super(interface) Byebug.handler = self + Byebug::Setting[:autolist] = false end def start @@ -26,6 +28,14 @@ def start # Wrap a Pry REPL to catch navigational commands and act on them. # def run(&_block) + @state ||= Byebug::RegularState.new( + Byebug.current_context, + [], + Byebug.current_context.frame_file, + interface, + Byebug.current_context.frame_line + ) + return_value = nil command = catch(:breakout_nav) do # Throws from PryByebug::Commands @@ -36,7 +46,7 @@ def run(&_block) # Pry instance to resume after stepping @pry = command[:pry] - perform(command[:action], (command[:times] || '1').to_i) + perform(command[:action], command[:options]) return_value end @@ -44,15 +54,12 @@ def run(&_block) # # Set up a number of navigational commands to be performed by Byebug. # - def perform(action, times) - case action - when :next - Byebug.current_context.step_over(times, 0) - when :step - Byebug.current_context.step_into(times) - when :finish - Byebug.current_context.step_out(times) - end + def perform(action, options = {}) + return unless [ + :next, :step, :finish, :up, :down, :frame + ].include?(action) + + send("perform_#{action}", options) end # --- Callbacks from byebug C extension --- @@ -99,7 +106,9 @@ def n_hits(breakpoint) # Resume an existing Pry REPL at the paused point. # def resume_pry(context) - new_binding = context.frame_binding(0) + frame_position = state ? state.frame : 0 + + new_binding = context.frame_binding(frame_position) run do if defined?(@pry) && @pry @@ -109,5 +118,49 @@ def resume_pry(context) end end end + + def perform_next(options) + lines = (options[:lines] || 1).to_i + state.context.step_over(lines, state.frame) + end + + def perform_step(options) + times = (options[:times] || 1).to_i + state.context.step_into(times, state.frame) + end + + def perform_finish(*) + state.context.step_out(1) + end + + def perform_up(options) + times = (options[:times] || 1).to_i + + command = Byebug::UpCommand.new(state) + command.match("up #{times}") + command.execute + + resume_pry(state.context) + end + + def perform_down(options) + times = (options[:times] || 1).to_i + + command = Byebug::DownCommand.new(state) + command.match("down #{times}") + command.execute + + resume_pry(state.context) + end + + def perform_frame(options) + index = options[:index] ? options[:index].to_i : '' + + command = Byebug::FrameCommand.new(state) + command.match("frame #{index}") + command.execute + + resume_pry(state.context) + end end end diff --git a/lib/pry-byebug.rb b/lib/pry-byebug.rb index dad872f..e779fe9 100644 --- a/lib/pry-byebug.rb +++ b/lib/pry-byebug.rb @@ -2,4 +2,5 @@ require 'pry-byebug/base' require 'pry-byebug/pry_ext' require 'pry/commands/stepping' +require 'pry/commands/frames' require 'pry/commands/breakpoint' diff --git a/lib/pry-byebug/version.rb b/lib/pry-byebug/version.rb index 574e6c3..8c06580 100644 --- a/lib/pry-byebug/version.rb +++ b/lib/pry-byebug/version.rb @@ -2,5 +2,5 @@ # Main container module for Pry-Byebug functionality # module PryByebug - VERSION = '3.0.1' + VERSION = '3.1.0' end diff --git a/lib/pry/commands/frames.rb b/lib/pry/commands/frames.rb new file mode 100644 index 0000000..354d4b9 --- /dev/null +++ b/lib/pry/commands/frames.rb @@ -0,0 +1,79 @@ +# +# Main Pry class. +# +# We're going to add to it custom frame commands for Pry-Byebug +# +class Pry + FrameCommands = CommandSet.new do + create_command 'up' do + description 'Move current frame up.' + + banner <<-BANNER + Usage: up [TIMES] + + Move current frame up. By default, moves by 1 frame. + + Examples: + up #=> Move up 1 frame. + up 5 #=> Move up 5 frames. + BANNER + + def process + PryByebug.check_file_context(target) + + frame_navigation :up, times: args.first + end + end + + create_command 'down' do + description 'Move current frame down.' + + banner <<-BANNER + Usage: down [TIMES] + + Move current frame down. By default, moves by 1 frame. + + Examples: + down #=> Move down 1 frame. + down 5 #=> Move down 5 frames. + BANNER + + def process + PryByebug.check_file_context(target) + + frame_navigation :down, times: args.first + end + end + + create_command 'frame' do + description 'Move to specified frame #.' + + banner <<-BANNER + Usage: frame [TIMES] + + Move to specified frame #. + + Examples: + frame #=> Show current frame #. + frame 5 #=> Move to frame 5. + BANNER + + def process + PryByebug.check_file_context(target) + + frame_navigation :frame, index: args.first + end + end + + helpers do + def frame_navigation(action, options = {}) + _pry_.binding_stack.clear # Clear the binding stack. + + # Break out of the REPL loop and signal tracer + throw :breakout_nav, action: action, options: options, pry: _pry_ + end + end + end + + Pry.commands.import(FrameCommands) +end diff --git a/lib/pry/commands/stepping.rb b/lib/pry/commands/stepping.rb index aaf86a6..0979c53 100644 --- a/lib/pry/commands/stepping.rb +++ b/lib/pry/commands/stepping.rb @@ -21,7 +21,7 @@ class Pry def process PryByebug.check_file_context(target) - breakout_navigation :step, args.first + breakout_navigation :step, times: args.first end end @@ -42,7 +42,7 @@ def process def process PryByebug.check_file_context(target) - breakout_navigation :next, args.first + breakout_navigation :next, lines: args.first end end @@ -75,11 +75,11 @@ def process end helpers do - def breakout_navigation(action, times = nil) + def breakout_navigation(action, options = {}) _pry_.binding_stack.clear # Clear the binding stack. # Break out of the REPL loop and signal tracer - throw :breakout_nav, action: action, times: times, pry: _pry_ + throw :breakout_nav, action: action, options: options, pry: _pry_ end end end diff --git a/test/examples/frames.rb b/test/examples/frames.rb new file mode 100644 index 0000000..5804e4f --- /dev/null +++ b/test/examples/frames.rb @@ -0,0 +1,14 @@ +# +# Toy class for testing frame commands +# +class Frames + def method_a + method_b + end + + def method_b + binding.pry + end +end + +Frames.new.method_a diff --git a/test/frames_test.rb b/test/frames_test.rb new file mode 100644 index 0000000..73715f6 --- /dev/null +++ b/test/frames_test.rb @@ -0,0 +1,58 @@ +require 'test_helper' + +# +# Tests for pry-byebug frame commands. +# +class FramesTest < MiniTest::Spec + let(:output) { StringIO.new } + + before do + Pry.color, Pry.pager, Pry.hooks = false, false, Pry::DEFAULT_HOOKS + end + + describe 'Up command' do + let(:input) { InputTester.new('up', 'down') } + + before do + redirect_pry_io(input, output) { load test_file('frames') } + end + + it 'shows current line' do + output.string.must_match(/=> \s*6: \s*method_b/) + end + end + + describe 'Down command' do + let(:input) { InputTester.new('up', 'down') } + + before do + redirect_pry_io(input, output) { load test_file('frames') } + end + + it 'shows current line' do + output.string.must_match(/=> \s*11: \s*end/) + end + end + + describe 'Frame command' do + before do + redirect_pry_io(input, output) { load test_file('frames') } + end + + describe 'jump to frame 1' do + let(:input) { InputTester.new('frame 1', 'frame 0') } + + it 'shows current line' do + output.string.must_match(/=> \s*6: \s*method_b/) + end + end + + describe 'jump to current frame' do + let(:input) { InputTester.new('frame 0') } + + it 'shows current line' do + output.string.must_match(/=> \s*11: \s*end/) + end + end + end +end diff --git a/test/commands_test.rb b/test/stepping_test.rb similarity index 95% rename from test/commands_test.rb rename to test/stepping_test.rb index a741999..697b03a 100644 --- a/test/commands_test.rb +++ b/test/stepping_test.rb @@ -14,9 +14,9 @@ def self.included(spec_class) end # -# Tests for pry-byebug commands. +# Tests for pry-byebug stepping commands. # -class CommandsTest < MiniTest::Spec +class SteppingTest < MiniTest::Spec let(:step_file) { test_file('stepping') } before do