github saberzero1/motions 0.20.0

latest releases: 1.4.0, 1.3.1, 1.3.0...
3 months ago

Fixed

  • let mapleader = "," (comma) and other keys with default Vim bindings not working as leader for EasyMotion — unmapDefaultBinding now passes { includeDefaults: true } to vim.unmap(), so built-in codemirror-vim bindings (e.g. , → repeatLastCharacterSearch, ; → forward repeat) are actually removed before registering EasyMotion mapCommand multi-key sequences. Previously, vim.unmap() silently skipped _isDefault keymap entries, meaning the default single-key binding consumed the first keystroke before the multi-key sequence (e.g. ,,w) could accumulate. Space as leader was unaffected because the default <Space> binding uses angle-bracket notation which doesn't collide with literal space in commandMatch. (#6)
  • gg/G and other keymaps intermittently stop working — comprehensive vim state hardening across the codemirror-vim fork and plugin to prevent keymaps from breaking until app reload. Root causes identified and fixed: stale normal-mode key prefix state persisting across focus changes, global singleton keymap corruption via unmap() removing default entries, incomplete leaveVimMode() cleanup leaking insert-mode listeners, and async motion race conditions. (#18)
    • Fork: blur handler resets partial key prefixes — the CM6 ViewPlugin now registers a blur listener on contentDOM that calls clearInputState() when the editor loses focus in normal mode. A stale prefix like g no longer persists across tab switches or modal opens, preventing the next keystroke from being silently swallowed.
    • Fork: leaveVimMode() cleanup hardened — now removes insert-mode change/keydown listeners if the editor was destroyed while in insert mode, clears the global lastInsertModeKeyTimer, clears virtualPrompt, and resets inputState before nulling cm.state.vim.
    • Fork: default keymaps protected from unmap() — default keymap entries are tagged with _isDefault and a frozen snapshot is stored at module init. unmap() now skips default entries unless explicitly requested. New Vim.resetKeymap() API restores defaults from the snapshot while preserving user mappings. mapclear() updated to use the _isDefault flag instead of fragile index-based partitioning.
    • Fork: async motion generation tracking — _commandGeneration counter on vim state prevents stale async motion callbacks from executing after a newer command has already run. Protects EasyMotion operator-pending mode (d + easymotion) from race conditions.
    • Plugin: pane-switch state reset — active-leaf-change handler now clears pending vim input state on all editors when switching panes, preventing partial commands from leaking across editors.
    • Plugin: resetKeymap() on load — calls Vim.resetKeymap() during plugin onload() to ensure a clean keymap baseline on plugin enable/reload, recovering from any prior corruption in the same app session.

Added

  • Which-key leader grouping — leader key bindings in the which-key overlay are now grouped by prefix key, matching Neovim's which-key plugin behavior. When grouping is enabled (default), pressing the leader key shows collapsed groups (e.g. t → Table (+11), \ → EasyMotion (+17)) instead of listing every binding individually. Pressing a group key drills down to show only bindings within that group. Configurable via Settings → Vim Motions → Which-key leader grouping (grouped / flat). (#27)
    • Groups are sorted first in the overlay, followed by ungrouped single-key bindings
    • Group rows are visually distinct (accent color, italic) via the .vim-motions-which-key-group CSS class
    • Grouping applies to all completions in "all partial keys" mode, not just leader-scoped bindings — any multi-key prefix (g, z, [, ], custom mappings) can be grouped
    • Drill-down works in both "leader key only" and "all partial keys" which-key modes
  • Which-key group labels — configurable names for key groups in the which-key overlay. Prefix keys can be labeled (e.g. \t → Table, gr → LSP) instead of showing the generic +N keys text. Built-in features register default labels (Table, EasyMotion) that can be overridden. Labels support <leader> token expansion (e.g. <leader>t resolves to the actual leader key + t). Configurable via Settings → Vim Motions → Which-key group labels.
  • E2E test suite test/specs/vim-state-hardening.e2e.ts with 7 tests: blur prefix recovery, gg/G after plugin reload, keymap protection via unmap(), resetKeymap() recovery after force-unmap, leaveVimMode cleanup from insert mode
  • Fork unit tests: 10 new tests for async motion generation tracking (superseded motion discarded, superseded delete discarded), keymap protection (unmap skips defaults, unmap removes user mapping preserving default, unmap gg preserves default, resetKeymap restores after force-unmap, resetKeymap preserves user mappings, mapclear preserves defaults), leaveVimMode cleanup (clears input state, cleanup from insert mode)
  • Fork test count: 1672 (up from 1660)

Documentation

  • DIFFERENCES.md (fork): added sections for blur handler, leaveVimMode cleanup hardening, default keymap protection (_isDefault tagging, resetKeymap(), mapclear() update), async motion generation tracking, clearInputState API exposure
  • KNOWN_LIMITATIONS.md: added "Vim state hardening" section documenting the multi-layered defense against intermittent keymap breakage
  • README.md: added "Improved vim state reliability" bullet to recommended setup section

Full Changelog: 0.19.0...0.20.0

Don't miss a new motions release

NewReleases is sending notifications on new releases.