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 Rename old_fname to new_fname
Parameters: ~ Parameters: ~
• {opts} (`table`) • {old_fname} (`string`)
• {new_fname} (`string`)
• {opts} (`table?`) options
• overwrite? boolean
• ignoreIfExists? boolean
*vim.lsp.util.show_document()* *vim.lsp.util.show_document()*
show_document({location}, {offset_encoding}, {opts}) 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) return vim.lsp._completion._lsp_to_complete_items(result, prefix)
end end
--- Get list of buffers for a directory local function path_components(path)
local function get_dir_bufs(path) return vim.split(path, '/', { plain = true })
path = path:gsub('([^%w])', '%%%1') 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 = {} local buffers = {}
for _, v in ipairs(vim.api.nvim_list_bufs()) do for _, v in ipairs(vim.api.nvim_list_bufs()) do
local bufname = vim.api.nvim_buf_get_name(v) local bname = vim.api.nvim_buf_get_name(v)
if bufname:find(path) then local path = path_components(vim.fs.normalize(bname, { expand_env = false }))
if path_under_prefix(path, prefix) then
table.insert(buffers, v) table.insert(buffers, v)
end end
end end
@ -654,24 +669,34 @@ end
--- Rename old_fname to new_fname --- Rename old_fname to new_fname
--- ---
---@param opts (table) ---@param old_fname string
-- overwrite? bool ---@param new_fname string
-- ignoreIfExists? bool ---@param opts? table options
--- - overwrite? boolean
--- - ignoreIfExists? boolean
function M.rename(old_fname, new_fname, opts) function M.rename(old_fname, new_fname, opts)
opts = opts or {} 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 local target_exists = uv.fs_stat(new_fname) ~= nil
if target_exists and not opts.overwrite or opts.ignoreIfExists then if target_exists and skip then
vim.notify('Rename target already exists. Skipping rename.') vim.notify(new_fname .. ' already exists. Skipping rename.', vim.log.levels.ERROR)
return return
end end
local oldbufs = {} local oldbufs = {}
local win = nil local win = nil
if vim.fn.isdirectory(old_fname) == 1 then if vim.fn.isdirectory(old_fname_full) == 1 then
oldbufs = get_dir_bufs(old_fname) oldbufs = get_bufs_with_prefix(old_fname_full)
else else
local oldbuf = vim.fn.bufadd(old_fname) local oldbuf = vim.fn.bufadd(old_fname_full)
table.insert(oldbufs, oldbuf) table.insert(oldbufs, oldbuf)
win = vim.fn.win_findbuf(oldbuf)[1] win = vim.fn.win_findbuf(oldbuf)[1]
end end
@ -687,7 +712,7 @@ function M.rename(old_fname, new_fname, opts)
local newdir = assert(vim.fs.dirname(new_fname)) local newdir = assert(vim.fs.dirname(new_fname))
vim.fn.mkdir(newdir, 'p') 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) assert(ok, err)
if vim.fn.isdirectory(new_fname) == 0 then if vim.fn.isdirectory(new_fname) == 0 then

View File

@ -2515,6 +2515,47 @@ describe('LSP', function()
os.remove(new_dir) os.remove(new_dir)
end) 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( it(
'Does not rename file if target exists and ignoreIfExists is set or overwrite is false', 'Does not rename file if target exists and ignoreIfExists is set or overwrite is false',
function() function()