fix(picker): better handling of buffers with custom URIs. Fixes #677

This commit is contained in:
Folke Lemaitre 2025-01-21 09:16:02 +01:00
parent 7d354726ff
commit cd5eddb1de
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
2 changed files with 51 additions and 5 deletions

View file

@ -60,10 +60,16 @@ end
---@param ctx snacks.picker.preview.ctx
function M.file(ctx)
if ctx.item.buf and not vim.api.nvim_buf_is_valid(ctx.item.buf) then
if ctx.item.buf and not ctx.item.file and not vim.api.nvim_buf_is_valid(ctx.item.buf) then
ctx.preview:notify("Buffer no longer exists", "error")
return
end
-- used by some LSP servers that load buffers with custom URIs
if ctx.item.buf and vim.uri_from_bufnr(ctx.item.buf):sub(1, 4) ~= "file" then
vim.fn.bufload(ctx.item.buf)
end
if ctx.item.buf and vim.api.nvim_buf_is_loaded(ctx.item.buf) then
local name = vim.api.nvim_buf_get_name(ctx.item.buf)
name = uv.fs_stat(name) and vim.fn.fnamemodify(name, ":t") or name

View file

@ -1,6 +1,8 @@
---@class snacks.picker.util
local M = {}
local uv = vim.uv or vim.loop
---@param item snacks.picker.Item
function M.path(item)
if not (item and item.file) then
@ -191,6 +193,36 @@ function M.resolve(item)
return item
end
--- Reads the lines of a file.
--- This is about 8x faster than `vim.fn.readfile`
--- and 3x faster than `io.lines` using a
--- test files of 225KB and 8300 lines.
---@param file string
function M.lines(file)
local fd = uv.fs_open(file, "r", 438)
if not fd then
return {}
end
local stat = assert(uv.fs_fstat(fd))
local data = assert(uv.fs_read(fd, stat.size, 0))
uv.fs_close(fd)
local lines, from = {}, 1 --- @type string[], number
while from <= #data do
local nl = data:find("\n", from, true)
if nl then
local cr = data:byte(nl - 1, nl - 1) == 13 -- \r
local line = data:sub(from, nl - (cr and 2 or 1))
lines[#lines + 1] = line
from = nl + 1
else
lines[#lines + 1] = data:sub(from)
break
end
end
return lines
end
---@param s string
---@param index number
---@param encoding string
@ -210,13 +242,21 @@ function M.resolve_loc(item, buf)
if not item or not item.loc or item.loc.resolved then
return item
end
-- return vim._str_byteindex(s, index, encoding == 'utf-16')
local lines = {} ---@type string[]
if buf and vim.api.nvim_buf_is_valid(buf) then
if item.buf and vim.api.nvim_buf_is_loaded(item.buf) then
-- valid and loaded buffer
lines = vim.api.nvim_buf_get_lines(item.buf, 0, -1, false)
elseif item.buf and vim.uri_from_bufnr(item.buf):sub(1, 4) ~= "file" then
-- item buffer with a custom uri
vim.fn.bufload(item.buf)
lines = vim.api.nvim_buf_get_lines(item.buf, 0, -1, false)
elseif buf and vim.api.nvim_buf_is_valid(buf) then
-- custom buffer (typically for preview)
lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
else
lines = vim.fn.readfile(item.file)
elseif item.file then
-- last resort, read the file
lines = M.lines(item.file)
end
---@param pos lsp.Position?