From e996d1eaa19295a0dd8aef51d776981e8810346d Mon Sep 17 00:00:00 2001 From: hanruzhou Date: Mon, 13 Jul 2026 01:55:05 +0800 Subject: [PATCH] fix: guard SIGWINCH trap so Windows Ruby doesn't crash TrySelector#setup_terminal trapped SIGWINCH unconditionally. On Windows Ruby (x64-mingw-ucrt) SIGWINCH isn't a supported signal, so Signal.trap('WINCH') raises `ArgumentError: unsupported signal 'SIGWINCH'` the instant the picker opens, aborting before the TUI renders. Trap it only when the platform has it (`Signal.list.key?('WINCH')`). WINCH is the only signal trapped and restore_terminal is already nil-guarded, so teardown is unaffected; POSIX behaviour (including live-resize redraw) is unchanged. Adds a Minitest that stubs Signal.list to assert setup_terminal doesn't raise when WINCH is absent. Co-Authored-By: Claude Opus 4.8 (1M context) --- test/try_selector_test.rb | 25 +++++++++++++++++++++++++ try.rb | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/test/try_selector_test.rb b/test/try_selector_test.rb index d22d8ca..f52502c 100644 --- a/test/try_selector_test.rb +++ b/test/try_selector_test.rb @@ -380,3 +380,28 @@ def test_valid_rename_sets_selected assert_equal "brand-new", selected[:new] end end + +# ------------------------------------------------------------------- +# setup_terminal — SIGWINCH guard (cross-platform) +# ------------------------------------------------------------------- +class SetupTerminalWinchGuardTest < TrySelectorTestCase + # On platforms without SIGWINCH (e.g. Windows Ruby, RUBY_PLATFORM + # x64-mingw-ucrt), Signal.list has no "WINCH" key and Signal.trap('WINCH') + # raises ArgumentError: unsupported signal. setup_terminal must not crash + # there. Simulate that platform by stubbing Signal.list to omit WINCH. + def test_no_raise_when_winch_unsupported + sel = build_selector + without_winch = Signal.list.reject { |name, _| name == "WINCH" } + original_list = Signal.method(:list) + Signal.define_singleton_method(:list) { without_winch } + begin + sel.send(:setup_terminal) # must not raise + ensure + Signal.define_singleton_method(:list, original_list) + sel&.send(:restore_terminal) + end + # Guard skipped the trap, so no handler was captured. + assert_nil sel.instance_variable_get(:@old_winch_handler) + end +end + diff --git a/try.rb b/try.rb index d966f80..d781f86 100755 --- a/try.rb +++ b/try.rb @@ -77,7 +77,7 @@ def setup_terminal STDERR.print("#{Tui::ANSI::ALT_SCREEN_ON}#{Tui::ANSI.set_title("try")}#{Tui::ANSI::CURSOR_BLINK}") end - @old_winch_handler = Signal.trap('WINCH') { @needs_redraw = true } + @old_winch_handler = Signal.trap('WINCH') { @needs_redraw = true } if Signal.list.key?('WINCH') end def restore_terminal