Skip to content

Instantly share code, notes, and snippets.

@MunifTanjim
Last active January 31, 2023 00:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MunifTanjim/8d9498c096719bdf4234321230fe3dc7 to your computer and use it in GitHub Desktop.
Save MunifTanjim/8d9498c096719bdf4234321230fe3dc7 to your computer and use it in GitHub Desktop.
Neovim LSP Rename with nui.nvim
local Input = require("nui.input")
local event = require("nui.utils.autocmd").event
local function nui_lsp_rename()
local curr_name = vim.fn.expand("<cword>")
local params = vim.lsp.util.make_position_params()
local function on_submit(new_name)
if not new_name or #new_name == 0 or curr_name == new_name then
-- do nothing if `new_name` is empty or not changed.
return
end
-- add `newName` property to `params`.
-- this is needed for making `textDocument/rename` request.
params.newName = new_name
-- send the `textDocument/rename` request to LSP server
vim.lsp.buf_request(0, "textDocument/rename", params, function(_, result, ctx, _)
if not result then
-- do nothing if server returns empty result
return
end
-- the `result` contains all the places we need to update the
-- name of the identifier. so we apply those edits.
local client = vim.lsp.get_client_by_id(ctx.client_id)
vim.lsp.util.apply_workspace_edit(result, client.offset_encoding)
-- after the edits are applied, the files are not saved automatically.
-- let's remind ourselves to save those...
local total_files = vim.tbl_count(result.changes)
print(
string.format(
"Changed %s file%s. To save them run ':wa'",
total_files,
total_files > 1 and "s" or ""
)
)
end)
end
local popup_options = {
-- border for the window
border = {
style = "rounded",
text = {
top = "[Rename]",
top_align = "left"
},
},
-- highlight for the window.
highlight = "Normal:Normal",
-- place the popup window relative to the
-- buffer position of the identifier
relative = {
type = "buf",
position = {
-- this is the same `params` we got earlier
row = params.position.line,
col = params.position.character,
}
},
-- position the popup window on the line below identifier
position = {
row = 1,
col = 0,
},
-- 25 cells wide, should be enough for most identifier names
size = {
width = 25,
height = 1,
},
}
local input = Input(popup_options, {
-- set the default value to current name
default_value = curr_name,
-- pass the `on_submit` callback function we wrote earlier
on_submit = on_submit,
prompt = "",
})
input:mount()
-- close on <esc> in normal mode
input:map("n", "<esc>", input.input_props.on_close, { noremap = true })
-- close when cursor leaves the buffer
input:on(event.BufLeave, input.input_props.on_close, { once = true })
end
return {
lsp_rename = nui_lsp_rename,
}
@disrupted
Copy link

would it be possible to set the initial state to NORMAL mode instead of INSERT mode? I tried adding vim.cmd('stopinsert') after input:mount() but that didn't work.

@MunifTanjim
Copy link
Author

would it be possible to set the initial state to NORMAL mode instead of INSERT mode? I tried adding vim.cmd('stopinsert') after input:mount() but that didn't work.

Hey @disrupted, you can do this after mounting:

  vim.schedule(function()
    vim.api.nvim_command("stopinsert")
  end)

This is how it runs:

  • input is mounted
  • It enters INSERT mode
  • The default_value (i.e. the current name of the variable you're renaming) is inserted to the prompt
  • After vim is done with all those, it'll run your scheduled callback, so it'll exit INSERT mode.

Just curious, what is your usecase for doing this? 🤔

@disrupted
Copy link

Hi @MunifTanjim, thanks for the explanation. That did the trick!

When renaming a symbol I usually don't append something to the name, more often than not I want to change the whole name or parts of it.

This is part of some other small tweaks I made to my config.
Since I am working with snake-case languages (Python, Rust, Lua) for the most part, temporarily changing the iskeyword setting makes it possible to move around and change parts of long_variable_name more easily.

Also made it so the window width is set dynamically depending on the string length.

https://github.com/disrupted/dotfiles/blob/3d239c8265d041ecd9e54fe2b66731e90800d6e5/.config/nvim/lua/conf/nui_lsp.lua

@RaafatTurki
Copy link

neovim updated the lsp-handler, line 20 must now be

vim.lsp.buf_request(0, "textDocument/rename", lsp_params, function(_, result, _, _)

@MunifTanjim
Copy link
Author

neovim updated the lsp-handler, line 20 must now be

vim.lsp.buf_request(0, "textDocument/rename", lsp_params, function(_, result, _, _)

Thanks @RaafatTurki!!! I was wondering why rename was not working on my setup, I thought something was wrong with the lsp server 😅
I just tried your suggestion and it's working again! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment