Skip to content

Commit

Permalink
Merge pull request #57 from christian-schulze/add-frame-nav-commands-…
Browse files Browse the repository at this point in the history
…up-and-down

Add frame nav commands up, down and frame through byebug
  • Loading branch information
David Rodríguez committed Mar 14, 2015
2 parents 13c19cb + bfa9f49 commit 721046a
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 18 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
75 changes: 64 additions & 11 deletions lib/byebug/processors/pry_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Byebug
#
class PryProcessor < Processor
attr_accessor :pry
attr_reader :state

extend Forwardable
def_delegators :@pry, :output
Expand All @@ -15,6 +16,7 @@ def initialize(interface = LocalInterface.new)
super(interface)

Byebug.handler = self
Byebug::Setting[:autolist] = false
end

def start
Expand All @@ -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
Expand All @@ -36,23 +46,20 @@ 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

#
# 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 ---
Expand Down Expand Up @@ -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
Expand All @@ -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
1 change: 1 addition & 0 deletions lib/pry-byebug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
2 changes: 1 addition & 1 deletion lib/pry-byebug/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# Main container module for Pry-Byebug functionality
#
module PryByebug
VERSION = '3.0.1'
VERSION = '3.1.0'
end
79 changes: 79 additions & 0 deletions lib/pry/commands/frames.rb
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions lib/pry/commands/stepping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

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

Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions test/examples/frames.rb
Original file line number Diff line number Diff line change
@@ -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
58 changes: 58 additions & 0 deletions test/frames_test.rb
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions test/commands_test.rb → test/stepping_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 721046a

Please sign in to comment.