fix(lsp): when renaming directory, check path prefix of buffer names (#27603)

For example, when renaming /path/to/dir, buffers like
fern://drawer/file:///path/to/dir, /path/to/dir123 should not be
matched.
This commit is contained in:
Jaehwang Jung 2024-02-25 00:47:34 +09:00 committed by GitHub
parent 04f723f1a5
commit 8addd27504
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 85 additions and 15 deletions

View File

@ -2003,7 +2003,11 @@ rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()*
Rename old_fname to new_fname
Parameters: ~
• {opts} (`table`)
• {old_fname} (`string`)
• {new_fname} (`string`)
• {opts} (`table?`) options
• overwrite? boolean
• ignoreIfExists? boolean
*vim.lsp.util.show_document()*
show_document({location}, {offset_encoding}, {opts})

View File

@ -639,13 +639,28 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
return vim.lsp._completion._lsp_to_complete_items(result, prefix)
end
--- Get list of buffers for a directory
local function get_dir_bufs(path)
path = path:gsub('([^%w])', '%%%1')
local function path_components(path)
return vim.split(path, '/', { plain = true })
end
local function path_under_prefix(path, prefix)
for i, c in ipairs(prefix) do
if c ~= path[i] then
return false
end
end
return true
end
--- Get list of buffers whose filename matches the given path prefix (normalized full path)
---@return integer[]
local function get_bufs_with_prefix(prefix)
prefix = path_components(prefix)
local buffers = {}
for _, v in ipairs(vim.api.nvim_list_bufs()) do
local bufname = vim.api.nvim_buf_get_name(v)
if bufname:find(path) then
local bname = vim.api.nvim_buf_get_name(v)
local path = path_components(vim.fs.normalize(bname, { expand_env = false }))
if path_under_prefix(path, prefix) then
table.insert(buffers, v)
end
end
@ -654,24 +669,34 @@ end
--- Rename old_fname to new_fname
---
---@param opts (table)
-- overwrite? bool
-- ignoreIfExists? bool
---@param old_fname string
---@param new_fname string
---@param opts? table options
--- - overwrite? boolean
--- - ignoreIfExists? boolean
function M.rename(old_fname, new_fname, opts)
opts = opts or {}
local skip = not opts.overwrite or opts.ignoreIfExists
local old_fname_full = vim.uv.fs_realpath(vim.fs.normalize(old_fname, { expand_env = false }))
if not old_fname_full then
vim.notify('Invalid path: ' .. old_fname, vim.log.levels.ERROR)
return
end
local target_exists = uv.fs_stat(new_fname) ~= nil
if target_exists and not opts.overwrite or opts.ignoreIfExists then
vim.notify('Rename target already exists. Skipping rename.')
if target_exists and skip then
vim.notify(new_fname .. ' already exists. Skipping rename.', vim.log.levels.ERROR)
return
end
local oldbufs = {}
local win = nil
if vim.fn.isdirectory(old_fname) == 1 then
oldbufs = get_dir_bufs(old_fname)
if vim.fn.isdirectory(old_fname_full) == 1 then
oldbufs = get_bufs_with_prefix(old_fname_full)
else
local oldbuf = vim.fn.bufadd(old_fname)
local oldbuf = vim.fn.bufadd(old_fname_full)
table.insert(oldbufs, oldbuf)
win = vim.fn.win_findbuf(oldbuf)[1]
end
@ -687,7 +712,7 @@ function M.rename(old_fname, new_fname, opts)
local newdir = assert(vim.fs.dirname(new_fname))
vim.fn.mkdir(newdir, 'p')
local ok, err = os.rename(old_fname, new_fname)
local ok, err = os.rename(old_fname_full, new_fname)
assert(ok, err)
if vim.fn.isdirectory(new_fname) == 0 then

View File

@ -2515,6 +2515,47 @@ describe('LSP', function()
os.remove(new_dir)
end)
it('Does not touch buffers that do not match path prefix', function()
local old = tmpname()
local new = tmpname()
os.remove(old)
os.remove(new)
helpers.mkdir_p(old)
local result = exec_lua(
[[
local old = select(1, ...)
local new = select(2, ...)
local old_prefixed = 'explorer://' .. old
local old_suffixed = old .. '.bak'
local new_prefixed = 'explorer://' .. new
local new_suffixed = new .. '.bak'
local old_prefixed_buf = vim.fn.bufadd(old_prefixed)
local old_suffixed_buf = vim.fn.bufadd(old_suffixed)
local new_prefixed_buf = vim.fn.bufadd(new_prefixed)
local new_suffixed_buf = vim.fn.bufadd(new_suffixed)
vim.lsp.util.rename(old, new)
return
vim.api.nvim_buf_is_valid(old_prefixed_buf) and
vim.api.nvim_buf_is_valid(old_suffixed_buf) and
vim.api.nvim_buf_is_valid(new_prefixed_buf) and
vim.api.nvim_buf_is_valid(new_suffixed_buf) and
vim.api.nvim_buf_get_name(old_prefixed_buf) == old_prefixed and
vim.api.nvim_buf_get_name(old_suffixed_buf) == old_suffixed and
vim.api.nvim_buf_get_name(new_prefixed_buf) == new_prefixed and
vim.api.nvim_buf_get_name(new_suffixed_buf) == new_suffixed
]],
old,
new
)
eq(true, result)
os.remove(new)
end)
it(
'Does not rename file if target exists and ignoreIfExists is set or overwrite is false',
function()