github saberzero1/motions 0.146.0

3 hours ago

Added

  • Table debug state inspector — new :tablestate (:tables) ex command and window.CodeMirrorAdapter.getTableDebugState(app) API that snapshots all hidden table interaction state into a single queryable object. Exposes: table-nav session (WeakMap state, widget connection, scopes, timers), CM6 StateField, mode tracker status, cell editor status, cursor suppression (global, per-view, override count), fork key intercept flag, cell crossing coordination, DOM markers, and table scroll metrics. Designed to make table interaction bugs observable instead of invisible. (#167)
    • Plugin: src/vim/table-debug-state.ts (new — snapshot and formatter)
    • Plugin: src/vim/table-nav-controller.ts (getTableNavSessionSnapshot() export, __DEV__ lifecycle logging)
    • Plugin: src/vim/table-cell-motions.ts (getCrossingState() export)
    • Plugin: src/vim/bundled-vim.ts (registerTableDebugState() lazy registration)
    • Plugin: src/workspace/commands.ts (:tablestate ex command)
    • Plugin: src/main.ts (debug state registration at startup and reload)
    • Plugin: src/types/codemirror-vim.d.ts (fork type declarations for new exports)
    • Fork: src/block-cursor.ts (getViewOverrideCount(), isCursorSuppressed() re-export)
    • Fork: src/index.ts (isKeyInterceptActive(), updated exports)
    • Fork: DIFFERENCES.md (documented new exports)

Fixed

  • Key intercept stuck after switching from Live Preview to source mode during table-navsetKeyInterceptActive(true) set during table-nav was never cleared when switching LP→source because the TableNavController ViewPlugin doesn't receive update() calls or destroy() during view reconfiguration. All vim key processing was suppressed globally, leaving the editor unresponsive. Fixed with a window capture-phase keydown safety handler that detects stale key intercept (active but no .vim-motions-table-nav-mode in DOM) and clears it. Pressing Escape restores key processing. Cursor suppression and stale Obsidian Scopes remain as a known limitation requiring fork-level fixes. (#167)
    • Plugin: src/main.ts (window keydown safety handler)
    • Plugin: src/vim/bundled-vim.ts (setKeyInterceptActive and clearCursorSuppressedForView exposed on bridge)
    • Plugin: src/vim/table-nav-controller.ts (forceTableNavCleanup() export, logTableEvent lifecycle logging)
    • Plugin: src/vim/table-debug-state.ts (event log: logTableEvent, getTableEventLog, recentEvents in state output, mainCursorLayerHidden field)
  • Horizontal scrolling missing in table-nav mode — navigating to off-screen columns in wide tables left the highlighted cell outside the visible viewport. The table widget had overflow: visible which prevented horizontal scrolling entirely — no ancestor element was scrollable. Fixed by changing the table widget's overflow to overflow-x: auto during table-nav mode and scrolling the widget element directly in scrollHighlightedCellIntoView(). (#167)
    • Plugin: src/vim/table-nav-controller.ts (horizontal scroll logic targeting s.widgetEl)
    • Styles: styles.css (.vim-motions-table-nav-mode: overflow-x: auto; overflow-y: visible)
  • Hot-reload and manual reload fail intermittently — when the initial config load hit Obsidian's adapter timing race (file exists on disk but adapter.read() returns empty during early lifecycle), vimrcWatchPath/luaWatchPath were set to null because found was derived from the read result, not the stat. This broke both the file watcher (never fires) and the reload command (softReloadVimrc bails on null path). Three fixes: (1) watch paths are now resolved via resolveVimrcPath/resolveLuaConfigPath (stat-based) as a fallback when the read-based load returns found: false; (2) softReloadVimrc no longer bails on empty commands — an empty parse is a valid reload (clears all vimrc mappings); (3) reloadAllConfigs resolves vimrcWatchPath on-demand when null. (#168)
    • Plugin: src/main.ts (stat-based watch path fallback in initial load, softReloadVimrc guard relaxed, reloadAllConfigs on-demand resolution)
  • Removed Lua keymaps persist after config reload — keymaps registered via vim.keymap.set() in init.lua were not cleaned up when removed from the config and reloaded. Two root causes: (1) softReloadLuaConfig did not unmap old Lua keymaps or undefine old ex commands before re-loading; (2) Lua keymaps were double-registered (eagerly during loadInitLua and again via applyLuaMaps in reloadFeatures), so a single vim.unmap only removed one entry. Fixed by adding an unmap loop in softReloadLuaConfig that removes all occurrences of each old mapping. (#168)
    • Plugin: src/main.ts (softReloadLuaConfig unmap/undefineEx cleanup with multi-occurrence loop)
  • Scroll offset activates during mouse selection — dragging to select text near the viewport edge triggered scrolloff-based scrolling prematurely. With high scrolloff values (e.g., scrolloff=999 for centered scrolling), the viewport jumped while the mouse was still well within the editor. Fixed by tracking pointer-down state via a CM6 ViewPlugin with eventObservers and suppressing the scrolloff listener while a mouse button is held (plus a 100ms settle delay after release to prevent post-selection scroll drift). (#175)
    • Plugin: src/vim/scrolloff.ts (added mouseTracker ViewPlugin, pointerdown/pointerup observers, document-level pointerup safety listener, 100ms settle timer)
  • Subword motion dw deletes across line boundaries — with subword motions enabled, dw on the last word of a line deleted the newline and text from the next line instead of stopping at end of line. Two root causes: (1) the subword motion mapCommand calls were missing forward: true/forward: false in motionArgs, so the fork's clipToLine (which prevents dw from crossing line boundaries) was never triggered; (2) the subword motion only recognized word-character boundaries (letters, numbers) and completely skipped non-word non-whitespace characters (:, ., !, etc.), causing w to jump past trailing punctuation to the next line instead of stopping at the punctuation group. Verified against Neovim 0.12.5 — all test scenarios now match Neovim's behavior. (#174)
    • Plugin: src/motions/register.ts (added forward: true to w/e and forward: false to b/ge subword motion registrations)
    • Plugin: src/motions/subword.ts (added punctuation group boundary detection via PUNCT_GROUP_RE, merged with subword boundaries in getLineStarts/getLineEnds)
  • Hover tooltips from other plugins clipped within scrolloff zone — hover tooltips (page preview, auto-linker, LanguageTool, and any CM6 hoverTooltip extension) were hidden for text within the top/bottom scrolloff lines. Root cause: the scrolloff implementation used EditorView.scrollMargins, which CM6's tooltip plugin reads to determine the "visible" viewport area — tooltips positioned within the margin zone were clipped to top: -10000px. With the default scrolloff of 5, the first ~5 lines were affected; with scrolloff=999 (centered scrolling), the entire top half of the viewport was affected. mousedown was unaffected because the mouse-down guard already returned null from scroll margins. Fixed by replacing EditorView.scrollMargins with an EditorView.updateListener that manually adjusts scrollDOM.scrollTop after cursor movement, preserving the scrolloff scroll behavior without marking the margin zone as invisible to the tooltip system. (#170)
    • Plugin: src/vim/scrolloff.ts (replaced scrollMargins facet with updateListener-based manual scroll adjustment, added 100ms settle delay after pointerup to prevent scroll drift on mouse selection end)

Tests

  • 3 e2e tests in test/specs/table-cursor-vanish.e2e.ts for #167 (stuck state detection, bridge cleanup, automatic keydown safety handler)
  • 2 e2e tests in test/specs/table-debug-state.e2e.ts for table debug state instrumentation (#167)
  • 3 additional e2e tests in test/specs/config-management.e2e.ts for #168 (removed vimrc mapping cleaned up via reload command; removed vimrc mapping cleaned up on disk change; removed Lua keymap cleaned up via reload command)
  • 2 e2e tests in test/specs/scrolloff-mouse.e2e.ts for #175 (mouse drag near viewport edge should not cause scroll drift; keyboard navigation should still trigger scrolloff)
  • 3 e2e tests in test/specs/subword-motions.e2e.ts for #174 (dw at end of line, dw on last word, dw before trailing punctuation)
  • 6 e2e tests in test/specs/cursor-pointer-events.e2e.ts for #170 (pointer-events, mousemove targeting, Decoration.mark hit-testing, caretRangeFromPoint, mouseover, hoverTooltip callback)

Documentation

  • CHANGELOG.md
  • KNOWN_LIMITATIONS.md: added horizontal scrolling fix, key intercept stuck limitation, table debug state documentation
  • CONTRIBUTING.md: added table-debug-state.ts to file tree, updated table-nav-controller.ts and table-cell-motions.ts descriptions, updated scrolloff.ts description to reflect new implementation
  • AGENTS.md: updated fork API surface with new diagnostic exports
  • docs/features/tables.md: updated viewport scrolling callout to mention horizontal scrolling

Full Changelog: 0.145.0...0.146.0

Don't miss a new motions release

NewReleases is sending notifications on new releases.