aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/buf.lua
diff options
context:
space:
mode:
authorMathias Fußenegger <mfussenegger@users.noreply.github.com>2022-05-05 23:56:00 +0200
committerGitHub <noreply@github.com>2022-05-05 23:56:00 +0200
commit55187de1157e05ea71c7c0404345dee0e27e963e (patch)
tree26992fedae9b7849990c047f83f1ed6f1dd91ea7 /runtime/lua/vim/lsp/buf.lua
parentd14d308ce80df428393bccd36bdfd1c295e8f35f (diff)
downloadrneovim-55187de1157e05ea71c7c0404345dee0e27e963e.tar.gz
rneovim-55187de1157e05ea71c7c0404345dee0e27e963e.tar.bz2
rneovim-55187de1157e05ea71c7c0404345dee0e27e963e.zip
fix(lsp): fix rename capability checks and multi client support (#18441)
Adds filter and id options to filter the client to use for rename. Similar to the recently added `format` function. rename will use all matching clients one after another and can handle a mix of prepareRename/rename support. Also ensures the right `offset_encoding` is used for the `make_position_params` calls
Diffstat (limited to 'runtime/lua/vim/lsp/buf.lua')
-rw-r--r--runtime/lua/vim/lsp/buf.lua148
1 files changed, 113 insertions, 35 deletions
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index aabafc422f..8db215829f 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -359,50 +359,128 @@ end
--- Renames all references to the symbol under the cursor.
---
----@param new_name (string) If not provided, the user will be prompted for a new
----name using |vim.ui.input()|.
-function M.rename(new_name)
- local opts = {
- prompt = "New Name: "
- }
+---@param new_name string|nil If not provided, the user will be prompted for a new
+--- name using |vim.ui.input()|.
+---@param options table|nil additional options
+--- - filter (function|nil):
+--- Predicate to filter clients used for rename.
+--- Receives the attached clients as argument and must return a list of
+--- clients.
+--- - name (string|nil):
+--- Restrict clients used for rename to ones where client.name matches
+--- this field.
+function M.rename(new_name, options)
+ options = options or {}
+ local bufnr = options.bufnr or vim.api.nvim_get_current_buf()
+ local clients = vim.lsp.buf_get_clients(bufnr)
- ---@private
- local function on_confirm(input)
- if not (input and #input > 0) then return end
- local params = util.make_position_params()
- params.newName = input
- request('textDocument/rename', params)
+ if options.filter then
+ clients = options.filter(clients)
+ elseif options.name then
+ clients = vim.tbl_filter(
+ function(client) return client.name == options.name end,
+ clients
+ )
+ end
+
+ if #clients == 0 then
+ vim.notify("[LSP] Rename request failed, no matching language servers.")
end
+ local win = vim.api.nvim_get_current_win()
+
+ -- Compute early to account for cursor movements after going async
+ local cword = vfn.expand('<cword>')
+
---@private
- local function prepare_rename(err, result)
- if err == nil and result == nil then
- vim.notify('nothing to rename', vim.log.levels.INFO)
+ local function get_text_at_range(range)
+ return vim.api.nvim_buf_get_text(
+ bufnr,
+ range.start.line,
+ range.start.character,
+ range['end'].line,
+ range['end'].character,
+ {}
+ )[1]
+ end
+
+ local try_use_client
+ try_use_client = function(idx, client)
+ if not client then
return
end
- if result and result.placeholder then
- opts.default = result.placeholder
- if not new_name then npcall(vim.ui.input, opts, on_confirm) end
- elseif result and result.start and result['end'] and
- result.start.line == result['end'].line then
- local line = vfn.getline(result.start.line+1)
- local start_char = result.start.character+1
- local end_char = result['end'].character
- opts.default = string.sub(line, start_char, end_char)
- if not new_name then npcall(vim.ui.input, opts, on_confirm) end
+
+ ---@private
+ local function rename(name)
+ local params = util.make_position_params(win, client.offset_encoding)
+ params.newName = name
+ local handler = client.handlers['textDocument/rename'] or vim.lsp.handlers['textDocument/rename']
+ client.request('textDocument/rename', params, function(...)
+ handler(...)
+ try_use_client(next(clients, idx))
+ end, bufnr)
+ end
+
+ if client.supports_method("textDocument/prepareRename") then
+ local params = util.make_position_params(win, client.offset_encoding)
+ client.request('textDocument/prepareRename', params, function(err, result)
+ if err or result == nil then
+ if next(clients, idx) then
+ try_use_client(next(clients, idx))
+ else
+ local msg = err and ('Error on prepareRename: ' .. (err.message or '')) or 'Nothing to rename'
+ vim.notify(msg, vim.log.levels.INFO)
+ end
+ return
+ end
+
+ if new_name then
+ rename(new_name)
+ return
+ end
+
+ local prompt_opts = {
+ prompt = "New Name: "
+ }
+ -- result: Range | { range: Range, placeholder: string }
+ if result.placeholder then
+ prompt_opts.default = result.placeholder
+ elseif result.start then
+ prompt_opts.default = get_text_at_range(result)
+ elseif result.range then
+ prompt_opts.default = get_text_at_range(result.range)
+ else
+ prompt_opts.default = cword
+ end
+ vim.ui.input(prompt_opts, function(input)
+ if not input or #input == 0 then
+ return
+ end
+ rename(input)
+ end)
+ end, bufnr)
+ elseif client.supports_method("textDocument/rename") then
+ if new_name then
+ rename(new_name)
+ return
+ end
+
+ local prompt_opts = {
+ prompt = "New Name: ",
+ default = cword
+ }
+ vim.ui.input(prompt_opts, function(input)
+ if not input or #input == 0 then
+ return
+ end
+ rename(input)
+ end)
else
- -- fallback to guessing symbol using <cword>
- --
- -- this can happen if the language server does not support prepareRename,
- -- returns an unexpected response, or requests for "default behavior"
- --
- -- see https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename
- opts.default = vfn.expand('<cword>')
- if not new_name then npcall(vim.ui.input, opts, on_confirm) end
+ vim.notify('Client ' .. client.id .. '/' .. client.name .. ' has no rename capability')
end
- if new_name then on_confirm(new_name) end
end
- request('textDocument/prepareRename', util.make_position_params(), prepare_rename)
+
+ try_use_client(next(clients))
end
--- Lists all the references to the symbol under the cursor in the quickfix window.