Module: PryDebuggerJRuby

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

Defined Under Namespace

Modules: Breakpoints Classes: Processor

Constant Summary collapse

TRACE_IGNORE_FILES =
[*list_debugger_files, *list_pry_files].freeze
VERSION =
'1.2.0'.freeze
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.'

    banner <<-BANNER
      Usage: finish

      Execute until current stack frame returns.
    BANNER

    def process
      check_file_context
      breakout_navigation :finish
    end
  end

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

    banner <<-BANNER
      Usage: finish

      End the Pry session and continue program execution.
    BANNER

    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
        return unless PryDebuggerJRuby.check_trace_enabled
        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 PryDebuggerJRuby.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]
          location = method_object.source_location
          location[1] += 1
          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)
      return unless PryDebuggerJRuby.check_trace_enabled

      _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 PryDebuggerJRuby.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.



52
53
54
# File 'lib/pry-debugger-jruby/base.rb', line 52

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



29
30
31
32
# File 'lib/pry-debugger-jruby/base.rb', line 29

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

#check_trace_enabledObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/pry-debugger-jruby/base.rb', line 34

def check_trace_enabled
  return true if org.jruby.RubyInstanceConfig.FULL_TRACE_ENABLED
  warn <<-EOS
You are currently running JRuby without the --debug flag enabled, and without it this command will not work correctly.

To fix it, either:

* add the --debug flag to your ruby/jruby command
* or add the --debug flag to the JRUBY_OPTS environment variable
* or enable the jruby.debug.fullTrace option

Do note that having this option on all the time has a performance penalty, so we recommend you only enable it while debugging.
Safe prying, fellow rubyst!
EOS
  false
end