diff options
author | Mathias Fußenegger <mfussenegger@users.noreply.github.com> | 2022-05-05 23:56:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-05 23:56:00 +0200 |
commit | 55187de1157e05ea71c7c0404345dee0e27e963e (patch) | |
tree | 26992fedae9b7849990c047f83f1ed6f1dd91ea7 /runtime/lua/vim/lsp/buf.lua | |
parent | d14d308ce80df428393bccd36bdfd1c295e8f35f (diff) | |
download | rneovim-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.lua | 148 |
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. |