Added
- Hotkey conflict detection wizard — on plugin load, detects when Obsidian's default hotkeys (Ctrl+W, Ctrl+D, Ctrl+F, Ctrl+B) conflict with workspace navigation keys. Shows a one-time Notice per plugin version with a "Check hotkey conflicts" button in Settings → Vim Motions → Navigation that lists each active conflict with step-by-step unbinding instructions. Skipped on mobile and when workspace nav is disabled.
- Plugin:
src/workspace/hotkey-conflicts.ts(new — conflict detection viahotkeys.json,VimInfoModaldisplay),src/settings.ts(conflictNoticeDismissedVersionsetting, button in both declarative and imperative settings paths),src/main.ts(detection ononLayoutReady)
- Plugin:
- Per-view
CursorMoved/TextYankPost/CursorHold/CmdlineEnter/CmdlineLeaveautocmd events — extends the per-view autocmd pattern (already implemented for mode events viaAutocmdModeWatcher) to 5 additional events.CursorMovedfires independently per view with position-change detection (only fires when cursor actually moved).TextYankPostfires from any view including popovers.CursorHoldfires per-view with configurable delay. Built-in vim mode retains active-leaf-only behavior.- Plugin:
src/vim/autocmd-event-watcher.ts(new —AutocmdEventWatcherViewPlugin),src/lua/autocmd.ts(useEventViewPluginflag, per-view handler methods, gated legacy bindings),src/main.ts(extension registration, callback wiring, hold delay sync)
- Plugin:
- Visual-mode paste cycling — yank-ring paste cycling now works after visual-mode paste (
viw+p+<C-p>to cycle). Detects visual paste via anchor/cursor position comparison at snapshot time. Computes paste range via doc-length arithmetic. Visual block paste is excluded. Normal-mode paste cycling is unaffected.- Plugin:
src/vim/yank-ring.ts(snapshot()helper,posMin(), visual paste detection inonCommandDone,prevAnchor/prevDocLength/prevSelectionLength/prevVisualLine/prevVisualBlocktracking)
- Plugin:
- Console warning for unknown
setoptions — unknownsetoptions in vimrc now produce aconsole.warnon first encounter per vimrc load. Options recognized by either the plugin (KNOWN_SET_OPTIONS) or CM Vim built-in options are not warned about. Deduplication prevents repeated warnings for the same option.- Plugin:
src/vimrc/loader.ts(KNOWN_CM_VIM_OPTIONSset,warnedSetOptionsdeduplication,clearSetOptionWarnings())
- Plugin:
Changed
- Flash count prefix now honored with labels —
3f{char}with 2+ matches now jumps directly to the 3rd match without showing the label overlay. When the count exceeds available matches, the last match is used (Neovim parity).f{char}without a count prefix still shows labels for 2+ matches. Works in operator-pending mode (d3f{char}) and witht/Ttill motions.- Plugin:
src/flash/char-mode.ts(count prefix check before label overlay)
- Plugin:
- Flash dot-repeat clarified — dot-repeat after
df{char}{label}already works correctly. The fork stores the resolved position via_asyncMotionTargetandrepeatLastEditreplays the operator to the same relative offset. The label UI does not re-appear (correct vim behavior). KNOWN_LIMITATIONS.md entry clarified.
Fixed
- Cursor focus lost when pressing Tab to navigate cells in Embedded table widget — pressing
Tabin insert mode inside an embedded table cell editor froze the editor. The cursor disappeared, vim mode got stuck in Insert mode, andEscapestopped working. Root cause:exitCellEdit()scheduled a 50msrefreshAfterOp()timer that was non-cancellable and had no state guard. WhenTabcalledexitCellEdit()→enterCellEdit()synchronously, the deferred refresh fired while the new cell editor was active — removing its key handlers, potentially rebuilding the widget DOM (orphaning the editor), and leaving the controller in an inconsistent state (cell-editstate withtable-navhandlers). Fixed with four layers of defense: (1)refreshAfterOp()now stores and deduplicates the timer ID in arefreshTimermember, cancelled inexitTable(),enterCellEdit(), anddestroy(). (2)doRefreshAfterOp()guards against firing incell-editorinactivestate. (3)exitCellEdit()accepts{ skipRefresh: true }— the Tab handler skips bothsetActiveEditTableRange(null)andrefreshAfterOp()to prevent widget DOM rebuilds during cell-to-cell transitions. (4)enterCellEdit()cancels any pending refresh timer as belt-and-suspenders protection. Additionally,Tabat the last cell of the last row (orShift-Tabat the first cell) now returns to table-nav mode instead of silently re-entering the same cell. (#92)- Plugin:
src/vim/table-nav-controller.ts(refreshTimermember,refreshAfterOptimer storage/dedup,doRefreshAfterOpstate guard,exitCellEditskipRefreshparam,handleCellEditKeyrewrite with widget re-query and boundary handling,enterCellEdittimer cancel andpendingDcleanup)
- Plugin:
- Visual mode highlighting in embedded table cell editors — entering charwise visual mode (
v) in an embedded table cell editor now shows selection highlighting. The cell editor's CM6 instance doesn't receive.cm-focused, which previously caused the browser to hide::selectionhighlights. Fixed by adding aCSSStyleSheetondocument.adoptedStyleSheetsthat forces::selectionvisibility in.cm-vimVisual:not(.cm-vimVisualLine)scoped to.vim-table-cell-editor. Linewise visual mode (V) already worked via the fork's focus-independentlinewiseVisualHighlightViewPlugin. (#19)- Plugin:
src/vim/table-cell-editor.ts(visualSelectionSheetviaadoptedStyleSheets)
- Plugin:
- Undo tree memory eviction on file close — in-memory undo trees (
undoTreeMap) are now evicted when all editors for a file are closed, preventing unbounded memory growth in long sessions. Dirty trees are persisted before eviction whenundoFileis enabled. Persisted data on disk is not deleted — reopening a file restores from persistence or starts fresh.- Plugin:
src/main.ts(undo tree eviction inactive-leaf-changehandler)
- Plugin:
- Undo tree stale-tree notification — when
undoFileis enabled and a file was modified outside Obsidian between sessions, an Obsidian Notice is now shown when the persisted undo tree'sdocLengthdoesn't match the current file size. Detection fires at most once per file per session. Legacy persisted trees withoutdocLengthgracefully skip the check.- Plugin:
src/main.ts(activateUndoTreeForFile—docLengthcomparison + Notice),src/vim/undo-tree.ts(docLengthfield onSerializedUndoTree)
- Plugin:
vim.v.insertmodenow populated — returns'i'for insert mode,'r'for replace mode (R),'v'for virtual replace mode (gR), and''in normal/visual modes. Available in keymap function callbacks viagetInsertModeChar(). Autocmd callbacks default to''(no adapter context available).- Plugin:
src/lua/api.ts(getInsertModeCharhelper,insertmodeadded to 5setVimVContextcallsites)
- Plugin:
Tests
- 2 unit tests in
test/unit/undo-tree.test.ts:docLengthround-trip preservation, legacy data withoutdocLengthgraceful deserialization - 9 unit tests in
test/unit/lua/vim-v.test.ts:insertmode'r'/'v'context values, 7getInsertModeChartests (null, normal, insert, replace, virtual replace, priority, missing state) - 2 unit tests in
test/unit/textarea-vim.test.ts:clearSetOptionWarningsexport and idempotency - 5 e2e tests in
test/specs/flash-char-mode.e2e.ts:3fadirect jump,5faclamp to last match,2fadirect jump,d3faoperator-pending,1fasingle match - 8 unit tests in
test/unit/hotkey-conflicts.test.ts: conflict array structure, detection logic (empty, full, partial, custom binding, unrelated keys) - 10 unit tests in
test/unit/vim/autocmd-event-watcher.test.ts: callback wiring (set/clear/extension), CursorMoved detection (fires on move, skips unchanged, fires on each distinct move), CursorHold timer (fires after delay, resets on new move, custom delay), TextYankPost - 1 e2e test in
test/specs/yank-ring.e2e.ts: normal-mode paste cycling regression after visual-paste changes
Documentation
CHANGELOG.mdKNOWN_LIMITATIONS.md: Reorganized — moved 20 fixed top-level sections to new "Resolved Issues" section at bottom; updated flash count prefix, undo tree eviction, undo tree stale notification, visual mode cell editor,vim.v.insertmode, exmap soft-reload, unknown set option, flash dot-repeat, hotkey conflicts, per-view autocmd events, visual paste cycling, and SettingDefinitionList investigation entriesdocs/features/flash.md: Added count prefix behavior and dot-repeat notedocs/features/undo-tree.md: Updated memory management and stale-tree notificationdocs/features/tables.md: Updated visual mode highlighting fix in cell editorsdocs/features/quality-of-life.md: Updated yank-ring with visual-mode paste cyclingdocs/configuration/lua-config.md: Updatedvim.v.insertmodefrom deferred to active, updated per-view autocmd event listdocs/configuration/vimrc.md: Added unknown set option warning behavior, updated exmap soft-reloaddocs/configuration/settings.md: Added hotkey conflict detection button to workspace navigation settingsdocs/getting-started/recommended-setup.md: Added hotkey conflict wizard noteREADME.md: Updated flash motions, workspace navigation, and Lua configuration feature descriptionsAGENTS.md: Updated autocmd event list with per-view CursorMoved/TextYankPost/CursorHold/CmdlineEnter/CmdlineLeave
Full Changelog: 0.86.0...0.87.0