Compare commits

...

3 Commits

Author SHA1 Message Date
Tom Praschan 75b3ff1cd4
Merge 72f296ad93 into 5e98439f6d 2024-05-05 12:02:43 -04:00
Evgeni Chasnovski 5e98439f6d
fix(defaults): diagnostic mappings descriptions #28646 2024-05-05 07:45:47 -07:00
tom-anders 72f296ad93 feat(lsp): add textDocument/rangesFormatting support to vim.lsp.buf.format()
Fixes #27293
2024-05-04 10:39:57 +02:00
8 changed files with 168 additions and 24 deletions

View File

@ -181,6 +181,7 @@ won't run if your server doesn't support them.
- textDocument/prepareTypeHierarchy
- textDocument/publishDiagnostics
- textDocument/rangeFormatting
- textDocument/rangesFormatting
- textDocument/references
- textDocument/rename
- textDocument/semanticTokens/full
@ -1362,11 +1363,15 @@ format({options}) *vim.lsp.buf.format()*
ID (client.id) matching this field.
• {name}? (`string`) Restrict formatting to the client with
name (client.name) matching this field.
• {range}? (`{start:integer[],end:integer[]}`, default:
current selection in visual mode, `nil` in other modes,
formatting the full buffer) Range to format. Table must
contain `start` and `end` keys with {row,col} tuples
using (1,0) indexing.
• {range}?
(`{start:integer[],end:integer[]}|{start:integer[],end:integer[]}[]`,
default: current selection in visual mode, `nil` in other
modes, formatting the full buffer) Range to format. Table
must contain `start` and `end` keys with {row,col} tuples
using (1,0) indexing. Can also be a list of tables that
contain `start` and `end` keys as described above, in
which case `textDocument/rangesFormatting` support is
required.
hover() *vim.lsp.buf.hover()*
Displays hover information about the symbol under the cursor in a floating

View File

@ -264,6 +264,9 @@ The following new APIs and features were added.
respective capability can be unset.
• |vim.lsp.start()| accepts a "silent" option for suppressing messages
if an LSP server failed to start.
• |vim.lsp.buf.format()| now supports passing a list of ranges
via the `range` parameter (this requires support for the
`textDocument/rangesFormatting` request).
• Treesitter
• Bundled parsers and queries (highlight, folds) for Markdown, Python, and

View File

@ -183,26 +183,22 @@ do
do
vim.keymap.set('n', ']d', function()
vim.diagnostic.goto_next({ float = false })
end, {
desc = 'Jump to the next diagnostic with the highest severity',
})
end, { desc = 'Jump to the next diagnostic' })
vim.keymap.set('n', '[d', function()
vim.diagnostic.goto_prev({ float = false })
end, {
desc = 'Jump to the previous diagnostic with the highest severity',
})
end, { desc = 'Jump to the previous diagnostic' })
vim.keymap.set('n', '<C-W>d', function()
vim.diagnostic.open_float()
end, {
desc = 'Open a floating window showing diagnostics under the cursor',
})
end, { desc = 'Show diagnostics under the cursor' })
vim.keymap.set('n', '<C-W><C-D>', '<C-W>d', {
remap = true,
desc = 'Open a floating window showing diagnostics under the cursor',
})
vim.keymap.set(
'n',
'<C-W><C-D>',
'<C-W>d',
{ remap = true, desc = 'Show diagnostics under the cursor' }
)
end
end

View File

@ -56,6 +56,7 @@ lsp._request_name_to_capability = {
[ms.workspace_symbol] = { 'workspaceSymbolProvider' },
[ms.textDocument_references] = { 'referencesProvider' },
[ms.textDocument_rangeFormatting] = { 'documentRangeFormattingProvider' },
[ms.textDocument_rangesFormatting] = { 'documentRangeFormattingProvider', 'rangesSupport' },
[ms.textDocument_formatting] = { 'documentFormattingProvider' },
[ms.textDocument_completion] = { 'completionProvider' },
[ms.textDocument_documentHighlight] = { 'documentHighlightProvider' },

View File

@ -206,9 +206,11 @@ end
--- Range to format.
--- Table must contain `start` and `end` keys with {row,col} tuples using
--- (1,0) indexing.
--- Can also be a list of tables that contain `start` and `end` keys as described above,
--- in which case `textDocument/rangesFormatting` support is required.
--- (Default: current selection in visual mode, `nil` in other modes,
--- formatting the full buffer)
--- @field range? {start:integer[],end:integer[]}
--- @field range? {start:integer[],end:integer[]}|{start:integer[],end:integer[]}[]
--- Formats a buffer using the attached (and optionally filtered) language
--- server clients.
@ -219,10 +221,20 @@ function M.format(options)
local bufnr = options.bufnr or api.nvim_get_current_buf()
local mode = api.nvim_get_mode().mode
local range = options.range
-- Try to use visual selection if no range is given
if not range and mode == 'v' or mode == 'V' then
range = range_from_selection(bufnr, mode)
end
local method = range and ms.textDocument_rangeFormatting or ms.textDocument_formatting
local passed_multiple_ranges = (range and #range ~= 0 and type(range[1]) == 'table')
local method ---@type string
if passed_multiple_ranges then
method = ms.textDocument_rangesFormatting
elseif range then
method = ms.textDocument_rangeFormatting
else
method = ms.textDocument_formatting
end
local clients = vim.lsp.get_clients({
id = options.id,
@ -242,10 +254,14 @@ function M.format(options)
--- @param params lsp.DocumentFormattingParams
--- @return lsp.DocumentFormattingParams
local function set_range(client, params)
if range then
local range_params =
util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding)
params.range = range_params.range
local to_lsp_range = function(r) ---@return lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams
return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range
end
if passed_multiple_ranges then
params.ranges = vim.tbl_map(to_lsp_range, range)
elseif range then
params.range = to_lsp_range(range)
end
return params
end

View File

@ -732,6 +732,7 @@ function protocol.make_client_capabilities()
},
rangeFormatting = {
dynamicRegistration = true,
rangesSupport = true,
},
completion = {
dynamicRegistration = false,

View File

@ -939,6 +939,48 @@ function tests.basic_formatting()
}
end
function tests.range_formatting()
skeleton {
on_init = function()
return {
capabilities = {
documentFormattingProvider = true,
documentRangeFormattingProvider = true,
},
}
end,
body = function()
notify('start')
expect_request('textDocument/rangeFormatting', function()
return nil, {}
end)
notify('shutdown')
end,
}
end
function tests.ranges_formatting()
skeleton {
on_init = function()
return {
capabilities = {
documentFormattingProvider = true,
documentRangeFormattingProvider = {
rangesSupport = true,
},
},
}
end,
body = function()
notify('start')
expect_request('textDocument/rangesFormatting', function()
return nil, {}
end)
notify('shutdown')
end,
}
end
function tests.set_defaults_all_capabilities()
skeleton {
on_init = function(_)

View File

@ -4463,6 +4463,86 @@ describe('LSP', function()
end,
}
end)
it('Sends textDocument/rangeFormatting request to format a range', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
{ NIL, {}, { method = 'start', client_id = 1 } },
}
local client
test_rpc_server {
test_name = 'range_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.api.nvim_buf_set_lines(bufnr, 0, -1, true, {'foo', 'bar'})
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, range = {
start = {1, 1},
['end'] = {1, 1},
}})
vim.notify = notify
return notify_msg
]])
eq(NIL, notify_msg)
elseif ctx.method == 'shutdown' then
client.stop()
end
end,
}
end)
it('Sends textDocument/rangesFormatting request to format multiple ranges', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
{ NIL, {}, { method = 'start', client_id = 1 } },
}
local client
test_rpc_server {
test_name = 'ranges_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.api.nvim_buf_set_lines(bufnr, 0, -1, true, {'foo', 'bar', 'baz'})
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, range = {
{
start = {1, 1},
['end'] = {1, 1},
},
{
start = {2, 2},
['end'] = {2, 2},
}
}})
vim.notify = notify
return notify_msg
]])
eq(NIL, notify_msg)
elseif ctx.method == 'shutdown' then
client.stop()
end
end,
}
end)
it('Can format async', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },