repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/lua/bugabinga/code_action.lua
Rawlocal auto = require 'std.auto'
local table = require 'std.table'
local NS = vim.api.nvim_create_namespace 'bugabinga.code_action'
local kind_icons = {
['source.fixAll'] = '●',
['quickfix'] = '◉',
['refactor.extract'] = '◎',
['source.organizeImports'] = '◎',
['source'] = '◎',
['refactor.move'] = '○',
['refactor'] = '○',
[''] = '○',
}
local kind_priority = {
['source.fixAll'] = 1,
['quickfix'] = 2,
['refactor.extract'] = 3,
['source.organizeImports'] = 3,
['source'] = 3,
['refactor.move'] = 4,
['refactor'] = 4,
[''] = 5,
}
local function resolve_kind(kind)
local k = kind or ''
while k ~= '' do
if kind_icons[k] then return k end
k = k:match '^(.+)%.[^%.]+$' or ''
end
return ''
end
local function get_kind_hl(kind)
local k = resolve_kind(kind)
return 'BugabingaCodeAction' .. (k == '' and 'Default' or k:gsub('%.', '_'):gsub('^%l', string.upper))
end
local function get_line_diagnostics(bufnr)
local row = vim.api.nvim_win_get_cursor(0)[1] - 1
local diagnostics = vim.diagnostic.get(bufnr, { lnum = row })
local lsp_diagnostics = {}
for _, d in ipairs(diagnostics) do
if d.user_data and d.user_data.lsp then
table.insert(lsp_diagnostics, d.user_data.lsp)
end
end
return lsp_diagnostics
end
local function update(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = 'textDocument/codeAction' })
if #clients == 0 then
vim.api.nvim_buf_clear_namespace(bufnr, NS, 0, -1)
return
end
local encoding = clients[1].offset_encoding or 'utf-16'
local params = vim.lsp.util.make_range_params(0, encoding)
---@cast params lsp.CodeActionParams
params.context = { diagnostics = get_line_diagnostics(bufnr) }
if vim.b[bufnr].bugabinga_code_action_cancel then
vim.b[bufnr].bugabinga_code_action_cancel()
end
local cancel = vim.lsp.buf_request_all(bufnr, 'textDocument/codeAction', params, function(results)
if not vim.api.nvim_buf_is_valid(bufnr) then return end
local best_kind, best_prio = '', math.huge
for _, resp in pairs(results) do
if resp.result then
for _, action in ipairs(resp.result) do
local kind = action.kind or ''
local prio = kind_priority[kind] or 99
if prio < best_prio then
best_prio = prio
best_kind = kind
end
end
end
end
vim.api.nvim_buf_clear_namespace(bufnr, NS, 0, -1)
if best_kind ~= '' or best_prio ~= math.huge then
local icon = kind_icons[resolve_kind(best_kind)] or '○'
local hl = get_kind_hl(best_kind)
local line = params.range.start.line
vim.api.nvim_buf_set_extmark(bufnr, NS, line, 0, {
sign_text = icon,
sign_hl_group = hl,
priority = 10,
})
end
end)
vim.b[bufnr].bugabinga_code_action_cancel = cancel
end
local function setup(bufnr)
local group = vim.api.nvim_create_augroup(
('bugabinga.code_action.%d'):format(bufnr),
{ clear = true }
)
vim.api.nvim_create_autocmd({ 'CursorHold', 'TextChanged', 'DiagnosticChanged' }, {
group = group,
buf = bufnr,
callback = function() update(bufnr) end,
})
vim.api.nvim_create_autocmd('InsertEnter', {
group = group,
buf = bufnr,
callback = function()
vim.api.nvim_buf_clear_namespace(bufnr, NS, 0, -1)
end,
})
vim.api.nvim_create_autocmd('InsertLeave', {
group = group,
buf = bufnr,
callback = function() update(bufnr) end,
})
vim.api.nvim_create_autocmd('LspDetach', {
group = group,
buf = bufnr,
callback = function()
vim.api.nvim_buf_clear_namespace(bufnr, NS, 0, -1)
end,
})
end
auto 'code_action' {
description = 'code action: show kind-specific circle sign when actions available',
events = 'LspAttach',
command = function(args)
setup(args.buf)
end,
}