summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2022-11-18 09:34:38 -0800
committergit <svn-admin@ruby-lang.org>2022-11-18 17:34:42 +0000
commitb1cbc883f2add06479113b61005f4cdfa90ff266 (patch)
tree6c21d94e81911b3d25387cb585c37cabe09ab026 /lib
parent10788166e7e568fdcd0b748e8d5dab442dcdc7ef (diff)
[ruby/irb] Minor fixes on debug command
(https://github.com/ruby/irb/pull/447) * Minor fixes on debug command * Update lib/irb/cmd/debug.rb
Diffstat (limited to 'lib')
-rw-r--r--lib/irb.rb9
-rw-r--r--lib/irb/cmd/debug.rb70
2 files changed, 59 insertions, 20 deletions
diff --git a/lib/irb.rb b/lib/irb.rb
index 1cd89d295c..64da852157 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -393,8 +393,6 @@ module IRB
end
class Irb
- DIR_NAME = __dir__
-
ASSIGNMENT_NODE_TYPES = [
# Local, instance, global, class, constant, instance, and index assignment:
# "foo = bar",
@@ -436,13 +434,14 @@ module IRB
@scanner = RubyLex.new
end
+ # A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up
def debug_break
# it means the debug command is executed
- if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:original_capture_frames)
+ if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb)
# after leaving this initial breakpoint, revert the capture_frames patch
- DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :original_capture_frames)
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb)
# and remove the redundant method
- DEBUGGER__.singleton_class.send(:undef_method, :original_capture_frames)
+ DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb)
end
end
diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb
index 8aab40cf84..369c112257 100644
--- a/lib/irb/cmd/debug.rb
+++ b/lib/irb/cmd/debug.rb
@@ -5,28 +5,68 @@ module IRB
module ExtendCommand
class Debug < Nop
+ BINDING_IRB_FRAME_REGEXPS = [
+ '<internal:prelude>',
+ binding.method(:irb).source_location.first,
+ ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
+ IRB_DIR = File.expand_path('..', __dir__)
+
def execute(*args)
- require "debug/session"
- DEBUGGER__.start(nonstop: true)
- DEBUGGER__.singleton_class.send(:alias_method, :original_capture_frames, :capture_frames)
-
- def DEBUGGER__.capture_frames(skip_path_prefix)
- frames = original_capture_frames(skip_path_prefix)
- frames.reject! do |frame|
- frame.realpath&.start_with?(::IRB::Irb::DIR_NAME) || frame.path.match?(/internal:prelude/)
- end
- frames
+ unless binding_irb?
+ puts "`debug` command is only available when IRB is started with binding.irb"
+ return
end
+ unless setup_debugger
+ puts <<~MSG
+ You need to install the debug gem before using this command.
+ If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
+ MSG
+ return
+ end
+
+ # To make debugger commands like `next` or `continue` work without asking
+ # the user to quit IRB after that, we need to exit IRB first and then hit
+ # a TracePoint on #debug_break.
file, lineno = IRB::Irb.instance_method(:debug_break).source_location
DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, oneshot: true, hook_call: false)
# exit current Irb#run call
throw :IRB_EXIT
- rescue LoadError => e
- puts <<~MSG
- You need to install the debug gem before using this command.
- If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
- MSG
+ end
+
+ private
+
+ def binding_irb?
+ caller.any? do |frame|
+ BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
+ frame.match?(regexp)
+ end
+ end
+ end
+
+ def setup_debugger
+ unless defined?(DEBUGGER__::SESSION)
+ begin
+ require "debug/session"
+ rescue LoadError
+ return false
+ end
+ DEBUGGER__.start(nonstop: true)
+ end
+
+ unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
+
+ def DEBUGGER__.capture_frames(*args)
+ frames = capture_frames_without_irb(*args)
+ frames.reject! do |frame|
+ frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
+ end
+ frames
+ end
+ end
+
+ true
end
end
end