diff --git a/src/prompt_toolkit/document.py b/src/prompt_toolkit/document.py index 003a7852e..5c86b40ec 100644 --- a/src/prompt_toolkit/document.py +++ b/src/prompt_toolkit/document.py @@ -1066,7 +1066,7 @@ def paste_clipboard_data( ) new_cursor_position = self.cursor_position + len(data.text) * count - if before: + if before and data.text: new_cursor_position -= 1 elif data.type == SelectionType.LINES: diff --git a/tests/test_cli.py b/tests/test_cli.py index a876f2993..9f5eca128 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -864,6 +864,19 @@ def test_vi_character_paste(): assert result.cursor_position == 2 +@pytest.mark.parametrize("paste", ["P", "3P"]) +def test_vi_paste_before_with_empty_clipboard(paste): + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) + + result, cli = feed(f"abcd\x1b{paste}\r") + assert result.text == "abcd" + assert result.cursor_position == 3 + + # A subsequent command should still act on the original character. + result, cli = feed(f"abcd\x1b{paste}x\r") + assert result.text == "abc" + + def test_vi_temp_navigation_mode(): """ Test c-o binding: go for one action into navigation mode. diff --git a/tests/test_document.py b/tests/test_document.py index ff54310d5..96f3173b5 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -4,7 +4,9 @@ import pytest +from prompt_toolkit.clipboard import ClipboardData from prompt_toolkit.document import Document +from prompt_toolkit.selection import PasteMode, SelectionType @pytest.fixture @@ -79,3 +81,31 @@ def test_get_word_before_cursor_with_whitespace_and_pattern(): _FIND_WORD_RE = re.compile(r"([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)") assert document.get_word_before_cursor(pattern=_FIND_WORD_RE) == "" + + +@pytest.mark.parametrize( + "text,cursor_position", [("", 0), ("abcd", 0), ("abcd", 2), ("abcd", 4)] +) +@pytest.mark.parametrize( + "paste_mode", [PasteMode.EMACS, PasteMode.VI_BEFORE, PasteMode.VI_AFTER] +) +def test_paste_empty_character_data(text, cursor_position, paste_mode): + document = Document(text, cursor_position) + + result = document.paste_clipboard_data(ClipboardData(), paste_mode=paste_mode) + + assert result.text == text + assert result.cursor_position == cursor_position + + +@pytest.mark.parametrize( + "paste_mode,text,cursor_position", + [(PasteMode.VI_BEFORE, "\nabc", 0), (PasteMode.VI_AFTER, "abc\n", 4)], +) +def test_paste_empty_line_data(paste_mode, text, cursor_position): + result = Document("abc", 1).paste_clipboard_data( + ClipboardData("", SelectionType.LINES), paste_mode=paste_mode + ) + + assert result.text == text + assert result.cursor_position == cursor_position