A Neovim plugin that provides a simple way to run and visualize code actions.
Preview • Installation • Options • Buffer Picker Options • FAQ
Supported pickers:
buffer
(a minimal picker that uses buffer)vim.ui.select
telescope.nvim
snacks.nvim
fzf-lua
(with select
)The code action protocol is nearly fully implemented in this plugin, so you can use it with any language server, even with, like in the preview, Omnisharp which uses partial code actions.
[!WARNING] I have not tested on all LSP, so do not hesitate to open an issue if it doesn't work for you.
[!NOTE] This plugins comes with NerdFonts icons by default. If you don't want to use them, you can remove them from the
signs
option.
With Lazy.nvim:
{
"rachartier/tiny-code-action.nvim",
dependencies = {
{"nvim-lua/plenary.nvim"},
-- optional picker via telescope
{"nvim-telescope/telescope.nvim"},
-- optional picker via fzf-lua
{"ibhagwan/fzf-lua"},
-- .. or via snacks
{
"folke/snacks.nvim",
opts = {
terminal = {},
}
}
},
event = "LspAttach",
opts = {},
}
And add the following snippet to your keymaps:
vim.keymap.set({ "n", "x" }, "<leader>ca", function()
require("tiny-code-action").code_action()
end, { noremap = true, silent = true })
[!NOTE] To use the delta backend you must install delta
[!WARNING] Due to some limitations, the
delta
backend can be slow if the action is really big. If you want optimal performance, use thevim
backend.
{
"rachartier/tiny-code-action.nvim",
dependencies = {
{"nvim-lua/plenary.nvim"},
},
event = "LspAttach",
opts = {
--- The backend to use, currently only "vim", "delta", "difftastic", "diffsofancy" are supported
backend = "vim",
-- The picker to use, "telescope", "snacks", "select", "buffer" are supported
-- And it's opts that will be passed at the picker's creation, optional
-- If you want to use the `fzf-lua` picker, you can simply set it to `select`
--
-- You can also set `picker = "<picker>"` without any opts.
picker = "telescope",
backend_opts = {
delta = {
-- Header from delta can be quite large.
-- You can remove them by setting this to the number of lines to remove
header_lines_to_remove = 4,
-- The arguments to pass to delta
-- If you have a custom configuration file, you can set the path to it like so:
-- args = {
-- "--config" .. os.getenv("HOME") .. "/.config/delta/config.yml",
-- }
args = {
"--line-numbers",
},
},
difftastic = {
header_lines_to_remove = 1,
-- The arguments to pass to difftastic
args = {
"--color=always",
"--display=inline",
"--syntax-highlight=on",
},
},
diffsofancy = {
header_lines_to_remove = 4,
}
},
-- The icons to use for the code actions
-- You can add your own icons, you just need to set the exact action's kind of the code action
-- You can set the highlight like so: { link = "DiagnosticError" } or like nvim_set_hl ({ fg ..., bg..., bold..., ...})
signs = {
quickfix = { "", { link = "DiagnosticWarning" } },
others = { "", { link = "DiagnosticWarning" } },
refactor = { "", { link = "DiagnosticInfo" } },
["refactor.move"] = { "", { link = "DiagnosticInfo" } },
["refactor.extract"] = { "", { link = "DiagnosticError" } },
["source.organizeImports"] = { "", { link = "DiagnosticWarning" } },
["source.fixAll"] = { "", { link = "DiagnosticError" } },
["source"] = { "", { link = "DiagnosticError" } },
["rename"] = { "", { link = "DiagnosticWarning" } },
["codeAction"] = { "", { link = "DiagnosticWarning" } },
},
}
}
The buffer
picker provides compact and customizable options for displaying and managing code actions.
Below is an example configuration for the buffer
picker:
require("tiny-code-action").setup({
picker = {
"buffer",
opts = {
hotkeys = true, -- Enable hotkeys for quick selection of actions
hotkeys_mode = "text_diff_based", -- Modes for generating hotkeys
auto_preview = false, -- Enable or disable automatic preview
auto_accept = false, -- Automatically accept the selected action
position = "cursor", -- Position of the picker window
winborder = "single", -- Border style for picker and preview windows
custom_keys = {
{ key = 'm', pattern = 'Fill match arms' },
{ key = 'r', pattern = 'Rename.*' }, -- Lua pattern matching
},
},
},
})
sequential
: Generates sequential hotkeys like a
, b
, c
, etc.text_based
: Assigns hotkeys based on the first unique character in the action title.text_diff_based
: Generates smarter hotkeys based on title differences.hotkeys_mode
to fully control hotkey generation. The function receives (titles, used_hotkeys)
and must return a list of hotkey strings. Example for numeric hotkeys:hotkeys_mode = function(titles, used_hotkeys)
local t = {}
for i = 1, #titles do t[i] = tostring(i) end
return t
end
vim.o.winborder
or "rounded"
.The custom_keys
option allows you to define specific hotkeys for actions. It supports two formats:
custom_keys = {
{ key = 'm', pattern = 'Fill match arms' },
{ key = 'm', pattern = 'Consider making this binding mutable: mut' },
{ key = 'r', pattern = 'Rename.*' }, -- Lua pattern matching
{ key = 'e', pattern = 'Extract Method' },
}
This format allows:
'Rename.*'
) for flexible matchingcustom_keys = {
['e'] = "Extract Method", -- Assigning 'e' for the 'Extract Method' action
['r'] = "Rename", -- Assigning 'r' for the 'Rename' action
}
Note: This format doesn't allow duplicate keys and only supports exact string matching.
The array format is recommended as it provides more flexibility for complex use cases while maintaining backward compatibility.
buffer
picker)The plugin provides autocmds that are triggered when code action windows are opened. You can listen to these events to customize behavior or integrate with other plugins.
TinyCodeActionWindowEnterMain
: Triggered when the main code action picker window is openedTinyCodeActionWindowEnterPreview
: Triggered when the preview window is openedBoth autocmds provide the following data:
buf
: Buffer ID of the opened windowwin
: Window ID of the opened window-- Listen for main window opening
vim.api.nvim_create_autocmd("User", {
pattern = "TinyCodeActionWindowEnterMain",
callback = function(event)
local buf = event.data.buf
local win = event.data.win
vim.notify("Code action main window opened: buf=" .. buf .. ", win=" .. win)
end,
})
-- Listen for preview window opening
vim.api.nvim_create_autocmd("User", {
pattern = "TinyCodeActionWindowEnterPreview",
callback = function(event)
local buf = event.data.buf
local win = event.data.win
-- Custom logic for preview window
end,
})
Filters can be provided via the filters
option, the context.only
option (LSP standard), or a user-supplied function. All filters are combined and applied in sequence.
context.only
)If you pass an opts.context = { only = ... }
object, only code actions matching the specified LSP CodeActionKind will be included.
require("tiny-code-action").code_action({
context = { only = "refactor" },
})
filters
option)The filters
table allows you to filter code actions by specific criteria. Supported filter keys:
str
: String or Lua pattern to match in the action titlekind
: Code action kind (e.g., "refactor", "source.organizeImports")client
: Name of the LSP clientline
: Line number the action applies toYou may combine multiple filters; all must match for an action to be included.
Example:
require("tiny-code-action").code_action({
filters = {
kind = "refactor",
str = "Wrap",
client = "omnisharp",
line = 10,
}
})
filter
option)You can supply a custom filter function, which will be called for each action. The function receives the action object and should return true to include it.
Example:
require("tiny-code-action").code_action({
filter = function(action)
-- Only show actions that have "Rename" in the title and are preferred
return action.title:find("Rename") and action.isPreferred
end,
})
You can use all filtering mechanisms together; they are applied in the following order:
context.only
filters
tablefilter
functionOnly code actions that pass all enabled filters will be shown.
delta
configuration here: config_path
to the path of your configuration file.