Skip to content

Commit

Permalink
feat(lsp): add range option to code_action; deprecate range_code_acti…
Browse files Browse the repository at this point in the history
…on (neovim#19551)

`code_action` gained extra functions (`filter` and `apply`) which
`range_code_action` didn't have.

To close this gap, this adds a `range` option to `code_action` and
deprecates `range_code_action`.

The option defaults to the current selection if in visual mode.
This allows users to setup a mapping like `vim.keymap.set({'v', 'n'},
'<a-CR>', vim.lsp.buf.code_action)`

`range_code_action` used to use the `<` and `>` markers to get the
_last_ selection which required using a `<Esc><Cmd>lua
vim.lsp.buf.range_code_action()<CR>` (note the `<ESC>`) mapping.
  • Loading branch information
mfussenegger authored and smjonas committed Dec 31, 2022
1 parent becde0a commit 463b903
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 55 deletions.
56 changes: 30 additions & 26 deletions runtime/doc/lsp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1096,21 +1096,26 @@ code_action({options}) *vim.lsp.buf.code_action()*
Parameters: ~
{options} (table|nil) Optional table which holds the
following optional fields:
• context (table|nil): Corresponds to `CodeActionContext` of the LSP specification:
• context: (table|nil) Corresponds to `CodeActionContext` of the LSP specification:
• diagnostics (table|nil): LSP`Diagnostic[]` . Inferred from the current position if not
provided.
• only (table|nil): List of LSP
`CodeActionKind`s used to filter the code
actions. Most language servers support
values like `refactor` or `quickfix`.

• filter (function|nil): Predicate function
taking an `CodeAction` and returning a
boolean.
• apply (boolean|nil): When set to `true`, and
• filter: (function|nil) Predicate taking an
`CodeAction` and returning a boolean.
• apply: (boolean|nil) When set to `true`, and
there is just one remaining action (after
filtering), the action is applied without
user query.
• range: (table|nil) Range for which code
actions should be requested. If in visual
mode this defaults to the active selection.
Table must contain `start` and `end` keys
with {row, col} tuples using mark-like
indexing. See |api-indexing|

See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
Expand Down Expand Up @@ -1606,7 +1611,7 @@ character_offset({buf}, {row}, {col}, {offset_encoding})
certain buffer.

Parameters: ~
{buf} buffer id (0 for current)
{buf} (number) buffer number (0 for current)
{row} 0-indexed line
{col} 0-indexed byte offset in line
{offset_encoding} (string) utf-8|utf-16|utf-32|nil
Expand Down Expand Up @@ -1768,17 +1773,17 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding})
that is similar to |vim.lsp.util.make_range_params()|.

Parameters: ~
{start_pos} ({number, number}, optional)
mark-indexed position. Defaults to the
start of the last visual selection.
{end_pos} ({number, number}, optional)
mark-indexed position. Defaults to the
end of the last visual selection.
{bufnr} (optional, number): buffer handle or 0
for current, defaults to current
{offset_encoding} (string) utf-8|utf-16|utf-32|nil
defaults to `offset_encoding` of first
client of `bufnr`
{start_pos} number[]|nil {row, col} mark-indexed
position. Defaults to the start of the
last visual selection.
{end_pos} number[]|nil {row, col} mark-indexed
position. Defaults to the end of the
last visual selection.
{bufnr} (number|nil) buffer handle or 0 for
current, defaults to current
{offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults
to `offset_encoding` of first client of
`bufnr`

Return: ~
{ textDocument = { uri = `current_file_uri` }, range = {
Expand All @@ -1790,8 +1795,8 @@ make_position_params({window}, {offset_encoding})
buffer and cursor position.

Parameters: ~
{window} (optional, number): window handle or 0
for current, defaults to current
{window} number|nil: window handle or 0 for
current, defaults to current
{offset_encoding} (string) utf-8|utf-16|utf-32|nil
defaults to `offset_encoding` of first
client of buffer of `window`
Expand All @@ -1811,11 +1816,11 @@ make_range_params({window}, {offset_encoding})
`textDocument/rangeFormatting`.

Parameters: ~
{window} (optional, number): window handle or 0
for current, defaults to current
{offset_encoding} (string) utf-8|utf-16|utf-32|nil
defaults to `offset_encoding` of first
client of buffer of `window`
{window} number|nil: window handle or 0 for
current, defaults to current
{offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults
to `offset_encoding` of first client of
buffer of `window`

Return: ~
{ textDocument = { uri = `current_file_uri` }, range = {
Expand All @@ -1827,8 +1832,7 @@ make_text_document_params({bufnr})
buffer.

Parameters: ~
{bufnr} (optional, number): Buffer handle, defaults to
current
{bufnr} number|nil: Buffer handle, defaults to current

Return: ~
`TextDocumentIdentifier`
Expand Down
69 changes: 52 additions & 17 deletions runtime/lua/vim/lsp/buf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ local M = {}
--- buffer.
---
---@param method (string) LSP method name
---@param params (optional, table) Parameters to send to the server
---@param handler (optional, functionnil) See |lsp-handler|. Follows |lsp-handler-resolution|
---@param params (table|nil) Parameters to send to the server
---@param handler (function|nil) See |lsp-handler|. Follows |lsp-handler-resolution|
--
---@returns 2-tuple:
--- - Map of client-id:request-id pairs for all successful requests.
Expand Down Expand Up @@ -842,20 +842,27 @@ end
--- cursor position.
---
---@param options table|nil Optional table which holds the following optional fields:
--- - context (table|nil):
--- Corresponds to `CodeActionContext` of the LSP specification:
--- - diagnostics (table|nil):
--- LSP `Diagnostic[]`. Inferred from the current
--- position if not provided.
--- - only (table|nil):
--- List of LSP `CodeActionKind`s used to filter the code actions.
--- Most language servers support values like `refactor`
--- or `quickfix`.
--- - filter (function|nil):
--- Predicate function taking an `CodeAction` and returning a boolean.
--- - apply (boolean|nil):
--- When set to `true`, and there is just one remaining action
--- (after filtering), the action is applied without user query.
--- - context: (table|nil)
--- Corresponds to `CodeActionContext` of the LSP specification:
--- - diagnostics (table|nil):
--- LSP `Diagnostic[]`. Inferred from the current
--- position if not provided.
--- - only (table|nil):
--- List of LSP `CodeActionKind`s used to filter the code actions.
--- Most language servers support values like `refactor`
--- or `quickfix`.
--- - filter: (function|nil)
--- Predicate taking an `CodeAction` and returning a boolean.
--- - apply: (boolean|nil)
--- When set to `true`, and there is just one remaining action
--- (after filtering), the action is applied without user query.
---
--- - range: (table|nil)
--- Range for which code actions should be requested.
--- If in visual mode this defaults to the active selection.
--- Table must contain `start` and `end` keys with {row, col} tuples
--- using mark-like indexing. See |api-indexing|
---
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
function M.code_action(options)
validate({ options = { options, 't', true } })
Expand All @@ -870,7 +877,34 @@ function M.code_action(options)
local bufnr = api.nvim_get_current_buf()
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr)
end
local params = util.make_range_params()
local params
local mode = api.nvim_get_mode().mode
if options.range then
assert(type(options.range) == 'table', 'code_action range must be a table')
local start = assert(options.range.start, 'range must have a `start` property')
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 })
else
params = util.make_range_params()
end
params.context = context
code_action_request(params, options)
end
Expand All @@ -891,6 +925,7 @@ end
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
function M.range_code_action(context, start_pos, end_pos)
vim.deprecate('vim.lsp.buf.range_code_action', 'vim.lsp.buf.code_action', '0.9.0')
validate({ context = { context, 't', true } })
context = context or {}
if not context.diagnostics then
Expand Down
24 changes: 12 additions & 12 deletions runtime/lua/vim/lsp/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1821,7 +1821,7 @@ function M.try_trim_markdown_code_blocks(lines)
end

---@private
---@param window (optional, number): window handle or 0 for current, defaults to current
---@param window number|nil: window handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
local function make_position_param(window, offset_encoding)
window = window or 0
Expand All @@ -1841,7 +1841,7 @@ end

--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
---
---@param window (optional, number): window handle or 0 for current, defaults to current
---@param window number|nil: window handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
---@returns `TextDocumentPositionParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
Expand Down Expand Up @@ -1894,8 +1894,8 @@ end
--- `textDocument/codeAction`, `textDocument/colorPresentation`,
--- `textDocument/rangeFormatting`.
---
---@param window (optional, number): window handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
---@param window number|nil: window handle or 0 for current, defaults to current
---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of buffer of `window`
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`current_position`, end = `current_position` } }
function M.make_range_params(window, offset_encoding)
Expand All @@ -1911,12 +1911,12 @@ end
--- Using the given range in the current buffer, creates an object that
--- is similar to |vim.lsp.util.make_range_params()|.
---
---@param start_pos ({number, number}, optional) mark-indexed position.
---Defaults to the start of the last visual selection.
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
---@param bufnr (optional, number): buffer handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `bufnr`
---@param start_pos number[]|nil {row, col} mark-indexed position.
--- Defaults to the start of the last visual selection.
---@param end_pos number[]|nil {row, col} mark-indexed position.
--- Defaults to the end of the last visual selection.
---@param bufnr number|nil buffer handle or 0 for current, defaults to current
---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr`
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`start_position`, end = `end_position` } }
function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
Expand Down Expand Up @@ -1956,7 +1956,7 @@ end

--- Creates a `TextDocumentIdentifier` object for the current buffer.
---
---@param bufnr (optional, number): Buffer handle, defaults to current
---@param bufnr number|nil: Buffer handle, defaults to current
---@returns `TextDocumentIdentifier`
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier
function M.make_text_document_params(bufnr)
Expand Down Expand Up @@ -2000,7 +2000,7 @@ end

--- Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer.
---
---@param buf buffer id (0 for current)
---@param buf number buffer number (0 for current)
---@param row 0-indexed line
---@param col 0-indexed byte offset in line
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf`
Expand Down

0 comments on commit 463b903

Please sign in to comment.