feat(lsp): support willSave & willSaveWaitUntil capability (#21315)

`willSaveWaitUntil` allows servers to respond with text edits before
saving a document. That is used by some language servers to format a
document or apply quick fixes like removing unused imports.
This commit is contained in:
Mathias Fußenegger
2022-12-08 10:55:01 +01:00
committed by GitHub
parent a505c1acc3
commit 54305443b9
4 changed files with 114 additions and 13 deletions

View File

@@ -1611,9 +1611,37 @@ function lsp.buf_attach_client(bufnr, client_id)
all_buffer_active_clients[bufnr] = buffer_client_ids
local uri = vim.uri_from_bufnr(bufnr)
local augroup = ('lsp_c_%d_b_%d_did_save'):format(client_id, bufnr)
local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr)
local group = api.nvim_create_augroup(augroup, { clear = true })
api.nvim_create_autocmd('BufWritePre', {
group = group,
buffer = bufnr,
desc = 'vim.lsp: textDocument/willSave',
callback = function(ctx)
for_each_buffer_client(ctx.buf, function(client)
local params = {
textDocument = {
uri = uri,
},
reason = protocol.TextDocumentSaveReason.Manual,
}
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then
client.notify('textDocument/willSave', params)
end
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then
local result, err =
client.request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf)
if result and result.result then
util.apply_text_edits(result.result, ctx.buf, client.offset_encoding)
elseif err then
log.error(vim.inspect(err))
end
end
end)
end,
})
api.nvim_create_autocmd('BufWritePost', {
group = api.nvim_create_augroup(augroup, { clear = true }),
group = group,
buffer = bufnr,
desc = 'vim.lsp: textDocument/didSave handler',
callback = function(ctx)

View File

@@ -151,6 +151,7 @@ local constants = {
},
-- Represents reasons why a text document is saved.
---@enum lsp.TextDocumentSaveReason
TextDocumentSaveReason = {
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
-- or by an API call.
@@ -631,11 +632,8 @@ function protocol.make_client_capabilities()
synchronization = {
dynamicRegistration = false,
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
willSave = false,
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil
willSaveWaitUntil = false,
willSave = true,
willSaveWaitUntil = true,
-- Send textDocument/didSave after saving (BufWritePost)
didSave = true,
@@ -870,8 +868,8 @@ function protocol._resolve_capabilities_compat(server_capabilities)
text_document_sync_properties = {
text_document_open_close = if_nil(textDocumentSync.openClose, false),
text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None),
text_document_will_save = if_nil(textDocumentSync.willSave, false),
text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false),
text_document_will_save = if_nil(textDocumentSync.willSave, true),
text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, true),
text_document_save = if_nil(textDocumentSync.save, false),
text_document_save_include_text = if_nil(
type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText,