Skip to content

fix(plugin-codemirror): sync cursor from editor selection on keydown#62

Open
babarot wants to merge 1 commit into
vimeejs:mainfrom
babarot:fix/codemirror-click-cursor-sync
Open

fix(plugin-codemirror): sync cursor from editor selection on keydown#62
babarot wants to merge 1 commit into
vimeejs:mainfrom
babarot:fix/codemirror-click-cursor-sync

Conversation

@babarot

@babarot babarot commented Apr 16, 2026

Copy link
Copy Markdown

What

Add syncCursorFromEditor() to the CodeMirror plugin's keydown handler. Before each keystroke is processed, the function reads view.state.selection.main.head and updates the vim context's cursor position if it differs. Visual modes (visual, visual-line, visual-block) are skipped because the editor selection head represents the visual range boundary, not the cursor position.

The CodeMirrorState type is extended with a minimal selection interface so the plugin can read the selection without depending on the full @codemirror/state types.

Why

The vimee CodeMirror plugin maintains its own cursor state (ctx.cursor) but never reads back from CodeMirror's selection. When a user clicks in the editor to move the cursor, CodeMirror updates its selection but ctx.cursor stays at the old position. The next keystroke (j/k movement, entering insert mode, entering visual mode, etc.) operates from the stale position, causing the cursor to jump back unexpectedly.

This is the same class of issue as #61 (shiki-editor cursor drift) — an external position change that the vim engine doesn't pick up.

Sync CodeMirror's selection.main.head back into the vim context at the
start of every keydown handler. This ensures mouse clicks and other
external selection changes are reflected before the next keystroke is
processed. Visual modes are skipped because the editor selection head
represents the visual range boundary, not the cursor position.
babarot added a commit to babarot/mo that referenced this pull request Apr 16, 2026
Patch @vimee/plugin-codemirror to read CodeMirror's selection back
into the vim context before each keystroke. Without this, clicking
in the editor moves the CodeMirror selection but leaves vimee's
internal cursor at the old position, causing it to jump back on the
next keypress. Upstream fix: vimeejs/vimee#62
@konojunya

Copy link
Copy Markdown
Member

@babarot Thank you for your contribution. 🎉
Sorry for the late reply.

I will review it later. Please wait.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes cursor drift in the CodeMirror plugin by syncing the vim engine cursor (ctx.cursor) from CodeMirror’s current selection head at the start of each keydown, so external cursor moves (e.g., mouse clicks) don’t cause the next vim command to operate from a stale position.

Changes:

  • Add syncCursorFromEditor() and call it at the start of the plugin’s keydown handler (skipping visual modes).
  • Extend the minimal CodeMirror state typing to include selection.main.head.
  • Add tests that simulate mouse-driven selection movement and verify subsequent vim actions operate from the clicked position.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/plugin-codemirror/src/types.ts Extends the minimal CodeMirror state type surface to include selection head access.
packages/plugin-codemirror/src/attach.ts Syncs vim cursor from editor selection before processing keystrokes.
packages/plugin-codemirror/src/tests/attach.test.ts Adds mock selection head control and tests for cursor syncing behavior.

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

return;
}
const offset = view.state.selection.main.head;
const editorCursor = offsetToCursor(buffer.getContent(), offset);
@konojunya

Copy link
Copy Markdown
Member

Since more than 30 days have passed since the PR was created, the CI approval button has disappeared. Therefore, I will close and then reopen the PR once.

@konojunya konojunya closed this May 21, 2026
@konojunya konojunya reopened this May 21, 2026
@codspeed-hq

codspeed-hq Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 14.6%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
❌ 1 regressed benchmark
✅ 68 untouched benchmarks
⏩ 12 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
dw (delete word) 472.5 µs 336.9 µs +40.25%
W (WORD) 495.7 µs 328.2 µs +51.01%
b 354.8 µs 499.4 µs -28.95%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing babarot:fix/codemirror-click-cursor-sync (2706087) with main (10740b9)

Open in CodSpeed

Footnotes

  1. 12 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants