summaryrefslogtreecommitdiff
path: root/lib/irb.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/irb.rb')
-rw-r--r--lib/irb.rb83
1 files changed, 76 insertions, 7 deletions
diff --git a/lib/irb.rb b/lib/irb.rb
index c3631715da..c884d70a67 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -18,6 +18,7 @@ require_relative "irb/color"
require_relative "irb/version"
require_relative "irb/easter-egg"
+require_relative "irb/debug"
# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
# expressions read from the standard input.
@@ -373,8 +374,6 @@ module IRB
class Abort < Exception;end
@CONF = {}
-
-
# Displays current configuration.
#
# Modifying the configuration is achieved by sending a message to IRB.conf.
@@ -441,7 +440,7 @@ module IRB
# Creates a new irb session
def initialize(workspace = nil, input_method = nil)
@context = Context.new(self, workspace, input_method)
- @context.main.extend ExtendCommandBundle
+ @context.workspace.load_commands_to_main
@signal_status = :IN_IRB
@scanner = RubyLex.new(@context)
end
@@ -457,6 +456,38 @@ module IRB
end
end
+ def debug_readline(binding)
+ workspace = IRB::WorkSpace.new(binding)
+ context.workspace = workspace
+ context.workspace.load_commands_to_main
+ scanner.increase_line_no(1)
+
+ # When users run:
+ # 1. Debugging commands, like `step 2`
+ # 2. Any input that's not irb-command, like `foo = 123`
+ #
+ # Irb#eval_input will simply return the input, and we need to pass it to the debugger.
+ input = if IRB.conf[:SAVE_HISTORY] && context.io.support_history_saving?
+ # Previous IRB session's history has been saved when `Irb#run` is exited
+ # We need to make sure the saved history is not saved again by reseting the counter
+ context.io.reset_history_counter
+
+ begin
+ eval_input
+ ensure
+ context.io.save_history
+ end
+ else
+ eval_input
+ end
+
+ if input&.include?("\n")
+ scanner.increase_line_no(input.count("\n") - 1)
+ end
+
+ input
+ end
+
def run(conf = IRB.conf)
in_nested_session = !!conf[:MAIN_CONTEXT]
conf[:IRB_RC].call(context) if conf[:IRB_RC]
@@ -542,6 +573,18 @@ module IRB
@scanner.each_top_level_statement do |line, line_no, is_assignment|
signal_status(:IN_EVAL) do
begin
+ # If the integration with debugger is activated, we need to handle certain input differently
+ if @context.with_debugger
+ command_class = load_command_class(line)
+ # First, let's pass debugging command's input to debugger
+ # Secondly, we need to let debugger evaluate non-command input
+ # Otherwise, the expression will be evaluated in the debugger's main session thread
+ # This is the only way to run the user's program in the expected thread
+ if !command_class || ExtendCommand::DebugCommand > command_class
+ return line
+ end
+ end
+
evaluate_line(line, line_no)
# Don't echo if the line ends with a semicolon
@@ -633,6 +676,12 @@ module IRB
@context.evaluate(line, line_no)
end
+ def load_command_class(line)
+ command, _ = line.split(/\s/, 2)
+ command_name = @context.command_aliases[command.to_sym]
+ ExtendCommandBundle.load_command(command_name || command)
+ end
+
def convert_invalid_byte_sequence(str, enc)
str.force_encoding(enc)
str.scrub { |c|
@@ -986,12 +1035,32 @@ class Binding
#
# See IRB@Usage for more information.
def irb(show_code: true)
+ # Setup IRB with the current file's path and no command line arguments
IRB.setup(source_location[0], argv: [])
+ # Create a new workspace using the current binding
workspace = IRB::WorkSpace.new(self)
+ # Print the code around the binding if show_code is true
STDOUT.print(workspace.code_around_binding) if show_code
- binding_irb = IRB::Irb.new(workspace)
- binding_irb.context.irb_path = File.expand_path(source_location[0])
- binding_irb.run(IRB.conf)
- binding_irb.debug_break
+ # Get the original IRB instance
+ debugger_irb = IRB.instance_variable_get(:@debugger_irb)
+
+ irb_path = File.expand_path(source_location[0])
+
+ if debugger_irb
+ # If we're already in a debugger session, set the workspace and irb_path for the original IRB instance
+ debugger_irb.context.workspace = workspace
+ debugger_irb.context.irb_path = irb_path
+ # If we've started a debugger session and hit another binding.irb, we don't want to start an IRB session
+ # instead, we want to resume the irb:rdbg session.
+ IRB::Debug.setup(debugger_irb)
+ IRB::Debug.insert_debug_break
+ debugger_irb.debug_break
+ else
+ # If we're not in a debugger session, create a new IRB instance with the current workspace
+ binding_irb = IRB::Irb.new(workspace)
+ binding_irb.context.irb_path = irb_path
+ binding_irb.run(IRB.conf)
+ binding_irb.debug_break
+ end
end
end