Apply Biome source assists on save

This commit is contained in:
2026-05-28 07:27:28 +03:00
parent b3fad1fd25
commit 632f3579ce

View File

@@ -359,11 +359,162 @@ function M.lint.setup()
})
end
local biome_save_action_kinds = {
'source.organizeImports.biome',
'source.biome',
}
local function matches_code_action_kind(action, action_kind)
local kind = action.kind or ''
return kind == action_kind or vim.startswith(kind, action_kind .. '.')
end
local function get_client_diagnostics(bufnr, client)
local diagnostics = {}
for _, pull in ipairs({ false, true }) do
local namespace = vim.lsp.diagnostic.get_namespace(client.id, pull)
for _, diagnostic in ipairs(vim.diagnostic.get(bufnr, { namespace = namespace })) do
local lsp_diagnostic = diagnostic.user_data and diagnostic.user_data.lsp
if lsp_diagnostic then
table.insert(diagnostics, lsp_diagnostic)
end
end
end
return diagnostics
end
local function get_document_range(bufnr, client)
local last_line = math.max(vim.api.nvim_buf_line_count(bufnr) - 1, 0)
local line = vim.api.nvim_buf_get_lines(bufnr, last_line, last_line + 1, true)[1] or ''
local last_character = vim.str_utfindex(line, client.offset_encoding or 'utf-16', #line)
return {
start = { line = 0, character = 0 },
['end'] = { line = last_line, character = last_character },
}
end
local function apply_code_action(bufnr, client, action)
local methods = vim.lsp.protocol.Methods
if
not (action.edit and action.command) and client:supports_method(methods.codeAction_resolve)
then
local resolved, resolve_error =
client:request_sync(methods.codeAction_resolve, action, 1000, bufnr)
if resolved and resolved.err then
vim.notify(resolved.err.message, vim.log.levels.ERROR)
return false
end
if resolved and resolved.result then
action = resolved.result
elseif resolve_error then
vim.notify(resolve_error, vim.log.levels.ERROR)
return false
end
end
local applied = false
if action.edit then
vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding)
applied = true
end
if action.command then
local command = type(action.command) == 'table' and action.command or action
client:exec_cmd(command, { bufnr = bufnr })
applied = true
end
return applied
end
local function apply_biome_save_actions(bufnr)
local methods = vim.lsp.protocol.Methods
local clients = vim.lsp.get_clients({
bufnr = bufnr,
name = 'biome',
method = methods.textDocument_codeAction,
})
for _, client in ipairs(clients) do
for _, action_kind in ipairs(biome_save_action_kinds) do
local remaining_passes = 20
local applied_action_kinds = {}
local noop_action_kinds = {}
while remaining_passes > 0 do
remaining_passes = remaining_passes - 1
local result, request_error = client:request_sync(methods.textDocument_codeAction, {
textDocument = vim.lsp.util.make_text_document_params(bufnr),
range = get_document_range(bufnr, client),
context = {
diagnostics = get_client_diagnostics(bufnr, client),
only = { action_kind },
triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Automatic,
},
}, 1000, bufnr)
local applied = false
if result and result.err then
vim.notify(result.err.message, vim.log.levels.ERROR)
elseif request_error then
vim.notify(request_error, vim.log.levels.ERROR)
elseif result and result.result then
for _, action in ipairs(result.result) do
local kind = action.kind or ''
if
matches_code_action_kind(action, action_kind)
and not applied_action_kinds[kind]
and not noop_action_kinds[kind]
then
applied = apply_code_action(bufnr, client, action)
if applied then
applied_action_kinds[kind] = true
break
else
noop_action_kinds[kind] = true
end
end
end
end
if not applied then
break
end
end
if remaining_passes == 0 then
vim.notify('Biome source actions did not converge on save', vim.log.levels.ERROR)
end
end
end
end
function M.format.setup()
require('conform').setup({
formatters_by_ft = (M.general and M.general.formatters_by_ft) or {},
default_format_opts = { stop_after_first = true, lsp_format = 'fallback' },
format_on_save = { timeout_ms = 500 },
})
vim.api.nvim_create_autocmd('BufWritePre', {
group = vim.api.nvim_create_augroup('language-manager.format', { clear = true }),
callback = function(args)
if not vim.bo[args.buf].modifiable then
return
end
apply_biome_save_actions(args.buf)
require('conform').format({ bufnr = args.buf, timeout_ms = 500, lsp_format = 'fallback' })
end,
})
end