feat: support for renaming symbols from aerial buffer

This commit is contained in:
Steven Arcangeli 2022-10-04 10:07:09 -07:00
parent 89a61daba8
commit 7349f37f9d
2 changed files with 160 additions and 0 deletions

View file

@ -33,6 +33,7 @@ M.keys = {
{ "}", "<cmd>AerialNext<CR>", "Jump to the next symbol" },
{ "[[", "<cmd>AerialPrevUp<CR>", "Jump up the tree, moving backwards" },
{ "]]", "<cmd>AerialNextUp<CR>", "Jump up the tree, moving forwards" },
{ "r", "<cmd>lua require'aerial'.rename()<CR>", "Rename the current symbol" },
{ "q", "<cmd>AerialClose<CR>", "Close the aerial window" },
{
{ "o", "za" },

View file

@ -326,6 +326,165 @@ M.was_closed = function(default)
end
end
local function lsp_rename(bufnr, position, new_name, options, callback)
options = options or {}
local clients = vim.lsp.get_active_clients({
bufnr = bufnr,
name = options.name,
})
if options.filter then
clients = vim.tbl_filter(options.filter, clients)
end
-- Clients must at least support rename, prepareRename is optional
clients = vim.tbl_filter(function(client)
return client.supports_method("textDocument/rename")
end, clients)
if #clients == 0 then
vim.notify("[LSP] Rename, no matching language servers with rename capability.")
end
local cword
local function get_cword()
if not cword then
local aerial_pos = vim.api.nvim_win_get_cursor(0)
vim.api.nvim_buf_call(bufnr, function()
local prev_pos = vim.api.nvim_win_get_cursor(0)
vim.api.nvim_win_set_cursor(0, position)
cword = vim.fn.expand("<cword>")
vim.api.nvim_win_set_cursor(0, prev_pos)
end)
vim.api.nvim_win_set_cursor(0, aerial_pos)
end
return cword
end
local function get_text_at_range(range, offset_encoding)
return vim.api.nvim_buf_get_text(
bufnr,
range.start.line,
util._get_line_byte_from_position(bufnr, range.start, offset_encoding),
range["end"].line,
util._get_line_byte_from_position(bufnr, range["end"], offset_encoding),
{}
)[1]
end
local line = vim.api.nvim_buf_get_lines(bufnr, position[1] - 1, position[1], true)[1]
if not line then
error("Invalid buffer position")
end
local function make_position_params(offset_encoding)
local col = vim.lsp.util._str_utfindex_enc(line, position[2], offset_encoding)
return {
textDocument = vim.lsp.util.make_text_document_params(bufnr),
position = { line = position[1] - 1, character = col },
}
end
local try_use_client
try_use_client = function(idx, client)
if not client then
callback()
return
end
local function rename(name)
local params = make_position_params(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 = make_position_params(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, client.offset_encoding)
elseif result.range then
prompt_opts.default = get_text_at_range(result.range, client.offset_encoding)
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)
else
assert(
client.supports_method("textDocument/rename"),
"Client must support textDocument/rename"
)
if new_name then
rename(new_name)
return
end
local prompt_opts = {
prompt = "New Name: ",
default = get_cword(),
}
vim.ui.input(prompt_opts, function(input)
if not input or #input == 0 then
return
end
rename(input)
end)
end
end
try_use_client(next(clients))
end
M.rename = function(new_name, options)
if not util.is_aerial_buffer() then
error("aerial.rename() must be called from inside the aerial buffer")
end
local bufnr = util.get_source_buffer()
if not data:has_symbols(bufnr) then
vim.notify("No symbols found", vim.log.levels.ERROR)
return
end
local lnum = vim.api.nvim_win_get_cursor(0)[1]
local item = data:get_or_create(bufnr):item(lnum)
local lnum = item.selection_range and item.selection_range.lnum or item.lnum
local col = item.selection_range and item.selection_range.col or item.col
lsp_rename(bufnr, { lnum, col }, new_name, options, function()
local backend = backends.get(bufnr)
backend.fetch_symbols(bufnr)
end)
end
_G.aerial_foldexpr = fold.foldexpr
return M