github saberzero1/motions 1.5.1

4 hours ago

Added

  • set tablewidget=auto, the new default: tables are revealed while the Neovim backend is connected, and use Obsidian's table editor otherwise. It switches when the backend connects, disconnects or dies.
    • Why the split: Obsidian's table editor gives each cell an editor of its own, and the backend mirrors only the note's editor, so under the backend Neovim never saw a cell being edited. A revealed table is lines of the note, which Neovim edits, follows its cursor into and draws its extmarks over. With the plugin's own Vim, Obsidian's editor keeps its buttons, sorting and menus while you edit, works on mobile, and is the most tested mode, so it stays.
    • The setting's options are now Automatic, Always native, Always reveal (experimental) and Raw (deprecated), in both settings implementations, set tablewidget and vim.opt.tablewidget.
    • Plugin: src/vim/table/gates.ts (effectiveTableWidgetMode), src/main.ts (followBackendForTables), src/vim/table-nav-controller.ts, src/settings.ts, src/vim/options.ts, src/vimrc/loader.ts

Changed

  • A stored tablewidget of native moves to auto once. Settings are saved whole, defaults included, so a native saved before auto existed cannot be told apart from the old default.
    • Nothing changes without the Neovim backend, where auto is native.
    • With the backend, tables are now revealed. To keep Obsidian's table editor there too, choose Always native again; it is then kept, because settings saved since carry tableWidgetAutoMigrated.
    • raw and reveal are left alone.
    • Plugin: src/settings-migration.ts (migrateTableWidgetMode)

Fixed

  • A vimrc mapping on a key the plugin binds itself was gone once Obsidian had started (#205). nmap gw <Plug>(easymotion-bd-w) left gw as hard-wrap's operator, and nnoremap Y yy left Y as the plugin's y$.
    • Cause: the plugin removes and re-registers its bindings at startup and on every settings change. The removal took your mapping for the same keys with it, and a mapping that survived ended up behind the re-added binding, which then won. Reloading the vimrc after startup put the mappings back, which is why it went unseen. A mapping on a key the plugin does not bind, such as <leader>w, was never affected.
    • Fix: your mappings, from the vimrc and from Lua, are lifted off before the plugin re-registers its bindings and put back on top afterwards, as user mappings beat built-in commands in Vim. Each is removed exactly, so nothing of the plugin's goes with it.
    • Two related leaks: every pane switch re-applied the vimrc's mappings without removing the previous copies, so they piled up. A vimrc reload removed the old mappings by key in normal mode only, so visual and insert mappings outlived their removal from the file.
    • Plugin: src/main.ts (liftUserKeyMaps, restoreUserKeyMaps, removeKeyMapsExactly), src/types/vim-api.d.ts
    • Fork: codemirror-vim 6.4.8 makes the engine's side exact too. unmap(lhs, ctx, { toKeys, noremap }) removes only that user mapping, and removeMapCommand no longer removes a user mapping on the same keys.
  • K inside a preview put the preview's cursor back at the top instead of previewing the link under it (#208). K always read the note's own editor and cursor, whichever editor it was pressed in, so inside a focused preview it acted as the note's second K and refocused the preview.
    • Fix: it now uses the editor it was pressed in. Inside a preview it opens the nested preview Obsidian's own hover would, with the preview's note as the hover parent and as the source the link resolves from. KK there moves into the nested preview.
    • Plugin: src/workspace/navigation.ts (keywordContext)
  • <C-o> after switching tabs went to an older note instead of the tab before (#206). Only the plugin's own navigations recorded a jump, such as gd, the pickers and ]b. A tab clicked or reached with Ctrl+Tab, or a note Obsidian opened in the current tab, recorded nothing.
    • Fix: every change of note is now a jump, recorded with the line the note was left at, as Neovim records a :b switch. With a, b and c in three tabs, visiting them in order and pressing <C-o>, <C-o>, <C-i> lands on b, a, b, matching Neovim 0.12.5, and focuses each note's own tab.
    • A navigation that records its own jump, and the walk itself, are not recorded twice.
    • Jumps within a note are walked in their place. After switching to a note and jumping inside it, <C-o> used to go back to the previous note before visiting those jumps. The editor kept them in a list of its own, which the walk consulted only once the plugin's list had no older note.
      • Fix: the bundled engine now reports each jump it records, and the plugin keeps it in the same list as the note switches, in the order they happened. Each position is a bookmark in the note's editor, so it follows edits.
      • Neovim's model: where a jump landed, or a note was entered, stands for the cursor until the next jump's start replaces it, as Neovim records only where jumps start. The first <C-o> moves it to the cursor, and once walked past it is an ordinary entry.
      • Measured parity with Neovim 0.12.5:
        • a, then G in b, walks b, a, b, b.
        • <C-o> back into a note visits the jumps made there.
        • j is not a jump, so <C-o> after it goes straight to the previous note.
        • <C-i> comes back to where <C-o> was pressed.
        • The walk stays put at the oldest entry.
      • Flash and EasyMotion jumps now record where they landed too, so <C-o> returns from them. :jumps lists the jumps within notes.
      • Only with the bundled engine: Obsidian's own Vim key bindings and the Neovim backend do not report their jumps, so there the previous order stands.
      • Still differs from Neovim: the list keeps an older entry on the same line as a newer one, where Neovim drops it. Both are recorded in KNOWN_LIMITATIONS.md.
    • Plugin: src/workspace/note-switch-jumps.ts, src/workspace/in-note-jumps.ts, src/vim/jumplist.ts, src/workspace/global-defaults.ts, src/workspace/navigate.ts, src/flash/{jump,char,search}-mode.ts, src/easymotion/register.ts, src/main.ts
    • Fork: src/vim.js (setJumpRecordedCallback), released as codemirror-vim 6.4.8
  • In Obsidian's table editor, keys table-nav does not use were typed into the cell (#167). Table-nav, on by default, holds vim off while it keeps the cell editor hidden but focused. So a key outside its keymap reached the cell as text and was written to the note: u, zz and x made Name into uzzxName. After a click on bb, x made it bxb. Such keys now do nothing.
    • A second race in the same mode: table-nav sends i to the cell's vim only once the cell editor is up, 150 ms on. Keys typed in between ran as normal-mode commands, so iq started recording a macro and iHi typed nothing. They are now held and played after the i.
    • Visual mode still ends at a native table: Obsidian's widget moves the cursor into its cell editor as soon as a selection reaches it. set tablewidget=reveal keeps the table as lines, where V extends through its rows. This is recorded in KNOWN_LIMITATIONS.md.
    • Plugin: src/vim/table-nav-controller.ts (holdEntryKeys), src/vim/table-nav-keymap.ts (typesIntoCell, heldEntryKey, replayEntryKeys)
  • The picker's "Results" title covered the first result (#171). The title took its height from the theme's line height, so a theme with a taller one covered the first result again. At a line height of 2.4 it overlapped the first result's text by 13.6 px. Even in the default theme its edge sat 1.2 px into the first result's box. It now has a height of its own, centred on the border, and the list clears it by 3 px in any theme.
    • Plugin: styles.css

Tests

  • vimrc-feature-key-overrides.e2e.ts loads a vimrc with nmap gw <Plug>(easymotion-bd-w) and nnoremap Y yy the way Obsidian's start does. That means a cold plugin load followed by exactly one leaf change. Earlier tests reloaded the vimrc after startup, which is what hid the defect.
    • Scenarios:
      • both mappings work after start;
      • they still work after settings changes and pane switches, with one copy each;
      • removing Y from the vimrc gives Y back to the plugin.
    • Negative controls:
      • Not lifting the mappings fails the first two, with no EasyMotion labels on gw and Y yanking three four five (the plugin's y$).
      • Re-applying on a pane switch without lifting fails the second, which then finds duplicate entries.
      • A vimrc reload that removes nothing fails the third, with Y still yanking the whole line.
  • keyword-preview.e2e.ts presses K on a link inside a focused preview. It expects a nested preview of that link, the first preview still focused on its second line, and KK moving into the nested one.
    • Negative controls:
      • Reading the note's own editor, the old behaviour, opens no nested preview.
      • Using the note's view as the hover parent replaces the preview instead of nesting.
      • KK never focusing a nested preview leaves focus in the first, at index 0 instead of 1.
  • jump-list-note-switch.e2e.ts switches between three notes in three tabs the way a tab click does, and walks back with <C-o>.
    • Scenarios:
      • a, b, c then <C-o>, <C-o>, <C-i> lands on b, a, b;
      • <C-o> returns to the line the note was left at;
      • it focuses the previous tab without opening a fourth;
      • a note opened in the same tab is a jump;
      • gd, a heading picked in :headings and ]b are each recorded once.
    • Negative controls:
      • Not installing the recorder fails the four switch scenarios, because <C-o> has nowhere to go and stays put: on c throughout the walk, on b instead of a's line 3, and on c after the same-tab open.
      • Not skipping a switch that left the list at the new note fails gd and ]b, recording a, c, a, c and a, b, a, b.
      • Not skipping one while a navigation is still opening its note fails :headings, recording a, b, a, b.
      • Forgetting the cursor of a note whose tab has since been reused fails the same-tab scenario, landing on line 0 instead of 2.
  • jump-list-note-switch.e2e.ts also walks the jumps made within notes among the switches, each scenario against a sequence measured on Neovim 0.12.5.
    • Scenarios:
      • a, then G in b, walks b:0, a:0, b:0, b:2;
      • G in a, then b, walks back a:4, a:0, a:0;
      • at the oldest jump <C-o> stays put;
      • jj is not a jump, so <C-o> goes to a, and <C-i> back to b:2;
      • after G and k, <C-i> comes back to where <C-o> was pressed;
      • once walked past, where G landed stays put;
      • a line put above a jump moves it;
      • a note replaced in its tab keeps where its jumps were, in :jumps and in the walk;
      • <C-o> to a closed fold's heading opens it;
      • a Flash jump is walked back and forth.
    • Negative controls:
      • Not installing the engine's callback fails seven scenarios: the first <C-o> after G in b lands on a again, the old order.
      • Handing jumps within a note back to the engine's own walk fails nine.
      • Each rule of the model fails its own scenarios when removed:
        • keeping where a note was entered stops on b:0 instead of reaching a;
        • not moving the newest entry to the cursor brings <C-i> back to b:0 and a:4 instead of b:2 and a:3;
        • moving its position but not its bookmark brings <C-i> back to a:4;
        • not making it an ordinary entry once walked past brings <C-i> to a:3 instead of a:4.
      • Fixed positions instead of bookmarks land on b:1 instead of b:2 after the edit.
      • Reading a replaced editor's bookmark lists a:2 instead of a:3 in :jumps.
      • Letting the walk fall through to the engine's list at the oldest entry lands on a:1.
      • Not marking the walk as foldopen's mark leaves the fold closed.
      • Recording only where a Flash jump started leaves <C-o> at a:4.
  • test/unit/jumplist.test.ts covers the list's handling of jumps within a note: landing entries replaced by the next start or departure, the cursor taking the newest one, bookmarks followed through edits, deleted text skipped, an editor gone, released marks and serialization.
    • Each rule fails a test when removed: comparing lines only, not releasing dropped marks, not skipping deleted text, not dropping a gone editor's mark, not replacing a landing, keeping more than the newest as a landing, not making a walked-past landing ordinary, and not clearing it when a jump starts there.
  • The fork's jumplist_recorded_callback and jumplist_recorded_callback_throws check that the callback reports G with its positions and nothing for k or the walk, and that a throwing callback leaves the jump and the list intact. Not calling it records no call, and letting the throw escape aborts the G.
  • table-nav-stray-keys.e2e.ts presses keys in Obsidian's own table editor with table-nav on.
    • Scenarios:
      • u, zz, x, Space and Backspace leave the cell and the note unchanged;
      • after a click on bb, x does nothing, and iQ inserts at the click, making bQb;
      • iHi sent at once types Hi;
      • iH then Escape at once leaves H in the cell in normal mode.
    • Negative controls:
      • Not swallowing the stray keys types uzzx into the cell and the note, and bxb after the click.
      • Not holding keys during the 150 ms loses Hi, H and Q, and leaves the last scenario in insert mode.
      • Holding them but not typing them in insert mode loses the same text.
  • picker-results-title.e2e.ts measures the "Results" title against the first result, in the default theme and with a line height of 2.4.
    • Results: the first result's box sits 3 px below the title, and its text 6 px below, in both themes.
    • Negative controls:
      • The 1.5.0 CSS fails both: −1.2 px and −16.6 px for the box, 1.8 px and −13.6 px for the text.
      • Centring the title without giving it a height of its own still fails at a line height of 2.4, at −14.6 px.
  • tablewidget=auto is covered with and without the Neovim backend, and across a migration.
    • Scenarios:
      • rpc-table-reveal.e2e.ts A1 to A3: auto reveals the table Neovim moves into once the backend connects, and gives tables back to Obsidian's table editor when the backend disconnects (A2) or its process is killed (A3);
      • table-nav-mode.e2e.ts: table-nav still starts on auto without the backend;
      • table-widget-migration.e2e.ts loads stored settings cold: a native saved before auto moves and records the move, a native chosen after it stays, a native picked on fresh settings stays, and reveal is left alone;
      • unit: table-widget-mode.test.ts resolves each setting with and without the backend, and settings-migration.test.ts covers the move with and without the marker.
    • Negative controls:
      • Resolving auto to native always fails A1, with Obsidian's widget and table-nav taking the table.
      • Not following the backend fails A1 and A2. A settings reload during a pending disconnect still sees the backend, so without the listener the table stayed revealed after it.
      • Following only the backend connecting fails A2 and A3, with the table revealed after the backend has gone.
      • Gating table-nav on the stored setting fails the no-backend scenario, A2 and A3.
      • Never moving native fails the first migration scenario.
      • A marker that defaults to false fails the first scenario and moves a native picked on fresh settings to auto.

Full Changelog: 1.5.0...1.5.1

Don't miss a new motions release

NewReleases is sending notifications on new releases.