mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
feat(lsp): add vim.lsp.buf.format (#18193)
This commit is contained in:

committed by
GitHub

parent
eecc6535eb
commit
5b04e46d23
@@ -1045,11 +1045,52 @@ execute_command({command_params}) *vim.lsp.buf.execute_command()*
|
|||||||
See also: ~
|
See also: ~
|
||||||
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
||||||
|
|
||||||
|
format({options}) *vim.lsp.buf.format()*
|
||||||
|
Formats a buffer using the attached (and optionally filtered)
|
||||||
|
language server clients.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{options} table|nil Optional table which holds the
|
||||||
|
following optional fields:
|
||||||
|
• formatting_options (table|nil): Can be used
|
||||||
|
to specify FormattingOptions. Some
|
||||||
|
unspecified options will be automatically
|
||||||
|
derived from the current Neovim options.
|
||||||
|
|
||||||
|
See also: ~
|
||||||
|
https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
|
||||||
|
• timeout_ms (integer|nil, default 1000): Time in
|
||||||
|
milliseconds to block for formatting requests.
|
||||||
|
Formatting requests are current synchronous to prevent
|
||||||
|
editing of the buffer.
|
||||||
|
• bufnr (number|nil): Restrict formatting to the clients
|
||||||
|
attached to the given buffer, defaults to the current
|
||||||
|
buffer (0).
|
||||||
|
• filter (function|nil): Predicate to filter clients used
|
||||||
|
for formatting. Receives the list of clients attached to
|
||||||
|
bufnr as the argument and must return the list of
|
||||||
|
clients on which to request formatting. Example: • >
|
||||||
|
|
||||||
|
-- Never request typescript-language-server for formatting
|
||||||
|
vim.lsp.buf.format {
|
||||||
|
filter = function(clients)
|
||||||
|
return vim.tbl_filter(
|
||||||
|
function(client) return client.name ~= "tsserver" end,
|
||||||
|
clients
|
||||||
|
)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
<
|
||||||
|
• id (number|nil): Restrict formatting to the client with
|
||||||
|
ID (client.id) matching this field.
|
||||||
|
• name (string|nil): Restrict formatting to the client
|
||||||
|
with name (client.name) matching this field.
|
||||||
|
|
||||||
formatting({options}) *vim.lsp.buf.formatting()*
|
formatting({options}) *vim.lsp.buf.formatting()*
|
||||||
Formats the current buffer.
|
Formats the current buffer.
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{options} (optional, table) Can be used to specify
|
{options} (table|nil) Can be used to specify
|
||||||
FormattingOptions. Some unspecified options
|
FormattingOptions. Some unspecified options
|
||||||
will be automatically derived from the current
|
will be automatically derived from the current
|
||||||
Neovim options.
|
Neovim options.
|
||||||
@@ -1073,15 +1114,13 @@ formatting_seq_sync({options}, {timeout_ms}, {order})
|
|||||||
<
|
<
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{options} (optional, table) `FormattingOptions`
|
{options} (table|nil) `FormattingOptions` entries
|
||||||
entries
|
{timeout_ms} (number|nil) Request timeout
|
||||||
{timeout_ms} (optional, number) Request timeout
|
{order} (table|nil) List of client names. Formatting
|
||||||
{order} (optional, table) List of client names.
|
is requested from clients in the following
|
||||||
Formatting is requested from clients in the
|
order: first all clients that are not in the
|
||||||
following order: first all clients that are
|
`order` list, then the remaining clients in
|
||||||
not in the `order` list, then the remaining
|
the order as they occur in the `order` list.
|
||||||
clients in the order as they occur in the
|
|
||||||
`order` list.
|
|
||||||
|
|
||||||
*vim.lsp.buf.formatting_sync()*
|
*vim.lsp.buf.formatting_sync()*
|
||||||
formatting_sync({options}, {timeout_ms})
|
formatting_sync({options}, {timeout_ms})
|
||||||
@@ -1096,7 +1135,8 @@ formatting_sync({options}, {timeout_ms})
|
|||||||
<
|
<
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{options} Table with valid `FormattingOptions` entries
|
{options} table|nil with valid `FormattingOptions`
|
||||||
|
entries
|
||||||
{timeout_ms} (number) Request timeout
|
{timeout_ms} (number) Request timeout
|
||||||
|
|
||||||
See also: ~
|
See also: ~
|
||||||
@@ -1471,8 +1511,7 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
|
|||||||
Returns indentation size.
|
Returns indentation size.
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{bufnr} (optional, number): Buffer handle, defaults to
|
{bufnr} (number|nil): Buffer handle, defaults to current
|
||||||
current
|
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(number) indentation size
|
(number) indentation size
|
||||||
@@ -1548,7 +1587,8 @@ make_formatting_params({options})
|
|||||||
buffer and cursor position.
|
buffer and cursor position.
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{options} Table with valid `FormattingOptions` entries
|
{options} table|nil with valid `FormattingOptions`
|
||||||
|
entries
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
`DocumentFormattingParams` object
|
`DocumentFormattingParams` object
|
||||||
|
@@ -143,9 +143,85 @@ local function select_client(method, on_choice)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Formats a buffer using the attached (and optionally filtered) language
|
||||||
|
--- server clients.
|
||||||
|
---
|
||||||
|
--- @param options table|nil Optional table which holds the following optional fields:
|
||||||
|
--- - formatting_options (table|nil):
|
||||||
|
--- Can be used to specify FormattingOptions. Some unspecified options will be
|
||||||
|
--- automatically derived from the current Neovim options.
|
||||||
|
--- @see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
|
||||||
|
--- - timeout_ms (integer|nil, default 1000):
|
||||||
|
--- Time in milliseconds to block for formatting requests. Formatting requests are current
|
||||||
|
--- synchronous to prevent editing of the buffer.
|
||||||
|
--- - bufnr (number|nil):
|
||||||
|
--- Restrict formatting to the clients attached to the given buffer, defaults to the current
|
||||||
|
--- buffer (0).
|
||||||
|
--- - filter (function|nil):
|
||||||
|
--- Predicate to filter clients used for formatting. Receives the list of clients attached
|
||||||
|
--- to bufnr as the argument and must return the list of clients on which to request
|
||||||
|
--- formatting. Example:
|
||||||
|
---
|
||||||
|
--- <pre>
|
||||||
|
--- -- Never request typescript-language-server for formatting
|
||||||
|
--- vim.lsp.buf.format {
|
||||||
|
--- filter = function(clients)
|
||||||
|
--- return vim.tbl_filter(
|
||||||
|
--- function(client) return client.name ~= "tsserver" end,
|
||||||
|
--- clients
|
||||||
|
--- )
|
||||||
|
--- end
|
||||||
|
--- }
|
||||||
|
--- </pre>
|
||||||
|
---
|
||||||
|
--- - id (number|nil):
|
||||||
|
--- Restrict formatting to the client with ID (client.id) matching this field.
|
||||||
|
--- - name (string|nil):
|
||||||
|
--- Restrict formatting to the client with name (client.name) matching this field.
|
||||||
|
|
||||||
|
function M.format(options)
|
||||||
|
options = options or {}
|
||||||
|
local bufnr = options.bufnr or vim.api.nvim_get_current_buf()
|
||||||
|
local clients = vim.lsp.buf_get_clients(bufnr)
|
||||||
|
|
||||||
|
if options.filter then
|
||||||
|
clients = options.filter(clients)
|
||||||
|
elseif options.id then
|
||||||
|
clients = vim.tbl_filter(
|
||||||
|
function(client) return client.id == options.id end,
|
||||||
|
clients
|
||||||
|
)
|
||||||
|
elseif options.name then
|
||||||
|
clients = vim.tbl_filter(
|
||||||
|
function(client) return client.name == options.name end,
|
||||||
|
clients
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
clients = vim.tbl_filter(
|
||||||
|
function(client) return client.supports_method("textDocument/formatting") end,
|
||||||
|
clients
|
||||||
|
)
|
||||||
|
|
||||||
|
if #clients == 0 then
|
||||||
|
vim.notify("[LSP] Format request failed, no matching language servers.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local timeout_ms = options.timeout_ms or 1000
|
||||||
|
for _, client in pairs(clients) do
|
||||||
|
local params = util.make_formatting_params(options.formatting_options)
|
||||||
|
local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr)
|
||||||
|
if result and result.result then
|
||||||
|
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
|
||||||
|
elseif err then
|
||||||
|
vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Formats the current buffer.
|
--- Formats the current buffer.
|
||||||
---
|
---
|
||||||
---@param options (optional, table) Can be used to specify FormattingOptions.
|
---@param options (table|nil) Can be used to specify FormattingOptions.
|
||||||
--- Some unspecified options will be automatically derived from the current
|
--- Some unspecified options will be automatically derived from the current
|
||||||
--- Neovim options.
|
--- Neovim options.
|
||||||
--
|
--
|
||||||
@@ -171,10 +247,11 @@ end
|
|||||||
--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
|
--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
|
||||||
--- </pre>
|
--- </pre>
|
||||||
---
|
---
|
||||||
---@param options Table with valid `FormattingOptions` entries
|
---@param options table|nil with valid `FormattingOptions` entries
|
||||||
---@param timeout_ms (number) Request timeout
|
---@param timeout_ms (number) Request timeout
|
||||||
---@see |vim.lsp.buf.formatting_seq_sync|
|
---@see |vim.lsp.buf.formatting_seq_sync|
|
||||||
function M.formatting_sync(options, timeout_ms)
|
function M.formatting_sync(options, timeout_ms)
|
||||||
|
vim.notify_once('vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
|
||||||
local params = util.make_formatting_params(options)
|
local params = util.make_formatting_params(options)
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
select_client('textDocument/formatting', function(client)
|
select_client('textDocument/formatting', function(client)
|
||||||
@@ -202,12 +279,13 @@ end
|
|||||||
--- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
|
--- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
|
||||||
--- </pre>
|
--- </pre>
|
||||||
---
|
---
|
||||||
---@param options (optional, table) `FormattingOptions` entries
|
---@param options (table|nil) `FormattingOptions` entries
|
||||||
---@param timeout_ms (optional, number) Request timeout
|
---@param timeout_ms (number|nil) Request timeout
|
||||||
---@param order (optional, table) List of client names. Formatting is requested from clients
|
---@param order (table|nil) List of client names. Formatting is requested from clients
|
||||||
---in the following order: first all clients that are not in the `order` list, then
|
---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.
|
---the remaining clients in the order as they occur in the `order` list.
|
||||||
function M.formatting_seq_sync(options, timeout_ms, order)
|
function M.formatting_seq_sync(options, timeout_ms, order)
|
||||||
|
vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
|
||||||
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
|
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
|
||||||
|
@@ -1873,7 +1873,7 @@ end
|
|||||||
--- Returns indentation size.
|
--- Returns indentation size.
|
||||||
---
|
---
|
||||||
---@see |shiftwidth|
|
---@see |shiftwidth|
|
||||||
---@param bufnr (optional, number): Buffer handle, defaults to current
|
---@param bufnr (number|nil): Buffer handle, defaults to current
|
||||||
---@returns (number) indentation size
|
---@returns (number) indentation size
|
||||||
function M.get_effective_tabstop(bufnr)
|
function M.get_effective_tabstop(bufnr)
|
||||||
validate { bufnr = {bufnr, 'n', true} }
|
validate { bufnr = {bufnr, 'n', true} }
|
||||||
@@ -1884,7 +1884,7 @@ end
|
|||||||
|
|
||||||
--- Creates a `DocumentFormattingParams` object for the current buffer and cursor position.
|
--- Creates a `DocumentFormattingParams` object for the current buffer and cursor position.
|
||||||
---
|
---
|
||||||
---@param options Table with valid `FormattingOptions` entries
|
---@param options table|nil with valid `FormattingOptions` entries
|
||||||
---@returns `DocumentFormattingParams` object
|
---@returns `DocumentFormattingParams` object
|
||||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
|
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
|
||||||
function M.make_formatting_params(options)
|
function M.make_formatting_params(options)
|
||||||
|
@@ -744,6 +744,26 @@ function tests.clientside_commands()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function tests.basic_formatting()
|
||||||
|
skeleton {
|
||||||
|
on_init = function()
|
||||||
|
return {
|
||||||
|
capabilities = {
|
||||||
|
documentFormattingProvider = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end;
|
||||||
|
body = function()
|
||||||
|
notify('start')
|
||||||
|
expect_request('textDocument/formatting', function()
|
||||||
|
return nil, {}
|
||||||
|
end)
|
||||||
|
notify('shutdown')
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- Tests will be indexed by TEST_NAME
|
-- Tests will be indexed by TEST_NAME
|
||||||
|
|
||||||
local kill_timer = vim.loop.new_timer()
|
local kill_timer = vim.loop.new_timer()
|
||||||
|
@@ -2773,4 +2773,65 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("vim.lsp.buf.format", function()
|
||||||
|
it("Aborts with notify if no client matches filter", function()
|
||||||
|
local client
|
||||||
|
test_rpc_server {
|
||||||
|
test_name = "basic_init",
|
||||||
|
on_init = function(c)
|
||||||
|
client = c
|
||||||
|
end,
|
||||||
|
on_handler = function()
|
||||||
|
local notify_msg = exec_lua([[
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
|
||||||
|
local notify_msg
|
||||||
|
local notify = vim.notify
|
||||||
|
vim.notify = function(msg, log_level)
|
||||||
|
notify_msg = msg
|
||||||
|
end
|
||||||
|
vim.lsp.buf.format({ name = 'does-not-exist' })
|
||||||
|
vim.notify = notify
|
||||||
|
return notify_msg
|
||||||
|
]])
|
||||||
|
eq("[LSP] Format request failed, no matching language servers.", notify_msg)
|
||||||
|
client.stop()
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
it("Sends textDocument/formatting request to format buffer", function()
|
||||||
|
local expected_handlers = {
|
||||||
|
{NIL, {}, {method="shutdown", client_id=1}};
|
||||||
|
{NIL, {}, {method="start", client_id=1}};
|
||||||
|
}
|
||||||
|
local client
|
||||||
|
test_rpc_server {
|
||||||
|
test_name = "basic_formatting",
|
||||||
|
on_init = function(c)
|
||||||
|
client = c
|
||||||
|
end,
|
||||||
|
on_handler = function(_, _, ctx)
|
||||||
|
table.remove(expected_handlers)
|
||||||
|
if ctx.method == "start" then
|
||||||
|
local notify_msg = exec_lua([[
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
|
||||||
|
local notify_msg
|
||||||
|
local notify = vim.notify
|
||||||
|
vim.notify = function(msg, log_level)
|
||||||
|
notify_msg = msg
|
||||||
|
end
|
||||||
|
vim.lsp.buf.format({ bufnr = bufnr })
|
||||||
|
vim.notify = notify
|
||||||
|
return notify_msg
|
||||||
|
]])
|
||||||
|
eq(NIL, notify_msg)
|
||||||
|
elseif ctx.method == "shutdown" then
|
||||||
|
client.stop()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user