aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lsp.txt68
-rw-r--r--runtime/lua/vim/lsp/buf.lua88
-rw-r--r--runtime/lua/vim/lsp/util.lua4
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua20
-rw-r--r--test/functional/plugin/lsp_spec.lua61
5 files changed, 220 insertions, 21 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 29f8fab68e..d5ee959c36 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1045,11 +1045,52 @@ execute_command({command_params}) *vim.lsp.buf.execute_command()*
See also: ~
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()*
Formats the current buffer.
Parameters: ~
- {options} (optional, table) Can be used to specify
+ {options} (table|nil) Can be used to specify
FormattingOptions. Some unspecified options
will be automatically derived from the current
Neovim options.
@@ -1073,15 +1114,13 @@ formatting_seq_sync({options}, {timeout_ms}, {order})
<
Parameters: ~
- {options} (optional, table) `FormattingOptions`
- entries
- {timeout_ms} (optional, number) Request timeout
- {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.
+ {options} (table|nil) `FormattingOptions` entries
+ {timeout_ms} (number|nil) Request timeout
+ {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 the remaining clients in
+ the order as they occur in the `order` list.
*vim.lsp.buf.formatting_sync()*
formatting_sync({options}, {timeout_ms})
@@ -1096,7 +1135,8 @@ formatting_sync({options}, {timeout_ms})
<
Parameters: ~
- {options} Table with valid `FormattingOptions` entries
+ {options} table|nil with valid `FormattingOptions`
+ entries
{timeout_ms} (number) Request timeout
See also: ~
@@ -1471,8 +1511,7 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
Returns indentation size.
Parameters: ~
- {bufnr} (optional, number): Buffer handle, defaults to
- current
+ {bufnr} (number|nil): Buffer handle, defaults to current
Return: ~
(number) indentation size
@@ -1548,7 +1587,8 @@ make_formatting_params({options})
buffer and cursor position.
Parameters: ~
- {options} Table with valid `FormattingOptions` entries
+ {options} table|nil with valid `FormattingOptions`
+ entries
Return: ~
`DocumentFormattingParams` object
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 62de8d7321..59682e8a0a 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -143,9 +143,85 @@ local function select_client(method, on_choice)
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.
---
----@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
--- Neovim options.
--
@@ -171,10 +247,11 @@ end
--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
--- </pre>
---
----@param options Table with valid `FormattingOptions` entries
+---@param options table|nil with valid `FormattingOptions` entries
---@param timeout_ms (number) Request timeout
---@see |vim.lsp.buf.formatting_seq_sync|
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 bufnr = vim.api.nvim_get_current_buf()
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()]]
--- </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
+---@param options (table|nil) `FormattingOptions` entries
+---@param timeout_ms (number|nil) Request timeout
+---@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
---the remaining clients in the order as they occur in the `order` list.
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 bufnr = vim.api.nvim_get_current_buf()
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 1f1a34b04a..77ab1d4224 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1873,7 +1873,7 @@ end
--- Returns indentation size.
---
---@see |shiftwidth|
----@param bufnr (optional, number): Buffer handle, defaults to current
+---@param bufnr (number|nil): Buffer handle, defaults to current
---@returns (number) indentation size
function M.get_effective_tabstop(bufnr)
validate { bufnr = {bufnr, 'n', true} }
@@ -1884,7 +1884,7 @@ end
--- 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
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
function M.make_formatting_params(options)
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index bd59e5dc1f..5403405905 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -744,6 +744,26 @@ function tests.clientside_commands()
}
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
local kill_timer = vim.loop.new_timer()
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 6cde96ceaf..6e28946cc4 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -2773,4 +2773,65 @@ describe('LSP', function()
}
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)