Skip to content

fix(tui): send a held key to vim Normal mode instead of typing it - #7126

Open
Lstarsky0 wants to merge 1 commit into
codewhale-hq:mainfrom
Lstarsky0:fix/vim-normal-keys-held-for-paste
Open

Lstarsky0 wants to merge 1 commit into
codewhale-hq:mainfrom
Lstarsky0:fix/vim-normal-keys-held-for-paste

Conversation

@Lstarsky0

Copy link
Copy Markdown
Contributor

What and why

With composer_vim_mode = "vim", Normal-mode keys type into the draft on any terminal that paste.rs doesn't trust to deliver bracketed paste: every Windows terminal, and anything whose TERM_PROGRAM is unset or isn't ghostty, iTerm.app, WezTerm or Apple_Terminal (tmux and VS Code's terminal set their own). There, the raw-paste guard holds each plain ASCII character for 8 ms to see whether a paste follows, and when nothing does, flush_paste_burst_before_composer inserts it. That happens before the event loop's Normal-mode arm sees the key, so i, x, dd and the motions all go into the draft as text, and Esc then clears the composer. The keys only start working after the session's first bracketed paste.

Launch composer, TERM_PROGRAM unset, one key at a time, before:

i            ❯ i
hello world  ❯ ihello world
Esc          ❯ Type a message…
0, x, w      ❯ 0xw

After:

i            ❯ Type a message…
hello world  ❯ hello world
Esc          ❯ hello world
0, x         ❯ ello world
w, x         ❯ ello orld
dd           ❯ Type a message…

A held character now goes to vim_mode::handle_vim_normal_key when no slash or mention menu is open, the condition that arm checks; it is absorbed in Visual mode, as the arm after it does; and it is typed into the composer otherwise. The function already does this for a held Space, which goes to the transcript when the composer is empty. A burst is still a paste: abc sent in one write lands in the draft as text in Normal mode, before and after.

Issue

No-Issue: vim Normal-mode keys are typed as text on a terminal without trusted bracketed paste; I searched and found no issue for it

How I tested it

  • Ran a debug build of this change and one of main at b8afed5 in a pseudo-terminal with an empty HOME and composer_vim_mode = "vim" in settings.toml, sending one key at a time on the launch composer. With TERM_PROGRAM unset, the results are above; v then q typed vq before and does nothing now. With TERM_PROGRAM=iTerm.app the two builds give the same results, and the steps before the abc burst match what this change now gives with it unset; the burst itself differs, since a trusted terminal reads it as typed keys rather than a paste. /he, Esc, x reads /hxe with this change, as it does with iTerm.app, because the slash menu is open.
  • cargo nextest run -p codewhale-tui --lib: 13908 of 13912 passed, with the 260 paste, vim, composer and event_loop tests among them. The 4 failures are plugin import tests (three in plugins_dsh_tests, plus plugins_skill_tests::dsh_packages_import_through_the_reviewed_install_and_update_flow, all "composition reviewer is unavailable"); the same 4 fail on main at b8afed5 without this change.
  • cargo fmt --all -- --check, cargo clippy -p codewhale-tui --all-targets --all-features with CI's flags (also on 1.99), cargo check -p codewhale-tui --all-targets and cargo +1.89.0 check -p codewhale-tui: clean.

Checklist

  • One focused change, on b8afed5; it merges cleanly into current main
  • cargo fmt --all passes, and tests cover new or changed behavior
  • If this adds or changes a user-facing feature, I updated its docs and its row in docs/features.toml (cargo test -p codewhale-tui --test feature_registry checks it) — n/a

On a terminal that isn't trusted to deliver bracketed paste, the raw-paste
guard holds each plain character for a few milliseconds and, when nothing
follows, types it into the composer. That happens before the event loop's
Normal-mode arm sees the key, so with vim mode on, i, x, dd and the
motions went into the draft as text until the session's first bracketed
paste. A held character now goes to the Normal-mode keys when no
completion menu is open, the condition that arm checks; it is absorbed in
Visual mode, as the arm after it does, and typed otherwise.

Signed-off-by: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com>
@Lstarsky0
Lstarsky0 requested a review from Hmbown as a code owner October 28, 2026 08:07

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@github-actions github-actions Bot added the contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm label Oct 28, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-28T08:10:11.604200Z
We couldn’t complete this review. Comment @codex review to try again.
8ef4df1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant