Skip to content

Commit 43ed7dc

Browse files
committed
feat(lsp): apply changes to related files when a file is renamed
Previously the language server could easily get out of sync when a file was renamed in yazi. The language server would e.g. have import statements that referenced the old file name. This feature only applies when a language server is running and the server has support for renaming. What now happens is: - neovim is open and a language server is running - neovim opens yazi - yazi renames (or moves) a file that's part of the current workspace - yazi is closed - neovim sends a request to the language server to rename the file - the language server responds with a list of changes to other files - neovim applies those changes to the other files but doesn't save changes automatically (at least not for me) I tested this in a typescript project as well as two rust projects. Everything worked nicely.
1 parent b256828 commit 43ed7dc

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

lua/yazi/lsp/rename.lua

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
local M = {}
2+
3+
---@param from string
4+
---@param to string
5+
local function notify_file_was_renamed(from, to)
6+
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_willRenameFiles
7+
local method = 'workspace/willRenameFiles'
8+
9+
local clients = vim.lsp.get_clients({
10+
method = method,
11+
bufnr = vim.api.nvim_get_current_buf(),
12+
})
13+
14+
for _, client in ipairs(clients) do
15+
local resp = client.request_sync(method, {
16+
files = {
17+
{
18+
oldUri = vim.uri_from_fname(from),
19+
newUri = vim.uri_from_fname(to),
20+
},
21+
},
22+
}, 1000, 0)
23+
24+
if resp and resp.result ~= nil then
25+
vim.lsp.util.apply_workspace_edit(resp.result, client.offset_encoding)
26+
end
27+
end
28+
end
29+
30+
---@param from string
31+
---@param to string
32+
local function notify_rename_complete(from, to)
33+
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didRenameFiles
34+
local method = 'workspace/didRenameFiles'
35+
36+
local clients = vim.lsp.get_clients({
37+
method = method,
38+
bufnr = vim.api.nvim_get_current_buf(),
39+
})
40+
41+
for _, client in ipairs(clients) do
42+
-- NOTE: this returns nothing, so no need to do anything with the response
43+
client.request_sync(method, {
44+
files = {
45+
oldUri = vim.uri_from_fname(from),
46+
newUri = vim.uri_from_fname(to),
47+
},
48+
}, 1000, 0)
49+
end
50+
end
51+
52+
-- Send a notification to LSP servers, letting them know that yazi just renamed some files
53+
---@param from string
54+
---@param to string
55+
function M.file_renamed(from, to)
56+
notify_file_was_renamed(from, to)
57+
notify_rename_complete(from, to)
58+
end
59+
60+
return M

lua/yazi/utils.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local fn = vim.fn
22
local RenameableBuffer = require('yazi.renameable_buffer')
3+
local lsp_rename = require('yazi.lsp.rename')
34

45
local M = {}
56

@@ -172,6 +173,8 @@ function M.rename_or_close_buffer(instruction)
172173
else
173174
vim.api.nvim_buf_set_name(instruction.bufnr, instruction.path.filename)
174175
end
176+
177+
lsp_rename.file_renamed(instruction.original_path, instruction.path.filename)
175178
end
176179

177180
---@param prev_win integer

tests/yazi/lsp_rename_spec.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local luassert = require('luassert')
2+
local lsp_rename = require('yazi.lsp.rename')
3+
4+
describe('renaming files with LSP support', function()
5+
-- It's pretty hard to test this as it would require a running LSP server.
6+
-- There are some examples of this, e.g.
7+
-- https://github.com/pmizio/typescript-tools.nvim/blob/master/tests/editor_spec.lua
8+
--
9+
-- Maybe more testing could be added later.
10+
it('does nothing if no LSP is running', function()
11+
local result = lsp_rename.file_renamed('test-file.txt', 'test-file2.txt')
12+
luassert.is_nil(result)
13+
end)
14+
end)

0 commit comments

Comments
 (0)