Module: PryDebugger

Extended by:
PryDebugger
Included in:
PryDebugger
Defined in:
lib/pry-debugger/base.rb,
lib/pry-debugger/version.rb,
lib/pry-debugger/commands.rb,
lib/pry-debugger/processor.rb,
lib/pry-debugger/breakpoints.rb

Defined Under Namespace

Modules: Breakpoints Classes: Processor

Constant Summary collapse

TRACE_IGNORE_FILES =
Dir[File.join(File.dirname(__FILE__), '..', '**', '*.rb')].map { |f| File.expand_path(f) }
VERSION =
'0.2.3'
Commands =
Pry::CommandSet.new do
  create_command 'step' do
    description 'Step execution into the next line or method.'

    banner <<-BANNER
      Usage: step [TIMES]

      Step execution forward. By default, moves a single step.

      Examples:

        step                           Move a single step forward.
        step 5                         Execute the next 5 steps.
    BANNER

    def process
      check_file_context
      breakout_navigation :step, args.first
    end
  end


  create_command 'next' do
    description 'Execute the next line within the current stack frame.'

    banner <<-BANNER
      Usage: next [LINES]

      Step over within the same frame. By default, moves forward a single
      line.

      Examples:

        next                           Move a single line forward.
        next 4                         Execute the next 4 lines.
    BANNER

    def process
      check_file_context
      breakout_navigation :next, args.first
    end
  end


  create_command 'finish' do
    description 'Execute until current stack frame returns.'

    def process
      check_file_context
      breakout_navigation :finish
    end
  end


  create_command 'continue' do
    description 'Continue program execution and end the Pry session.'

    def process
      check_file_context
      run 'exit-all'
    end
  end


  create_command 'break' do
    description 'Set or edit a breakpoint.'

    banner <<-BANNER
      Usage:   break <METHOD | FILE:LINE | LINE> [if CONDITION]
               break --condition N [CONDITION]
               break [--show | --delete | --enable | --disable] N
               break [--delete-all | --disable-all]
      Aliases: breakpoint

      Set a breakpoint. Accepts a line number in the current file, a file and
      line number, or a method, and an optional condition.

      Pass appropriate flags to manipulate existing breakpoints.

      Examples:

        break SomeClass#run            Break at the start of `SomeClass#run`.
        break Foo#bar if baz?          Break at `Foo#bar` only if `baz?`.
        break app/models/user.rb:15    Break at line 15 in user.rb.
        break 14                       Break at line 14 in the current file.

        break --condition 4 x > 2      Add/change condition on breakpoint #4.
        break --condition 3            Remove the condition on breakpoint #3.

        break --delete 5               Delete breakpoint #5.
        break --disable-all            Disable all breakpoints.

        break                          List all breakpoints. (Same as `breakpoints`)
        break --show 2                 Show details about breakpoint #2.
    BANNER

    def options(opt)
      opt.on :c, :condition,     'Change the condition of a breakpoint.', :argument => true, :as => Integer
      opt.on :s, :show,          'Show breakpoint details and source.',   :argument => true, :as => Integer
      opt.on :D, :delete,        'Delete a breakpoint.',                  :argument => true, :as => Integer
      opt.on :d, :disable,       'Disable a breakpoint.',                 :argument => true, :as => Integer
      opt.on :e, :enable,        'Enable a disabled breakpoint.',         :argument => true, :as => Integer
      opt.on     :'disable-all', 'Disable all breakpoints.'
      opt.on     :'delete-all',  'Delete all breakpoints.'
      method_options(opt)
    end

    def process
      Pry.processor.pry = _pry_

      { :delete        => :delete,
        :disable       => :disable,
        :enable        => :enable,
        :'disable-all' => :disable_all,
        :'delete-all'  => :clear
      }.each do |action, method|
        if opts.present?(action)
          Breakpoints.__send__ method, *(method == action ? [opts[action]] : [])
          return run 'breakpoints'
        end
      end

      if opts.present?(:condition)
        Breakpoints.change(opts[:condition], args.empty? ? nil : args.join(' '))
        run 'breakpoints'
      elsif opts.present?(:show)
        print_full_breakpoint Breakpoints.find_by_id(opts[:show])
      elsif args.empty?
        run 'breakpoints'
      else
        new_breakpoint
      end
    end

    def new_breakpoint
      place = args.shift
      condition = args.join(' ') if 'if' == args.shift

      file, line =
        case place
        when /^(\d+)$/       # Line number only
          line = $1
          unless PryDebugger.check_file_context(target)
            raise ArgumentError, 'Line number declaration valid only in a file context.'
          end
          [target.eval('__FILE__'), line]
        when /^(.+):(\d+)$/  # File and line number
          [$1, $2]
        else               # Method or class name
          self.args = [place]
          method_object.source_location
        end

      print_full_breakpoint Breakpoints.add(file, line.to_i, condition)
    end
  end
  alias_command 'breakpoint', 'break'


  create_command 'breakpoints' do
    description 'List defined breakpoints.'

    banner <<-BANNER
      Usage:   breakpoints [OPTIONS]
      Aliases: breaks

      List registered breakpoints and their current status.
    BANNER

    def options(opt)
      opt.on :v, :verbose, 'Print source around each breakpoint.'
    end

    def process
      if Breakpoints.count > 0
        if opts.verbose?   # Long-form with source output
          Breakpoints.each { |b| print_full_breakpoint(b) }
        else               # Simple table output
          max_width = [Math.log10(Breakpoints.count).ceil, 1].max
          header = "#{' ' * (max_width - 1)}#  Enabled  At "

          output.puts
          output.puts text.bold(header)
          output.puts text.bold('-' * header.size)
          Breakpoints.each do |breakpoint|
            output.printf "%#{max_width}d  ", breakpoint.id
            output.print  breakpoint.enabled? ? 'Yes      ' : 'No       '
            output.print  "#{breakpoint.source}:#{breakpoint.pos}"
            output.print  " (if #{breakpoint.expr})" if breakpoint.expr
            output.puts
          end
          output.puts
        end
      else
        output.puts text.bold('No breakpoints defined.')
      end
    end
  end
  alias_command 'breaks', 'breakpoints'


  helpers do
    def breakout_navigation(action, times = nil)
      _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,
        :pry    => _pry_
      }
    end

    # Ensures that a command is executed in a local file context.
    def check_file_context
      unless PryDebugger.check_file_context(target)
        raise Pry::CommandError, 'Cannot find local context. Did you use `binding.pry`?'
      end
    end

    # Print out full information about a breakpoint including surrounding code
    # at that point.
    def print_full_breakpoint(breakpoint)
      line = breakpoint.pos
      output.print text.bold("Breakpoint #{breakpoint.id}: ")
      output.print "#{breakpoint.source} @ line #{line} "
      output.print breakpoint.enabled? ? '(Enabled)' : '(Disabled)'
      output.puts  ' :'
      if (expr = breakpoint.expr)
        output.puts "#{text.bold('Condition:')} #{expr}"
      end
      output.puts
      output.puts  Pry::Code.from_file(breakpoint.source).
                     around(line, 3).
                     with_line_numbers.
                     with_marker(line).to_s
      output.puts
    end
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#current_remote_serverObject

Reference to currently running pry-remote server. Used by the processor.



14
15
16
# File 'lib/pry-debugger/base.rb', line 14

def current_remote_server
  @current_remote_server
end

Instance Method Details

#check_file_context(target) ⇒ Object

Checks that a binding is in a local file context. Extracted from github.com/pry/pry/blob/master/lib/pry/default_commands/context.rb



8
9
10
11
# File 'lib/pry-debugger/base.rb', line 8

def check_file_context(target)
  file = target.eval('__FILE__')
  file == Pry.eval_path || (file !~ /(\(.*\))|<.*>/ && file != '' && file != '-e')
end