diff --git a/.github/workflows/protocol.yml b/.github/workflows/protocol.yml index b2889aab1..4225bd934 100644 --- a/.github/workflows/protocol.yml +++ b/.github/workflows/protocol.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.6', '2.7', '3.0', '3.1', 'head', 'debug'] + ruby-version: ["2.7", "3.0", "3.1", "head", "debug"] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index a41da836a..8f8685665 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2', 'head', 'debug'] + ruby-version: ["2.7", "3.0", "3.1", "3.2", "head", "debug"] steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index f47914053..ddc085cd5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # debug.rb -This library provides debugging functionality to Ruby (MRI) 2.6 and later. +This library provides debugging functionality to Ruby (MRI) 2.7 and later. This debug.rb is replacement of traditional lib/debug.rb standard library which is implemented by `set_trace_func`. New debug.rb has several advantages: @@ -477,6 +477,7 @@ config set no_color true * `RUBY_DEBUG_NO_RELINE` (`no_reline`): Do not use Reline library (default: false) * `RUBY_DEBUG_NO_HINT` (`no_hint`): Do not show the hint on the REPL (default: false) * `RUBY_DEBUG_NO_LINENO` (`no_lineno`): Do not show line numbers (default: false) + * `RUBY_DEBUG_IRB_CONSOLE` (`irb_console`): Use IRB as the console (default: false) * CONTROL * `RUBY_DEBUG_SKIP_PATH` (`skip_path`): Skip showing/entering frames for given paths diff --git a/debug.gemspec b/debug.gemspec index a07018a77..2a5891f32 100644 --- a/debug.gemspec +++ b/debug.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |spec| spec.description = %q{Debugging functionality for Ruby. This is completely rewritten debug.rb which was contained by the ancient Ruby versions.} spec.homepage = "https://github.com/ruby/debug" spec.licenses = ["Ruby", "BSD-2-Clause"] - spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0") + spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0") spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.extensions = ['ext/debug/extconf.rb'] - spec.add_dependency "irb", ">= 1.5.0" # for binding.irb(show_code: false) + spec.add_dependency "irb", "~> 1.10" # for irb:debug integration spec.add_dependency "reline", ">= 0.3.8" end diff --git a/lib/debug/config.rb b/lib/debug/config.rb index e954b56bf..66383c88c 100644 --- a/lib/debug/config.rb +++ b/lib/debug/config.rb @@ -22,6 +22,7 @@ module DEBUGGER__ no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"], no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"], no_lineno: ['RUBY_DEBUG_NO_LINENO', "UI: Do not show line numbers", :bool, "false"], + irb_console: ["RUBY_DEBUG_IRB_CONSOLE", "UI: Use IRB as the console", :bool, "false"], # control setting skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path], diff --git a/lib/debug/irb_integration.rb b/lib/debug/irb_integration.rb new file mode 100644 index 000000000..a2ea34b65 --- /dev/null +++ b/lib/debug/irb_integration.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'irb' + +module DEBUGGER__ + module IrbPatch + def evaluate(line, line_no) + SESSION.send(:restart_all_threads) + super + # This is to communicate with the test framework so it can feed the next input + puts "INTERNAL_INFO: {}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal' + ensure + SESSION.send(:stop_all_threads) + end + end + + class ThreadClient + def activate_irb_integration + IRB.setup(location, argv: []) + workspace = IRB::WorkSpace.new(current_frame&.binding || TOPLEVEL_BINDING) + irb = IRB::Irb.new(workspace) + IRB.conf[:MAIN_CONTEXT] = irb.context + IRB::Debug.setup(irb) + IRB::Context.prepend(IrbPatch) + end + end +end diff --git a/lib/debug/session.rb b/lib/debug/session.rb index 321ba8231..3d51af192 100644 --- a/lib/debug/session.rb +++ b/lib/debug/session.rb @@ -202,6 +202,11 @@ def activate ui = nil, on_fork: false end @tp_thread_end.enable + if CONFIG[:irb_console] && !CONFIG[:open] + require_relative "irb_integration" + thc.activate_irb_integration + end + # session start q << true session_server_main @@ -936,10 +941,11 @@ def register_default_command # * Invoke `irb` on the current frame. register_command 'irb' do |arg| if @ui.remote? - @ui.puts "not supported on the remote console." + @ui.puts "\nIRB is not supported on the remote console." :retry + else + request_eval :irb, nil end - request_eval :irb, nil end ### Trace diff --git a/lib/debug/thread_client.rb b/lib/debug/thread_client.rb index 3ec53fb81..6f9da4af1 100644 --- a/lib/debug/thread_client.rb +++ b/lib/debug/thread_client.rb @@ -1048,13 +1048,8 @@ def wait_next_action_ when :call result = frame_eval(eval_src) when :irb - require 'irb' # prelude's binding.irb doesn't have show_code option - begin - result = frame_eval('binding.irb(show_code: false)', binding_location: true) - ensure - # workaround: https://github.com/ruby/debug/issues/308 - Reline.prompt_proc = nil if defined? Reline - end + require_relative "irb_integration" + activate_irb_integration when :display, :try_display failed_results = [] eval_src.each_with_index{|src, i| diff --git a/misc/README.md.erb b/misc/README.md.erb index 5778e5a90..ff800fabf 100644 --- a/misc/README.md.erb +++ b/misc/README.md.erb @@ -2,7 +2,7 @@ # debug.rb -This library provides debugging functionality to Ruby (MRI) 2.6 and later. +This library provides debugging functionality to Ruby (MRI) 2.7 and later. This debug.rb is replacement of traditional lib/debug.rb standard library which is implemented by `set_trace_func`. New debug.rb has several advantages: diff --git a/test/console/irb_test.rb b/test/console/irb_test.rb new file mode 100644 index 000000000..478a37036 --- /dev/null +++ b/test/console/irb_test.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require_relative '../support/console_test_case' + +module DEBUGGER__ + class IrbTest < ConsoleTestCase + def setup + @original_pager = ENV["PAGER"] + ENV["PAGER"] = "cat" + end + + def teardown + ENV["PAGER"] = @original_pager + end + + def program + <<~RUBY + 1| a = 1 + 2| b = 2 + RUBY + end + + def test_irb_command_is_disabled_in_remote_mode + debug_code(program, remote: :remote_only) do + type 'irb' + assert_line_text 'IRB is not supported on the remote console.' + type 'q!' + end + end + + def test_irb_command_switches_console_to_irb + debug_code(program, remote: false) do + type 'irb' + type '123' + assert_line_text 'irb:rdbg(main):002> 123' + type 'irb_info' + assert_line_text('IRB version:') + type 'next' + type 'info' + assert_line_text([/a = 1/, /b = nil/]) + type 'q!' + end + end + + def test_irb_console_config_activates_irb + ENV["RUBY_DEBUG_IRB_CONSOLE"] = "true" + + debug_code(program, remote: false) do + type '123' + assert_line_text 'irb:rdbg(main):002> 123' + type 'irb_info' + assert_line_text('IRB version:') + type 'next' + type 'info' + assert_line_text([/a = 1/, /b = nil/]) + type 'q!' + end + ensure + ENV["RUBY_DEBUG_IRB_CONSOLE"] = nil + end + end +end diff --git a/test/support/console_test_case.rb b/test/support/console_test_case.rb index 49e0bb87f..efdc1bafe 100644 --- a/test/support/console_test_case.rb +++ b/test/support/console_test_case.rb @@ -217,6 +217,7 @@ def prepare_test_environment(program, test_steps, &block) ENV['RUBY_DEBUG_TEST_UI'] = 'terminal' ENV['RUBY_DEBUG_NO_RELINE'] = 'true' ENV['RUBY_DEBUG_HISTORY_FILE'] = '' + ENV['TERM'] = 'dumb' write_temp_file(strip_line_num(program)) @scenario = []