Last active
March 1, 2023 14:45
-
-
Save smjonas/fd4094c96db02810693909efcb0f9999 to your computer and use it in GitHub Desktop.
Incremental LSP rename command based on Neovim's command-preview feature
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Usage: type :IncrementalRename <new_name> while your cursor is placed above an identifier (LSP must be enabled) | |
-- Update: this gist will no longer be updated since this command has been turned into a plugin! | |
-- You can find the plugin here: https://github.com/smjonas/inc-rename.nvim | |
local state = { | |
added_lines = false, | |
orig_lines = {}, | |
lsp_positions = {}, | |
} | |
local function incremental_rename(opts, preview_ns, preview_buf) | |
-- Store the lines of the buffer at the first invocation | |
if not state.added_lines then | |
-- Get positions of LSP reference symbols | |
local params = vim.lsp.util.make_position_params() | |
vim.lsp.buf_request(0, "textDocument/references", params, function(err, result, _, _) | |
if err then | |
vim.notify("Error while finding references: " .. err.message, vim.lsp.log_levels.ERROR) | |
return | |
end | |
if not result or vim.tbl_isempty(result) then | |
vim.notify("No results from textDocument/references", vim.lsp.log_levels.WARN) | |
return | |
end | |
state.lsp_positions = vim.tbl_map(function(x) | |
return x.range | |
end, result) | |
local buf = vim.api.nvim_get_current_buf() | |
state.orig_lines = {} | |
for _, position in ipairs(state.lsp_positions) do | |
local line_nr = position.start.line | |
local line = vim.api.nvim_buf_get_lines(buf, line_nr, line_nr + 1, 0)[1] | |
local start_col, end_col = position.start.character, position["end"].character | |
local line_item = { text = line, start_col = start_col, end_col = end_col } | |
-- Same line was already seen, this case needs to be handled separately | |
if state.orig_lines[line_nr] then | |
table.insert(state.orig_lines[line_nr], line_item) | |
else | |
state.orig_lines[line_nr] = { line_item } | |
end | |
end | |
state.added_lines = true | |
end) | |
return | |
end | |
for line_nr, line_items in pairs(state.orig_lines) do | |
local offset = 0 | |
local updated_line = line_items[1].text | |
local highlight_positions = {} | |
for _, item in ipairs(line_items) do | |
updated_line = updated_line:sub(1, item.start_col + offset) | |
.. opts.args | |
.. updated_line:sub(item.end_col + 1 + offset) | |
table.insert(highlight_positions, { | |
start_col = item.start_col + offset, | |
end_col = item.start_col + #opts.args + offset, | |
}) | |
-- Offset by the length difference between the old and new names | |
offset = offset + #opts.args - (item.end_col - item.start_col) | |
end | |
if preview_ns then | |
vim.api.nvim_buf_set_lines(0, line_nr, line_nr + 1, false, { updated_line }) | |
if preview_buf then | |
vim.api.nvim_buf_set_lines(preview_buf, line_nr, line_nr + 1, false, { updated_line }) | |
end | |
for _, hl_pos in ipairs(highlight_positions) do | |
vim.api.nvim_buf_add_highlight( | |
0, | |
preview_ns, | |
"Substitute", | |
line_nr, | |
hl_pos.start_col, | |
hl_pos.end_col | |
) | |
if preview_buf then | |
vim.api.nvim_buf_add_highlight( | |
preview_buf, | |
preview_ns, | |
"Substitute", | |
line_nr, | |
hl_pos.start_col, | |
hl_pos.end_col | |
) | |
end | |
end | |
end | |
end | |
if not preview_ns then | |
-- Now execute the actual LSP rename command | |
vim.lsp.buf.rename(opts.args) | |
state.added_lines = false | |
end | |
if preview_ns then | |
return 2 | |
end | |
end | |
-- Create the user command | |
vim.api.nvim_create_user_command( | |
"IncrementalRename", | |
incremental_rename, | |
{ nargs = 1, range = "%", addr = "lines", preview = incremental_rename } | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment