From 647854d6c8c17862e48eac5933d90dfd4d7ac579 Mon Sep 17 00:00:00 2001 From: bvdmitri Date: Thu, 2 Apr 2026 13:02:22 +0200 Subject: [PATCH 1/6] support for tab pages --- lua/recall/navigation.lua | 6 +++--- lua/recall/utils.lua | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lua/recall/navigation.lua b/lua/recall/navigation.lua index d0103c5..6557478 100644 --- a/lua/recall/navigation.lua +++ b/lua/recall/navigation.lua @@ -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 diff --git a/lua/recall/utils.lua b/lua/recall/utils.lua index 98922d7..4287af5 100644 --- a/lua/recall/utils.lua +++ b/lua/recall/utils.lua @@ -36,7 +36,39 @@ M.sorted_global_marks = function() return marks end -M.set_cursor_for_mark_in_window = function(window_id, mark) +M.find_buf_locations = function(bufnr) + local results = {} + + if bufnr == -1 then + return results + end + + 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 + table.insert(results, { + tab = tab, + win = win, + }) + end + end + end + + return results +end + +M.find_first_buf_location = function(bufnr) + local locations = M.find_buf_locations(bufnr) + if #locations == 0 then + return nil + end + return results[1] +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 @@ -44,7 +76,7 @@ M.set_cursor_for_mark_in_window = function(window_id, mark) 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) end return M From 1b43ba121e0aa5a009d6ca0a631ead82c9377141 Mon Sep 17 00:00:00 2001 From: bvdmitri Date: Thu, 2 Apr 2026 13:04:12 +0200 Subject: [PATCH 2/6] oop typo --- lua/recall/utils.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/recall/utils.lua b/lua/recall/utils.lua index 4287af5..1a74915 100644 --- a/lua/recall/utils.lua +++ b/lua/recall/utils.lua @@ -62,7 +62,7 @@ M.find_first_buf_location = function(bufnr) if #locations == 0 then return nil end - return results[1] + return locations[1] end M.set_cursor_for_mark_in_location = function(tab_id, window_id, mark) From 3353b977832e93899fd1606e464ef0ac8b581de1 Mon Sep 17 00:00:00 2001 From: bvdmitri Date: Thu, 9 Apr 2026 14:04:29 +0200 Subject: [PATCH 3/6] added tests and simplified the implementation --- CHANGELOG.md | 7 ++++++ README.md | 2 +- lua/recall/utils.lua | 29 ++++++++++------------- tests/plenary/recall_spec.lua | 43 +++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69efd49..d6419f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index bfc109e..00b610a 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/lua/recall/utils.lua b/lua/recall/utils.lua index 1a74915..9e98e25 100644 --- a/lua/recall/utils.lua +++ b/lua/recall/utils.lua @@ -36,33 +36,28 @@ M.sorted_global_marks = function() return marks end -M.find_buf_locations = function(bufnr) - local results = {} - +M.find_first_buf_location = function(bufnr) if bufnr == -1 then - return results + 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 - table.insert(results, { - tab = tab, - win = win, - }) + 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 results -end - -M.find_first_buf_location = function(bufnr) - local locations = M.find_buf_locations(bufnr) - if #locations == 0 then - return nil - end - return locations[1] + return first end M.set_cursor_for_mark_in_location = function(tab_id, window_id, mark) diff --git a/tests/plenary/recall_spec.lua b/tests/plenary/recall_spec.lua index 8c23077..a0ba9a3 100644 --- a/tests/plenary/recall_spec.lua +++ b/tests/plenary/recall_spec.lua @@ -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) From 89b4f0c82181168eb0c1b0b03987804f7d81561b Mon Sep 17 00:00:00 2001 From: bvdmitri Date: Thu, 9 Apr 2026 14:27:21 +0200 Subject: [PATCH 4/6] simplify the code even further --- lua/recall/utils.lua | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lua/recall/utils.lua b/lua/recall/utils.lua index 9e98e25..5eaf6d2 100644 --- a/lua/recall/utils.lua +++ b/lua/recall/utils.lua @@ -41,23 +41,15 @@ M.find_first_buf_location = function(bufnr) 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 + return { tab = tab, win = win } end end end - return first + return nil end M.set_cursor_for_mark_in_location = function(tab_id, window_id, mark) From e234f346310268026d68fd438bf27699278018d9 Mon Sep 17 00:00:00 2001 From: bvdmitri Date: Thu, 9 Apr 2026 14:32:29 +0200 Subject: [PATCH 5/6] Revert "simplify the code even further" This reverts commit 89b4f0c82181168eb0c1b0b03987804f7d81561b. --- lua/recall/utils.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lua/recall/utils.lua b/lua/recall/utils.lua index 5eaf6d2..9e98e25 100644 --- a/lua/recall/utils.lua +++ b/lua/recall/utils.lua @@ -41,15 +41,23 @@ M.find_first_buf_location = function(bufnr) 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 - return { tab = tab, win = win } + 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 nil + return first end M.set_cursor_for_mark_in_location = function(tab_id, window_id, mark) From 2fc00b3c92bb8753714fc3ecffabeac759ab1f61 Mon Sep 17 00:00:00 2001 From: Bagaev Dmitry Date: Fri, 24 Apr 2026 11:58:22 +0200 Subject: [PATCH 6/6] refactor(utils): collapse cursor helpers into single set_cursor_for_mark(loc, mark) Replace set_cursor_for_mark_in_location(tab, win, mark) and set_cursor_for_mark_in_current_window(mark) with one function that takes a {tab, win} table. Pass nil from find_first_buf_location when no window is found, naturally meaning "stay in current tab/window". --- CHANGELOG.md | 4 ++-- lua/recall/navigation.lua | 12 ++++-------- lua/recall/utils.lua | 18 ++++++++---------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6419f3..cc7e965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.2.1](https://github.com/fnune/recall.nvim/releases/tag/1.2.1) - 2026-03-09 + ### 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 - Navigation now reuses existing windows displaying the target buffer instead of switching buffers diff --git a/lua/recall/navigation.lua b/lua/recall/navigation.lua index 6557478..923c060 100644 --- a/lua/recall/navigation.lua +++ b/lua/recall/navigation.lua @@ -61,16 +61,12 @@ end M.goto_mark = function(direction) local mark = M.find_mark(direction) if mark then - local bufnr = vim.fn.bufnr(mark.file) - if bufnr == -1 then + local buf_nr = vim.fn.bufnr(mark.file) + if buf_nr == -1 then vim.cmd("silent buffer " .. mark.file) -- loads buffer if not loaded at all end - 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 + local buf_location = utils.find_first_buf_location(buf_nr) + utils.set_cursor_for_mark(buf_location, mark) else print("No global marks set") end diff --git a/lua/recall/utils.lua b/lua/recall/utils.lua index 9e98e25..37c0e21 100644 --- a/lua/recall/utils.lua +++ b/lua/recall/utils.lua @@ -60,18 +60,16 @@ M.find_first_buf_location = function(bufnr) 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) +M.set_cursor_for_mark = function(buf_location, mark) + if buf_location then + if buf_location.tab then + vim.api.nvim_set_current_tabpage(buf_location.tab) + end + if buf_location.win then + vim.api.nvim_set_current_win(buf_location.win) + end 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_location(0, 0, mark) -end - return M