Skip to content

Commit 490c7e9

Browse files
Tobias LarossTobias Laross
authored andcommitted
Implemented goto_next & goto_previous
1 parent f603aa4 commit 490c7e9

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Vim's built-in marks are great, but they're global and get messy fast. Marksman
2020
- **Persistent storage** - Your marks survive Neovim restarts with automatic backup
2121
- **Smart naming** - Context-aware auto-generation using Treesitter and pattern matching
2222
- **Quick access** - Jump to marks with single keys or interactive UI
23+
- **Sequential navigation** — Jump to next/previous marks with wrap-around behavior
2324
- **Enhanced search** - Find marks by name, file path, or content with real-time filtering
2425
- **Mark reordering** - Move marks up/down to organize them as needed
2526
- **Multiple integrations** - Works with Telescope, Snacks.nvim, and more
@@ -238,6 +239,8 @@ local marksman = require("marksman")
238239
-- Basic operations return { success, message, ... }
239240
local result = marksman.add_mark("my_mark")
240241
local result = marksman.goto_mark("my_mark") -- or goto_mark(1) for index
242+
local result = marksman.goto_next()
243+
local result = marksman.goto_previous()
241244
local result = marksman.delete_mark("my_mark")
242245
local result = marksman.rename_mark("old", "new")
243246

lua/marksman/init.lua

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,77 @@ function M.goto_mark(name_or_index)
289289
end
290290
end
291291

292+
local function get_current_mark_index(storage_module)
293+
local mark_names = storage_module.get_mark_names()
294+
local count = #mark_names
295+
296+
if count == 0 then
297+
return nil, "No marks available"
298+
end
299+
if count == 1 then
300+
return nil, "Not enough marks to move"
301+
end
302+
303+
local marks = storage_module.get_marks()
304+
local current_file = vim.fn.expand("%:p")
305+
local current_line = vim.fn.line(".")
306+
307+
local best_index = nil
308+
local best_distance = nil
309+
310+
for index, mark_name in ipairs(mark_names) do
311+
local mark = marks[mark_name]
312+
if mark.file == current_file then
313+
if mark.line == current_line then
314+
return index, nil
315+
end
316+
local distance = math.abs(mark.line - current_line)
317+
if not best_distance or distance < best_distance then
318+
best_index = index
319+
best_distance = distance
320+
end
321+
end
322+
end
323+
324+
return best_index or 1, nil
325+
end
326+
327+
---Jump to next mark
328+
---@return table result Result with success and message
329+
function M.goto_next()
330+
local storage_module = get_storage()
331+
if not storage_module then
332+
return { success = false, message = "Failed to load storage module" }
333+
end
334+
335+
local current_index, err = get_current_mark_index(storage_module)
336+
if not current_index then
337+
return { success = false, message = err }
338+
end
339+
340+
local mark_names = storage_module.get_mark_names()
341+
local next_index = (current_index % #mark_names) + 1
342+
return M.goto_mark(next_index)
343+
end
344+
345+
---Jump to previous mark
346+
---@return table result Result with success and message
347+
function M.goto_previous()
348+
local storage_module = get_storage()
349+
if not storage_module then
350+
return { success = false, message = "Failed to load storage module" }
351+
end
352+
353+
local current_index, err = get_current_mark_index(storage_module)
354+
if not current_index then
355+
return { success = false, message = err }
356+
end
357+
358+
local mark_names = storage_module.get_mark_names()
359+
local previous_index = ((current_index - 2) % #mark_names) + 1
360+
return M.goto_mark(previous_index)
361+
end
362+
292363
---Delete a mark by name
293364
---@param name string Mark name to delete
294365
---@return table result Result with success and message

tests/marksman_spec.lua

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,65 @@ describe("marksman.nvim", function()
125125
assert.equals(3, vim.fn.col("."))
126126
end)
127127

128+
it("jumps to next mark with wrap-around", function()
129+
-- open the test file and place marks on lines 1, 2, and 3
130+
vim.cmd("edit " .. test_file)
131+
132+
vim.fn.cursor(1, 1)
133+
marksman.add_mark("m1")
134+
135+
vim.fn.cursor(2, 1)
136+
marksman.add_mark("m2")
137+
138+
vim.fn.cursor(3, 1)
139+
marksman.add_mark("m3")
140+
141+
-- start at m1
142+
vim.fn.cursor(1, 1)
143+
local result = marksman.goto_next()
144+
assert.is_true(result.success)
145+
assert.equals(2, vim.fn.line("."), "Should jump from m1 to m2")
146+
147+
-- now at m2 -> next should be m3
148+
result = marksman.goto_next()
149+
assert.is_true(result.success)
150+
assert.equals(3, vim.fn.line("."), "Should jump from m2 to m3")
151+
152+
-- now at m3 -> next should wrap back to m1
153+
result = marksman.goto_next()
154+
assert.is_true(result.success)
155+
assert.equals(1, vim.fn.line("."), "Should wrap from m3 to m1")
156+
end)
157+
158+
it("jumps to previous mark with wrap-around", function()
159+
vim.cmd("edit " .. test_file)
160+
161+
vim.fn.cursor(1, 1)
162+
marksman.add_mark("m1")
163+
164+
vim.fn.cursor(2, 1)
165+
marksman.add_mark("m2")
166+
167+
vim.fn.cursor(3, 1)
168+
marksman.add_mark("m3")
169+
170+
-- start at m1 -> previous should wrap to m3
171+
vim.fn.cursor(1, 1)
172+
local result = marksman.goto_previous()
173+
assert.is_true(result.success)
174+
assert.equals(3, vim.fn.line("."), "Should wrap from m1 to m3")
175+
176+
-- now at m3 -> previous should be m2
177+
result = marksman.goto_previous()
178+
assert.is_true(result.success)
179+
assert.equals(2, vim.fn.line("."), "Should jump from m3 to m2")
180+
181+
-- now at m2 -> previous should be m1
182+
result = marksman.goto_previous()
183+
assert.is_true(result.success)
184+
assert.equals(1, vim.fn.line("."), "Should jump from m2 to m1")
185+
end)
186+
128187
it("deletes marks", function()
129188
vim.cmd("edit " .. test_file)
130189
marksman.add_mark("delete_me")

0 commit comments

Comments
 (0)