From a1943daaf436af1a8f757a25342d43947dceb129 Mon Sep 17 00:00:00 2001 From: tomoya ishida Date: Tue, 10 Dec 2024 19:28:16 +0900 Subject: [PATCH] Fix line wrapped cursor position (#791) Cursor position calculation was wrong when the input line contains "\1" or CSI escape sequence. --- lib/reline/line_editor.rb | 2 +- test/reline/test_line_editor.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 0ba44f8bc6..86c7596df7 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -436,7 +436,7 @@ def render_line_differential(old_items, new_items) # Calculate cursor position in word wrapped content. def wrapped_cursor_position prompt_width = calculate_width(prompt_list[@line_index], true) - line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer) + line_before_cursor = Reline::Unicode.escape_for_print(whole_lines[@line_index].byteslice(0, @byte_pointer)) wrapped_line_before_cursor = split_line_by_width(' ' * prompt_width + line_before_cursor, screen_width) wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1 wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last) diff --git a/test/reline/test_line_editor.rb b/test/reline/test_line_editor.rb index 85a814a799..28fcbfa6df 100644 --- a/test/reline/test_line_editor.rb +++ b/test/reline/test_line_editor.rb @@ -59,6 +59,35 @@ def test_retrieve_completion_quote end end + class CursorPositionTest < Reline::TestCase + def setup + @line_editor = Reline::LineEditor.new(nil) + @line_editor.instance_variable_set(:@config, Reline::Config.new) + end + + def test_cursor_position_with_escaped_input + @line_editor.instance_variable_set(:@screen_size, [4, 16]) + @line_editor.instance_variable_set(:@prompt, "\e[1mprompt\e[0m> ") + @line_editor.instance_variable_set(:@buffer_of_lines, ["\e[1m\0\1\2\3\4\5\6\7abcd"]) + @line_editor.instance_variable_set(:@line_index, 0) + # prompt> ^[[1m^@^ + # A^B^C^D^E^F^Gabc + # d + @line_editor.instance_variable_set(:@byte_pointer, 0) + assert_equal [8, 0], @line_editor.wrapped_cursor_position + @line_editor.instance_variable_set(:@byte_pointer, 5) + assert_equal [15, 0], @line_editor.wrapped_cursor_position + @line_editor.instance_variable_set(:@byte_pointer, 6) + assert_equal [1, 1], @line_editor.wrapped_cursor_position + @line_editor.instance_variable_set(:@byte_pointer, 14) + assert_equal [15, 1], @line_editor.wrapped_cursor_position + @line_editor.instance_variable_set(:@byte_pointer, 15) + assert_equal [0, 2], @line_editor.wrapped_cursor_position + @line_editor.instance_variable_set(:@byte_pointer, 16) + assert_equal [1, 2], @line_editor.wrapped_cursor_position + end + end + class RenderLineDifferentialTest < Reline::TestCase class TestIO < Reline::IO def write(string)