Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial attempt at basic 'step' and 'next' commands

Based on set_trace_func and similar logic to stdlib's debug.rb.
  • Loading branch information...
commit 04889373c5d81c93a1a1f6d3e544c120e343b392 0 parents
@nixme authored
17 .gitignore
@@ -0,0 +1,17 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
4 Gemfile
@@ -0,0 +1,4 @@
+source 'http://rubygems.org'
+
+# Specify your gem's dependencies in pry-nav.gemspec
+gemspec
29 README.md
@@ -0,0 +1,29 @@
+pry-nav
+=======
+
+_Simple execution control in Pry_
+
+Adds **step** and **next** commands to your [Pry][pry]
+console. Makes for a simple but ghetto debugger.
+
+Note: In order to get the correct flow control, `Pry.start` is overriden. Use at
+your own risk.
+
+
+## Acknowledgments
+
+- Ruby stdlib [debug.rb][debug.rb]
+- [@Mon-Ouie][Mon-Ouie]'s [pry_debug][pry_debug]
+
+
+## TODO
+
+- Thread safety
+- Compatibility with [pry-remote][pry-remote]
+
+
+[pry]: http://pry.github.com
+[debug.rb]: https://github.com/ruby/ruby/blob/trunk/lib/debug.rb
+[Mon-Ouie]: https://github.com/Mon-Ouie
+[pry_debug]: https://github.com/Mon-Ouie/pry_debug
+[pry-remote]: https://github.com/Mon-Ouie/pry-remote
2  Rakefile
@@ -0,0 +1,2 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
4 lib/pry-nav.rb
@@ -0,0 +1,4 @@
+require 'pry-nav/version'
+require 'pry-nav/pry_ext'
+require 'pry-nav/commands'
+require 'pry-nav/tracer'
28 lib/pry-nav/commands.rb
@@ -0,0 +1,28 @@
+require 'pry'
+
+module PryNav
+ Commands = Pry::CommandSet.new do
+ command 'step', 'Step execution into the next line or method.' do |steps|
+ breakout_navigation :step, steps
+ end
+
+ command 'next', 'Execute the next line within the same stack frame.' do |lines|
+ breakout_navigation :next, lines
+ end
+
+ alias_command 'n', 'next'
+ alias_command 's', 'step'
+
+ helpers do
+ def breakout_navigation(action, times)
+ _pry_.binding_stack.clear # Clear the binding stack.
+ throw :breakout_nav, { # Break out of the REPL loop and
+ action: action, # signal the tracer.
+ times: times
+ }
+ end
+ end
+ end
+end
+
+Pry.commands.import PryNav::Commands
11 lib/pry-nav/pry_ext.rb
@@ -0,0 +1,11 @@
+require 'pry'
+
+class << Pry
+ alias_method :start_existing, :start
+
+ def start(target = TOPLEVEL_BINDING, options = {})
+ PryNav::Tracer.new(options) do
+ start_existing(target, options)
+ end
+ end
+end
85 lib/pry-nav/tracer.rb
@@ -0,0 +1,85 @@
+require 'pry'
+
+module PryNav
+ class Tracer
+ FILE = File.expand_path(__FILE__)
+
+ def initialize(pry_start_options = {}, &block)
+ @step_in_lines = -1 # Break after this many lines
+ @frames_when_stepping = nil # Only break at this frame level
+ @frames = [] # Traced stack frames
+ @pry_start_options = pry_start_options # Options to use for Pry.start
+
+ run &block
+ end
+
+ def run(&block)
+ start # Set the trace function
+
+ return_value = nil
+ command = catch(:breakout_nav) do # Coordinate with PryNav::Commands
+ return_value = yield
+ {} # Nothing thrown == no navigational command
+ end
+
+ process_command command
+
+ return_value
+ end
+
+ def start
+ set_trace_func method(:tracer).to_proc
+ end
+
+ def stop
+ set_trace_func nil
+ end
+
+ def process_command(command = {})
+ times = (command[:times] || 1).to_i
+ times = 1 if times <= 0
+
+ case command[:action]
+ when :step
+ @step_in_lines = times
+ @frames_when_stepping = nil
+ when :next
+ @step_in_lines = times
+ @frames_when_stepping = @frames.size
+ else
+ stop
+ end
+ end
+
+
+ private
+
+ def tracer(event, file, line, id, binding, klass)
+ # Ignore traces inside pry-nav code
+ return if (file && File.expand_path(file) == FILE)
+
+ case event
+ when 'line'
+ # Are we stepping? Or continuing by line ('next') and we're at the right
+ # frame? Then decrement our line counter cause this line counts.
+ if !@frames_when_stepping || @frames.size == @frames_when_stepping
+ @step_in_lines -= 1
+ @step_in_lines = -1 if @step_in_lines < 0
+
+ # Did we go up a frame and not break for a 'next' yet?
+ elsif @frames.size < @frames_when_stepping
+ @step_in_lines = 0 # Break right away
+ end
+
+ # Break on this line?
+ Pry.start(binding, @pry_start_options) if @step_in_lines == 0
+
+ when 'call', 'class'
+ @frames.unshift [binding, file, line, id] # Track entering a frame
+
+ when 'return', 'end'
+ @frames.shift # Track leaving a stack frame
+ end
+ end
+ end
+end
3  lib/pry-nav/version.rb
@@ -0,0 +1,3 @@
+module PryNav
+ VERSION = '0.0.1'
+end
23 pry-nav.gemspec
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+
+require File.expand_path('../lib/pry-nav/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.name = 'pry-nav'
+ gem.version = PryNav::VERSION
+ gem.author = 'Gopal Patel'
+ gem.email = 'nixme@stillhope.com'
+ gem.license = 'MIT'
+ gem.homepage = 'https://github.com/nixme/pry-nav'
+ gem.summary = 'Simple execution navigation for Pry.'
+ gem.description = "Turn Pry into a primitive debugger. Adds 'step' and 'next' commands to control execution."
+
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.require_paths = ["lib"]
+
+ # Dependencies
+ gem.required_ruby_version = '>= 1.9.2'
+ gem.add_runtime_dependency 'pry', '~> 0.9.7.4'
+end
Please sign in to comment.
Something went wrong with that request. Please try again.