Fixed
- Note freezes in Reading Mode after closing Oil explorer — closing the Oil explorer view (via
q,:q,:wq, or Luavim.ob.oil.close()) reopened the previous file in Obsidian's default mode (often Reading/Preview) instead of the mode the user was in when they opened Oil. Root cause:openOil()capturedpreviousFile(path only) but not the editor's view mode. Fixed by capturingpreviousViewMode(theMarkdownViewstate: source mode, live preview, or reading mode) when opening Oil and restoring it vialeaf.openFile(file, { state: previousViewMode })on close. All 4 close paths (keybindingsq, ex commands:q/:wq, and Lua APIvim.ob.oil.close()) are unified into a singlecloseOil()method onOilManager. (#93)- Plugin:
src/oil/oil-view.ts(previousViewModefield,getState/setStateextended,getPreviousViewModegetter),src/oil/manager.ts(openOilcaptures mode viaMarkdownView.getState(), newcloseOil()shared method with mode restoration),src/oil/keybindings.ts(oilClosedelegates tomanager.closeOil()),src/workspace/commands.ts(closeOilViewdelegates tooilManager.closeOil()),src/main.ts(Lua APIoilClosedelegates tooilMgr.closeOil())
- Plugin:
- Cursor focus lost when switching back to Oil tab — after opening a file from Oil and then switching back to the Oil tab via
gTor Obsidian's tab navigation, the cursor focus was missing. Keystrokes were not captured by the Oil editor until the user clicked with the mouse. Root cause: Oil's editor focus was set only once inonOpen()and never re-applied when switching back. Fixed by adding afocusEditor()method toOilViewand calling it fromOilKeybindingManager.onActiveLeafChange()when switching into an Oil view. (#93)- Plugin:
src/oil/oil-view.ts(focusEditor()public method),src/oil/keybindings.ts(onActiveLeafChangecallsview.focusEditor()when switching to Oil)
- Plugin:
:Oil .opens current file's directory instead of vault root — running:Oil .opened the directory containing the current active file rather than the vault root. In oil.nvim,.means current working directory, which maps to the vault root in Obsidian. Root cause: the conditionif (!dirPath || dirPath === '.' || dirPath === '/')treated.identically to an empty argument. Fixed by separating.and/into their own branch that resolves to vault root (""), while the empty-argument case continues to resolve to the current file's parent directory. Both the ex command handler (commands.ts) and global ex command handler (global-ex-command.ts) are updated. (#93)- Plugin:
src/workspace/commands.ts(:Oilex command path resolution),src/ui/global-ex-command.ts(global ex command path resolution)
- Plugin:
- Hidden files (dotfiles) not shown in Oil explorer — hidden files and folders (e.g.,
.gitignore,.git/) were not visible in Oil even with "Show hidden files" enabled. Root cause:app.vault.getFiles()andapp.vault.getAllFolders()only return Obsidian-indexed files, and Obsidian does not index dotfiles. Fixed by adding a two-pass rendering approach: the initial sync render uses the Vault API (unchanged), then an async second pass discovers hidden entries viaapp.vault.adapter.list()(which returns all filesystem entries including dotfiles) and merges them into the listing. A race condition guard prevents overwriting user edits during the async merge. Hidden files are currently view-only — CRUD operations on dotfiles may fail because they lackTFile/TFolderobjects in the Vault index. (#93)- Plugin:
src/oil/render.ts(exportedgetParentPath/isInConfigDir, newdiscoverHiddenEntries()function),src/oil/manager.ts(newdiscoverAndMergeHidden()method with race condition guard),src/oil/oil-view.ts(setEditorContent()method, async trigger inonOpen()andrefreshContent())
- Plugin:
- Inconsistent behavior when deleting surroundings with doubled symmetric delimiters —
ds$on$$example$$did nothing instead of deleting the innermost$pair to produce$example$. Same failure fords"on""hi"",cs$on$$example$$, and other symmetric (same open/close) surround characters when doubled. Root cause:findSurroundingQuotes()in the codemirror-vim fork paired all quote positions sequentially at even/odd indices (i += 2). For$$example$$with positions[0, 1, 9, 10], this created pairs(0,1)and(9,10)— the two adjacent$$on each side — leaving the cursor between them with no match. Fixed by replacing the sequential pairing with cursor-expansion: search backward from cursor for the nearest quote character (open), then forward for the next one (close). This correctly handles both doubled delimiters ($$example$$→ finds inner pair(1, 9)) and adjacent pairs ("hello" "world"→ finds pair around cursor). (#96)- Fork:
~/Repos/codemirror-vim/src/vim.js(findSurroundingQuotes— cursor-expansion algorithm replacing sequentiali += 2pairing) - Fork:
~/Repos/codemirror-vim/DIFFERENCES.md(added "Symmetric surround quote matching" section)
- Fork:
- Snippet ex commands do not work after vimrc/Lua config reload —
:snippet <name>and:snippetsex commands silently stopped working after anyreloadFeatures()cycle (triggered by vimrc loading, Lua config loading, or settings changes). Root cause:registerSnippetCommands()was called only inonload(), butreloadFeatures()callsunregisterAll()which replaces all registered ex commands with no-ops — and snippet commands were never re-registered. The Picker-based snippet insertion was unaffected because it uses a separatepickerRegistrynot managed byVimRegistration. Fixed by addingregisterSnippetCommands()toreloadFeatures(), matching the pattern used by all other feature registrations. (#95)- Plugin:
src/main.ts(reloadFeatures— addedregisterSnippetCommandscall gated byenableSnippets)
- Plugin:
- Which-key shows EasyMotion commands incorrectly with space leader — EasyMotion commands (prefixed with
<leader><leader>) appeared at the wrong level in the which-key popup when using space as the leader key. Two root causes: (1)LeaderRegistry.addBinding()stripped the leader prefix using the raw leader key (" "), butonKeyPressLeaderOnly()compared against normalized keys ("<Space>"fromvim-keypressevents). The stored binding keys (" f") never matched the normalized drill-down prefix ("<Space>"). Similarly,addGroupLabel()stored the group label key in raw format, causinggetRelativeGroupLabels()lookups to miss. Fixed by normalizing bothlhsandprefixvianormalizeVimKey()at storage time inaddBinding()andaddGroupLabel(). (2) In grouped mode,buildNextKeyEntries()calledisSpecialKey()to filter out non-typeable keys like<CR>,<Left>, etc. — but<Space>was also treated as special, causing all EasyMotion bindings (whose first key after leader-stripping is<Space>) to be silently dropped from the grouping. Fixed by exempting<Space>from the special key check. (#94)- Plugin:
src/ui/which-key.ts(LeaderRegistry.addBinding— normalizelhsand leader before stripping;LeaderRegistry.addGroupLabel— normalizeprefixbefore storing;isSpecialKey— exempt<Space>from special key filtering)
- Plugin:
Tests
- 6 fork tests in
~/Repos/codemirror-vim/test/vim_test.js:ds_doubled_dollar_deletes_inner,ds_doubled_quote_deletes_inner,cs_doubled_dollar_changes_inner,ds_single_dollar_pair,ds_adjacent_dollar_pairs,ds_dollar_cursor_on_delimiter - 5 e2e tests in
test/specs/surround.e2e.ts(doubled symmetric delimiters — #96):ds$on$$example$$in Live Preview,ds"on""hi"",cs$on$$example$$,ds$on single$hello$,ds$on adjacent$hello$ $world$ - 28 unit tests in
test/unit/which-key.test.ts:LeaderRegistrynormalization (raw space leader, pre-normalized leader, format consistency, non-leader rejection, bare-leader rejection, deduplication, backslash leader, comma leader), group label normalization (raw vs normalized prefix, cross-format consistency),clearBuiltinBindingswith normalized keys, double-leader drill-down (issue #94 scenario — EasyMotion bindings filterable by<Space>prefix, single-leader bindings excluded),isSpecialKey(<Space>exempt, other angle-bracket keys special, plain keys not special) - 2 e2e tests unskipped in
test/specs/snippets/snippet-variables.e2e.ts::snippetcommand expands by name,:snippetsopens picker - 1 e2e test in
test/specs/settings-reload.e2e.ts: snippet ex commands survivereloadFeatures()(regression test for #95) - 17 unit tests in
test/unit/oil-render.test.ts:getParentPath(4 tests),isInConfigDir(4 tests),discoverHiddenEntries(9 tests — dotfiles, dot-folders, index exclusion, config dir exclusion, non-dotfile exclusion, adapter.list failure graceful fallback, nested paths, mixed entries, empty results) - 5 e2e tests in
test/specs/oil-poc.e2e.ts(Oil explorer #93)::Oil .opens vault root,:Oil /opens vault root, closing oil restores source mode, closing oil restores live preview mode,closeOil()restores previous file
Documentation
CHANGELOG.mdKNOWN_LIMITATIONS.md: Added surround doubled symmetric delimiter fixAGENTS.md: Updated fork test count (1882)DIFFERENCES.md(fork): Added "Symmetric surround quote matching" sectiondocs/features/surround.md: Added doubled delimiter behavior noteKNOWN_LIMITATIONS.md: Added hidden files view-only limitation to Oil section; marked Reading Mode freeze, focus loss, and:Oil .path resolution as fixedCONTRIBUTING.md: Updated Oil codebase structure descriptions (oil-view.ts,manager.ts,render.ts)docs/features/oil-explorer.md: Updated with mode restoration on close, focus restoration on tab switch,:Oil ./:Oil /path semantics, hidden files via adapter API, view-only dotfile limitationdocs/features/ex-commands.md: Updated:Oilargument descriptiondocs/reference/keybindings.md: Updated:Oildescription with.//path supportKNOWN_LIMITATIONS.md: Marked ex command snippet expansion as fixed; added which-key EasyMotion double-leader fix to which-key overlay sectiondocs/features/snippets.md: Updated ex command trigger description noting reload survivaldocs/configuration/which-key.md: Added note about double-leader prefix grouping for EasyMotion
Full Changelog: 0.89.0...0.90.0