mirror of
https://github.com/neovim/neovim.git
synced 2025-12-14 02:22:49 +00:00
feat(lsp): incremental-selection operator-pending mode #36575
Problem: LSP incremental selection provides default visual-mode keymaps for `an` and `in`. Operator-pending mode is not supported, so `dan` and `can` do not apply the operation. Solution: Modify selection_range() to be synchronous. Add operator-pending mappings.
This commit is contained in:
@@ -83,7 +83,7 @@ These GLOBAL keymaps are created unconditionally when Nvim starts:
|
|||||||
- "grt" is mapped to |vim.lsp.buf.type_definition()|
|
- "grt" is mapped to |vim.lsp.buf.type_definition()|
|
||||||
- "gO" is mapped to |vim.lsp.buf.document_symbol()|
|
- "gO" is mapped to |vim.lsp.buf.document_symbol()|
|
||||||
- CTRL-S (Insert mode) is mapped to |vim.lsp.buf.signature_help()|
|
- CTRL-S (Insert mode) is mapped to |vim.lsp.buf.signature_help()|
|
||||||
- "an" and "in" (Visual mode) are mapped to outer and inner incremental
|
- "an" and "in" (Visual and Operator-pending mode) are mapped to outer and inner incremental
|
||||||
selections, respectively, using |vim.lsp.buf.selection_range()|
|
selections, respectively, using |vim.lsp.buf.selection_range()|
|
||||||
|
|
||||||
BUFFER-LOCAL DEFAULTS
|
BUFFER-LOCAL DEFAULTS
|
||||||
@@ -1512,13 +1512,16 @@ rename({new_name}, {opts}) *vim.lsp.buf.rename()*
|
|||||||
ones where client.name matches this field.
|
ones where client.name matches this field.
|
||||||
• {bufnr}? (`integer`) (default: current buffer)
|
• {bufnr}? (`integer`) (default: current buffer)
|
||||||
|
|
||||||
selection_range({direction}) *vim.lsp.buf.selection_range()*
|
*vim.lsp.buf.selection_range()*
|
||||||
|
selection_range({direction}, {timeout_ms})
|
||||||
Perform an incremental selection at the cursor position based on ranges
|
Perform an incremental selection at the cursor position based on ranges
|
||||||
given by the LSP. The `direction` parameter specifies the number of times
|
given by the LSP. The `direction` parameter specifies the number of times
|
||||||
to expand the selection. Negative values will shrink the selection.
|
to expand the selection. Negative values will shrink the selection.
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
• {direction} (`integer`)
|
• {direction} (`integer`)
|
||||||
|
• {timeout_ms} (`integer?`) (default: `1000`) Maximum time
|
||||||
|
(milliseconds) to wait for a result.
|
||||||
|
|
||||||
signature_help({config}) *vim.lsp.buf.signature_help()*
|
signature_help({config}) *vim.lsp.buf.signature_help()*
|
||||||
Displays signature information about the symbol under the cursor in a
|
Displays signature information about the symbol under the cursor in a
|
||||||
|
|||||||
@@ -227,6 +227,14 @@ do
|
|||||||
vim.lsp.buf.selection_range(-vim.v.count1)
|
vim.lsp.buf.selection_range(-vim.v.count1)
|
||||||
end, { desc = 'vim.lsp.buf.selection_range(-vim.v.count1)' })
|
end, { desc = 'vim.lsp.buf.selection_range(-vim.v.count1)' })
|
||||||
|
|
||||||
|
vim.keymap.set('o', 'an', function()
|
||||||
|
vim.lsp.buf.selection_range(vim.v.count1, 1000)
|
||||||
|
end, { desc = 'vim.lsp.buf.selection_range(vim.v.count1, timeout_ms)' })
|
||||||
|
|
||||||
|
vim.keymap.set('o', 'in', function()
|
||||||
|
vim.lsp.buf.selection_range(-vim.v.count1, 1000)
|
||||||
|
end, { desc = 'vim.lsp.buf.selection_range(-vim.v.count1, timeout_ms)' })
|
||||||
|
|
||||||
vim.keymap.set('n', 'gO', function()
|
vim.keymap.set('n', 'gO', function()
|
||||||
vim.lsp.buf.document_symbol()
|
vim.lsp.buf.document_symbol()
|
||||||
end, { desc = 'vim.lsp.buf.document_symbol()' })
|
end, { desc = 'vim.lsp.buf.document_symbol()' })
|
||||||
|
|||||||
@@ -1408,8 +1408,10 @@ end
|
|||||||
--- will shrink the selection.
|
--- will shrink the selection.
|
||||||
---
|
---
|
||||||
--- @param direction integer
|
--- @param direction integer
|
||||||
function M.selection_range(direction)
|
--- @param timeout_ms integer? (default: `1000`) Maximum time (milliseconds) to wait for a result.
|
||||||
|
function M.selection_range(direction, timeout_ms)
|
||||||
validate('direction', direction, 'number')
|
validate('direction', direction, 'number')
|
||||||
|
validate('timeout_ms', timeout_ms, 'number', true)
|
||||||
|
|
||||||
if selection_ranges then
|
if selection_ranges then
|
||||||
local new_index = selection_ranges.index + direction
|
local new_index = selection_ranges.index + direction
|
||||||
@@ -1434,63 +1436,61 @@ function M.selection_range(direction)
|
|||||||
positions = { position_params.position },
|
positions = { position_params.position },
|
||||||
}
|
}
|
||||||
|
|
||||||
lsp.buf_request(
|
timeout_ms = timeout_ms or 1000
|
||||||
0,
|
local result, err = lsp.buf_request_sync(0, method, params, timeout_ms)
|
||||||
method,
|
if err then
|
||||||
params,
|
lsp.log.error('selectionRange request failed: ' .. err)
|
||||||
---@param response lsp.SelectionRange[]?
|
return
|
||||||
function(err, response)
|
end
|
||||||
if err then
|
if not result or not result[client.id] or not result[client.id].result then
|
||||||
lsp.log.error(err.code, err.message)
|
return
|
||||||
return
|
end
|
||||||
end
|
if result[client.id].error then
|
||||||
if not response then
|
lsp.log.error(result[client.id].error.code, result[client.id].error.message)
|
||||||
return
|
end
|
||||||
end
|
|
||||||
-- We only requested one range, thus we get the first and only response here.
|
|
||||||
response = response[1]
|
|
||||||
local ranges = {} ---@type lsp.Range[]
|
|
||||||
local lines = api.nvim_buf_get_lines(0, 0, -1, false)
|
|
||||||
|
|
||||||
-- Populate the list of ranges from the given request.
|
-- We only requested one range, thus we get the first and only reponse here.
|
||||||
while response do
|
local response = assert(result[client.id].result[1]) ---@type lsp.SelectionRange
|
||||||
local range = response.range
|
local ranges = {} ---@type lsp.Range[]
|
||||||
if not is_empty(range) then
|
local lines = api.nvim_buf_get_lines(0, 0, -1, false)
|
||||||
local start_line = range.start.line
|
|
||||||
local end_line = range['end'].line
|
|
||||||
range.start.character = vim.str_byteindex(
|
|
||||||
lines[start_line + 1] or '',
|
|
||||||
client.offset_encoding,
|
|
||||||
range.start.character,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
range['end'].character = vim.str_byteindex(
|
|
||||||
lines[end_line + 1] or '',
|
|
||||||
client.offset_encoding,
|
|
||||||
range['end'].character,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
ranges[#ranges + 1] = range
|
|
||||||
end
|
|
||||||
response = response.parent
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clear selection ranges when leaving visual mode.
|
-- Populate the list of ranges from the given request.
|
||||||
api.nvim_create_autocmd('ModeChanged', {
|
while response do
|
||||||
once = true,
|
local range = response.range
|
||||||
pattern = 'v*:*',
|
if not is_empty(range) then
|
||||||
callback = function()
|
local start_line = range.start.line
|
||||||
selection_ranges = nil
|
local end_line = range['end'].line
|
||||||
end,
|
range.start.character = vim.str_byteindex(
|
||||||
})
|
lines[start_line + 1] or '',
|
||||||
|
client.offset_encoding,
|
||||||
if #ranges > 0 then
|
range.start.character,
|
||||||
local index = math.min(#ranges, math.max(1, direction))
|
false
|
||||||
selection_ranges = { index = index, ranges = ranges }
|
)
|
||||||
select_range(ranges[index])
|
range['end'].character = vim.str_byteindex(
|
||||||
end
|
lines[end_line + 1] or '',
|
||||||
|
client.offset_encoding,
|
||||||
|
range['end'].character,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
ranges[#ranges + 1] = range
|
||||||
end
|
end
|
||||||
)
|
response = response.parent
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear selection ranges when leaving visual mode.
|
||||||
|
api.nvim_create_autocmd('ModeChanged', {
|
||||||
|
once = true,
|
||||||
|
pattern = 'v*:*',
|
||||||
|
callback = function()
|
||||||
|
selection_ranges = nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
if #ranges > 0 then
|
||||||
|
local index = math.min(#ranges, math.max(1, direction))
|
||||||
|
selection_ranges = { index = index, ranges = ranges }
|
||||||
|
select_range(ranges[index])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
Reference in New Issue
Block a user