github saberzero1/motions 0.99.0

latest releases: 1.0.1, 1.0.0, 0.150.0...
one month ago

Added

  • set nopcre — Vim-style regular expressions — users can now switch from JavaScript/PCRE regexps to Vim-style regex syntax in search and substitution via set nopcre (vimrc), vim.opt.pcre = false (Lua), or the Settings UI toggle (Settings → Vim Motions → Vim engine → PCRE). The codemirror-vim fork already implemented the full pcre option (regex translation with magic modes, \</\> word boundaries, \zs/\ze, backreference conversion); this change wires it into the plugin's option tracking, settings UI, and documentation. Default: true (JavaScript regexps, no behavior change for existing users). (#111)
    • Plugin: src/settings.ts (pcre: boolean in VimMotionsSettings, pcre: true in DEFAULT_SETTINGS, toggle in both declarative and imperative settings UI — General page, Vim engine group)
    • Plugin: src/vimrc/loader.ts (pcre added to KNOWN_SET_OPTIONS and KNOWN_CM_VIM_OPTIONS)
    • Plugin: src/main.ts (initialization sync — vim.setOption('pcre', false) when user has disabled PCRE)
    • Plugin: test/unit/known-set-options.test.ts (pcre added to newOptions test array)
  • 37 snippet variables (up from 16 documented) — expanded the snippet variable system to cover the full VSCode snippet specification, plus vim-ecosystem aliases. New variables: $TM_SELECTED_TEXT (wired — was stubbed), $VISUAL (alias for $TM_SELECTED_TEXT, vim convention), $TM_CURRENT_LINE, $TM_CURRENT_WORD, $WORD (alias for $TM_CURRENT_WORD, vim convention), $TM_LINE_NUMBER (1-based), $TM_LINE_INDEX (0-based), $CLIPBOARD (wired via cache-ahead pattern — was stubbed), $RELATIVE_FILEPATH, $WORKSPACE_NAME, $WORKSPACE_FOLDER, $CURSOR_INDEX, $CURSOR_NUMBER, $CURRENT_MILLISECOND, $CURRENT_MILLISECONDS_UNIX, $CURRENT_TIMEZONE_NAME, plus previously undocumented $CURRENT_YEAR_SHORT, $CURRENT_MONTH_NAME_SHORT, $CURRENT_DAY_NAME_SHORT, $CURRENT_SECONDS_UNIX, $CURRENT_TIMEZONE_OFFSET. $CLIPBOARD uses a cache-ahead pattern (refreshed on window focus and visibilitychange) to avoid making the synchronous snippet pipeline async. On mobile, $CLIPBOARD resolves to empty due to browser clipboard API restrictions. $TM_SELECTED_TEXT / $VISUAL resolve to the editor selection at expansion time; in tab-expand mode, selection is not available (tab expansion requires an empty selection). (#110)
    • Plugin: src/snippets/types.ts (PreprocessContext — added currentLine, currentWord, lineNumber, lineIndex, workspaceName fields)
    • Plugin: src/snippets/variables.ts (added 15 new variable entries including VISUAL, WORD, TM_CURRENT_LINE, TM_CURRENT_WORD, TM_LINE_NUMBER, TM_LINE_INDEX, RELATIVE_FILEPATH, WORKSPACE_NAME, WORKSPACE_FOLDER, CURSOR_INDEX, CURSOR_NUMBER, CURRENT_MILLISECOND, CURRENT_MILLISECONDS_UNIX, CURRENT_TIMEZONE_NAME; added pad3() and getTimezoneName() helpers)
    • Plugin: src/main.ts (_clipboardCache field, refreshClipboardCache() method, clipboard cache listeners on window focus + visibilitychange + initial population; getSnippetPreprocessContext() rewritten to populate all fields from the active editor including selection, current line/word, line number, and workspace name)

Fixed

  • EasyMotion operator-pending inclusivity — EasyMotion motions (f, t, e, s, ge, E, gE) now correctly include the target character in operator-pending mode, matching native Vim semantics. Previously, all EasyMotion motions were registered with empty motionArgs, so the fork treated them as exclusive — y<leader><leader>fk{label} excluded the target k from the yank. Visual mode was unaffected (it extends the selection directly without consulting the inclusive flag). Backward motions (F, T) remain exclusive, matching native Vim. (#109)
    • Plugin: src/easymotion/register.ts (EasyMotionDef interface — added motionArgs?: Record<string, unknown>; EASYMOTION_DEFS — added motionArgs: { inclusive: true } to 8 of 17 defs matching Vim's native inclusivity; registration loop — passes def.motionArgs ?? {} to mapCommand)
  • Cursor stuck below YAML frontmatter in Live Preview with "Properties in document: Source"k, gk, and <Up> could not move into the frontmatter region when the editor was in Live Preview mode and Obsidian's "Properties in document" setting was set to "Source". In this configuration, the .metadata-container DOM element exists but is hidden (display: none). The fork's focusBefore callback found the hidden element via querySelector, focused it (no visible effect), and moveByLines/moveByDisplayLines returned the original cursor position — leaving the cursor stuck. Fixed by adding a setPropertiesSource(fn: () => boolean) API to the fork, parallel to setLivePreviewField. When the callback returns true, the frontmatter interception block is skipped entirely and the cursor moves through raw frontmatter text normally. The plugin passes () => getVaultConfig(app, 'propertiesInDocument') === 'source', evaluated per cursor movement so runtime setting changes take effect immediately. (#77)
    • Fork: ~/Repos/codemirror-vim/src/cm_adapter.ts (setPropertiesSource API, _propertiesSourceFn gate in findPosV)
    • Fork: ~/Repos/codemirror-vim/src/index.ts (export setPropertiesSource)
    • Fork: ~/Repos/codemirror-vim/DIFFERENCES.md (added setPropertiesSource API section, updated "Properties navigation" section with two-level gate)
    • Plugin: src/vim/bundled-vim.ts (createBundledVimExtension accepts isPropertiesSource callback, calls setPropertiesSource)
    • Plugin: src/main.ts (passes propertiesInDocument === 'source' callback)
    • Plugin: src/types/codemirror-vim.d.ts (added setPropertiesSource type declaration)
  • Escape in operator-pending mode exits embedded text area editor — pressing d then Escape in the textarea vim overlay exited the editor instead of clearing the pending operator. The Escape handler checked vim.mode === 'normal' without accounting for operator-pending, surround, partial key sequences, and literal-character-await sub-states. Additionally, the CM6 keymap handler could never run because vim's eventObservers.keydown called e.preventDefault() before CM6 keymaps processed the event. Fixed by moving Escape handling to an Obsidian Scope.register handler (fires before vim's observer) with a new isVimIdle() check covering all compound-command sub-states. (#112)
    • Plugin: src/editors/embeddable-editor.ts (isVimIdle helper, VimIdleState interface, Scope-based Escape handler replacing CM6 keymap handler)
  • Keydown events leak from embedded text area editor to parent modals — typing keys (e.g., Space) in insert mode inside the textarea vim overlay propagated keydown events to the parent modal, triggering unintended actions in third-party plugins (e.g., Spaced Repetition). Fixed with a new opt-in isolateKeyEvents option on EmbeddableEditorOptions that stops keydown and keyup propagation via CM6 domEventHandlers. Only enabled for textarea-vim overlays; Oil and table-cell editors are unaffected. (#112)
    • Plugin: src/editors/embeddable-editor.ts (isolateKeyEvents option, domEventHandlers with stopPropagation)
    • Plugin: src/vim/textarea-vim-manager.ts (isolateKeyEvents: true)
  • Unmatched <Space> inserted as text after failed multi-key sequence — pressing an unmapped key after a partial multi-key sequence (e.g., <leader><leader><Space> where no EasyMotion motion matches) inserted a literal space character into the document. Root cause: the fork's findKey used key.length === 1 to suppress unmatched single-character keys in normal mode, but vimKeyFromEvent converts Space to "<Space>" (7 characters) via the specialKey map, bypassing the guard. The function returned undefined instead of a consuming no-op, letting the keydown propagate to CM6's text input handler. Fixed by replacing the guard with key.length === 1 || /^<.+>$/.test(key) to match both plain characters and angle-bracket notation keys. (#112)
    • Fork: ~/Repos/codemirror-vim/src/vim.js (findKey — generalized key length guard)
    • Fork: ~/Repos/codemirror-vim/DIFFERENCES.md (added "Unmatched angle-bracket keys consumed in normal mode" section)

Tests

  • 4 e2e tests in test/specs/easymotion-comprehensive.e2e.ts (issue #109): inclusive f yank includes target character, inclusive e delete includes end-of-word character, exclusive w yank excludes target (regression), visual mode f yank includes target (regression)
  • 1 unit test in test/unit/known-set-options.test.ts (issue #111): pcre option registered in KNOWN_SET_OPTIONS with correct type and settingsKey, default value verified
  • 49 unit tests in test/unit/snippets/variables.test.ts: resolveVariables() coverage for all 37 variables (selection/content, file/path, workspace/cursor, date/time, random), syntax variants ($VAR and ${VAR}), alias parity ($VISUAL = $TM_SELECTED_TEXT, $WORD = $TM_CURRENT_WORD, $RELATIVE_FILEPATH = $TM_FILEPATH, $WORKSPACE_FOLDER = $WORKSPACE_NAME), edge cases (empty fields, unknown variables, tabstop defaults, adjacent variables)
  • 20 e2e tests in test/specs/snippets/snippet-variables-integration.e2e.ts (issue #110): file/path variables against live Welcome.md (5 tests), editor content variables with cursor positioning (4 tests), line number variables 1-based/0-based (3 tests), workspace name and alias (2 tests), cursor index/number constants (2 tests), selection variable resolution via getSnippetPreprocessContext() (3 tests), combined multi-variable expansion (1 test)
  • 3 e2e tests in test/specs/vim-builtin/g-commands.e2e.ts (issue #77): k moves up through source-rendered frontmatter, k navigates through multiple frontmatter properties, gk moves up through source-rendered frontmatter. Tests set propertiesInDocument to 'source' and ensure Live Preview mode, with save/restore of the original setting.
  • 13 unit tests in test/unit/embedded-editor-idle.test.ts (issue #112): isVimIdle coverage for null/undefined, idle normal, insert/visual/replace modes, operator pending, surround state, partial key buffer, expectLiteralNext, multiple sub-states, missing inputState, missing keyBuffer
  • 3 e2e tests in test/specs/textarea-vim.e2e.ts (issue #112): operator-pending Escape does not exit overlay, idle normal Escape exits overlay, insert-mode typing does not leak keydown to parent modal

Documentation

  • CHANGELOG.md
  • KNOWN_LIMITATIONS.md: Updated KNOWN_CM_VIM_OPTIONS list in set option scope section to include pcre; added snippet variable limitations section ($CLIPBOARD mobile restriction, $TM_SELECTED_TEXT tab-expand limitation, snip.env deferred, comment variables deferred); updated properties navigation section with "Properties in document: Source" edge case fix and updated test coverage; marked EasyMotion operator-pending inclusivity as fixed; added 4 new known limitations (linewise j/k, motionArgs.forward/clipToLine, EXTRA_DEFS bidirectional motions, easyMotionRepeat operator-pending)
  • CONTRIBUTING.md: Updated variables.ts description in codebase structure; updated bundled-vim.ts description with setPropertiesSource wiring; updated easymotion/register.ts description with per-motion motionArgs for operator-pending inclusivity
  • README.md: Updated Snippets feature line with variable count and vim-ecosystem aliases
  • AGENTS.md: Updated codemirror-vim fork description with setPropertiesSource API
  • docs/features/snippets.md: Expanded variable table from 16 to 37 entries organized into sections (selection/content, file/path, workspace/cursor, date/time, random) with info callout about selection and clipboard behavior
  • docs/features/easymotion.md: Updated operator-pending section with inclusivity semantics; fixed stale dot-repeat note
  • docs/configuration/vimrc.md: Added pcre row to boolean options table
  • docs/configuration/settings.md: Added pcre row to Vim engine settings table
  • docs/configuration/lua-config.md: Added pcre row to vim.opt options table
  • DIFFERENCES.md (fork): Added setPropertiesSource API section, updated "Properties navigation" section with two-level gate
  • KNOWN_LIMITATIONS.md: Updated embedded editor Escape handler description with Scope-based approach and isVimIdle sub-state detection; added key event isolation note
  • CONTRIBUTING.md: Updated embeddable-editor.ts description with isVimIdle helper, Scope-based Escape, and isolateKeyEvents option
  • AGENTS.md: Updated dual-vim architecture section with Scope-based Escape handling for embedded editors
  • DIFFERENCES.md (fork): Added "Unmatched angle-bracket keys consumed in normal mode" section under Behavioral fixes

Full Changelog: 0.98.0...0.99.0

Don't miss a new motions release

NewReleases is sending notifications on new releases.