Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion lua/jumpy/diff.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ local function myers_diff(old_lines, new_lines)

if d > 1 then
if x == prev_x then
table.insert(edits, 1, { op = "insert", new_idx = y })
table.insert(edits, 1, { op = "insert", old_idx = x + 1, new_idx = y })
y = y - 1
else
table.insert(edits, 1, { op = "delete", old_idx = x })
Expand Down Expand Up @@ -120,6 +120,9 @@ function M.compute(old_lines, new_lines)
current_hunk.old_count = current_hunk.old_count + 1
table.insert(current_hunk.removed_lines, old_lines[edit.old_idx])
elseif edit.op == "insert" then
if not current_hunk.old_start then
current_hunk.old_start = edit.old_idx
end
if not current_hunk.new_start then
current_hunk.new_start = edit.new_idx
end
Expand Down
44 changes: 32 additions & 12 deletions lua/jumpy/navigate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ local render = require("jumpy.render")
local MSG_NO_HUNKS = "jumpy: no hunks"
local MSG_NO_HUNK_UNDER_CURSOR = "jumpy: no hunk under cursor"

local function center_cursor()
vim.cmd("normal! zz")
end

local function set_cursor_centered(line)
local line_count = vim.api.nvim_buf_line_count(0)
local target_line = math.min(math.max(line, 1), line_count)
vim.api.nvim_win_set_cursor(0, { target_line, 0 })
center_cursor()
end

local function current_hunk_line(bufnr, idx, hunk)
local line = hunk.old_start + M._get_offset(bufnr, idx)
local line_count = vim.api.nvim_buf_line_count(bufnr)
return math.min(math.max(line, 1), line_count)
end

local function get_active_hunks(bufnr)
local state = render.get_state(bufnr)
if not state then
Expand Down Expand Up @@ -32,8 +49,8 @@ function M.hunk_at_cursor()

for _, entry in ipairs(active) do
local hunk = entry.hunk
local hunk_start = hunk.old_start
local hunk_end = hunk.old_start + math.max(hunk.old_count, 1) - 1
local hunk_start = current_hunk_line(bufnr, entry.idx, hunk)
local hunk_end = hunk_start + math.max(hunk.old_count, 1) - 1

if cursor_line >= hunk_start and cursor_line <= hunk_end then
return entry.idx
Expand All @@ -54,13 +71,14 @@ function M.next_hunk()
end

for _, entry in ipairs(active) do
if entry.hunk.old_start > cursor_line then
vim.api.nvim_win_set_cursor(0, { entry.hunk.old_start, 0 })
local hunk_line = current_hunk_line(bufnr, entry.idx, entry.hunk)
if hunk_line > cursor_line then
set_cursor_centered(hunk_line)
return
end
end

vim.api.nvim_win_set_cursor(0, { active[1].hunk.old_start, 0 })
set_cursor_centered(current_hunk_line(bufnr, active[1].idx, active[1].hunk))
end

function M.prev_hunk()
Expand All @@ -74,13 +92,14 @@ function M.prev_hunk()
end

for i = #active, 1, -1 do
if active[i].hunk.old_start < cursor_line then
vim.api.nvim_win_set_cursor(0, { active[i].hunk.old_start, 0 })
local hunk_line = current_hunk_line(bufnr, active[i].idx, active[i].hunk)
if hunk_line < cursor_line then
set_cursor_centered(hunk_line)
return
end
end

vim.api.nvim_win_set_cursor(0, { active[#active].hunk.old_start, 0 })
set_cursor_centered(current_hunk_line(bufnr, active[#active].idx, active[#active].hunk))
end

function M.accept()
Expand Down Expand Up @@ -267,7 +286,7 @@ local function jump_to(bufnr, line)
end
vim.cmd("normal! m'")
vim.api.nvim_win_set_buf(0, bufnr)
vim.api.nvim_win_set_cursor(0, { line, 0 })
set_cursor_centered(line)
end

function M.first_hunk_any_buf()
Expand All @@ -285,12 +304,13 @@ function M._advance_to_next(bufnr)
if #active > 0 then
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
for _, entry in ipairs(active) do
if entry.hunk.old_start >= cursor_line then
vim.api.nvim_win_set_cursor(0, { entry.hunk.old_start, 0 })
local hunk_line = current_hunk_line(bufnr, entry.idx, entry.hunk)
if hunk_line >= cursor_line then
set_cursor_centered(hunk_line)
return
end
end
vim.api.nvim_win_set_cursor(0, { active[1].hunk.old_start, 0 })
set_cursor_centered(current_hunk_line(bufnr, active[1].idx, active[1].hunk))
else
offset_table[bufnr] = nil
local all = get_all_active_hunks()
Expand Down
18 changes: 12 additions & 6 deletions lua/jumpy/render.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ local function build_virt_lines(added_lines)
return virt_lines
end

local function insertion_anchor(bufnr, old_start)
local line_count = vim.api.nvim_buf_line_count(bufnr)
local is_eof = old_start > line_count
local anchor_line = is_eof and line_count - 1 or old_start - 1
return math.max(0, anchor_line), not is_eof
end

local buf_states = {}

function M.get_state(bufnr)
Expand Down Expand Up @@ -89,12 +96,11 @@ function M.show(bufnr, hunks, original_lines, proposed_lines)

local virt_lines = build_virt_lines(hunk.added_lines)

local anchor_line = math.max(0, hunk.old_start - 1)
anchor_line = math.min(anchor_line, vim.api.nvim_buf_line_count(bufnr) - 1)
local anchor_line, virt_lines_above = insertion_anchor(bufnr, hunk.old_start)

local id = vim.api.nvim_buf_set_extmark(bufnr, ns, anchor_line, 0, {
virt_lines = virt_lines,
virt_lines_above = anchor_line ~= 0,
virt_lines_above = virt_lines_above,
sign_text = "+",
sign_hl_group = "JumpyAddedSign",
priority = 100,
Expand Down Expand Up @@ -179,17 +185,17 @@ function M.update_hunk_lines(bufnr, hunk_idx, new_added_lines)
local virt_lines = build_virt_lines(new_added_lines)

local anchor_line
local virt_lines_above = false
if hunk.old_count > 0 then
anchor_line = math.min(hunk.old_start - 1 + hunk.old_count - 1, vim.api.nvim_buf_line_count(bufnr) - 1)
anchor_line = math.max(0, anchor_line)
else
anchor_line = math.max(0, hunk.old_start - 1)
anchor_line = math.min(anchor_line, vim.api.nvim_buf_line_count(bufnr) - 1)
anchor_line, virt_lines_above = insertion_anchor(bufnr, hunk.old_start)
end

local id = vim.api.nvim_buf_set_extmark(bufnr, ns, anchor_line, 0, {
virt_lines = virt_lines,
virt_lines_above = hunk.old_count == 0 and anchor_line ~= 0,
virt_lines_above = virt_lines_above,
priority = 100,
})
table.insert(hunk.extmarks, id)
Expand Down
37 changes: 37 additions & 0 deletions tests/diff_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ describe("diff.compute", function()
local hunks = diff.compute(old, new)

assert.are.equal(1, #hunks)
assert.are.equal(1, hunks[1].old_start)
assert.are.same({ "a" }, hunks[1].added_lines)
end)

Expand All @@ -98,9 +99,45 @@ describe("diff.compute", function()
local hunks = diff.compute(old, new)

assert.are.equal(1, #hunks)
assert.are.equal(3, hunks[1].old_start)
assert.are.same({ "c" }, hunks[1].added_lines)
end)

it("keeps repeated insertion hunks anchored to old-file lines", function()
local old = {
"local function one()",
"end",
"",
"local function two()",
"end",
"",
"local function three()",
"end",
}
local new = {
"--- One.",
"local function one()",
"end",
"",
"--- Two.",
"local function two()",
"end",
"",
"--- Three.",
"local function three()",
"end",
}
local hunks = diff.compute(old, new)

assert.are.equal(3, #hunks)
assert.are.equal(1, hunks[1].old_start)
assert.are.equal(4, hunks[2].old_start)
assert.are.equal(7, hunks[3].old_start)
assert.are.same({ "--- One." }, hunks[1].added_lines)
assert.are.same({ "--- Two." }, hunks[2].added_lines)
assert.are.same({ "--- Three." }, hunks[3].added_lines)
end)

it("handles complete replacement", function()
local old = { "a", "b" }
local new = { "x", "y", "z" }
Expand Down
Loading