diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index acf08dee90..c0e7b8a762 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -83,7 +83,7 @@ These GLOBAL keymaps are created unconditionally when Nvim starts: - "grt" is mapped to |vim.lsp.buf.type_definition()| - "gO" is mapped to |vim.lsp.buf.document_symbol()| - 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()| BUFFER-LOCAL DEFAULTS @@ -1512,13 +1512,16 @@ rename({new_name}, {opts}) *vim.lsp.buf.rename()* ones where client.name matches this field. • {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 given by the LSP. The `direction` parameter specifies the number of times to expand the selection. Negative values will shrink the selection. 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()* Displays signature information about the symbol under the cursor in a diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 1d3648ce4a..ddec0695f7 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -227,6 +227,14 @@ do 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.lsp.buf.document_symbol() end, { desc = 'vim.lsp.buf.document_symbol()' }) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 411f44b7fe..6f3f91e7a6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1408,8 +1408,10 @@ end --- will shrink the selection. --- --- @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('timeout_ms', timeout_ms, 'number', true) if selection_ranges then local new_index = selection_ranges.index + direction @@ -1434,63 +1436,61 @@ function M.selection_range(direction) positions = { position_params.position }, } - lsp.buf_request( - 0, - method, - params, - ---@param response lsp.SelectionRange[]? - function(err, response) - if err then - lsp.log.error(err.code, err.message) - return - end - if not response then - return - 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) + timeout_ms = timeout_ms or 1000 + local result, err = lsp.buf_request_sync(0, method, params, timeout_ms) + if err then + lsp.log.error('selectionRange request failed: ' .. err) + return + end + if not result or not result[client.id] or not result[client.id].result then + return + end + if result[client.id].error then + lsp.log.error(result[client.id].error.code, result[client.id].error.message) + end - -- Populate the list of ranges from the given request. - while response do - local range = response.range - if not is_empty(range) then - 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 + -- We only requested one range, thus we get the first and only reponse here. + local response = assert(result[client.id].result[1]) ---@type lsp.SelectionRange + local ranges = {} ---@type lsp.Range[] + local lines = api.nvim_buf_get_lines(0, 0, -1, false) - -- 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 + -- Populate the list of ranges from the given request. + while response do + local range = response.range + if not is_empty(range) then + 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. + 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 return M