Added
- Bundled codemirror-vim fork — when Obsidian's built-in vim mode is disabled, the plugin provides a forked
@replit/codemirror-vimas a CM6 extension with Neovim-parity behavioral fixes. Awindow.CodeMirrorAdapter.Vimbridge ensures ecosystem plugins (obsidian-vimrc-support, vim-im-control, etc.) work transparently. - Async motion support — the fork's
defineMotionnow accepts async functions returningPromise<Pos>, enabling EasyMotion to work natively as a motion instead of an action. Operator-pending (d/c/y+ easymotion) and visual mode (v+ easymotion) work through the standard vim dispatch. - Neovim golden comparison infrastructure in fork — 496/688 tests passing against headless Neovim, with per-step extraction, golden recording, and automated comparison (
npx tsx test/neovim/compare.ts). - E2E tests for operator-pending easymotion (
d/c/y+ easymotion w) - E2E tests for multiline bracket text objects (
di{/di[/di<across lines, same-line verification) - E2E tests for
%string-awareness,db/d2wcross-line whitespace,ddcursor column preservation,Jtrailing whitespace - Expected-failure test cases for 6 remaining fixable deviations (dw cursor, d2w scope, dge empty, db cross-line, % quoted brackets, N after search)
- Full vim-easymotion default motion set — all 17 default-mapped motions: find (
f,F,s,t,T), word (w,b,e,ge,W,B,E,gE), line (j,k), search (n,N) - Bidirectional easymotion variants —
easyMotionBdWord,easyMotionBdEndWord,easyMotionBdWORD,easyMotionBdEndWORD,easyMotionBdLine,easyMotionBdTillavailable as named actions for vimrc remapping - Repeat last easymotion motion —
easyMotionRepeataction replays the most recent easymotion jump - 2-character combo labels — SCTree algorithm assigns single-char labels to nearby targets and 2-char labels to distant targets when there are more targets than label characters (>26). Backspace resets after typing the first char of a 2-char label.
- Text dimming — non-target text is dimmed when easymotion is active, making labels more visible. Controlled by Settings → Vim Motions → EasyMotion dimming (on by default).
- Visual mode support — all easymotion motions work in visual mode.
v+ easymotion extends the character selection to the target,V+ easymotion extends the line selection. Uses CM6dispatch({ selection })to manipulate the selection range directly. - EasyMotion dimming setting —
easyMotionDimmingtoggle in settings UI - Spike test
test/specs/spikes/spike19-easymotion-visual.e2e.tsinvestigating CM Vim visual mode and operator-pending feasibility (6 questions answered) - E2E test file
test/specs/easymotion-comprehensive.e2e.tswith 22 tests covering cursor landing (word, char, line, ge/gE), 2-char labels, dimming, repeat, visual mode, and edge cases (empty document, single word, empty lines, non-existent char) - E2E test file
test/specs/easymotion-visual.e2e.tswith 4 tests covering visual mode overlay, charwise selection, linewise selection, and escape preservation - CSS classes:
.vim-motions-easymotion-shade(dimming overlay),.vim-motions-easymotion-label-firstand.vim-motions-easymotion-label-second(2-char label styling)
Fixed
ddcursor column preservation — cursor now stays at its original column after linewise delete (matching Neovim), instead of moving to first non-blank characterJtrailing whitespace — join now strips trailing whitespace from the current line before adding the join space, preventing double spacesdi{/di[/di<multiline — inner bracket text objects on multiline brackets now preserve the bracket lines (producinga{\n}binstead ofa{}b), matching Neovimdj/dkat document boundary —djon the last line anddkon the first line are now no-ops (matching Neovim), instead of deleting the content:scursor positioning — cursor after substitute now goes to first non-blank of the last affected line instead of column 0%string-awareness —%now aborts (no movement) when the first bracket candidate found via forward-seeking is inside a string token, matching Neovimdb/d2wcross-line whitespace — when a delete crosses a line boundary, the whitespace-only prefix before the cursor is now included in the deletion, matching Neovimdgeat document start —geat the start of the document is now a no-op instead of deleting the character under cursordgeon empty lines —dgeon double-empty-lines now deletes both lines (matching Neovim) instead of leaving one]ptab remainder —]pwithindentWithTabsnow preserves remainder spaces when indent doesn't divide evenly by tabSize- EasyMotion visual mode — async motions now properly update visual selection head/anchor instead of just moving cursor
- EasyMotion escape dismissal — Escape overlay dismissal in e2e tests now uses real DOM events (
browser.keys) instead ofVim.handleKeywhich bypasses the DOM listener - Hint mode escape dismissal — same fix as EasyMotion
- Workspace test isolation — workspace tests now use
beforeEachwithloadSingleFileWorkspace()to prevent cascading failures fromgdnavigation - Settings reload Y/Q test — uses
Vim.handleKeyinstead ofbrowser.keysto avoid DOM event routing issues afterreloadFeatures() - Vim cursor styling — fork's hardcoded
#ff9696cursor color replaced with Obsidian CSS variables (--interactive-accent,--text-on-accent) directly in the fork'sblock-cursor.tswith fallbacks for non-Obsidian environments - Settings notice — when Obsidian's built-in Vim mode is enabled, the plugin settings tab shows a callout-style warning recommending to disable it, with an explanation of the fork's benefits
dwon empty line cursor — cursor afterdwon an empty line before a whitespace-only line now positions atch:1instead ofch:0- Ambient type declarations —
src/types/codemirror-vim.d.tsprovides fallback types forvim(),getCM(), andVimwhen the fork's build artifacts are unavailable (e.g. in the community scanner's sandboxed environment)
Changed
- Recommended setup: disabling Obsidian's built-in vim mode is now the recommended configuration. The plugin's bundled fork provides Neovim-correct behavior, async motion support, and theme-aligned cursor styling that are not available with the built-in vim engine.
- EasyMotion architecture — EasyMotion motions are now registered via
defineMotion(async, returningPromise<Pos>) instead ofdefineAction. The capture-phase operator-pending interceptor (src/easymotion/operator-pending.ts) has been removed — operator-pending and visual mode work natively through the fork's async motion dispatch. - EasyMotion module refactored from single
easymotion.ts(243 lines) into 6 focused files:register.ts(data-driven registration),targets.ts(direction-aware target finding),labels.ts(SCTree algorithm),overlay.ts(DOM rendering with dimming and re-render support),keypress.ts(key capture with 2-char narrowing),types.ts(interfaces) <leader><leader>w,<leader><leader>j,<leader><leader>fare now forward-only, matching vim-easymotion parity. Previously these scanned the entire visible viewport regardless of cursor position.registerEasyMotion()now accepts adimmingparameter and uses a data-drivenEASYMOTION_DEFSarray for registration instead of per-motion imperative codeshowOverlay()returns anOverlayHandlewithupdateLabels()for dynamic re-rendering during 2-char label narrowingwaitForLabel()replaceswaitForKey()as the primary label capture function, supporting multi-char labels, backspace reset, and narrowing callbacks- Removed
test/specs/easymotion-motions.e2e.ts— superseded byeasymotion-comprehensive.e2e.tswith correct async test patterns for char-input motions - Fork dependency —
@replit/codemirror-vimnow referenceshttps://github.com/saberzero1/codemirror-vim.gitinstead of a local file path, enabling CI/scanner environments to install without local checkouts reportUnusedDisableDirectivesset tooffin eslint config to avoid conflicts between local and scanner lint rule sets- Added
Obsidianto sentence-case brands list in eslint config
Documentation
KNOWN_LIMITATIONS.md: EasyMotion operator-pending rewritten — now uses async motions natively instead of capture-phase interceptorKNOWN_LIMITATIONS.md: added 8 behavioral deviation entries for fork fixes (ddcursor,Jwhitespace,di{}multiline,dj/dkboundary,:scursor,%strings,dbcross-line,dwcursor)KNOWN_LIMITATIONS.md: added "DOM keyboard events not routed after settings reload" and "EasyMotion visual mode label selection via DOM events" sectionsAGENTS.md: added codemirror-vim fork section with dual-vim architecture documentationREADME.md: added "Recommended setup" section explaining benefits of disabling built-in vimDIFFERENCES.md(fork): comprehensive rewrite documenting all behavioral fixes and infrastructure changes
Full Changelog: 0.9.0...0.10.0