mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-16 02:10:15 -04:00
feat(refactor): add smart rename module
This commit is contained in:
parent
6ca2b947cf
commit
5bd58d7a47
5 changed files with 115 additions and 24 deletions
|
|
@ -31,6 +31,14 @@ local config = {
|
||||||
enable = false,
|
enable = false,
|
||||||
disable = {},
|
disable = {},
|
||||||
is_supported = queries.has_locals
|
is_supported = queries.has_locals
|
||||||
|
},
|
||||||
|
smart_rename = {
|
||||||
|
enable = false,
|
||||||
|
disable = {},
|
||||||
|
is_supported = queries.has_locals,
|
||||||
|
keymaps = {
|
||||||
|
smart_rename = "grr"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ local function read_query_files(filenames)
|
||||||
return table.concat(contents, '\n')
|
return table.concat(contents, '\n')
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_query_gaurd(query)
|
-- Creates a function that checks whether a certain query exists
|
||||||
|
-- for a specific language.
|
||||||
|
local function get_query_guard(query)
|
||||||
return function(lang)
|
return function(lang)
|
||||||
return M.get_query(lang, query) ~= nil
|
return M.get_query(lang, query) ~= nil
|
||||||
end
|
end
|
||||||
|
|
@ -27,8 +29,8 @@ M.base_language_map = {
|
||||||
tsx = {'typescript', 'javascript'},
|
tsx = {'typescript', 'javascript'},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.has_locals = get_query_gaurd('locals')
|
M.has_locals = get_query_guard('locals')
|
||||||
M.has_highlights = get_query_gaurd('highlights')
|
M.has_highlights = get_query_guard('highlights')
|
||||||
|
|
||||||
function M.get_query(lang, query_name)
|
function M.get_query(lang, query_name)
|
||||||
local query_files = api.nvim_get_runtime_file(string.format('queries/%s/%s.scm', lang, query_name), true)
|
local query_files = api.nvim_get_runtime_file(string.format('queries/%s/%s.scm', lang, query_name), true)
|
||||||
|
|
|
||||||
|
|
@ -11,24 +11,6 @@ local M = {}
|
||||||
|
|
||||||
local usage_namespace = api.nvim_create_namespace('nvim-treesitter-usages')
|
local usage_namespace = api.nvim_create_namespace('nvim-treesitter-usages')
|
||||||
|
|
||||||
local function find_usages(node, scope_node)
|
|
||||||
local usages = {}
|
|
||||||
local node_text = ts_utils.get_node_text(node)[1]
|
|
||||||
|
|
||||||
if not node_text or #node_text < 1 then return end
|
|
||||||
|
|
||||||
for _, def in ipairs(locals.collect_locals(bufnr, scope_node)) do
|
|
||||||
if def.reference
|
|
||||||
and def.reference.node
|
|
||||||
and ts_utils.get_node_text(def.reference.node)[1] == node_text then
|
|
||||||
|
|
||||||
table.insert(usages, def.reference.node)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return usages
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.highlight_usages(bufnr)
|
function M.highlight_usages(bufnr)
|
||||||
M.clear_usage_highlights(bufnr)
|
M.clear_usage_highlights(bufnr)
|
||||||
|
|
||||||
|
|
@ -37,7 +19,7 @@ function M.highlight_usages(bufnr)
|
||||||
if not node_at_point then return end
|
if not node_at_point then return end
|
||||||
|
|
||||||
local def_node, scope = ts_utils.find_definition(node_at_point, bufnr)
|
local def_node, scope = ts_utils.find_definition(node_at_point, bufnr)
|
||||||
local usages = find_usages(node_at_point, scope)
|
local usages = ts_utils.find_usages(node_at_point, scope)
|
||||||
|
|
||||||
for _, usage_node in ipairs(usages) do
|
for _, usage_node in ipairs(usages) do
|
||||||
local start_row, start_col, _, end_col = usage_node:range()
|
local start_row, start_col, _, end_col = usage_node:range()
|
||||||
|
|
|
||||||
75
lua/nvim-treesitter/refactor/smart_rename.lua
Normal file
75
lua/nvim-treesitter/refactor/smart_rename.lua
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
-- Binds a keybinding to smart rename definitions and usages.
|
||||||
|
-- Can be used directly using the `smart_rename` function.
|
||||||
|
|
||||||
|
local ts_utils = require'nvim-treesitter.ts_utils'
|
||||||
|
local configs = require'nvim-treesitter.configs'
|
||||||
|
local api = vim.api
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.smart_rename(bufnr)
|
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf()
|
||||||
|
local node_at_point = ts_utils.get_node_at_cursor()
|
||||||
|
|
||||||
|
if not node_at_point then
|
||||||
|
print('No node to rename!')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local node_text = ts_utils.get_node_text(node_at_point)[1]
|
||||||
|
local new_name = vim.fn.input('New name: ', node_text or '')
|
||||||
|
|
||||||
|
-- Empty name cancels the interaction or ESC
|
||||||
|
if not new_name or #new_name < 1 then return end
|
||||||
|
|
||||||
|
local definition, scope = ts_utils.find_definition(node_at_point, bufnr)
|
||||||
|
local nodes_to_rename = ts_utils.find_usages(node_at_point, scope)
|
||||||
|
|
||||||
|
if not vim.tbl_contains(nodes_to_rename, node_at_point) then
|
||||||
|
table.insert(nodes_to_rename, node_at_point)
|
||||||
|
end
|
||||||
|
|
||||||
|
if definition and not vim.tbl_contains(nodes_to_rename, definition) then
|
||||||
|
table.insert(nodes_to_rename, definition)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #nodes_to_rename < 1 then
|
||||||
|
print('No nodes to rename!')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, node in ipairs(nodes_to_rename) do
|
||||||
|
local start_row, start_col, end_row, end_col = node:range()
|
||||||
|
|
||||||
|
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1]
|
||||||
|
|
||||||
|
if line then
|
||||||
|
local new_line = line:sub(1, start_col) .. new_name .. line:sub(end_col + 1, -1)
|
||||||
|
|
||||||
|
api.nvim_buf_set_lines(bufnr, start_row, start_row + 1, false, { new_line })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.attach(bufnr)
|
||||||
|
local bufnr = bufnr or api.nvim_get_current_buf()
|
||||||
|
|
||||||
|
local config = configs.get_module('refactor.smart_rename')
|
||||||
|
|
||||||
|
for fn_name, mapping in pairs(config.keymaps) do
|
||||||
|
local cmd = string.format([[:lua require'nvim-treesitter.refactor.smart_rename'.%s(%d)<CR>]], fn_name, bufnr)
|
||||||
|
|
||||||
|
api.nvim_buf_set_keymap(bufnr, 'n', mapping, cmd, { silent = true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.detach(bufnr)
|
||||||
|
local buf = bufnr or api.nvim_get_current_buf()
|
||||||
|
local config = configs.get_module('refactor.smart_rename')
|
||||||
|
|
||||||
|
for fn_name, mapping in pairs(config.keymaps) do
|
||||||
|
api.nvim_buf_del_keymap(bufnr, 'n', mapping)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
@ -15,14 +15,16 @@ function M.get_node_text(node, bufnr)
|
||||||
|
|
||||||
-- We have to remember that end_col is end-exclusive
|
-- We have to remember that end_col is end-exclusive
|
||||||
local start_row, start_col, end_row, end_col = node:range()
|
local start_row, start_col, end_row, end_col = node:range()
|
||||||
|
|
||||||
if start_row ~= end_row then
|
if start_row ~= end_row then
|
||||||
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row+1, false)
|
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row+1, false)
|
||||||
lines[1] = string.sub(lines[1], start_col+1)
|
lines[1] = string.sub(lines[1], start_col+1)
|
||||||
lines[#lines] = string.sub(lines[#lines], 1, end_col)
|
lines[#lines] = string.sub(lines[#lines], 1, end_col)
|
||||||
return lines
|
return lines
|
||||||
else
|
else
|
||||||
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1]
|
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, false)[1]
|
||||||
return { string.sub(line, start_col+1, end_col) }
|
-- If line is nil then the line is empty
|
||||||
|
return line and { string.sub(line, start_col+1, end_col) } or {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -263,4 +265,26 @@ function M.get_local_nodes(local_def)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Finds usages of a node in a particula scope
|
||||||
|
-- @param node the node to find usages for
|
||||||
|
-- @param scope_node the node to look within
|
||||||
|
-- @returns a list of nodes
|
||||||
|
function M.find_usages(node, scope_node)
|
||||||
|
local usages = {}
|
||||||
|
local node_text = M.get_node_text(node)[1]
|
||||||
|
|
||||||
|
if not node_text or #node_text < 1 then return {} end
|
||||||
|
|
||||||
|
for _, def in ipairs(locals.collect_locals(bufnr, scope_node)) do
|
||||||
|
if def.reference
|
||||||
|
and def.reference.node
|
||||||
|
and M.get_node_text(def.reference.node)[1] == node_text then
|
||||||
|
|
||||||
|
table.insert(usages, def.reference.node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return usages
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue