fix(cli): fix vim mode Esc leak, Enter submit, render lag and implement missing VIM commands - #4677
Conversation
…ders via context split
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…n movement
keys
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…by-one, and
paste newline
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
expectations with accurate yank range
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
timeout and Linux clipboard tool caching
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
vim NORMAL mode commands
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
|
@wenshao Thank you for the thorough review. Here's what I addressed: Critical bugs (all fixed):
Suggestions:
|
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
vimMoveToLineStart before vimMoveRight in E movement
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
join N lines not N+1
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
before repeat in Np/NP paste
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
before f/F/t/T char-read to prevent stale state
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
executeCommand to populate yank register
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…ine count Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
|
@wenshao Thanks for the continued review! All R8 comments have been addressed:
|
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
|
@wenshao Thanks for the review! All R9 comments addressed:
|
wenshao
left a comment
There was a problem hiding this comment.
[Critical] p linewise paste on the last line of the buffer places cursor one line too low (line 1824 — posted as body because the line already has an existing comment).
In the last-line branch (row + 1 >= buffer.lines.length), replaceRange(lastRow, lastLineLen, lastRow, lastLineLen, '\n' + repeated.replace(/\n$/, '')) appends pasted text starting at 0-based row row + 1. But vimMoveToLine(row + 2) maps to 0-based row + 1 via Math.min(lineNumber - 1, ...), which IS the first pasted line. However, after the paste adds N new lines, vimMoveToLine(row + 2) with Math.min(2 - 1, state.lines.length - 1) = Math.min(1, ...) → 0-based row 1 for a 2-line buffer. Wait — for buffer ["hello", "world"] with cursor on row 1: after paste the buffer becomes ["hello", "world", "world"] (3 lines). vimMoveToLine(3) → Math.min(3 - 1, 3 - 1) = 2 → 0-based row 2 (the NEW "world"). But the first pasted line is at row 1 (original "world").
buffer.vimMoveToLine(row + 1);
— qwen3.7-max via Qwen Code /review
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
|
@wenshao Thanks for the review! All R10 comments addressed:
|
…learing Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
|
Thanks for the review! @wenshao All R11 comments addressed:
|
wenshao
left a comment
There was a problem hiding this comment.
All R11 findings correctly addressed in this increment. J/>>/<< now dispatch SET_LAST_COMMAND + CLEAR_COUNT + SET_PENDING_OPERATOR for dot-repeat support; p last-line cursor fixed to vimMoveToLine(row + 2) (1-based API); T and r bounds checks converted to cpLen(line) for unicode correctness. Build passes, 206 tests pass. No new high-confidence issues found. LGTM! ✅ — qwen3.7-max via Qwen Code /review
Verification ReportBranch: Test Results
Test Coverage Summary (428 tests)
Code Review Notes
Verdict✅ Ready to merge — all 428 tests pass across 7 test suites, lint is clean, build succeeds. No typecheck errors in any PR-touched files. Comprehensive vim command coverage with proper Esc key isolation and context optimization. Verified by wenshao |
|
Thanks for the review and approval! 🙏 @wenshao |
tanzhenxin
left a comment
There was a problem hiding this comment.
Review
Re-reviewed against the latest commit. The linewise-paste regression from the previous round is resolved — pasting a yanked line onto a single-line or bottom-of-buffer prompt now works correctly.
One narrow edge case remains: submitting from vim NORMAL mode (Esc, then Enter) with a pending image attachment or large paste skips the normal submit processing, so the attachment is dropped or the placeholder marker is sent literally. It only affects the NORMAL-mode Enter path and is acceptable for now — a follow-up can route it through the same submit path INSERT mode uses.
Verdict
APPROVE — the named fixes are correct, the paste regression is resolved, and the remaining edge case is narrow enough to handle as a follow-up.
What this PR does
Fixes three vim mode issues and implements missing NORMAL mode commands:
useMemoto prevent unnecessary re-renders, and adds-- NORMAL --indicatorFixes: #4675
Why it's needed
Esc key leak:
KeypressContext.broadcasthas no "consume" mechanism — all subscribers receive every event. When vim'svimHandleInputand AppContainer'suseKeypressboth receive Esc, the AppContainer handler triggers unwanted behavior.Enter not sending: The vim handler in NORMAL mode consumed Enter without forwarding it to the submit handler.
Render lag: The original single Context created a new value object on every mode change, forcing all 6 consumers (including AppContainer, SettingsDialog, etc.) to re-render even when they only needed stable callbacks.
Missing commands: Many standard commands were missing or swallowed, making vim mode feel incomplete and inconsistent with user expectations.
Reviewer Test Plan
Evidence (Before & After)
Before:
-- NORMAL --indicator shownAfter:
-- NORMAL --indicator shows immediatelyHow to Verify
Build and run
Esc key isolation (INSERT mode)
/vim-- NORMAL --indicator appears in FooterEnter submits in NORMAL mode
Context split optimization
toggleVimEnabled), SettingsDialog, Footer, Composer should all work correctlyNORMAL mode commands
u: undo last changer+ char: replace character under cursor~: toggle case and move forwardJ: join current line with next>>/<<: indent/unindentW/B/E: WORD motions (whitespace-delimited)f/F/t/T+ char: find/till character motions;/,: repeat/undo findy+ motion /yy/Y: yank to registerp/P: paste from registerd+ motion /c+ motion /dd/cc: delete/change with motionSystem clipboard integration
yy) and paste (p) on macOS, Windows, and LinuxOperator-pending combinations
dw(delete word),cw(change word),yy(yank line),d$(delete to end of line)Regression
Planned follow-ups
Future work (to be discussed)
v): Visual selection mode for text highlighting and selection. In Vim mode, the “Ctrl+?” shortcut doesn't work. It would be nice if VISUAL mode worked in Vim mode. #2779中文
此 PR 做了什么
修复三个 vim 模式问题并实现缺失的 NORMAL 模式命令:
useMemo,防止不必要的重渲染,并添加-- NORMAL --指示器Fixes: #
为什么需要
Esc 穿透:
KeypressContext.broadcast没有"消费"机制,所有订阅者都会收到每个事件。当 vim 的vimHandleInput和 AppContainer 的useKeypress都收到 Esc 时,AppContainer handler 触发了不必要的行为。Enter 无法发送:NORMAL 模式下的 vim handler 消费了 Enter 但没有转发给提交 handler。
渲染延迟:原始单一 Context 在每次模式切换时创建新的 value 对象,导致所有 6 个消费者(包括 AppContainer、SettingsDialog 等)即使只需要稳定回调也被迫重渲染。
缺失命令:许多标准命令缺失或被吞掉,使 vim 模式感觉不完整且不符合用户预期。
Reviewer 测试计划
证据(修改前 vs 修改后)
修改前:
-- NORMAL --指示器修改后:
-- NORMAL --指示器立即显示