From c35c315970885ea5ae0abf644ff2f9d5b61faebf Mon Sep 17 00:00:00 2001 From: Shigeru Hagiwara Date: Sat, 9 Aug 2025 16:03:38 +0900 Subject: [PATCH] Fix display of wrapped lines at file end --- e2e/test_wrapping.py | 30 +++++++++++++++++++++++++++++ src/command/commands/move_cursor.rs | 21 +++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/e2e/test_wrapping.py b/e2e/test_wrapping.py index caf89cc..6639164 100644 --- a/e2e/test_wrapping.py +++ b/e2e/test_wrapping.py @@ -176,6 +176,36 @@ def test_scroll_up_into_wrapped_line(): os.unlink(path) +def test_last_line_wrapped_visible(): + long_line = "x" * 120 + other_lines = "\n".join(str(i) for i in range(1, 10)) + file_content = f"{other_lines}\n{long_line}\n" + fd, path = tempfile.mkstemp() + try: + with os.fdopen(fd, "w") as f: + f.write(file_content) + + env = os.environ.copy() + env.setdefault("TERM", "xterm") + child = pexpect.spawn(EVI_BIN, [path], env=env, encoding="utf-8") + child.delaybeforesend = float(os.getenv("EVI_DELAY_BEFORE_SEND", "0.1")) + child.setwinsize(10, 60) + + get_screen_and_cursor(child) + child.send("j" * 9) + screen, _ = get_screen_and_cursor(child) + lines = _parse_screen(screen) + + x_rows = [row for row, text in lines.items() if text.startswith("x")] + assert len(x_rows) == 2 + assert sum(len(lines[row]) for row in x_rows) == 120 + + child.send(":q!\r") + child.expect(pexpect.EOF) + finally: + os.unlink(path) + + # def test_full_width_lines_no_extra_blank_lines(): # file_content = "{}\n{}\n{}\n".format("A" * 80, "B" * 80, "C" * 80) # fd, path = tempfile.mkstemp() diff --git a/src/command/commands/move_cursor.rs b/src/command/commands/move_cursor.rs index 4ade4ef..fbb372d 100644 --- a/src/command/commands/move_cursor.rs +++ b/src/command/commands/move_cursor.rs @@ -155,8 +155,27 @@ impl Command for NextLine { editor.cursor_position_in_buffer.row = next_row; - // 目的の列に移動 + // Ensure the entire current line is visible. If the line would + // extend beyond the bottom of the screen, scroll the window up + // until it fits (leaving at least one blank line after it). let next_line = &editor.buffer.lines[editor.cursor_position_in_buffer.row]; + let mut next_line_height = + get_line_height(next_line, editor.terminal_size.width) as u16; + while editor.cursor_position_on_screen.row + next_line_height + >= editor.content_height() + && editor.window_position_in_buffer.row + 1 < editor.buffer.lines.len() + { + let first_line = &editor.buffer.lines[editor.window_position_in_buffer.row]; + let first_line_height = + get_line_height(first_line, editor.terminal_size.width) as u16; + editor.window_position_in_buffer.row += 1; + editor.cursor_position_on_screen.row = editor + .cursor_position_on_screen + .row + .saturating_sub(first_line_height); + } + + // 目的の列に移動 let num_of_chars_of_next_line = next_line.chars().count(); let destination_col = if current_cursor_col_in_buffer > num_of_chars_of_next_line { num_of_chars_of_next_line