fix(picker.diff): better filename parsing. See #2366

This commit is contained in:
Folke Lemaitre 2025-10-28 10:20:27 +01:00
parent c91e23060c
commit 377f3bfeca
No known key found for this signature in database
GPG key ID: 9B52594D560070AB
3 changed files with 20 additions and 55 deletions

View file

@ -106,14 +106,8 @@ function M.parse(lines)
emit()
local file ---@type string?
if text:find("^diff") then
local parsed = Snacks.picker.util.parse_cmdline(text)
for i = 2, #parsed do
local arg = parsed[i]
if not arg:find("^%-") then
file = arg:match("^%a/(.*)") or arg
break
end
end
text = text:gsub("^diff%s*", ""):gsub("^%-%S+%s*", "")
file = text:match('^"%a/(.-)"') or text:match("^%a/(.-) %a/") or text:match("^%a/(.*)$") or text
elseif text:find("^%-%-%-") then
file = text:match("^%-%-%- %a/([^\t]+)") or text:match("^%-%-%- ([^\t]+)")
end

View file

@ -646,53 +646,9 @@ function M.globber(globs)
end
end
---@param cmdline string
---@return string[]
function M.parse_cmdline(cmdline)
local args = {} ---@type string[]
local current = ""
local in_quote = false
local quote_char = nil
local i = 1
while i <= #cmdline do
local char = cmdline:sub(i, i)
local next_char = cmdline:sub(i + 1, i + 1)
if char == "\\" and next_char ~= "" then
-- Escaped character - take next char literally
current = current .. next_char
i = i + 2
elseif not in_quote and (char == '"' or char == "'") then
-- Start quote
in_quote = true
quote_char = char
i = i + 1
elseif in_quote and char == quote_char then
-- End quote
in_quote = false
quote_char = nil
i = i + 1
elseif not in_quote and char:match("%s") then
-- Whitespace outside quotes - split arg
if current ~= "" then
args[#args + 1] = current
current = ""
end
i = i + 1
else
-- Regular character or whitespace inside quotes
current = current .. char
i = i + 1
end
end
-- Add last arg if any
if current ~= "" then
args[#args + 1] = current
end
return args
---@param buf number
function M.spinner(buf)
return require("snacks.picker.util.spinner").new(buf)
end
return M

View file

@ -235,6 +235,21 @@ describe("picker.diff", function()
assert.equals("my file.txt", blocks[1].file)
end)
it("handles files with spaces in name without quotes", function()
local lines = {
"diff --git a/my file.txt b/my file.txt",
"--- a/my file.txt",
"+++ b/my file.txt",
"@@ -1,1 +1,1 @@",
"-old",
"+new",
}
local blocks = diff.parse(lines)
assert.equals(1, #blocks)
assert.equals("my file.txt", blocks[1].file)
end)
it("handles files in subdirectories", function()
local lines = {
"diff --git a/path/to/file.txt b/path/to/file.txt",