Skip to content

fix(agenda): keep cursor on item after redo - #1185

Open
seflue wants to merge 4 commits into
nvim-orgmode:masterfrom
seflue:fix/agenda-redo-cursor
Open

seflue wants to merge 4 commits into
nvim-orgmode:masterfrom
seflue:fix/agenda-redo-cursor

Conversation

@seflue

@seflue seflue commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

ref #656, #868

Refreshing the agenda on save, as suggested there, calls agenda:redo('remote_edit', true) from a BufWritePost autocmd. After the refresh the cursor is positioned on the last line of the agenda window and the scroll position is gone. A todo state change from the agenda does the same whenever the edit adds lines to the file, for example the CLOSED timestamp on DONE. That is annoying: on a long agenda every state change loses the context around the item.

The cause: redo saved and restored the view of whatever window was current. The callers inside the agenda (filter, clock report) run with the agenda window current, and there it worked. From the write hook the current window is the org buffer, from a remote edit it is the hidden edit window. The restore then jumped to a line number in the agenda that was in fact the cursor line of the org file. When that number was larger than the agenda's line count, the cursor landed on the last agenda line.

With this patch, redo takes the view from the agenda window and remembers the headline under the cursor (filename plus :ID:, or the title when there is none). After the rebuild the cursor follows that headline, and the window scrolls only when the headline left the visible range. When the headline is gone from the agenda, the cursor stays on its line.

Two more things the write hook setup turned up:

  • The headline is identified from data recorded when the agenda line was built. The OrgHeadline on the line points at tree-sitter nodes of the old parse; once lines were inserted above the item (a capture or refile into the same file), it describes a different headline.
  • A todo state change from the agenda updates its line in place after the write. With a redo from the write hook in between, the in-place update matched by line number and overwrote a line of the fresh render. The item then appeared in both its old and its new block. replace_line now matches the line object; a line from an earlier render is not in the view and the update does nothing.

The last commit is separate: the guard that skips the automatic resize after a manual resize compared only the window's screen position (#219). A horizontal split opens above the current window, so making it taller changes nothing there, and every redo shrank it back. The guard now also compares the height.

Agenda redo saved and restored the view of whatever window was
current. Redo runs while orgmode's hidden edit window is current
(remote edits) or an org buffer is (BufWritePost hooks that refresh
the agenda), so the org file's cursor line was applied to the agenda
and clamped to its last line. Users lost their position on every
remote todo change.

Redo now saves and restores the view of the agenda window itself and
remembers the headline under the cursor. After the rebuild the cursor
follows that headline (by :ID:, or by title when it has none), moving
the window only when the headline left the visible range. If the
headline is gone from the agenda, the cursor stays on the same line.

Refs: nvim-orgmode#656, nvim-orgmode#868
The guard that skips the automatic resize after a manual resize only
compared the window's screen position. A horizontal agenda split opens
above the current window, so its position never changes when the user
makes it taller, and every redo shrank it back to the default height.

Remember the height alongside the position and skip the resize when
either differs.

Refs: nvim-orgmode#219
The cursor relocation after redo read :ID: and title from the headline
object of the old agenda line. That object still points at tree-sitter
nodes of the previous parse, so once the file was edited above the item
(a write with a refresh hook, a refile or capture into the same file)
it described a different headline and the cursor jumped to it.

Each agenda line now records filename, :ID: and title when it is built,
while the headline is fresh. ID lookup no longer follows property
inheritance, and when a headline occupies several lines (scheduled and
deadline on one day) the line nearest the previous cursor wins.
A todo state change from the agenda updates the line in place. Users
who refresh the agenda on save, as suggested in nvim-orgmode#656 and nvim-orgmode#868, run a
redo from BufWritePost during that same edit. The redo finishes first
and the in-place update then targets a line from the previous render.
It was matched by line number, so it overwrote whatever the fresh view
had there and the item showed up in both its old and its new block.

Match the line object instead. A line from an earlier render is not in
the current view, so the in-place update does nothing and the redo
stands.

Refs: nvim-orgmode#656, nvim-orgmode#868
@seflue
seflue force-pushed the fix/agenda-redo-cursor branch from 19f17be to 51a667b Compare September 19, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant