Skip to content

fix(shell): prevent Ctrl+W from overwriting system clipboard [WIP]#1686

Open
wbxl2000 wants to merge 1 commit intoMoonshotAI:mainfrom
wbxl2000:fix/ctrl-w-clipboard-pollution
Open

fix(shell): prevent Ctrl+W from overwriting system clipboard [WIP]#1686
wbxl2000 wants to merge 1 commit intoMoonshotAI:mainfrom
wbxl2000:fix/ctrl-w-clipboard-pollution

Conversation

@wbxl2000
Copy link
Copy Markdown
Contributor

@wbxl2000 wbxl2000 commented Mar 31, 2026

Warning

WIP — 核心思路已验证,还需要补充测试和进一步验证边界情况后再标记为 merge ready。

Summary

Closes #1567

On macOS, pressing Ctrl+W (unix-word-rubout) in the kimi-cli prompt overwrites the system clipboard with the deleted word. This happens because PyperclipClipboard is passed to PromptSession, causing all Emacs-style kill commands (Ctrl+W/K/U) to sync deleted text to the system clipboard via pyperclip.copy().

Root cause

PyperclipClipboard was introduced in commit 0703579 to fix Ctrl+V text paste — the default InMemoryClipboard cannot read the system clipboard, so event.app.clipboard.get_data() returned empty content. However, PyperclipClipboard is bidirectional: it also writes to the system clipboard on every kill operation.

Fix

  • Replace PyperclipClipboard with the default InMemoryClipboard (pass clipboard=None to PromptSession)
  • Change the Ctrl+V text paste handler to read the system clipboard directly via pyperclip.paste() instead of event.app.clipboard.get_data()

This approach is consistent with how zsh/bash handle kill operations (internal kill ring only, never touching the system clipboard). For reference, among similar projects using prompt_toolkit (aider, IPython, mycli), none use PyperclipClipboard. xonsh uses it but explicitly implements disable_copy_on_deletion() to prevent this exact issue.

Behavior after fix

Operation Before After
Ctrl+W/K/U Writes to system clipboard ❌ Internal kill ring only ✅
Ctrl+Y (yank) Reads system clipboard Reads internal kill ring (same as zsh/bash) ✅
Ctrl+V (paste) Reads system clipboard via PyperclipClipboard Reads system clipboard via pyperclip.paste() directly ✅
Bracketed paste (terminal Cmd+V) Unaffected Unaffected ✅

Test plan

  • Verify Ctrl+W no longer overwrites system clipboard
  • Verify Ctrl+V still pastes text from system clipboard
  • Verify Ctrl+V still pastes images
  • Verify Ctrl+Y yanks killed text from internal kill ring
  • Verify bracketed paste (terminal Cmd+V) still works
  • Add unit tests

Open with Devin

Replace PyperclipClipboard with the default InMemoryClipboard so that
Emacs-style kill commands (Ctrl+W/K/U) only write to the internal kill
ring instead of the system clipboard.  Ctrl+V text paste now reads the
system clipboard directly via pyperclip.paste(), keeping paste
functionality intact.

Closes MoonshotAI#1567
Copilot AI review requested due to automatic review settings March 31, 2026 13:09
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a macOS UX issue in the interactive prompt where Emacs-style kill commands (e.g. Ctrl+W) were overwriting the system clipboard by removing prompt_toolkit’s bidirectional PyperclipClipboard integration and switching Ctrl+V paste to read the system clipboard directly.

Changes:

  • Stop passing PyperclipClipboard into PromptSession so kill/yank operations use prompt_toolkit’s internal kill ring instead of the system clipboard.
  • Update the Ctrl+V keybinding to paste via pyperclip.paste() (with early-return on failure/empty clipboard).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1477 to 1490
if clipboard_available:

@_kb.add("c-v", eager=True)
def _(event: KeyPressEvent) -> None:
if self._try_paste_media(event):
return
clipboard_data = event.app.clipboard.get_data()
if clipboard_data is None: # type: ignore[reportUnnecessaryComparison]
try:
text = pyperclip.paste()
except Exception:
return
if not text:
return
self._insert_pasted_text(event.current_buffer, clipboard_data.text)
self._insert_pasted_text(event.current_buffer, text)
event.app.invalidate()
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Ctrl+V handler was changed to read from the system clipboard via pyperclip.paste(), but there are no unit tests covering this keybinding behavior (successful paste, empty clipboard, and exception path). Since there are already prompt clipboard-related tests under tests/ui_and_conv/test_prompt_clipboard.py, please add coverage that mocks pyperclip.paste() and asserts the handler inserts text into the buffer and invalidates the app (and that exceptions don’t crash the prompt).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Disable ctrl+w clipboard

2 participants