diff --git a/lib/irb.rb b/lib/irb.rb index f3abed820..ad6ec78aa 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -886,7 +886,11 @@ def IRB.start(ap_path = nil) # Quits irb def IRB.irb_exit(*) - throw :IRB_EXIT + throw :IRB_EXIT, false + end + + def IRB.irb_exit!(*) + throw :IRB_EXIT, true end # Aborts then interrupts irb. @@ -968,7 +972,8 @@ def run(conf = IRB.conf) conf[:IRB_RC].call(context) if conf[:IRB_RC] conf[:MAIN_CONTEXT] = context - save_history = !in_nested_session && conf[:SAVE_HISTORY] && context.io.support_history_saving? + supports_history_saving = conf[:SAVE_HISTORY] && context.io.support_history_saving? + save_history = !in_nested_session && supports_history_saving if save_history context.io.load_history @@ -979,13 +984,21 @@ def run(conf = IRB.conf) end begin - catch(:IRB_EXIT) do + forced_exit = false + + forced_exit = catch(:IRB_EXIT) do eval_input end ensure trap("SIGINT", prev_trap) conf[:AT_EXIT].each{|hook| hook.call} - context.io.save_history if save_history + + if forced_exit + context.io.save_history if supports_history_saving + Kernel.exit(0) + else + context.io.save_history if save_history + end end end diff --git a/lib/irb/cmd/exit_forced_action.rb b/lib/irb/cmd/exit_forced_action.rb new file mode 100644 index 000000000..e5df75b68 --- /dev/null +++ b/lib/irb/cmd/exit_forced_action.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative "nop" + +module IRB + # :stopdoc: + + module ExtendCommand + class ExitForcedAction < Nop + category "IRB" + description "Exit the current process." + + def execute(*) + IRB.irb_exit! + rescue UncaughtThrowError + Kernel.exit(0) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index 91ca96e91..2db2b8057 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -36,6 +36,11 @@ def irb_context [:quit, OVERRIDE_PRIVATE_ONLY], [:irb_quit, OVERRIDE_PRIVATE_ONLY], ], + [ + :irb_exit!, :ExitForcedAction, "cmd/exit_forced_action", + [:exit!, OVERRIDE_PRIVATE_ONLY], + ], + [ :irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws", [:cwws, NO_OVERRIDE], diff --git a/test/irb/test_debug_cmd.rb b/test/irb/test_debug_cmd.rb index 0fb45af47..cbd012009 100644 --- a/test/irb/test_debug_cmd.rb +++ b/test/irb/test_debug_cmd.rb @@ -255,6 +255,47 @@ def test_exit assert_match(/irb\(main\):001> next/, output) end + def test_forced_exit_finishes_process_when_nested_sessions + write_ruby <<~'ruby' + puts "First line" + puts "Second line" + binding.irb + puts "Third line" + binding.irb + puts "Fourth line" + ruby + + output = run_ruby_file do + type "123" + type "456" + type "exit!" + end + + assert_match(/First line\r\n/, output) + assert_match(/Second line\r\n/, output) + assert_match(/irb\(main\):001> 123/, output) + assert_match(/irb\(main\):002> 456/, output) + refute_match(/Third line\r\n/, output) + refute_match(/Fourth line\r\n/, output) + end + + def test_forced_exit + write_ruby <<~'ruby' + puts "Hello" + binding.irb + ruby + + output = run_ruby_file do + type "123" + type "456" + type "exit!" + end + + assert_match(/Hello\r\n/, output) + assert_match(/irb\(main\):001> 123/, output) + assert_match(/irb\(main\):002> 456/, output) + end + def test_quit write_ruby <<~'RUBY' binding.irb diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index e6448ada3..49f3698f9 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -379,6 +379,24 @@ def foo HISTORY end + def test_history_saving_with_exit! + write_history "" + + write_ruby <<~'RUBY' + binding.irb + RUBY + + run_ruby_file do + type "'starting session'" + type "exit!" + end + + assert_equal <<~HISTORY, @history_file.open.read + 'starting session' + exit! + HISTORY + end + def test_history_saving_with_nested_sessions_and_prior_history write_history <<~HISTORY old_history_1