Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to Recall will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Navigation now searches across all tab pages when jumping to a mark, switching to the correct
tab page if the target buffer is already displayed there.

## [1.2.1](https://github.com/fnune/recall.nvim/releases/tag/1.2.1) - 2026-03-09

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ streamlining their usage and enhancing their visibility and navigability.
- Simplifies mark setting and removal with `:RecallToggle`, abstracting away
the burden of choosing and remembering specific letters.
- When navigating marks in a split-window layout, `recall` reuses existing windows
instead of switching buffers in the current window.
instead of switching buffers in the current window. This also works across tab pages.

Recall attempts to eliminate the cognitive overhead associated with mark
management, enabling you to focus on your workflow, not on mark administration.
Expand Down
6 changes: 3 additions & 3 deletions lua/recall/navigation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ M.goto_mark = function(direction)
if bufnr == -1 then
vim.cmd("silent buffer " .. mark.file) -- loads buffer if not loaded at all
end
local window_id_to_reuse = vim.fn.bufwinid(bufnr)
if window_id_to_reuse ~= -1 then
utils.set_cursor_for_mark_in_window(window_id_to_reuse, mark)
local bufloc = utils.find_first_buf_location(bufnr)
if bufloc ~= nil then
utils.set_cursor_for_mark_in_location(bufloc.tab, bufloc.win, mark)
else
utils.set_cursor_for_mark_in_current_window(mark)
end
Expand Down
31 changes: 29 additions & 2 deletions lua/recall/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,42 @@ M.sorted_global_marks = function()
return marks
end

M.set_cursor_for_mark_in_window = function(window_id, mark)
M.find_first_buf_location = function(bufnr)
if bufnr == -1 then
return nil
end

local current_tab = vim.api.nvim_get_current_tabpage()
local first = nil

for _, tab in ipairs(vim.api.nvim_list_tabpages()) do
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tab)) do
if vim.api.nvim_win_get_buf(win) == bufnr then
if tab == current_tab then
return { tab = tab, win = win }
end
if first == nil then
first = { tab = tab, win = win }
end
end
end
end

return first
end

M.set_cursor_for_mark_in_location = function(tab_id, window_id, mark)
if tab_id ~= 0 then
vim.api.nvim_set_current_tabpage(tab_id)
end
if window_id ~= 0 then
vim.api.nvim_set_current_win(window_id)
end
vim.api.nvim_win_set_cursor(0, { mark.pos[2], mark.pos[3] })
end

M.set_cursor_for_mark_in_current_window = function(mark)
M.set_cursor_for_mark_in_window(0, mark)
M.set_cursor_for_mark_in_location(0, 0, mark)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit/off-topic: the naming and signature is getting a bit convoluted (I started this, though).

  • set_cursor_for_mark_in_location seems to be the lower-level tool. Maybe just call it set_cursor_for_mark(...)?
  • But also set_cursor_for_mark reads strange. Maybe go_to_mark? We could rename the others then?
  • I don't like it when scalar arguments go one after another. One could easily get confused and pass (winid, tabid) instead of (tabid, winid). Should we take a table with fields winid and tabid to avoid confusion?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this could be done as a separate PR, it's not really on-topic here.

end

return M
43 changes: 43 additions & 0 deletions tests/plenary/recall_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,47 @@ describe("Recall", function()
assert.are.equal(current_buf, bufnr2)
assert.are.same(cursor_pos, { 20, 1 })
end)

it("reuses opened windows across tab pages", function()
local bufnr1 = create_temp_buffer()
place_cursor(10, 1)
recall.mark()
local tab1 = vim.api.nvim_get_current_tabpage()
local win1 = vim.api.nvim_get_current_win()

vim.cmd("tabnew")
local bufnr2 = create_temp_buffer()
place_cursor(20, 1)
recall.mark()
local tab2 = vim.api.nvim_get_current_tabpage()
local win2 = vim.api.nvim_get_current_win()

assert.are.not_equal(tab1, tab2)

-- From tab2, navigate to the mark in tab1
recall.goto_next()

local current_tab = vim.api.nvim_get_current_tabpage()
local current_win = vim.api.nvim_get_current_win()
local current_buf = vim.api.nvim_get_current_buf()
local cursor_pos = vim.api.nvim_win_get_cursor(current_win)

assert.are.equal(current_tab, tab1, "Should switch to the tab page displaying the target buffer")
assert.are.equal(current_win, win1, "Should reuse the window in the other tab page")
assert.are.equal(current_buf, bufnr1)
assert.are.same(cursor_pos, { 10, 1 })

-- Navigate back to the mark in tab2
recall.goto_next()

current_tab = vim.api.nvim_get_current_tabpage()
current_win = vim.api.nvim_get_current_win()
current_buf = vim.api.nvim_get_current_buf()
cursor_pos = vim.api.nvim_win_get_cursor(current_win)

assert.are.equal(current_tab, tab2, "Should switch back to the other tab page")
assert.are.equal(current_win, win2, "Should reuse the window in the other tab page")
assert.are.equal(current_buf, bufnr2)
assert.are.same(cursor_pos, { 20, 1 })
end)
end)
Loading