Fixed
- Gutter settings ignored when set via vimrc or Lua —
set nonumber,set signcolumn=no,vim.opt.number = false, and other gutter-related settings (number,relativenumber,numberwidth,linenumbermode,cursorline,cursorlineopt,signcolumn,statuscolumn,foldcolumn) had no effect when configured via.obsidian.vimrcor.obsidian.init.lua. Root cause: vimrc/Lua overrides were in-memory only and never persisted, but gutter CM6 extensions are created at startup from persisted values. Fixed with aconfigOverridespersistence system: after vimrc/Lua loading, override values are captured and persisted indata.json. On next startup,configOverridesare merged on top of base settings before CM6 extensions are created, so gutters use the correct values from the start. Also added gutter reconfiguration calls toreloadFeatures()for in-session changes. (#101)- Plugin:
src/main.ts(loadSettings— configOverrides extraction and merge;captureConfigOverrides— new shared capture method;saveSettings— persist configOverrides;reloadFeatures— gutter reconfiguration;softReloadVimrc— clear stale overrides and re-capture;clearSettingOverride— new helper) - Plugin:
src/settings.ts(replacedvimrcOverrides?.deletewithclearSettingOverrideacross all onChange handlers — covers both declarative and imperative paths, fixes missingluaOverridesdeletion)
- Plugin:
preVimrcSettingsshallow copy — nested objects (cursorShapes,modePrompts,pickerKeymap) shared references withthis.settings, causingsaveSettings()to accidentally persist overridden cursor shapes. Fixed with deep copy.- Plugin:
src/main.ts(line 677 — deep copy nested objects inpreVimrcSettingssnapshot)
- Plugin:
- Clipboard/textwidth falsely shown as "Set by vimrc" — the initial settings restoration at startup called
onSettingOverride()forclipboardandtextwidth, writing tovimrcOverrideseven without a vimrc file. Fixed by using direct side-effect calls.- Plugin:
src/main.ts(replacedonSettingOverridecalls with directsetClipboardOption/setTextwidthcalls)
- Plugin:
- Oil explorer loses focus after committing staged changes — after making changes in Oil (e.g., deleting a file) and committing with
:w, the Oil editor lost focus when the confirmation dialog was confirmed or dismissed. Two bugs: (1)OilConfirmModal.onClose()never resolved the promise when the user pressedEscto dismiss the modal, causingcommit()to hang permanently. Fixed by adding aresolvedguard —onClose()resolvesfalsewhen no button was clicked. (2) After the confirmation dialog closed (via Confirm, Cancel, or Esc), focus was never returned to the Oil editor. Fixed by callingview.focusEditor()on both the cancel and commit paths. (#100)- Plugin:
src/oil/manager.ts(OilConfirmModal—resolvedguard flag,onCloseresolves on Esc dismissal;commit—view.focusEditor()after confirm and cancel paths)
- Plugin:
:sortcursor positioning —:sort(and ranged:2,3sort) now positions the cursor at the first line of the sorted range, matching Neovim. Previously the cursor stayed at line 0 regardless of the sort range.- Fork:
~/Repos/codemirror-vim/src/vim.js(exCommands.sort—cm.setCursorafterreplaceRange)
- Fork:
CTRL-V $ dcursor overshoot — after a block visual delete to end-of-line (CTRL-V jj $ d), the cursor column is now clamped to the remaining line length. Previously the cursor could land past the last character on shortened lines.- Fork:
~/Repos/codemirror-vim/src/vim.js(operators.delete— block visual cursor clamping)
- Fork:
- Hint mode: modifier keydown event propagates to Obsidian handlers — pressing
Ctrlalone during hint mode could trigger Obsidian's own key handlers because the modifier-only early return inwaitForHintKey()did not callpreventDefault()orstopPropagation(). The event leaked through to Obsidian's hotkey system via bubble-phase listeners, potentially causing side effects depending on the user's Obsidian configuration. Fixed by addinge.preventDefault()ande.stopPropagation()before the early return for modifier-only keys. (#98)- Plugin:
src/ui/hint-mode.ts(waitForHintKeyhandler —preventDefault+stopPropagationon modifier-only keydown)
- Plugin:
- Hint mode: count prefix (
2F) focus restoration races with async navigation — when using2Fon an internal link target,hintActivate()firednavigateWithJump()without awaiting it (viavoid), thensetActiveLeaf(originalLeaf)ran synchronously. The asyncopenLinkText()insidenavigateWithJumpcould resolve after the focus restoration and steal focus back to the new tab — a race condition that manifested on slower machines or with heavier vaults. Similarly,duplicateLeaf()was fire-and-forgotten. Fixed by makinghintActivateasync and awaiting bothnavigateWithJump()andduplicateLeaf(). ThecreateHintActioncallback now awaits the action result before restoring focus, making the behavior deterministic. (#98)- Plugin:
src/ui/hint-mode.ts(hintActivate—async,await navigateWithJump,await duplicateLeaf;hintOpenNew— returnsPromise<boolean>;createHintAction—asynccallback,await action(),Promise.resolve()wrappers for sync actions)
- Plugin:
Added
- 27 new vimrc/Lua configurable options — the following settings were previously only configurable via the Settings UI and are now available via
:setin vimrc andvim.optin Lua:subword,picker,pickerleadermappings,pickermatcher,pickeromnisearch,pickertasks,pickerdataview,ripgrep,ripgreppath,ripgrepargs,grepmode,oil,oilhiddenfiles,oilconfirmdeletethreshold,oilsort,hinthotkey,undotreeposition,undotreeautoopen,imswitching,impreset,imbinarypath,imobtainargs,imswitchargs,imdefaultnormal,imrestorebehavior,imdefaultinsert. All options work identically across Settings UI, vimrc, and Lua.- Plugin:
src/vimrc/loader.ts(27 newKNOWN_SET_OPTIONSentries)
- Plugin:
- Runtime invariant system —
invariant()(always-on, type-narrowing) anddevAssert()(dev-only, stripped from production) helpers insrc/util/invariant.ts. 21 invariants placed across 9 source files protecting mode transitions, dual-vim architecture, settings resolution, Lua engine lifecycle, extension cleanup, cursor state, and cell editor singleton. Violations are logged to console, rate-limited via Notice, and inspectable via the:violationsex command.__DEV__build-time flag via esbuilddefineenables dev-only checks in development/watch builds and strips them from production.- Plugin:
src/util/invariant.ts(new —invariant,devAssert,getViolations,clearViolations),src/types/globals.ts(new —__DEV__global type declaration),esbuild.config.mjs(defineoption),vitest.config.ts(defineoption)
- Plugin:
:violationsex command — displays accumulated invariant violations with timestamps.:violations!clears the violation log.- Plugin:
src/workspace/commands.ts(:violationsand:violations!registration)
- Plugin:
Tests
- 57 new unit tests across 5 new test files:
test/unit/invariant.test.ts(12 tests): invariant/devAssert helpers, violation cap, rate limiting, stack traces, shallow copytest/unit/mode-tracker.test.ts(11 tests): getDialogPrefix, resolveMode logictest/unit/dual-vim.test.ts(7 tests): bundled-vim lifecycle, bridge install/uninstall, invariant triggertest/unit/settings-resolution.test.ts(16 tests): DEFAULT_SETTINGS completeness, settings merge, configMode migration, signcolumn migration, idempotencytest/unit/lua/lifecycle.test.ts(8 tests): sandboxed state creation/destruction, instruction guard, coroutine runner lifecycle
- 3 expanded tests in
test/unit/animated-cursor.test.ts: manager register/deregister, destroy clears all, MAX_CONTROLLERS warning - 70 new Neovim golden test cases (490 → 560): operator+motion combos (+28), visual mode operations (+15), insert mode operations (+12), ex command operations (+15)
- 2 Neovim deviations closed (22 → 20):
:2,3sortcursor positioning,CTRL-V $ delete to EOLcursor overshoot - 30 unit tests in
test/unit/known-set-options.test.ts: KNOWN_SET_OPTIONS coverage guard (every non-excluded settings key has an entry, excluded keys list has no stale entries, no settingsKey points to non-existent setting), 27 new option entry validations (type, settingsKey, validValues) - 6 e2e tests in
test/specs/gutter-vimrc-lua.e2e.ts: gutter settings via Lua config (enable/disable line numbers, enable/disable sign column, disable all gutter elements, hybrid line numbers) - 4 e2e tests in
test/specs/oil-poc.e2e.ts(issue #100): oil retains focus after no-op commit, oil retains focus after confirmed destructive commit, oil retains focus after cancelled destructive commit, oil retains focus after Esc-dismissing the confirm modal - 3 e2e tests in
test/specs/hint-mode.e2e.ts(issue #98):Fon wikilink opens in new tab via command,openNew(2)on wikilink keeps focus on original leaf after first hint, Ctrl keydown stopped from propagating during hint mode
Documentation
CHANGELOG.mdKNOWN_LIMITATIONS.md: Marked gutter vimrc/Lua reconfiguration as fixed; marked preVimrcSettings shallow copy as fixed; marked clipboard/textwidth false override as fixed; updatedsetoption scope section with configOverrides persistence; marked Oil focus loss after commit as fixeddocs/features/oil-explorer.md: Added focus retention after commit noteAGENTS.md: Updated hint mode page ownership withhinthotkeyREADME.md: Updated vimrc configurable settings countCONTRIBUTING.md: Added configOverrides persistence andclearSettingOverridehelper to conventions; updated vimrc loader descriptiondocs/configuration/vimrc.md: Added 27 new options to vimrc tables; updated override behavior section with configOverrides persistence and gutter restart notedocs/configuration/lua-config.md: Added 27 new options to vim.opt tableKNOWN_LIMITATIONS.md: Marked:sortcursor andCTRL-V $cursor deviations as fixedCONTRIBUTING.md: Added invariant system to codebase structure, updated testing instructions forbuild:devAGENTS.md: Updated manual testing instructions forbuild:dev, added:violationscommand, updated golden test count and deviation countREADME.md: Addednpm run test:unitto development commandsDIFFERENCES.md(fork): Added:sortcursor positioning and block visual delete cursor clamping sectionsdocs/features/ex-commands.md: Added:violationsand:violations!ex commandsKNOWN_LIMITATIONS.md: Updated hint mode modifier key fix withstopPropagation; updated count prefix focus fix with asynchintActivateAGENTS.md: Updatedhint-mode.tsdescription with asynchintActivateand modifierstopPropagation
Full Changelog: 0.93.0...0.94.0