Summary
When using Snacks.picker.git_diff() with a base option, each picker entry's line number reflects the first line of the rendered diff preview block rather than the actual hunk start line in the file. This causes incorrect jump targets when opening a file from the picker and wrong lnum values when sending results to the quickfix list via <c-q>.
Environment
- snacks.nvim:
9912042 (main, 2026-03-04)
- Neovim:
v0.12.0-dev-2435+g18c5f06c9f
- OS: Linux
Steps to reproduce
-- Minimal config
Snacks.picker.git_diff({
base = "origin/main", -- or any commit/branch
})
- Open the picker. Each entry shows a file with a line number.
- Select an entry and press
<CR> — the cursor lands at the wrong line (often line 1).
- Alternatively, press
<c-q> to send all entries to the quickfix list. Every lnum is incorrect.
Repro
vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
require("lazy.minit").repro({
spec = {
{ "folke/snacks.nvim", opts = {} },
},
})
-- From a git repo on a branch that has commits ahead of main, run:
-- :lua Snacks.picker.git_diff({ base = "main" })
Expected behaviour
Each entry's pos (and the resulting quickfix lnum) should be the hunk start line on the right-hand side of the unified diff header — i.e. the +N value from @@ -a,b +N,M @@.
Actual behaviour
Entries use the first line of the rendered diff preview block as their pos, rather than the hunk start line from the @@ ... +N,M @@ header. Each entry's line number therefore reflects wherever the preview panel begins rendering, not the actual changed line in the file.
Investigation
The data flow for line numbers is:
snacks/picker/source/git.lua:261 — runs git diff --no-color --no-ext-diff --diff-filter=u [--merge-base <base>]
snacks/picker/source/diff.lua:112 — M.parse() iterates raw diff lines
snacks/picker/source/diff.lua:262 — M.parse_hunk_header() extracts right.line from @@ -a,b +line,count @@
snacks/picker/source/diff.lua:89 — add() sets pos = { line, 0 } on each item
snacks/picker/actions.lua:540 — setqflist() reads item.pos[1] as quickfix lnum
The static parsing logic in parse_hunk_header appears correct for standard unified diff output. For example, with the following actual diff produced by the repo under test:
@@ -108,39 +108,8 @@
@@ -154,7 +123,6 @@
@@ -12,14 +12,12 @@ some context
@@ -34,5 +32,4 @@ some context
The expected pos values are 108, 123, 12, and 32 respectively. The bug appears to be a runtime issue — either in how opts.group is propagated to Diff.diff(), or in how pos is overwritten downstream — rather than a parsing failure.
Notes
Summary
When using
Snacks.picker.git_diff()with abaseoption, each picker entry's line number reflects the first line of the rendered diff preview block rather than the actual hunk start line in the file. This causes incorrect jump targets when opening a file from the picker and wronglnumvalues when sending results to the quickfix list via<c-q>.Environment
9912042(main, 2026-03-04)v0.12.0-dev-2435+g18c5f06c9fSteps to reproduce
<CR>— the cursor lands at the wrong line (often line 1).<c-q>to send all entries to the quickfix list. Everylnumis incorrect.Repro
Expected behaviour
Each entry's
pos(and the resulting quickfixlnum) should be the hunk start line on the right-hand side of the unified diff header — i.e. the+Nvalue from@@ -a,b +N,M @@.Actual behaviour
Entries use the first line of the rendered diff preview block as their
pos, rather than the hunk start line from the@@ ... +N,M @@header. Each entry's line number therefore reflects wherever the preview panel begins rendering, not the actual changed line in the file.Investigation
The data flow for line numbers is:
snacks/picker/source/git.lua:261— runsgit diff --no-color --no-ext-diff --diff-filter=u [--merge-base <base>]snacks/picker/source/diff.lua:112—M.parse()iterates raw diff linessnacks/picker/source/diff.lua:262—M.parse_hunk_header()extractsright.linefrom@@ -a,b +line,count @@snacks/picker/source/diff.lua:89—add()setspos = { line, 0 }on each itemsnacks/picker/actions.lua:540—setqflist()readsitem.pos[1]as quickfixlnumThe static parsing logic in
parse_hunk_headerappears correct for standard unified diff output. For example, with the following actual diff produced by the repo under test:The expected
posvalues are108,123,12, and32respectively. The bug appears to be a runtime issue — either in howopts.groupis propagated toDiff.diff(), or in howposis overwritten downstream — rather than a parsing failure.Notes
baseset (branch diff via--merge-base). Untested withoutbase.group = falseby default, which should produce one entry per hunk with individualh.linevalues. Ifgroupis inadvertentlytrueat runtime, all hunks collapse into one entry per file using onlyblock.hunks[1].line.auto_confirm = true#543 (1-0 indexing), fix(git): setdiff.noprefix=falseforgit diffto ensure correct format #2174 (diff.noprefix=false).