Added
set tablecolumnhighlight, to turn off the highlight on the cursor's column in a revealed table (#167). On by default, as before. Off, only the cursor's own cell is highlighted. Also Settings → Vim Motions → General → Highlight table cursor column, in both settings implementations, andvim.opt.tablecolumnhighlight. A change restyles an open table at once, without the cursor moving.- Plugin:
src/vim/table/reveal/grid-decorations.ts,src/settings.ts,src/vim/options.ts,src/vimrc/loader.ts
- Plugin:
set tableautorealign=typing: a revealed table realigns as you type (#167). Every typed edit realigns the table straight away, so the columns widen and narrow with the text, and leaving insert mode realigns once more. Each realign changes only the padding that differs; realigning a 1,000-row, 8-column table takes 0.72 ms, a 20-row one 0.02 ms. During an IME composition it waits for the commit, and a table with a row that is still missing its closing pipe is left alone, as withinsertleave. A space typed at the end of a cell's text cannot be told from padding, so the realign waits for the next character there rather than trim it. Typing|in a row adds a column to every row at once, as vim-table-mode does; the caret stays after the|, the regression that ended an earlier realign-on-|(#66), and an escaped\|stays text (#67).- One
uundoes an edit and its realign together, and.and macros repeat only the typed text. - Fork: codemirror-vim 6.4.11 (
notTypedAnnotation) - Plugin:
src/vim/table/reveal/auto-realign.ts(wasrealign-on-leave.ts)
- One
Changed
tableautorealignis three-way:off,insertleaveandtyping. It was a toggle. A storedtruemoves toinsertleaveandfalsetooff, saved vimrc and Lua overrides included, and the old forms still work: a bareset tableautorealignandvim.opt.tableautorealign = truemeaninsertleave,set notableautorealignandfalsemeanoff. In settings it is the dropdown Realign revealed tables on edits, in both implementations.- Plugin:
src/settings.ts,src/settings-migration.ts(migrateTableAutoRealign),src/vim/options.ts(tableAutoRealignValue),src/vimrc/loader.ts(bare/negatedfor a string option that was boolean)
- Plugin:
- Visual-line mode (
V) now gives the editor the whole lines it selects (#231). It used to give it only a cursor, soEditorState.selectionandlistSelections()showed nothing selected, and other plugins could not see the selection. It now works likev, like upstream codemirror-vim and like Obsidian's own Vim key bindings.- What you will see: in Live Preview, links, formatting marks and callouts on the selected lines show as source, as they already do under
v. A table stays rendered and keeps its highlight. - The cursor keeps its column, as in Neovim. It was drawn at column 0. Measured in Neovim 0.12.5:
wwVjreportscol('.') == 12. - Fork: codemirror-vim 6.4.10 (
updateCmSelection,makeCmSelection,measureCursor) - Plugin:
src/vim/visual-line-cursor.ts,src/vim/animated-cursor/controller.ts
- What you will see: in Live Preview, links, formatting marks and callouts on the selected lines show as source, as they already do under
Removed
- The workaround layer for the old visual-line selection.
src/vim/visual-line-command-fix.tspatched Obsidian'sEditormethods, the command registry and every command'scheckCallback. The fork also widened the selection for each key vim did not handle. With the real selection, Obsidian's commands, Note Composer and async callers such as Note Refactor see it unaided.
Fixed
gjafter a click went back to the column of thegjbefore it (#228). A click moves the cursor without a motion, so the screen columngjandgkkeep was not reset, and the nextgjheaded for wherever the earlier ones had been going. Vim resets the column on a click. Measured on Obsidian 1.15.0: aftergjpresses at x 348, a click on column 13 and thengjwent back to x 348. Obsidian's own Vim key bindings have the same gap, from the same upstream code.gjalso stopped short of a last display row shorter than the column, where Neovim moves to the end of that row. The check for "nothing to move to" compared the character column with the column in pixels. Obsidian's own Vim key bindings stop there too.- Not changed: in a paragraph with links or bold text, Live Preview shows each span's markup only while the cursor is in it. The paragraph re-wraps as the cursor leaves, and
gjloses the column, in Obsidian's own Vim as well. This is recorded inKNOWN_LIMITATIONS.md. - Fork: codemirror-vim 6.4.9 (
handleExternalSelection,findPosV)
- Plugins reading the selection saw nothing in visual-line mode (#231).
cm.listSelections()andEditorState.selectionput the anchor and head at one point, while onlyEditor.getSelection()returned the lines. See Changed. - Escape in a focused nested preview left the preview beneath it open for good (#208). Closing the focused preview left the focus nowhere. The next Escape went to the note, and the preview beneath stayed open with no key left to close it. Closing a focused preview, by Escape or
q, now returns you to where it was opened from: the preview beneath it, with its cursor where it was, or the note. This is what closing a Neovim float does.- Plugin:
src/vim/escape-guard.ts(dismissParentPopover)
- Plugin:
- A revealed table drew Obsidian's blue search ring around rows after an edit (#167).
yypdrew it around the pasted row, pasting several rows drew one around each, and a deletion that took the start of a row drew one around what was left. The reveal keeps a table as source with one search-match highlight per row, styled to stay invisible. Two bugs left a second one on a row, which looks like a real search match:- The reveal ran twice on every edit. Obsidian's own fold filter makes CodeMirror run transaction extenders a second time. Both runs added each new row's highlight, and only one of them could be removed.
- A deletion across a row's start dropped that row from the plugin's record, while CodeMirror kept the rest of the highlight. The plugin could not remove what it no longer knew about.
- Not font-dependent. Measured from the report's video, every character in the revealed rows has the same advance. The uneven pipes there come from a row with more padding than the rows above it;
:tablerealign, ortableautorealign, lines them up. - Plugin:
src/vim/table/reveal/reveal-field.ts(revealExtender,mapReveal)
.after a revealed table'sTaborEnteradded a row typed the row back in. The vim engine records every change made in insert mode as typed text, whatever made it.ix,Tabfrom the last cell,y,<Esc>, then.on another line insertedx, a line break, an empty row andy. Neovim leaves text an insert-mode<Cmd>mapping sets through the API out of., and its cursor move does not start a new insert; measured on 0.12.6, the same session repeatsxy. The row-adding keys and the moves between cells are now marked as not typed, so.repeatsxy, andiab,Tabto the next cell,cdrepeatsabcdwhere it repeatedcd. The<br>ofShift-Enteris typed at the cursor and is still repeated, as a key mapping to<br>is in Neovim.- Fork: codemirror-vim 6.4.11 (
notTypedAnnotation; the adapter gives such a change the origin+notTyped, whichonChangedoes not record, andonCursorActivitydoes not start a new insert for its move) - Plugin:
src/vim/table/reveal/cell-keys.ts
- Fork: codemirror-vim 6.4.11 (
Tests
display-line-goal.e2e.tspressesgjafter a real click, and onto a short last display row, on Obsidian 1.13.7 and 1.15.0.- Negative controls:
- Without the reset,
gjafter the click returns to the old column. - With the old check,
gjstays at column 239 instead of reaching the last row's end at 254.
- Without the reset,
- The fork's
gj_after_external_cursor_moveandgj_onto_shorter_last_rowfail the same two ways, on[0,14]and[0,59].
- Negative controls:
visual-line-editor-selection.e2e.tsreads what aVselection gives the editor:EditorState.selection,listSelections(),getSelection()and the column the cursor is drawn at, forV,Vjand an upwardVk.animated-cursor-edits.e2e.tschecks the animated cursor's column.- Negative controls:
- With the old fork, every case fails as the report describes: head equal to anchor at
(0,0), andgetSelection()empty. The visual-line command, Note Composer and Note Refactor specs fail too, now that the workaround layer is gone. - With the fork's cursor branch removed, the cursor is drawn at the end of the range:
ch10 and 16 instead of 6. - With the plugin helper removed, the animated cursor sits on the line end.
- With the old fork, every case fails as the report describes: head equal to anchor at
- The fork's
visual_line_editor_selectionandvisual_line_editor_selection_backwardsfail on the old bundle. - Golden comparisons now read a
Vcursor from vim's head, since the editor's head is now a line boundary. linewise-widget-selection-issue190.e2e.tsproves the widget highlight on a table, which stays rendered under the selection, instead of a callout, which now shows as source.- The synthetic #157 test that pressed
<Esc>beforereplaceSelection()was retired. It exercised the removed snapshot rather than a user path. The real command-palette Note Composer extract still runs.
- Negative controls:
keyword-preview.e2e.tspresses Escape twice in a nested preview opened byK/KK, andqonce.- Negative control: without the refocus, the focus is lost after the first Escape (
focusedIndex-1), and the first preview is still open after the second (count1). Theqcase fails the same way.
- Negative control: without the refocus, the focus is lost after the first Escape (
table-reveal-highlight-rings-issue167.e2e.tsreads Obsidian's own highlight field afteryyp,yjPand a deletion across a row's start, and the nested highlights that draw the ring.reveal-field.test.tsadds the same cases behind a filter shaped like Obsidian's fold filter.- Negative controls, recorded in
table-reveal-negative-controls.md(E11, E12, U8, U9):- Rebuilding from the start state on the second run leaves a second mark on each pasted row, nested at lines 4 and 5 for
yjP. - Mapping a row's start with
TrackDelleaves4:5-4:20, the rest of the deleted row's mark.
- Rebuilding from the start state on the second run leaves a second mark on each pasted row, nested at lines 4 and 5 for
- Negative controls, recorded in
table-reveal-editing.e2e.tsturnstablecolumnhighlightoff and on with the cursor still, and reads the highlighted column and cell each time.- Negative controls: with the setting ignored, and with the setting read only when the decorations would rebuild anyway, the column stays highlighted after
set notablecolumnhighlight(Value,11,22).
- Negative controls: with the setting ignored, and with the setting read only when the decorations would rebuild anyway, the column stays highlighted after
table-reveal-editing.e2e.tscoverstableautorealign=typingand.across the cell keys: the rows realign before insert mode ends, oneutakes back the edits and their realigns,.repeats only the typed text,.does not replay a rowTabadds, and.repeats the text on both sides of aTabto the next cell. It also types|outside a table and inside a row, an escaped\|, and a space between words, withtypingon.known-set-options.test.tsreads every vimrc form oftableautorealign, andsettings-migration.test.tsthe stored booleans.table-settings-labels.e2e.tschecks the dropdown.- Negative controls (S11 to S15 in
table-reveal-editing-negative-controls.md): without the live realign the rows stay unaligned in insert mode; without the annotation on the realign.types the padding back (Bx ---- | ----- xx - x - elow…); without it on the added row.adds a line; without it on the move.repeatscd; without the hold in a cell's trailing space,aa b ccomes outaa bc(S16). A bare vimrcset tableautorealignwrote nothing before the loader read''as bare. - The fork's
insert_not_typed_change_not_repeated,insert_not_typed_move_keeps_insertandinsert_not_typed_edit_then_real_movefail without the origin, without the cursor-activity exemption, and when a not-typed change armsexpectCursorActivityForChange, which made a real cursor jump after a realign keep the insert going (abcdforcd).
- Negative controls (S11 to S15 in
jump-list-open-tab-settings.e2e.tsruns<C-o>to a closed tab with Open Tab Settings 3.4.0 enabled (#206). The note opens in a new tab and the current tab keeps its note; without the plugin it opens in the current tab, as Vim reuses the current window. The jump list asksgetLeaf(false)for the leaf, which Open Tab Settings patches, so no integration code is needed. The plugin is added towdio.conf.mtsdisabled, like the other community plugins there.- Negative control: taking the active leaf instead of asking
getLeaf(false)opens the note in the current tab with Open Tab Settings on, which is the behaviour the report describes (newLeaffalse,OtsC.mdgone).
- Negative control: taking the active leaf instead of asking
Documentation
- The pages describing plugin features under Obsidian's built-in Vim mode were wrong. Since the Vim toggle commands (2026-08-31), the plugin stands its whole Vim layer down while built-in Vim mode is on, so none of its motions, text objects, flash, EasyMotion, which-key, jump list or configuration apply there. Measured on 1.13.7:
]hdoes not move, and<C-o>never leaves the note.recommended-setup.mdnow says so, andREADME.md, the flash, EasyMotion, surround, which-key, status bar, jump list, architecture and ecosystem pages point to it. The reveal table mode, registered outside the Vim layer, still applies.
Full Changelog: 1.5.1...1.6.0