Manually implement auto mass-renaming using LSP

This commit is contained in:
ItsDrike 2021-12-09 17:34:42 +01:00
parent bd3ece59b7
commit f0fbf261ec
No known key found for this signature in database
GPG key ID: FB8CA11A2CF3A843

View file

@ -1,3 +1,160 @@
-- Nvim's LSP lacks rename-all functionality which plugins like Coc provide
-- this is a manual implementation of this feature
-- TODO: Actually implement this
local vim = require("vim")
local api = vim.api
local cmd = vim.cmd
local lsp = vim.lsp
local fn = vim.fn
local M = {}
-- Unique name for the rename window, so we can access it
-- from close_rename_win function.
local unique_name = "lsp-rename-win"
-- File require string. Neede because we will be defining keymaps
-- applied only to the rename window buffer which will refer to
-- functions within this file, for that, they need to call require
local file_require_string = "lsp.rename"
-- Check whether LSP is actually active.
local function check_lsp_active()
local active_clients = lsp.get_active_clients()
if next(active_clients) == nil then
return false
end
return true
end
-- Once in the rename window buffer, apply specific mappings to confirm or
-- cancel renaming, and define a specific autocmd to close the window if
-- we leave it.
local function apply_actions()
local prefix = string.format("lua require('%s')", file_require_string)
local close_win_s = prefix .. ".close_rename_win()"
local do_rename_s = prefix .. ".do_rename()"
-- Automatically close the window if it's escaped
api.nvim_command("autocmd QuitPre <buffer> ++nested ++once :silent " .. close_win_s)
-- Define confirm and exit buffer-specific keymaps
api.nvim_command("inoremap <buffer><nowait><silent><CR> <cmd>" .. do_rename_s .. "<CR>")
api.nvim_command("nnoremap <buffer><silent>q <cmd>" .. close_win_s .. "<CR>")
end
-- Closes the rename window (identified by the `unique_name`)
function M.close_rename_win()
if fn.mode() == "1" then
cmd[[stopinsert]]
end
local exists, winid = pcall(api.nvim_win_get_var, 0, unique_name)
if exists then
api.nvim_win_close(winid, true)
end
end
local function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
-- Trigger renaming
function M.do_rename()
local new_name = vim.trim(fn.getline('.'))
M.close_rename_win()
local current_name = fn.expand("<cword>")
if not new_name or #new_name == 0 or new_name == current_name then
return
end
local params = lsp.util.make_position_params()
params.newName = new_name
lsp.buf_request(0, "textDocument/rename", params, function(_, result, _, _)
if not result then
-- If the server returns an empty result, don't do anything
return
end
-- The result contains all places we need to update the name
-- of the identifier, so we apply those edits.
lsp.util.apply_workspace_edit(result)
if not result.changes then
return
end
local total_files = 0
local total_renames = 0
for _, renames in pairs(result.changes) do
total_files = total_files + 1
for _ in pairs(renames) do
total_renames = total_renames + 1
end
end
-- Once the changes were applied, these files won't be saved
-- automatically, let's remind ourselves to save those...
print(string.format(
"Changed %s file%s (%s rename%s). To save %s, run ':w%s'",
total_files,
total_files > 1 and "s" or "",
total_renames,
total_renames > 1 and "s" or "",
total_files > 1 and "them" or "it",
total_files > 1 and "a" or ""
))
end)
end
-- Create the rename window
function M.rename()
if not check_lsp_active() then
print("No LSP client available, can't rename!")
return
end
-- In case there already is another rename window opened, close it
M.close_rename_win()
-- Read the current name here, before we're in the rename window
local current_name = fn.expand('<cword>')
-- Create a window within our buffer with our `unique_name` so that it
-- can be found from the close fucntion and automatically enter it
local win_opts = {
relative = 'cursor',
row = 0,
col = 0,
width = 30,
height = 1,
style = 'minimal',
border = 'single'
}
local buf = api.nvim_create_buf(false, true)
local win = api.nvim_open_win(buf, true, win_opts)
api.nvim_win_set_var(0, unique_name, win)
-- Automatically enter insert mode
cmd[[startinsert]]
-- Pre-write the current name of given object into the rename window
-- and set cursor behind it
api.nvim_buf_set_lines(buf, 0, -1, false, {current_name})
api.nvim_win_set_cursor(win, {1, #current_name})
-- Set actions for auto-closing the window and buffer-specific mappings
-- to confirm or close rename
apply_actions()
end
return M