Apply Biome source assists on save
This commit is contained in:
@@ -359,11 +359,162 @@ function M.lint.setup()
|
|||||||
})
|
})
|
||||||
end
|
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()
|
function M.format.setup()
|
||||||
require('conform').setup({
|
require('conform').setup({
|
||||||
formatters_by_ft = (M.general and M.general.formatters_by_ft) or {},
|
formatters_by_ft = (M.general and M.general.formatters_by_ft) or {},
|
||||||
default_format_opts = { stop_after_first = true, lsp_format = 'fallback' },
|
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
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user