summaryrefslogtreecommitdiff
path: root/lib/irb/debug/ui.rb
diff options
context:
space:
mode:
authorStan Lo <stan001212@gmail.com>2023-08-13 19:30:30 +0100
committergit <svn-admin@ruby-lang.org>2023-08-13 18:30:34 +0000
commit7f8f62c93bf3d11a0321fa91823065a2ff36f6d0 (patch)
tree2dffe13305f50883f33644f9d701ebb832ec0ab4 /lib/irb/debug/ui.rb
parent9099d62ac77cdca548bc4110e2cb03057ef0ac8f (diff)
[ruby/irb] Support seamless integration with ruby/debug
(https://github.com/ruby/irb/pull/575) * Support native integration with ruby/debug * Prevent using multi-irb and activating debugger at the same time Multi-irb makes a few assumptions: - IRB will manage all threads that host sub-irb sessions - All IRB sessions will be run on the threads created by IRB itself However, when using the debugger these assumptions are broken: - `debug` will freeze ALL threads when it suspends the session (e.g. when hitting a breakpoint, or performing step-debugging). - Since the irb-debug integration runs IRB as the debugger's interface, it will be run on the debugger's thread, which is not managed by IRB. So we should prevent the 2 features from being used at the same time. To do that, we check if the other feature is already activated when executing the commands that would activate the other feature. https://github.com/ruby/irb/commit/d8fb3246be
Diffstat (limited to 'lib/irb/debug/ui.rb')
-rw-r--r--lib/irb/debug/ui.rb104
1 files changed, 104 insertions, 0 deletions
diff --git a/lib/irb/debug/ui.rb b/lib/irb/debug/ui.rb
new file mode 100644
index 0000000000..a4ca4fdf0f
--- /dev/null
+++ b/lib/irb/debug/ui.rb
@@ -0,0 +1,104 @@
+require 'io/console/size'
+require 'debug/console'
+
+module IRB
+ module Debug
+ class UI < DEBUGGER__::UI_Base
+ def initialize(thread, irb)
+ @thread = thread
+ @irb = irb
+ end
+
+ def remote?
+ false
+ end
+
+ def activate session, on_fork: false
+ end
+
+ def deactivate
+ end
+
+ def width
+ if (w = IO.console_size[1]) == 0 # for tests PTY
+ 80
+ else
+ w
+ end
+ end
+
+ def quit n
+ yield
+ exit n
+ end
+
+ def ask prompt
+ setup_interrupt do
+ print prompt
+ ($stdin.gets || '').strip
+ end
+ end
+
+ def puts str = nil
+ case str
+ when Array
+ str.each{|line|
+ $stdout.puts line.chomp
+ }
+ when String
+ str.each_line{|line|
+ $stdout.puts line.chomp
+ }
+ when nil
+ $stdout.puts
+ end
+ end
+
+ def readline _
+ setup_interrupt do
+ tc = DEBUGGER__::SESSION.get_thread_client(@thread)
+ cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
+
+ case cmd
+ when nil # when user types C-d
+ "continue"
+ else
+ cmd
+ end
+ end
+ end
+
+ def setup_interrupt
+ DEBUGGER__::SESSION.intercept_trap_sigint false do
+ current_thread = Thread.current # should be session_server thread
+
+ prev_handler = trap(:INT){
+ current_thread.raise Interrupt
+ }
+
+ yield
+ ensure
+ trap(:INT, prev_handler)
+ end
+ end
+
+ def after_fork_parent
+ parent_pid = Process.pid
+
+ at_exit{
+ DEBUGGER__::SESSION.intercept_trap_sigint_end
+ trap(:SIGINT, :IGNORE)
+
+ if Process.pid == parent_pid
+ # only check child process from its parent
+ begin
+ # wait for all child processes to keep terminal
+ Process.waitpid
+ rescue Errno::ESRCH, Errno::ECHILD
+ end
+ end
+ }
+ end
+ end
+ end
+end