Skip to content

Commit

Permalink
Fix line wrapped cursor position (#791)
Browse files Browse the repository at this point in the history
Cursor position calculation was wrong when the input line contains "\1" or CSI escape sequence.
  • Loading branch information
tompng authored Dec 10, 2024
1 parent 6a7e249 commit a1943da
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 29 additions & 0 deletions test/reline/test_line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit a1943da

Please sign in to comment.