github saberzero1/motions 0.22.0

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

Added

  • Mobile support — the plugin is no longer desktop-only. Changed isDesktopOnly to false in manifest.json. EasyMotion and hint mode are disabled on mobile via Platform.isMobile guards because they depend on activeDocument/activeWindow (desktop-only Obsidian globals). All other features (core vim, text objects, navigation, workspace commands, vimrc, status bar, tables, surround) work on mobile. (#30)
    • src/main.ts: added Platform.isMobile guards to skip EasyMotion and hint mode registration on mobile (in onload, reloadFeatures, and reregisterLeaderFeatures)
    • eslint.config.mts: added @codemirror/* and @lezer/* to import/no-nodejs-modules allow list — eslint-plugin-obsidianmd enables this rule when isDesktopOnly: false

Fixed

  • EasyMotion big-WORD regex crashes on iOS < 16.4 — BIG_WORD_START_RE used a lookbehind assertion ((?<=\s|^)\S) which is not supported on iOS versions before 16.4. Rewritten as a two-pass scanner: first checks start-of-line for non-whitespace, then finds \s\S transitions mid-line. The obsidianmd/regex-lookbehind lint rule (enabled when isDesktopOnly: false) caught this. (#30)
  • import/no-nodejs-modules false positives on @codemirror/* imports — eslint-plugin-obsidianmd enables this rule when isDesktopOnly: false in manifest.json. The existing import/core-modules setting does not affect this rule's allow list. Added explicit allow entries for all @codemirror/* and @lezer/* packages to the rule configuration.
  • Configurable insert mode escape timeout — set insertmodeescapetimeout=N (alias imet, range 100–5000ms, default: 1000ms) controls how long the plugin waits between keystrokes when matching the insertmodeescape sequence (e.g. jk). Matches Neovim's timeoutlen default of 1000ms. Previously hardcoded at 200ms — too tight for normal typing. Configurable via vimrc, Settings UI (Settings → Vim Motions → Vim engine → Insert mode escape timeout), or runtime Vim.setOption('insertmodeescapetimeout', 500). (#31)
  • Vimrc ↔ Settings parity — all plugin settings are now configurable via .obsidian.vimrc in addition to the Settings UI. When vimrc is enabled (the default), vimrc values override the corresponding Settings UI values. Settings overridden by vimrc are shown as disabled controls in the settings tab with a note indicating the vimrc directive that set them (e.g., "Set by vimrc: set scrolloff=10").
    • Boolean feature toggles via set/set no: textobjects, navigation, hardwrap, listcontinuation, tablenav, workspacenav, easymotion, easymotiondimming, hintmode, statusbar, chorddisplay, powerline
    • Number options via set <option>=<value>: scrolloff (0–20), scanlimit (5–200), labelfontsize (10–20)
    • String options: easymotionlabels, hintlabels
    • Enum options: tablewidget (off/cursor/always), whichkey (off/leader/all), whichkeygrouping (flat/grouped)
    • Mode prompt customization via let g:mode_prompt_normal = "N" (and insert/visual/replace)
    • Which-key group labels via whichkeygroup <leader>t Table — name key prefix groups in the which-key popup
    • Which-key command labels via whichkeylabel <leader>w Save file — describe individual bindings in the which-key popup
    • Reverse-direction settings — clipboard, tabstop, shiftwidth, expandtab, insertmodeescape, and textwidth now have Settings UI controls (previously vimrc-only)
    • Priority rule: vimrc values override Settings UI values when enableVimrc is true. Overrides are in-memory only — the on-disk settings file always reflects UI-set values. Changing an overridden setting in the UI clears the override for the current session.
    • List merge: which-key group labels and command labels from vimrc are merged with labels configured in Settings. Vimrc entries appear as read-only rows; the "Add" button remains active for user additions. Vimrc wins on conflict.
    • source directive fix: settings and guicursor in sourced vimrc files now propagate correctly (pre-existing bug where onCursorShapeChange was not passed to recursive loadVimrcFile calls)
    • vimrcLoading flag fix: the flag is now reset to false after successful vimrc load, enabling runtime :set commands to trigger immediate reloadFeatures()
  • Vimrc set command routing — all known set options are now handled directly in the vimrc loader via a KNOWN_SET_OPTIONS mapping table, calling onSettingOverride directly instead of relying on defineOption callback dispatch through vim.handleEx. This ensures reliable settings override regardless of codemirror-vim initialization order. Unknown options fall through to handleEx for forward compatibility.
  • Spurious defineOption callback prevention — registerVimOptions now uses a registered flag to prevent defineOption callbacks from firing during initial option registration (codemirror-vim calls setOption(name, defaultValue) internally during defineOption). Without this guard, every option with a truthy default would spuriously populate vimrcOverrides and trigger reloadFeatures during plugin startup.
  • E2E test suite test/specs/vimrc-settings.e2e.ts with 11 tests covering boolean/number/string/enum option overrides, mode prompts, which-key labels, override tracking, and combined overrides
  • set insertmodeescape=jk not working (frame-perfect timing required) — the InsertEscapeHandler listened to vim-keypress events, which only fire for keys processed by codemirror-vim as vim commands. In insert mode, regular character keys bypass vim entirely and go through CM6's text input pipeline — the handler never saw them. Rewrote to use DOM keydown events on the editor element, correctly intercepting keystrokes in insert mode. Also fixed the insertmodeescape vim option not storing its value for getOption() retrieval (callback returned undefined instead of the stored value). (#31)
  • dk not deleting in operator-pending mode — dk (delete current and previous line) was a no-op because tableAwareMoveUp was registered with context: 'normal', causing it to be filtered out in operator-pending mode. CM Vim's keymap search then failed to fall through to the default k motion. Removed the context restriction since the motion already handles operator-pending mode internally via its hasOperator check.
  • Cursor snaps to formatting mark boundary in Live Preview — placing the cursor inside formatted text (*italic*, **bold**, `code`, ~~strike~~, ==highlight==) would snap to the delimiter boundary instead of the intended position. Obsidian's Live Preview uses Decoration.replace({}) to hide formatting marks on inactive lines, creating zero-width gaps that cause CM6's position mapping to collapse. Fixed by intercepting Decoration.replace({}) via RangeSetBuilder.prototype.add patching (same pattern as the table widget suppressor) and suppressing empty replace decorations whose text matches known formatting marks (*, **, _, __, `, ~~, ==). A StateField tracks the current document for text extraction. CSS styling (color: transparent) hides the now-visible marks on inactive lines while preserving their positional space.
  • % bracket matching skips brackets in strings and comments — the fork's scanForBracket fallback now calls getTokenTypeAt() for each bracket candidate and skips brackets inside "string" or "comment" tokens. Previously, positional stack counting would match a bracket inside a string literal. Note: in Markdown mode, Lezer does not classify double-quoted text as string tokens, so this primarily benefits languages with proper syntax trees.
  • <</>> indent respects shiftwidth and expandtab — the fork's indent operator now reads the vim options shiftwidth and expandtab (via getOption()) before falling back to CM6's tabSize and indentWithTabs. When set shiftwidth=2 or set expandtab is set in .obsidian.vimrc, the indent operator uses those values for both visual-block and line-by-line indentation.
  • V linewise visual cursor at end of line instead of column 0 — linewise visual mode (V, Vj, etc.) now positions the cursor at column 0 of the head line, matching Neovim. The fork's makeCmSelection was setting head.ch = lineLength(line) for display, which placed the cursor at the end of the line. A ViewPlugin with Decoration.line now provides the full-line visual highlight independently of the CM6 selection head position.
  • Vimrc map re-application — vimrc key mappings are now re-applied 200ms after initial load as a safety net against CM Vim initialization timing. If the initial applyVimrcMaps call runs before the CM6 vim extension has fully settled, the delayed retry ensures mappings take effect.

Changed

  • minAppVersion bumped to 1.2.3 — required for setDisabled() API on settings controls (used to disable vimrc-overridden settings in the UI). Obsidian 1.2.3 was released March 2023.

Documentation

  • KNOWN_LIMITATIONS.md: replaced "Desktop only" section with "Mobile support" section documenting Platform.isMobile guards and feature-by-platform compatibility matrix
  • README.md: updated Requirements from "Desktop only" to "Desktop and mobile" with link to known limitations
  • KNOWN_LIMITATIONS.md: added "Insert mode escape" section documenting the keydown-based handler, configurable timeout, and the vim-keypress event limitation; updated vi* single-character status to fixed via formatting mark suppression; updated % + strings to note Lezer limitation in Markdown; updated << unindent entry to note fork fix; removed V linewise cursor deviation; updated nmap L $ section with investigation findings; added "Formatting mark suppression" section
  • README.md: added insertmodeescapetimeout to number options table and vimrc example; added insert mode escape timeout to settings list
  • DIFFERENCES.md (fork): added sections for scanForBracket string/comment awareness, indent operator shiftwidth/expandtab support, linewise visual cursor positioning with decoration-based highlight

Full Changelog: 0.21.2...0.22.0

Don't miss a new motions release

NewReleases is sending notifications on new releases.