File: pry_processor.rb

package info (click to toggle)
ruby-pry-byebug 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 324 kB
  • sloc: ruby: 1,171; makefile: 4
file content (166 lines) | stat: -rw-r--r-- 3,530 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# frozen_string_literal: true

require "byebug/core"

module Byebug
  #
  # Extends raw byebug's processor.
  #
  class PryProcessor < CommandProcessor
    attr_accessor :pry

    extend Forwardable
    def_delegators :@pry, :output
    def_delegators Pry::Helpers::Text, :bold

    def self.start
      Byebug.start
      Setting[:autolist] = false
      Context.processor = self
      Byebug.current_context.step_out(4, true)
    end

    #
    # Wrap a Pry REPL to catch navigational commands and act on them.
    #
    def run(&_block)
      return_value = nil

      command = catch(:breakout_nav) do # Throws from PryByebug::Commands
        return_value = allowing_other_threads { yield }
        {} # Nothing thrown == no navigational command
      end

      # Pry instance to resume after stepping
      @pry = command[:pry]

      perform(command[:action], command[:options])

      return_value
    end

    #
    # Set up a number of navigational commands to be performed by Byebug.
    #
    def perform(action, options = {})
      return unless %i[
        backtrace
        down
        finish
        frame
        next
        step
        up
      ].include?(action)

      send("perform_#{action}", options)
    end

    # --- Callbacks from byebug C extension ---

    #
    # Called when the debugger wants to stop at a regular line
    #
    def at_line
      resume_pry
    end

    #
    # Called when the debugger wants to stop right before a method return
    #
    def at_return(_return_value)
      resume_pry
    end

    #
    # Called when the debugger wants to stop right before the end of a class
    # definition
    #
    def at_end
      resume_pry
    end

    #
    # Called when a breakpoint is hit. Note that `at_line`` is called
    # inmediately after with the context's `stop_reason == :breakpoint`, so we
    # must not resume the pry instance here
    #
    def at_breakpoint(breakpoint)
      @pry ||= Pry.new

      output.puts bold("\n  Breakpoint #{breakpoint.id}. ") + n_hits(breakpoint)

      expr = breakpoint.expr
      return unless expr

      output.puts bold("Condition: ") + expr
    end

    private

    def n_hits(breakpoint)
      n_hits = breakpoint.hit_count

      n_hits == 1 ? "First hit" : "Hit #{n_hits} times."
    end

    #
    # Resume an existing Pry REPL at the paused point.
    #
    def resume_pry
      new_binding = frame._binding

      run do
        if defined?(@pry) && @pry
          @pry.repl(new_binding)
        else
          @pry = Pry.start_without_pry_byebug(new_binding)
        end
      end
    end

    def perform_backtrace(_options)
      Byebug::WhereCommand.new(self, "backtrace").execute

      resume_pry
    end

    def perform_next(options)
      lines = (options[:lines] || 1).to_i
      context.step_over(lines, frame.pos)
    end

    def perform_step(options)
      times = (options[:times] || 1).to_i
      context.step_into(times, frame.pos)
    end

    def perform_finish(*)
      context.step_out(1)
    end

    def perform_up(options)
      times = (options[:times] || 1).to_i

      Byebug::UpCommand.new(self, "up #{times}").execute

      resume_pry
    end

    def perform_down(options)
      times = (options[:times] || 1).to_i

      Byebug::DownCommand.new(self, "down #{times}").execute

      resume_pry
    end

    def perform_frame(options)
      index = options[:index] ? options[:index].to_i : ""

      Byebug::FrameCommand.new(self, "frame #{index}").execute

      resume_pry
    end
  end
end