Apply Biome source assists on save
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user