break 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 13fa3f9ac130eeffc32382e965d87102c8abb8a2fab3a8e7c202c0f22fd005b4
4
+ data.tar.gz: 0d0e7b2e40529c5529bf7bd87383c92443348a69fb1bdc8cb8918d4f8ac404eb
5
+ SHA512:
6
+ metadata.gz: ad3ea9e586d651c5929f6235ba969142615bc71e10bae00e9ce18a56a4f413a12413d188f854e9e5812118885b353f3fed942b3e047853a105f0bd2bdf9b82e9
7
+ data.tar.gz: 22b0d7d70b05bf44bb4a39795a3d37ac1f4b023b6b9deb37db7b38edacc347f490857ffd3cc0942d008e65395ccd6daee7cc0db5f1fad74031b80cfad732eb5d
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "break/version"
4
+ require_relative "break/session"
5
+ require_relative "break/context"
6
+ require_relative "break/command"
7
+ require_relative "break/commands"
8
+ require_relative "break/filter"
9
+ require_relative "break/binding_ext"
10
+
11
+ require_relative "break/irb"
12
+ require_relative "break/pry"
13
+
14
+ Break::Filter.register_internal __dir__
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Binding
4
+ unless method_defined?(:source_location)
5
+ def source_location
6
+ eval "[__FILE__, __LINE__.to_i]"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Break
6
+ class Command
7
+ extend Forwardable
8
+
9
+ def initialize(session)
10
+ @session = session
11
+ end
12
+
13
+ def execute
14
+ raise NotImplementedError
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :session
20
+ def_delegators :session, :context, :context!, :frontend
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ require_relative "commands/tracepoint_command"
2
+ require_relative "commands/next_command"
3
+ require_relative "commands/step_command"
4
+ require_relative "commands/up_command"
5
+ require_relative "commands/down_command"
6
+ require_relative "commands/where_command"
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class DownCommand < TracePointCommand
5
+ def execute(*)
6
+ if context.depth >= 0
7
+ frontend.notify("Cannot go further down the stack")
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def execute_trace(trace, *)
14
+ trace.disable
15
+
16
+ context!(*context.bindings, depth: context.depth + 1)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class NextCommand < TracePointCommand
5
+ trace :line, :call, :return, :class, :end, :b_call, :b_return
6
+
7
+ def execute_trace(trace, *)
8
+ case trace.event
9
+ when :call, :class, :b_call
10
+ context.bindings << trace.binding
11
+ context.depth += 1
12
+ when :return, :end, :b_return
13
+ context.bindings.pop
14
+ context.depth -= 1
15
+ when :line
16
+ return if context.depth.positive?
17
+
18
+ trace.disable
19
+
20
+ context!(*context.bindings[0...-1], trace.binding)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class StepCommand < TracePointCommand
5
+ trace :line, :call, :return, :class, :end, :b_call, :b_return
6
+
7
+ def execute_trace(trace, *)
8
+ case trace.event
9
+ when :call, :class, :b_call
10
+ trace.disable
11
+
12
+ context!(*context.bindings, trace.binding)
13
+ when :return, :end, :b_return
14
+ context.bindings.pop
15
+ context.depth -= 1
16
+ when :line
17
+ return if context.depth.positive?
18
+
19
+ trace.disable
20
+
21
+ context!(*context.bindings[0...-1], trace.binding)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class TracePointCommand < Command
5
+ class << self
6
+ attr_reader :trace_events
7
+
8
+ def trace(*events)
9
+ @trace_events = events
10
+ end
11
+ end
12
+
13
+ def initialize(*)
14
+ super
15
+
16
+ @delayed_context = Fiber.new do |*args|
17
+ session.context!(*args)
18
+
19
+ loop do
20
+ session.context!(*Fiber.yield)
21
+ end
22
+ end
23
+ end
24
+
25
+ def execute(*args)
26
+ TracePoint.trace(*trace_events) do |trace|
27
+ next if Filter.internal?(trace.path)
28
+
29
+ execute_trace(trace, *args)
30
+ end
31
+
32
+ session.leave
33
+ end
34
+
35
+ def execute_trace
36
+ raise NotImplementedError
37
+ end
38
+
39
+ private
40
+
41
+ def context!(*args)
42
+ @delayed_context.resume(*args)
43
+ rescue FiberError
44
+ session.context!(*args)
45
+ end
46
+
47
+ def trace_events
48
+ self.class.trace_events
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class UpCommand < TracePointCommand
5
+ def execute(*)
6
+ if context.bindings[context.depth - 2]
7
+ super
8
+ else
9
+ frontend.notify("Cannot go further up the stack")
10
+ end
11
+ end
12
+
13
+ def execute_trace(trace, *)
14
+ trace.disable
15
+
16
+ context!(*context.bindings, depth: context.depth - 1)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class WhereCommand < Command
5
+ def execute(*)
6
+ frontend.where
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class Context
5
+ attr_accessor :bindings
6
+ attr_accessor :depth
7
+
8
+ def initialize(*bindings, depth: 0)
9
+ @bindings = bindings
10
+ @depth = depth
11
+ end
12
+
13
+ def binding
14
+ @bindings[@depth - 1]
15
+ end
16
+
17
+ def inspect
18
+ "#{path}:#{lineno}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ module Filter
5
+ extend self
6
+
7
+ attr_reader :internal
8
+
9
+ def register_internal(*paths)
10
+ (@internal ||= []).concat(paths)
11
+ end
12
+
13
+ def internal?(path)
14
+ @internal.any? { |location| path.include?(location) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "irb/frontend"
4
+ require_relative "irb/commands"
5
+ require_relative "irb/overrides"
6
+
7
+ require "open-uri"
8
+
9
+ Break::Filter.register_internal IRB.method(:start).source_location.first.chomp(".rb")
10
+ Break::Filter.register_internal URI.method(:open).source_location.first.chomp(".rb")
11
+ Break::Filter.register_internal "(irb)"
12
+
13
+ Binding.prepend Break::IRB::Overrides
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Break::IRB
6
+ class Commands < Module
7
+ def initialize(session)
8
+ define_command session, :next, Break::NextCommand
9
+ define_command session, :step, Break::StepCommand
10
+ define_command session, :up, Break::UpCommand
11
+ define_command session, :down, Break::DownCommand
12
+ define_command session, :whereami, Break::WhereCommand
13
+ end
14
+
15
+ private
16
+
17
+ def define_command(session, name, cls)
18
+ define_method(name) do |*args|
19
+ cls.new(session).execute(*args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "irb"
4
+
5
+ module Break::IRB
6
+ class Frontend
7
+ def initialize
8
+ IRB.setup caller_locations.first.path, argv: []
9
+ end
10
+
11
+ def attach(session)
12
+ @workspace = IRB::WorkSpace.new(session.context.binding)
13
+
14
+ begin
15
+ @irb = IRB::Irb.new(@workspace)
16
+ @irb.context.main.extend Commands.new(session)
17
+ rescue TypeError
18
+ return
19
+ end
20
+
21
+ where
22
+
23
+ @irb.suspend_context special_case_next_eval(@irb.context) do
24
+ @irb.run(IRB.conf)
25
+ end
26
+ end
27
+
28
+ def detach
29
+ @irb&.context&.exit
30
+ end
31
+
32
+ def where
33
+ puts @workspace.code_around_binding if @workspace
34
+ end
35
+
36
+ def notify(message)
37
+ puts message
38
+ end
39
+
40
+ private
41
+
42
+ # Evaling `next` is a `SyntaxError` in Ruby. Since IRB does not have
43
+ # commands support in the lexer level, we need to call the `next` command
44
+ # in syntactically correct way.
45
+ def special_case_next_eval(irb_context)
46
+ def irb_context.evaluate(line, line_no, *args, **kwargs)
47
+ line = "self.next\n" if line == "next\n"
48
+ super(line, line_no, *args, **kwargs)
49
+ end
50
+
51
+ irb_context
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::IRB
4
+ module Overrides
5
+ def irb
6
+ session = Break::Session.new(self, frontend: Frontend.new)
7
+ session.enter
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "pry"
5
+ require "break"
6
+
7
+ require_relative "pry/frontend"
8
+ require_relative "pry/commands"
9
+
10
+ Break::Filter.register_internal MethodSource.method(:source_helper).source_location.first.chomp(".rb")
11
+ Break::Filter.register_internal CodeRay.method(:scan).source_location.first.chomp(".rb")
12
+ Break::Filter.register_internal Pry.method(:start).source_location.first.chomp("/pry_class.rb")
13
+ Break::Filter.register_internal Forwardable.instance_method(:def_delegator).source_location.first.chomp(".rb")
14
+ Break::Filter.register_internal "(pry)"
15
+ Break::Filter.register_internal __dir__
16
+
17
+ Pry.config.commands.import Break::Pry::Commands
18
+ rescue LoadError
19
+ # Do nothing if we cannot require pry.
20
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::Pry
4
+ Commands = Pry::CommandSet.new do
5
+ create_command "next", "Go to the next line." do
6
+ group "Break"
7
+
8
+ banner <<-BANNER
9
+ Usage: next
10
+
11
+ Step over within the same frame.
12
+
13
+ Examples:
14
+ next #=> Move a line forward.
15
+ BANNER
16
+
17
+ def process
18
+ pry = defined?(pry_instance) ? pry_instance : _pry_
19
+ session = Break::Session.new(pry.binding_stack.first, frontend: Frontend.new(pry))
20
+
21
+ command = Break::NextCommand.new(session)
22
+ command.execute
23
+ end
24
+ end
25
+
26
+ create_command "step", "Step into the current line invocation." do
27
+ group "Break"
28
+
29
+ banner <<-BANNER
30
+ Usage: step
31
+
32
+ Step into a method call.
33
+
34
+ Examples:
35
+ step #=> Step into the method invocation.
36
+ BANNER
37
+
38
+ def process
39
+ pry = defined?(pry_instance) ? pry_instance : _pry_
40
+ session = Break::Session.new(pry.binding_stack.first, frontend: Frontend.new(pry))
41
+
42
+ command = Break::StepCommand.new(session)
43
+ command.execute
44
+ end
45
+ end
46
+
47
+ create_command "up", "Go up a frame." do
48
+ group "Break"
49
+
50
+ banner <<-BANNER
51
+ Usage: up
52
+
53
+ Go to the frame that called the current one. Can be used only if the
54
+ command `step` was issued before.
55
+
56
+ Examples:
57
+ up #=> Step into the method invocation.
58
+ BANNER
59
+
60
+ def process
61
+ pry = defined?(pry_instance) ? pry_instance : _pry_
62
+ session = Break::Session.new(pry.binding_stack.first, frontend: Frontend.new(pry))
63
+
64
+ command = Break::UpCommand.new(session)
65
+ command.execute
66
+ end
67
+ end
68
+
69
+ create_command "down", "Go down a frame." do
70
+ group "Break"
71
+
72
+ banner <<-BANNER
73
+ Usage: down
74
+
75
+ Go to the frame called from the current one. Can be used only if the
76
+ command `step` was issued before.
77
+
78
+ Examples:
79
+ down #=> Step to the previous frame.
80
+ BANNER
81
+
82
+ def process
83
+ pry = defined?(pry_instance) ? pry_instance : _pry_
84
+ session = Break::Session.new(pry.binding_stack.first, frontend: Frontend.new(pry))
85
+
86
+ command = Break::DownCommand.new(session)
87
+ command.execute
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break::Pry
4
+ class Frontend
5
+ def initialize(pry)
6
+ @pry = pry
7
+ end
8
+
9
+ def attach(session)
10
+ @pry = Pry.start(session.context.binding)
11
+ where
12
+ end
13
+
14
+ def detach
15
+ throw :breakout
16
+ end
17
+
18
+ def where
19
+ @pry&.run_command "whereami".dup
20
+ rescue MethodSource::SourceNotFoundError
21
+ puts "Cannot find method code."
22
+ end
23
+
24
+ def notify(message)
25
+ puts message
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ class Session
5
+ attr_reader :contexts
6
+ attr_reader :frontend
7
+
8
+ def initialize(binding, frontend:)
9
+ @contexts = [Context.new(binding)]
10
+ @frontend = frontend
11
+ end
12
+
13
+ def enter
14
+ frontend.attach(self)
15
+ end
16
+
17
+ def leave
18
+ frontend.detach
19
+ end
20
+
21
+ def context
22
+ contexts.last
23
+ end
24
+
25
+ def context!(*bindings, depth: 0)
26
+ contexts << Context.new(*bindings, depth: depth)
27
+ enter
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest"
4
+
5
+ module Break
6
+ class TestingFrontend
7
+ class << self
8
+ def build(&block)
9
+ Class.new(self, &block)
10
+ end
11
+
12
+ def stack
13
+ @stack ||= []
14
+ end
15
+
16
+ def [](index)
17
+ stack[index]
18
+ end
19
+
20
+ def last
21
+ stack.last
22
+ end
23
+
24
+ def clear
25
+ stack.clear
26
+ end
27
+ end
28
+
29
+ attr_reader :session
30
+
31
+ def initialize
32
+ self.class.stack << self
33
+ end
34
+
35
+ def attach(session)
36
+ @session = session
37
+ end
38
+
39
+ def detach; end
40
+
41
+ def where; end
42
+
43
+ def notify(_message); end
44
+ end
45
+
46
+ class Test < Minitest::Test
47
+ def self.test(name, &block)
48
+ define_method("test_#{name}", &block)
49
+ end
50
+
51
+ def pass
52
+ Kernel.binding.tap do |binding|
53
+ yield binding if block_given?
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Break
4
+ VERSION = "0.2.0"
5
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: break
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Genadi Samokovarov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 12.3.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 12.3.3
55
+ description: Lightweight Ruby debugger written in plain Ruby using the TracePoint
56
+ API
57
+ email:
58
+ - gsamokovarov@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - lib/break.rb
64
+ - lib/break/binding_ext.rb
65
+ - lib/break/command.rb
66
+ - lib/break/commands.rb
67
+ - lib/break/commands/down_command.rb
68
+ - lib/break/commands/next_command.rb
69
+ - lib/break/commands/step_command.rb
70
+ - lib/break/commands/tracepoint_command.rb
71
+ - lib/break/commands/up_command.rb
72
+ - lib/break/commands/where_command.rb
73
+ - lib/break/context.rb
74
+ - lib/break/filter.rb
75
+ - lib/break/irb.rb
76
+ - lib/break/irb/commands.rb
77
+ - lib/break/irb/frontend.rb
78
+ - lib/break/irb/overrides.rb
79
+ - lib/break/pry.rb
80
+ - lib/break/pry/commands.rb
81
+ - lib/break/pry/frontend.rb
82
+ - lib/break/session.rb
83
+ - lib/break/testing.rb
84
+ - lib/break/version.rb
85
+ homepage: https://github.com/gsamokovarov/break
86
+ licenses:
87
+ - MIT
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 2.5.0
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubygems_version: 3.0.3
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Lightweight Ruby debugger
108
+ test_files: []