Add formatting_seq_sync, change formatting and formatting_sync

This commit is contained in:
Karim Abou Zeid 2021-04-30 13:40:20 +02:00
parent 59eae3b38f
commit 48a59f8f4f
2 changed files with 123 additions and 7 deletions

View File

@ -890,6 +890,42 @@ function lsp.start_client(config)
end) end)
end end
--@private
--- Sends a request to the server and synchronously waits for the response.
---
--- This is a wrapper around {client.request}
---
--@param method (string) LSP method name.
--@param params (table) LSP request params.
--@param timeout_ms (number, optional, default=100) Maximum time in
---milliseconds to wait for a result.
--@param bufnr (number) Buffer handle (0 for current).
--@returns { err, result }, where `err` and `result` come from the |lsp-handler|.
---On timeout, cancel or error, returns `(nil, err)` where `err` is a
---string describing the failure reason. If the request was unsuccessful
---returns `nil`.
--@see |vim.lsp.buf_request_sync()|
function client.request_sync(method, params, timeout_ms, bufnr)
local request_result = nil
local function _sync_handler(err, _, result)
request_result = { error = err, result = result }
end
local success, request_id = client.request(method, params, _sync_handler,
bufnr)
if not success then return nil end
local wait_result, reason = vim.wait(timeout_ms or 100, function()
return request_result ~= nil
end, 10)
if not wait_result then
client.cancel_request(request_id)
return nil, wait_result_reason[reason]
end
return request_result
end
--@private --@private
--- Sends a notification to an LSP server. --- Sends a notification to an LSP server.
--- ---

View File

@ -111,6 +111,39 @@ function M.completion(context)
return request('textDocument/completion', params) return request('textDocument/completion', params)
end end
--@private
--- If there is more than one client with formatting capability, asks the user
--- which one to use.
--
--@returns The client to use for formatting
local function get_formatting_client()
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
clients = vim.tbl_filter(function (client)
return client.resolved_capabilities.document_formatting
end, clients)
-- better UX when choices are always in the same order (between restarts)
table.sort(clients, function (a, b) return a.name < b.name end)
if #clients > 1 then
local choices = {}
for k,v in ipairs(clients) do
table.insert(choices, string.format("%d %s", k, v.name))
end
local user_choice = vim.fn.confirm(
"Select a language server for formatting:",
table.concat(choices, "\n"),
0,
"Question"
)
if user_choice == 0 then return nil end
return clients[user_choice]
elseif #clients < 1 then
return nil
else
return clients[1]
end
end
--- Formats the current buffer. --- Formats the current buffer.
--- ---
--@param options (optional, table) Can be used to specify FormattingOptions. --@param options (optional, table) Can be used to specify FormattingOptions.
@ -119,8 +152,11 @@ end
-- --
--@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting --@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
function M.formatting(options) function M.formatting(options)
local client = get_formatting_client()
if client == nil then return end
local params = util.make_formatting_params(options) local params = util.make_formatting_params(options)
return request('textDocument/formatting', params) return client.request("textDocument/formatting", params)
end end
--- Performs |vim.lsp.buf.formatting()| synchronously. --- Performs |vim.lsp.buf.formatting()| synchronously.
@ -134,14 +170,58 @@ end
--- ---
--@param options Table with valid `FormattingOptions` entries --@param options Table with valid `FormattingOptions` entries
--@param timeout_ms (number) Request timeout --@param timeout_ms (number) Request timeout
--@see |vim.lsp.buf.formatting_seq_sync|
function M.formatting_sync(options, timeout_ms) function M.formatting_sync(options, timeout_ms)
local client = get_formatting_client()
if client == nil then return end
local params = util.make_formatting_params(options) local params = util.make_formatting_params(options)
local result = vim.lsp.buf_request_sync(0, "textDocument/formatting", params, timeout_ms) local result = client.request_sync("textDocument/formatting", params, timeout_ms)
if not result or vim.tbl_isempty(result) then return end if result and result.result then
local _, formatting_result = next(result) util.apply_text_edits(result.result)
result = formatting_result.result end
if not result then return end end
vim.lsp.util.apply_text_edits(result)
--- Formats the current buffer by sequentially requesting formatting from attached clients.
---
--- Useful when multiple clients with formatting capability are attached.
---
--- Since it's synchronous, can be used for running on save, to make sure buffer is formatted
--- prior to being saved. {timeout_ms} is passed on to the |vim.lsp.client| `request_sync` method.
--- Example:
--- <pre>
--- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
--- </pre>
---
--@param options (optional, table) `FormattingOptions` entries
--@param timeout_ms (optional, number) Request timeout
--@param order (optional, table) List of client names. Formatting is requested from clients
---in the following order: first all clients that are not in the `order` list, then
---the remaining clients in the order as they occur in the `order` list.
function M.formatting_seq_sync(options, timeout_ms, order)
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
-- sort the clients according to `order`
for _, client_name in ipairs(order or {}) do
-- if the client exists, move to the end of the list
for i, client in ipairs(clients) do
if client.name == client_name then
table.insert(clients, table.remove(clients, i))
break
end
end
end
-- loop through the clients and make synchronous formatting requests
for _, client in ipairs(clients) do
if client.resolved_capabilities.document_formatting then
local params = util.make_formatting_params(options)
local result = client.request_sync("textDocument/formatting", params, timeout_ms)
if result and result.result then
util.apply_text_edits(result.result)
end
end
end
end end
--- Formats a given range. --- Formats a given range.