feat(lsp): add range option to lsp.buf.format (#19998)

This commit is contained in:
Mathias Fußenegger
2022-09-08 11:33:04 +02:00
committed by GitHub
parent 8c59d7e6a7
commit 11167ab6d5
3 changed files with 110 additions and 25 deletions

View File

@@ -150,6 +150,33 @@ local function select_client(method, on_choice)
end
end
---@private
---@return table {start={row, col}, end={row, col}} using (1, 0) indexing
local function range_from_selection()
-- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896
-- [bufnum, lnum, col, off]; both row and column 1-indexed
local start = vim.fn.getpos('v')
local end_ = vim.fn.getpos('.')
local start_row = start[2]
local start_col = start[3]
local end_row = end_[2]
local end_col = end_[3]
-- A user can start visual selection at the end and move backwards
-- Normalize the range to start < end
if start_row == end_row and end_col < start_col then
end_col, start_col = start_col, end_col
elseif end_row < start_row then
start_row, end_row = end_row, start_row
start_col, end_col = end_col, start_col
end
return {
['start'] = { start_row, start_col - 1 },
['end'] = { end_row, end_col - 1 },
}
end
--- Formats a buffer using the attached (and optionally filtered) language
--- server clients.
---
@@ -184,7 +211,12 @@ end
--- 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.
---
--- - range (table|nil) Range to format.
--- Table must contain `start` and `end` keys with {row, col} tuples using
--- (1,0) indexing.
--- Defaults to current selection in visual mode
--- Defaults to `nil` in other modes, formatting the full buffer
function M.format(options)
options = options or {}
local bufnr = options.bufnr or api.nvim_get_current_buf()
@@ -206,16 +238,32 @@ function M.format(options)
vim.notify('[LSP] Format request failed, no matching language servers.')
end
local mode = api.nvim_get_mode().mode
local range = options.range
if not range and mode == 'v' or mode == 'V' then
range = range_from_selection()
end
---@private
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
end
return params
end
local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting'
if options.async then
local do_format
do_format = function(idx, client)
if not client then
return
end
local params = util.make_formatting_params(options.formatting_options)
client.request('textDocument/formatting', params, function(...)
local handler = client.handlers['textDocument/formatting']
or vim.lsp.handlers['textDocument/formatting']
local params = set_range(client, util.make_formatting_params(options.formatting_options))
client.request(method, params, function(...)
local handler = client.handlers[method] or vim.lsp.handlers[method]
handler(...)
do_format(next(clients, idx))
end, bufnr)
@@ -224,8 +272,8 @@ function M.format(options)
else
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)
local params = set_range(client, util.make_formatting_params(options.formatting_options))
local result, err = client.request_sync(method, params, timeout_ms, bufnr)
if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
@@ -356,6 +404,7 @@ end
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
function M.range_formatting(options, start_pos, end_pos)
vim.deprecate('vim.lsp.buf.range_formatting', 'vim.lsp.formatexpr or vim.lsp.format', '0.9.0')
local params = util.make_given_range_params(start_pos, end_pos)
params.options = util.make_formatting_params(options).options
select_client('textDocument/rangeFormatting', function(client)
@@ -885,23 +934,8 @@ function M.code_action(options)
local end_ = assert(options.range['end'], 'range must have a `end` property')
params = util.make_given_range_params(start, end_)
elseif mode == 'v' or mode == 'V' then
-- [bufnum, lnum, col, off]; both row and column 1-indexed
local start = vim.fn.getpos('v')
local end_ = vim.fn.getpos('.')
local start_row = start[2]
local start_col = start[3]
local end_row = end_[2]
local end_col = end_[3]
-- A user can start visual selection at the end and move backwards
-- Normalize the range to start < end
if start_row == end_row and end_col < start_col then
end_col, start_col = start_col, end_col
elseif end_row < start_row then
start_row, end_row = end_row, start_row
start_col, end_col = end_col, start_col
end
params = util.make_given_range_params({ start_row, start_col - 1 }, { end_row, end_col - 1 })
local range = range_from_selection()
params = util.make_given_range_params(range.start, range['end'])
else
params = util.make_range_params()
end