mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	lsp: Add support for file rename via workspaceEdit
This commit is contained in:
		| @@ -749,6 +749,9 @@ function protocol.make_client_capabilities() | ||||
|       }; | ||||
|       workspaceFolders = true; | ||||
|       applyEdit = true; | ||||
|       workspaceEdit = { | ||||
|         resourceOperations = {'rename',}, | ||||
|       }; | ||||
|     }; | ||||
|     callHierarchy = { | ||||
|       dynamicRegistration = false; | ||||
|   | ||||
| @@ -612,6 +612,29 @@ function M.text_document_completion_list_to_complete_items(result, prefix) | ||||
|   return matches | ||||
| end | ||||
|  | ||||
|  | ||||
| --- Rename old_fname to new_fname | ||||
| -- | ||||
| --@param opts (table) | ||||
| --         overwrite? bool | ||||
| --         ignoreIfExists? bool | ||||
| function M.rename(old_fname, new_fname, opts) | ||||
|   opts = opts or {} | ||||
|   local bufnr = vim.fn.bufadd(old_fname) | ||||
|   vim.fn.bufload(bufnr) | ||||
|   local target_exists = vim.loop.fs_stat(new_fname) ~= nil | ||||
|   if target_exists and not opts.overwrite or opts.ignoreIfExists then | ||||
|     vim.notify('Rename target already exists. Skipping rename.') | ||||
|     return | ||||
|   end | ||||
|   local ok, err = os.rename(old_fname, new_fname) | ||||
|   assert(ok, err) | ||||
|   api.nvim_buf_call(bufnr, function() | ||||
|     vim.cmd('saveas! ' .. vim.fn.fnameescape(new_fname)) | ||||
|   end) | ||||
| end | ||||
|  | ||||
|  | ||||
| --- Applies a `WorkspaceEdit`. | ||||
| --- | ||||
| --@param workspace_edit (table) `WorkspaceEdit` | ||||
| @@ -619,8 +642,14 @@ end | ||||
| function M.apply_workspace_edit(workspace_edit) | ||||
|   if workspace_edit.documentChanges then | ||||
|     for idx, change in ipairs(workspace_edit.documentChanges) do | ||||
|       if change.kind then | ||||
|         -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile | ||||
|       if change.kind == "rename" then | ||||
|         M.rename( | ||||
|           vim.uri_to_fname(change.oldUri), | ||||
|           vim.uri_to_fname(change.newUri), | ||||
|           change.options | ||||
|         ) | ||||
|       elseif change.kind then | ||||
|         -- TODO(ashkan) handle CreateFile/DeleteFile | ||||
|         error(string.format("Unsupported change: %q", vim.inspect(change))) | ||||
|       else | ||||
|         M.apply_text_document_edit(change, idx) | ||||
|   | ||||
| @@ -11,6 +11,8 @@ local pesc = helpers.pesc | ||||
| local insert = helpers.insert | ||||
| local retry = helpers.retry | ||||
| local NIL = helpers.NIL | ||||
| local read_file = require('test.helpers').read_file | ||||
| local write_file = require('test.helpers').write_file | ||||
|  | ||||
| -- Use these to get access to a coroutine so that I can run async tests and use | ||||
| -- yield. | ||||
| @@ -1309,6 +1311,72 @@ describe('LSP', function() | ||||
|     end) | ||||
|   end) | ||||
|  | ||||
|   describe('lsp.util.rename', function() | ||||
|     it('Can rename an existing file', function() | ||||
|       local old = helpers.tmpname() | ||||
|       write_file(old, 'Test content') | ||||
|       local new = helpers.tmpname() | ||||
|       os.remove(new)  -- only reserve the name, file must not exist for the test scenario | ||||
|       local lines = exec_lua([[ | ||||
|         local old = select(1, ...) | ||||
|         local new = select(2, ...) | ||||
|         vim.lsp.util.rename(old, new) | ||||
|  | ||||
|         -- after rename the target file must have the contents of the source file | ||||
|         local bufnr = vim.fn.bufadd(new) | ||||
|         return vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) | ||||
|       ]], old, new) | ||||
|       eq({'Test content'}, lines) | ||||
|       local exists = exec_lua('return vim.loop.fs_stat(...) ~= nil', old) | ||||
|       eq(false, exists) | ||||
|       exists = exec_lua('return vim.loop.fs_stat(...) ~= nil', new) | ||||
|       eq(true, exists) | ||||
|       os.remove(new) | ||||
|     end) | ||||
|     it('Does not rename file if target exists and ignoreIfExists is set or overwrite is false', function() | ||||
|       local old = helpers.tmpname() | ||||
|       write_file(old, 'Old File') | ||||
|       local new = helpers.tmpname() | ||||
|       write_file(new, 'New file') | ||||
|  | ||||
|       exec_lua([[ | ||||
|         local old = select(1, ...) | ||||
|         local new = select(2, ...) | ||||
|  | ||||
|         vim.lsp.util.rename(old, new, { ignoreIfExists = true }) | ||||
|       ]], old, new) | ||||
|  | ||||
|       eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', old)) | ||||
|       eq('New file', read_file(new)) | ||||
|  | ||||
|       exec_lua([[ | ||||
|         local old = select(1, ...) | ||||
|         local new = select(2, ...) | ||||
|  | ||||
|         vim.lsp.util.rename(old, new, { overwrite = false }) | ||||
|       ]], old, new) | ||||
|  | ||||
|       eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', old)) | ||||
|       eq('New file', read_file(new)) | ||||
|     end) | ||||
|     it('Does override target if overwrite is true', function() | ||||
|       local old = helpers.tmpname() | ||||
|       write_file(old, 'Old file') | ||||
|       local new = helpers.tmpname() | ||||
|       write_file(new, 'New file') | ||||
|       exec_lua([[ | ||||
|         local old = select(1, ...) | ||||
|         local new = select(2, ...) | ||||
|  | ||||
|         vim.lsp.util.rename(old, new, { overwrite = true }) | ||||
|       ]], old, new) | ||||
|  | ||||
|       eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', old)) | ||||
|       eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', new)) | ||||
|       eq('Old file\n', read_file(new)) | ||||
|     end) | ||||
|   end) | ||||
|  | ||||
|   describe('lsp.util.locations_to_items', function() | ||||
|     it('Convert Location[] to items', function() | ||||
|       local expected = { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Mathias Fussenegger
					Mathias Fussenegger