Added
- Surround
csf(change surrounding function name) —csfchanges the function name around the cursor. Prompts for the new function name via afunc:status bar prompt; press Enter to apply or Escape to cancel. Dot-repeat (.) replays with the saved name. Uses the samefindSurroundingFunctionasdsf(single-line only). Handles nested calls and method chains.- Fork:
~/Repos/codemirror-vim/src/vim.js(target === 'f'case in change operator,pendingInputprompt,funcResultfallback inhandleSurroundSubState)
- Fork:
- Oil which-key integration — the which-key popup now shows Oil-specific keybindings (
-,Enter,~,g.,gs,y.,gf,g?,q,Ctrl-l) with descriptions when an Oil view is active. Descriptions are single-sourced from theOIL_MAPPINGSarray and fed into the which-keycommandLabelsmap viagetCommandLabels().- Plugin:
src/oil/keybindings.ts(descfield onOilMapping,getCommandLabels()method),src/main.ts(oil labels injected intorebuildWhichKey())
- Plugin:
- Oil
g?help overlay — pressg?in Oil to toggle a help overlay listing all Oil keybindings with descriptions. Entries are derived fromOIL_MAPPINGSto prevent drift. Dismissible viag?(toggle) or Escape.- Plugin:
src/oil/keybindings.ts(g?mapping,showOilHelpmethod),styles.css(oil help overlay styles)
- Plugin:
- IM platform presets — a settings dropdown auto-fills binary path, arguments, and default IM for common tools: macism (macOS), im-select (Windows), fcitx5-remote (Linux), ibus (Linux). Values are editable after selection.
- Plugin:
src/settings.ts(imPresetsetting,IM_PRESETSdata, preset dropdown in both legacy and searchable settings UI)
- Plugin:
:IMToggle/:IMStatusex commands —:IMToggleenables/disables IM switching and saves the setting.:IMStatusqueries the current IM identifier and displays it via a Notice.- Plugin:
src/main.ts(registerImExCommands())
- Plugin:
- IM session persistence — per-editor IM state is persisted to plugin settings via
saveData()(30-second interval + immediate save on unload). On plugin load, the persisted state is restored so the firstInsertEnteruses the correct IM instead of the default.- Plugin:
src/im/im-switcher.ts(loadPersistedState(),getPersistedState()),src/settings.ts(persistedImStatefield),src/main.ts(load/save wiring)
- Plugin:
- Special marks in picker — the
:markspicker now shows special marks (',.,<,>) under a "Special marks" group between buffer and global marks.- Plugin:
src/picker/sources/mark-providers.ts(SpecialMarkProvider),src/main.ts(registered as third provider)
- Plugin:
:grepregex support —:grepnow uses JavaScriptRegExpfor pattern matching instead of Obsidian'sprepareSimpleSearch. Invalid regex patterns gracefully fall back to substring matching. This matches Neovim's:grepbehavior where the pattern is a regex.- Plugin:
src/picker/sources/grep.ts(createMatcher()withRegExp+ fallback),src/picker/sources/live-grep.ts(same pattern)
- Plugin:
Changed
loadInitLua()parameter refactor — the function signature was refactored from 11 positional parameters to(app, vim, options?)with aLoadInitLuaOptionsinterface. All callers updated.- Plugin:
src/lua/loader.ts(LoadInitLuaOptionsinterface, destructured options),src/main.ts(caller updated)
- Plugin:
Fixed
- Vimrc loading reliability —
readVimrcFilenow retries with exponential backoff (50ms, 100ms, 200ms) when the vault adapter returns empty content during earlyactive-leaf-changeevents. The arbitrary 100ms safety-net timeout for map re-application has been removed. This addresses the intermittent issue wherenmap L $orset textwidthcommands were silently dropped on plugin load.- Plugin:
src/vimrc/loader.ts(readVimrcFileretry logic),src/main.ts(removed 100ms setTimeout)
- Plugin:
- Vimrc soft-reload — the vimrc file is now watched via
vault.on('modify'). When modified, maps and settings are re-applied without a plugin reload. Previous vimrc-sourced maps are unmapped before re-application to prevent accumulation.exmapdefinitions from the initial load persist (documented as known limitation).- Plugin:
src/main.ts(softReloadVimrc(),vimrcMapKeystracking,vault.on('modify')handler)
- Plugin:
- BufEnter initial fire destroying buffer-local keymaps — function-callback keymaps registered in
BufEnterautocmd handlers during the initial syntheticBufEnterwere destroyed by the subsequentreloadFeatures()→vim.resetKeymap()call. Fixed by deferring the initialBufEnterfire until afterreloadFeatures()andapplyLuaMaps()complete. TheAutocmdManagernow stores the initial file path inpendingInitialBufEnterPathand exposesfireInitialBufEnter()to fire it at the correct lifecycle point.- Plugin:
src/lua/autocmd.ts(pendingInitialBufEnterPath,fireInitialBufEnter()),src/main.ts(call after reload)
- Plugin:
- Blockquote fenced code block detection —
findFenceLinesnow matches fences prefixed with blockquote markers (> ```). The regex was updated from/^```/to/^(?:>\s*)*```/with blockquote depth matching to ensure open/close fences are at the same nesting level. This fixes multi-line text objects and smart list continuation incorrectly matching delimiters inside blockquote code blocks.- Plugin:
src/text-objects/code-block.ts(FENCE_OPEN/FENCE_CLOSEregexes,blockquoteDepth())
- Plugin:
- EasyMotion operator-pending dot-repeat —
d<leader><leader>w{label}followed by.now replays the delete to the same relative position. The fork stores the resolved async motion position as a relative offset inlastEditInputState._asyncMotionTargetafterapplyOperatorsucceeds. During dot-repeat,repeatLastEditdetects this stored target and applies the operator directly instead of re-executing the async motion overlay.- Fork:
~/Repos/codemirror-vim/src/vim.js(_asyncMotionTargetstorage in async.then(),repeatCommand()offset replay)
- Fork:
- Surround
ysdot-repeat with text object motions —ysiwb(surround inner word with parentheses) then.on a different word now correctly replays the surround. The fork stores the text object motion characters (_ysTextObjectMotionand_ysTextObjectChar) inlastEditInputStatevia theonRepeatcallback. During dot-repeat,repeatLastEditre-evaluates the text object at the current cursor viatextObjectManipulation()and appliesaddSurroundToRange(). Works for all simple delimiters (ysiwb,ysiw",ysaw',ysiw]). Tag (ysiw<em>) and function (ysiwflen) dot-repeat is verified at the fork level (1806/0) but cannot be tested via WDIO due to</>key dispatch conflicts with vim's angle-bracket notation parser — these tests are skipped in the plugin e2e suite with a reference to the fork tests.- Fork:
~/Repos/codemirror-vim/src/vim.js(_ysTextObjectMotion/_ysTextObjectCharinonRepeat,repeatCommand()text object replay),~/Repos/codemirror-vim/src/types.ts(3 newInputStateInterfacefields),~/Repos/codemirror-vim/DIFFERENCES.md
- Fork:
- Settings parity between pre-1.13 and post-1.13 Settings UI — all plugin settings are now exposed in both the legacy
PluginSettingTab.display()method (Obsidian <1.13) and the newgetSettingDefinitions()API (Obsidian 1.13+). Previously, 22 settings were missing from the legacy UI and 9 from the new UI:- Added to legacy settings (pre-1.13): Input method section (7 settings: enable, binary path, obtain/switch args, normal mode IM, restore behavior, default insert IM), Fuzzy picker for buffers, Picker leader mappings, Picker matching engine, Third-party integrations (Omnisearch, Obsidian Tasks, Dataview), Show config load notifications, Which-key popup delay, 7 mode prompts (visual line, visual block, select, virtual replace, command, search, insert-normal)
- Added to new settings (1.13+): Snippets group (4 settings: enable, bundled, directory, trigger mode), File explorer group (4 settings: oil explorer, show hidden files, confirm delete threshold, default sort order), Workspace navigation view types
- Plugin:
src/settings.ts(display()andgetSettingDefinitions())
- Lua runtime callbacks now have infinite loop protection — all 5 runtime
lua_pcallsites (function keymaps, user commands, autocmd handlers, timer callbacks, snippet f()/d() nodes) are now wrapped withwithInstructionGuard, which setslua_sethookwithLUA_MASKCOUNTbefore each call and clears it after. Instruction limit: 500,000 for callbacks, 100,000 for snippet nodes. On timeout, a throttled Notice is shown (5-second cooldown). Obsidian remains responsive.- Plugin:
src/lua/types.d.ts(type fix),src/lua/engine.ts(withInstructionGuard,showLuaErrorNotice),src/lua/api.ts(3 sites),src/lua/timers.ts(1 site),src/snippets/dynamic-bridge.ts(2 sites)
- Plugin:
- Global marks updated on file rename/delete — renaming a file now updates all global marks (
A–Z) pointing to it. Deleting a file removes the marks. Follows the samevault.on('rename')/vault.on('delete')pattern as harpoon and fold persistence.- Plugin:
src/vim/mark-store.ts(renamePath(),removeByPath()),src/main.ts(4 lines in event handlers)
- Plugin:
- Surround
csbBysaBbchain now works —yswith text object motions (aB,iw,a", etc.) aftercsor standalone now correctly applies the surround. Theys_motionhandler directly evaluates text object motions instead of dispatching throughhandleKey→evalInput, whereclearInputStatewould lose theselectedCharacter. 74/74 nvim-surround golden comparison tests now pass (up from 73/74).- Fork:
~/Repos/codemirror-vim/src/vim.js(ys_motionhandler,operatorArgson surround state),~/Repos/codemirror-vim/src/types.ts(operatorArgsfield)
- Fork:
- Surround dot-repeat cross-type leak prevention — the surround dot-repeat guard was tightened to only use saved replacements when
_surroundTypematches the current operation type (cs,ys,yss), preventing stale state from a priorcsoperation from silently consuming a subsequentyscommand.- Fork:
~/Repos/codemirror-vim/src/vim.js(tightenedsavedReplacementguards with_surroundTypematch)
- Fork:
- Vimrc loading decoupled from CM adapter — vimrc parsing no longer requires a CM adapter.
loadVimrcnow uses a two-phase approach:readAndParseVimrcFilereads and parses the file without needing an editor (reusingparseVimrcfromparser.ts), thenapplyVimrcCommandsapplies all 14VimrcCommandtypes with explicit handling —exmapis applied eagerly (no CM needed), unknown commands deferred topendingExCommandsand applied when a CM adapter becomes available. The 5×50ms retry loop has been removed. The 100ms safety-net map reapplication is kept for CM Vim keymap init timing.- Plugin:
src/vimrc/loader.ts(readAndParseVimrcFile,applyVimrcCommands,applyPendingExCommands, refactoredloadVimrc),src/main.ts(retry loop removed,pendingVimrcExCommandsfield, deferred application inactive-leaf-changehandler)
- Plugin:
Documentation
KNOWN_LIMITATIONS.md: EasyMotion dot-repeat → Fixed; surroundysdot-repeat with text objects → Fixed (simple delimiters), tag/function pending; blockquote fence detection → Fixed; grep regex → Fixed; special marks in picker → Fixed; IM presets/persistence/ex commands → Implemented;loadInitLuarefactor → Implemented; vimrc hot-reload → updated to soft-reload; vimrc loading reliability → updated with retry logic; tagcst/yst→ Verified;ci*Live Preview → Permanent;selectmode=mouse→ PermanentDIFFERENCES.md(fork): Addedcsfsection; updated surround summary; addedys_motiontext object fix; added async motion dot-repeat (_asyncMotionTarget); added surroundystext object dot-repeat (_ysTextObjectMotion)docs/features/oil-explorer.md: Addedg?to oil ex commands tabledocs/configuration/which-key.md: Added Oil explorer context section (fork mode only)docs/configuration/settings.md: Added IM preset row to Input method tabledocs/features/ex-commands.md: Added:IMToggleand:IMStatusto ex commands referenceCHANGELOG.md: Added entries for all 13 implemented items across Phases 1–6
Full Changelog: 0.55.0...0.56.0