From 3e606100225e4b97bab5e93836a78349922f8f17 Mon Sep 17 00:00:00 2001 From: YO4 Date: Fri, 19 Dec 2025 23:58:35 +0900 Subject: [PATCH 1/3] fix JRuby display issue JRuby with newer JVM dows not seems to flush stdout at an expected timing. By explicitly flushing the output, the screen state is finalized before issuing win32api calls. --- lib/reline/io/windows.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/reline/io/windows.rb b/lib/reline/io/windows.rb index 5c1ab6d080..8663aca54c 100644 --- a/lib/reline/io/windows.rb +++ b/lib/reline/io/windows.rb @@ -3,6 +3,8 @@ class Reline::Windows < Reline::IO attr_writer :output + attr_reader :jruby_p + alias flush_before_control? jruby_p def initialize @input_buf = [] @@ -32,6 +34,7 @@ def initialize @WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L') @legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 + @jruby_p = RUBY_ENGINE == 'jruby' end def encoding @@ -182,11 +185,6 @@ def call(*args) call_with_console_handle(@SetConsoleMode, mode) end - #if @legacy_console - # setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) - # @legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) - #end - def msys_tty?(io = @hConsoleInputHandle) # check if fd is a pipe if @GetFileType.call(io) != FILE_TYPE_PIPE @@ -366,22 +364,26 @@ def get_console_screen_buffer_info ALTERNATIVE_CSBI = [80, 24, 0, 0, 7, 0, 0, 79, 23].freeze def get_screen_size + @output.flush if flush_before_control? width, _, _, _, _, _, top, _, bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI [bottom - top + 1, width] end def cursor_pos + @output.flush if flush_before_control? _, _, x, y, _, _, top, = get_console_screen_buffer_info || ALTERNATIVE_CSBI Reline::CursorPos.new(x, y - top) end def move_cursor_column(val) + @output.flush if flush_before_control? _, _, _, y, = get_console_screen_buffer_info call_with_console_handle(@SetConsoleCursorPosition, y * 65536 + val) if y end def move_cursor_up(val) if val > 0 + @output.flush if flush_before_control? _, _, x, y, _, _, top, = get_console_screen_buffer_info return unless y y = (y - top) - val @@ -394,6 +396,7 @@ def move_cursor_up(val) def move_cursor_down(val) if val > 0 + @output.flush if flush_before_control? _, _, x, y, _, _, top, _, bottom = get_console_screen_buffer_info return unless y screen_height = bottom - top @@ -406,6 +409,7 @@ def move_cursor_down(val) end def erase_after_cursor + @output.flush if flush_before_control? width, _, x, y, attributes, = get_console_screen_buffer_info return unless x written = 0.chr * 4 @@ -423,6 +427,7 @@ def scroll_down(x) def clear_screen if @legacy_console + @output.flush if flush_before_control? width, _, _, _, attributes, _, top, _, bottom = get_console_screen_buffer_info return unless width fill_length = width * (bottom - top + 1) From 8f1a188464dd7fbc6a69288f73d58e2d239b4cb7 Mon Sep 17 00:00:00 2001 From: YO4 Date: Sat, 20 Dec 2025 02:10:35 +0900 Subject: [PATCH 2/3] handling cases of control sequence being unavailable MRI addresses this by converting the control code output to the console to win32api or by enabling VT sequence output to the console. JRuby does not do these things. Handle this by enabling VT sequence output to the console. If that fails, when in legacy console, SGR code printed as a garbage. --- lib/reline/io/windows.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/reline/io/windows.rb b/lib/reline/io/windows.rb index 8663aca54c..a57e75e13c 100644 --- a/lib/reline/io/windows.rb +++ b/lib/reline/io/windows.rb @@ -35,6 +35,10 @@ def initialize @legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 @jruby_p = RUBY_ENGINE == 'jruby' + if @jruby_p && @legacy_console + setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + @legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 + end end def encoding From 50166f72ef6cd0a147b3f3935d8d29088cb0c965 Mon Sep 17 00:00:00 2001 From: YO4 Date: Sat, 20 Dec 2025 02:10:43 +0900 Subject: [PATCH 3/3] support SGR color unavailable environment JRuby with legacy console window on windows can not use ANSI control sequence. So SGR controls are displayed as garbage. Introduce character-only dialog display can provide reduced services. The current Command Prompt window and Terminal do not have this issue. --- lib/reline/io/windows.rb | 5 +++++ lib/reline/line_editor.rb | 11 +++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/reline/io/windows.rb b/lib/reline/io/windows.rb index a57e75e13c..7a797439b7 100644 --- a/lib/reline/io/windows.rb +++ b/lib/reline/io/windows.rb @@ -4,6 +4,7 @@ class Reline::Windows < Reline::IO attr_writer :output attr_reader :jruby_p + alias jruby? jruby_p alias flush_before_control? jruby_p def initialize @@ -38,6 +39,10 @@ def initialize if @jruby_p && @legacy_console setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) @legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 + if @legacy_console + self.class.const_set(:RESET_COLOR, "") + ENV['RELINE_ALT_SCROLLBAR'] = '1' + end end end diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index bfffd17d59..0352d38f81 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -700,6 +700,8 @@ def add_dialog_proc(name, p, context = nil) end private def update_each_dialog(dialog, cursor_column, cursor_row, key = nil) + no_color = Reline::IOGate.win? && Reline::IOGate.win_legacy_console? && Reline::IOGate.jruby? + pointer_width = no_color ? 1 : 0 dialog.set_cursor_pos(cursor_column, cursor_row) dialog_render_info = dialog.call(key) if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty? @@ -743,6 +745,7 @@ def add_dialog_proc(name, p, context = nil) end dialog.column = dialog_render_info.pos.x dialog.width += @block_elem_width if scrollbar_pos + dialog.width += pointer_width diff = (dialog.column + dialog.width) - screen_width if diff > 0 dialog.column -= diff @@ -759,12 +762,12 @@ def add_dialog_proc(name, p, context = nil) dialog.width = screen_width end face = Reline::Face[dialog_render_info.face || :default] - scrollbar_sgr = face[:scrollbar] - default_sgr = face[:default] - enhanced_sgr = face[:enhanced] + scrollbar_sgr = no_color ? "" : face[:scrollbar] + default_sgr = no_color ? " " : face[:default] + enhanced_sgr = no_color ? "*" : face[:enhanced] dialog.contents = contents.map.with_index do |item, i| line_sgr = i == pointer ? enhanced_sgr : default_sgr - str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width) + str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width) - pointer_width str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true) colored_content = "#{line_sgr}#{str}" if scrollbar_pos