- Provides refactoring and print-debugging operators (see
:h operator) powered by LSP and tree-sitter. - Supports dot-repeat and any textobject/motion.
- Allows fine-grained customization while providing reasonable defaults.
More details on Features
2026-04-20.14-53-04.mp4
- Inline variable:
- Inline the definition of the variable under cursor into all its references.
- Requires:
- LSP server with support for
textDocument/referencesandtextDocument/definition - Tree-sitter parser and queries (
refactor_referenceandrefactor_variable)
- LSP server with support for
- Extract variable:
- Extract an expression, and all its usages in a buffer, into a variable.
- Requires:
- Tree-sitter parser and queries (
refactor_scopeandrefactor_output_statement)
- Tree-sitter parser and queries (
- Inline function:
- Inline the definition of the function under cursor into all its references (only supports functions with a single return statement).
- Requires:
- LSP server with support for
textDocument/referencesandtextDocument/definition - Tree-sitter parser and queries (
refactor_functionandrefactor_function_call)
- LSP server with support for
- Extract function:
- Extract text into a function and replace it with a call to that function.
- Requires:
- Tree-sitter parser and queries (
refactor_reference,refactor_scope,refactor_output_functionandrefactor_input_function)
- Tree-sitter parser and queries (
- Print location:
- Inserts a debug print statement with the location under cursor (e.g.
some_function#if#for). - Requires:
- Tree-sitter parser and queries (
refactor_commentandrefactor_output_statement)
- Tree-sitter parser and queries (
- Inserts a debug print statement with the location under cursor (e.g.
- Print variable:
- Inserts a debug print statement with all the variable and locations (e.g.
some_function#if#for) in the selected range. - Requires:
- Tree-sitter parser and queries (
refactor_comment,refactor_reference,refactor_output_statementandrefactor_scope)
- Tree-sitter parser and queries (
- Inserts a debug print statement with all the variable and locations (e.g.
- Print expression:
- Inserts a debug print statement with the selected expression and location (e.g.
some_function#if#for). - Requires:
- Tree-sitter parser and queries (
refactor_commentandrefactor_output_statement)
- Tree-sitter parser and queries (
- Inserts a debug print statement with the selected expression and location (e.g.
- Debug print cleanup:
- Cleanup the debug print statements in the selected range.
- Requires:
- Tree-sitter parser and queries (
refactor_comment)
- Tree-sitter parser and queries (
Note
Tree-sitter queries for supported languages are bundled with refactoring.nvim
refactoring.nvim requires Neovim 0.12.
With vim.pack
vim.pack.add {
"https://github.com/lewis6991/async.nvim",
"https://github.com/theprimeagen/refactoring.nvim"
}
-- calling `require("refactoring").setup()` is not required for the plugin to workWith folke/lazy.nvim
{
"ThePrimeagen/refactoring.nvim",
dependencies = {
"lewis6991/async.nvim",
},
lazy = false,
},The default configuration can be found at ./lua/refactoring/config.lua, any field can be overridden:
- globally:
require("refactoring").setup({...}) - for a single call:
require("refactoring").inline_var({...}) - for a given buffer:
vim.b.refactor_config = {...}
No keymaps are created by default. These are the suggested keymaps:
Option 1: a dedicated keymap for each refactoring operation
local keymap = vim.keymap
keymap.set({ "n", "x" }, "<leader>re", function()
return require("refactoring").extract_func()
end, { desc = "Extract Function", expr = true })
-- `_` is the default textobject for "current line"
keymap.set("n", "<leader>ree", function()
return require("refactoring").extract_func() .. "_"
end, { desc = "Extract Function (line)", expr = true })
keymap.set({ "n", "x" }, "<leader>rE", function()
return require("refactoring").extract_func_to_file()
end, { desc = "Extract Function To File", expr = true })
keymap.set({ "n", "x" }, "<leader>rv", function()
return require("refactoring").extract_var()
end, { desc = "Extract Variable", expr = true })
-- `_` is the default textobject for "current line"
keymap.set("n", "<leader>rvv", function()
return require("refactoring").extract_var() .. "_"
end, { desc = "Extract Variable (line)", expr = true })
keymap.set({ "n", "x" }, "<leader>ri", function()
return require("refactoring").inline_var()
end, { desc = "Inline Variable", expr = true })
keymap.set({ "n", "x" }, "<leader>rI", function()
return require("refactoring").inline_func()
end, { desc = "Inline function", expr = true })
keymap.set({ "n", "x" }, "<leader>rs", function()
return require("refactoring").select_refactor()
end, { desc = "Select refactor" })
-- `iw` is the builtin textobject for "in word". You can use any other textobject or even create the keymap without any textobject if you prefer to provide one yourself each time that you use the keymap
keymap.set({ "x", "n" }, "<leader>pv", function()
return require("refactoring.debug").print_var { output_location = "below" } .. "iw"
end, { desc = "Debug print var below", expr = true })
-- `iw` is the builtin textobject for "in word". You can use any other textobject or even create the keymap without any textobject if you prefer to provide one yourself each time that you use the keymap
keymap.set({ "x", "n" }, "<leader>pV", function()
return require("refactoring.debug").print_var { output_location = "above" } .. "iw"
end, { desc = "Debug print var above", expr = true })
keymap.set({ "x", "n" }, "<leader>pe", function()
return require("refactoring.debug").print_exp { output_location = "below" }
end, { desc = "Debug print exp below", expr = true })
-- `_` is the default textobject for "current line"
keymap.set("n", "<leader>pee", function()
return require("refactoring.debug").print_exp { output_location = "below" } .. "_"
end, { desc = "Debug print exp below", expr = true })
keymap.set({ "x", "n" }, "<leader>pE", function()
return require("refactoring.debug").print_exp { output_location = "above" }
end, { desc = "Debug print exp above", expr = true })
-- `_` is the default textobject for "current line"
keymap.set("n", "<leader>pEE", function()
return require("refactoring.debug").print_exp { output_location = "above" } .. "_"
end, { desc = "Debug print exp above", expr = true })
keymap.set("n", "<leader>pP", function()
return require("refactoring.debug").print_loc { output_location = "above" }
end, { desc = "Debug print location", expr = true })
keymap.set("n", "<leader>pp", function()
return require("refactoring.debug").print_loc { output_location = "below" }
end, { desc = "Debug print location", expr = true })
keymap.set({ "x", "n" }, "<leader>pc", function()
-- `ag` is a custom textobject that selects the whole buffer. It's provided by plugins like `mini.ai` (requires manual configuration using `MiniExtra.gen_ai_spec.buffer()`).
-- return require("refactoring.debug").cleanup { restore_view = true } .. "ag"
-- this keymap doesn't select any textobject by default, so you need to provide one each time you use it.
return require("refactoring.debug").cleanup { restore_view = true }
end, { desc = "Debug print clean", expr = true, remap = true })Option 2: single keymap to select refactor
local keymap = vim.keymap
keymap.set({ "n", "x" }, "<leader>rs", function()
-- this keymap doesn't select any textobject by default, so you may need to provide one each time you use it.
require("refactoring").select_refactor()
end, { desc = "Select refactor" })The :Refactor command can be used to run any refactor. It supports previewing changes and offers completions for available refactors. For refactors that require additional input, extra arguments to the :Refactor command will be used as input.
- C
- C#
- C++
- Go
- Java
- JavaScript/Typescript/Jsx/Tsx
- Lua
- Php
- Powershell
- Python
- Ruby
- Vimscript
To add/improve support for a language, first do it on your local config and daily drive it for a while. To do so:
- Create the required tree-sitter queries for the language in your config directory (
~/.config/nvim/queries/<lang>/<query_name>.scm). You can find the name of the queries required for each feature in Features and the structure of the queries in./queries/. The query files should start with;; extends(see:h treesitter-query-modeline-extends). - Add code generation functions required by the feature to your global config. You can see the code generation functions required by each feature in
./lua/refactoring/config.lua - After daily driving your changes for a while, open a PR to the
refactoring.nvimrepo.
Note
The goal of the plugin is to keep the Lua logic as generic and language agnostic as possible. That's why all of the input language specific information comes from LSP servers and tree-sitter queries. The better the tree-sitter queries are, the better the support for a given language will be.