diff options
| author | Branden Call <54908229+brandencall@users.noreply.github.com> | 2025-11-24 18:10:50 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-24 17:10:50 -0800 |
| commit | e82aef2e22a57688dcc19a978cbe083349ad8a2a (patch) | |
| tree | 17c48b580f38791491578d1143cc5808f59b1770 /runtime/lua/vim | |
| parent | 60c35cc4c7b713c27e8bfdd196cbee46cf050bbb (diff) | |
| download | rneovim-e82aef2e22a57688dcc19a978cbe083349ad8a2a.tar.gz rneovim-e82aef2e22a57688dcc19a978cbe083349ad8a2a.tar.bz2 rneovim-e82aef2e22a57688dcc19a978cbe083349ad8a2a.zip | |
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.
Diffstat (limited to 'runtime/lua/vim')
| -rw-r--r-- | runtime/lua/vim/_defaults.lua | 8 | ||||
| -rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 112 |
2 files changed, 64 insertions, 56 deletions
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) - - -- 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, - }) + 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 - if #ranges > 0 then - local index = math.min(#ranges, math.max(1, direction)) - selection_ranges = { index = index, ranges = ranges } - select_range(ranges[index]) - 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) + + -- 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 |