diff --git a/src/prompt_toolkit/key_binding/bindings/vi.py b/src/prompt_toolkit/key_binding/bindings/vi.py index e3fca1b98..0451a8e31 100644 --- a/src/prompt_toolkit/key_binding/bindings/vi.py +++ b/src/prompt_toolkit/key_binding/bindings/vi.py @@ -544,13 +544,22 @@ def _complete_prev(event: E) -> None: b.start_completion(select_last=True) @handle("c-g", filter=vi_insert_mode) - @handle("c-y", filter=vi_insert_mode) def _accept_completion(event: E) -> None: """ Accept current completion. """ event.current_buffer.complete_state = None + @handle("c-y", filter=vi_insert_mode) + def _accept_completion_or_yank(event: E) -> None: + """ + Accept current completion, or yank when no completion is active. + """ + if event.current_buffer.complete_state is None: + get_by_name("yank").handler(event) + else: + event.current_buffer.complete_state = None + @handle("c-e", filter=vi_insert_mode) def _cancel_completion(event: E) -> None: """ diff --git a/tests/test_vi_insert_bindings.py b/tests/test_vi_insert_bindings.py new file mode 100644 index 000000000..0f1aca0ac --- /dev/null +++ b/tests/test_vi_insert_bindings.py @@ -0,0 +1,70 @@ +from prompt_toolkit.buffer import CompletionState +from prompt_toolkit.clipboard import ClipboardData, InMemoryClipboard +from prompt_toolkit.completion import Completion +from prompt_toolkit.document import Document +from prompt_toolkit.enums import EditingMode +from prompt_toolkit.input.defaults import create_pipe_input +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.output import DummyOutput +from prompt_toolkit.shortcuts import PromptSession + + +def test_ctrl_y_yanks_text_killed_by_ctrl_u_in_vi_insert_mode(): + with create_pipe_input() as inp: + inp.send_text("hello\x15\x19\r") + session = PromptSession( + input=inp, + output=DummyOutput(), + editing_mode=EditingMode.VI, + clipboard=InMemoryClipboard(), + ) + result = session.prompt() + + assert result == "hello" + + +def test_ctrl_y_still_accepts_active_completion_in_vi_insert_mode(): + clipboard = InMemoryClipboard(ClipboardData("clipboard text")) + + with create_pipe_input() as inp: + inp.send_text("\x19\r") + session = PromptSession( + input=inp, + output=DummyOutput(), + editing_mode=EditingMode.VI, + clipboard=clipboard, + ) + + def activate_completion() -> None: + buffer = session.default_buffer + buffer.document = Document("he", 2) + buffer.complete_state = CompletionState( + buffer.document, + [Completion("hello", start_position=-2)], + ) + buffer.go_to_completion(0) + + result = session.prompt(pre_run=activate_completion) + + assert result == "hello" + assert clipboard.get_data().text == "clipboard text" + + +def test_custom_ctrl_y_binding_overrides_vi_default(): + bindings = KeyBindings() + + @bindings.add("c-y") + def custom_ctrl_y(event): + event.current_buffer.insert_text("CUSTOM") + + with create_pipe_input() as inp: + inp.send_text("\x19\r") + session = PromptSession( + input=inp, + output=DummyOutput(), + editing_mode=EditingMode.VI, + key_bindings=bindings, + ) + result = session.prompt() + + assert result == "CUSTOM"