Repository: neovim/neovim
File: runtime/lua/vim/lsp/buf.lua (Lua)

Code before:
  -- If the selection ends at column 0, adjust the position to the end of the previous line.
  if end_col == 0 then
    end_line = end_line - 1
    local end_line_text = api.nvim_buf_get_lines(0, end_line - 1, end_line, true)[1]
    end_col = #end_line_text
  end

  vim.fn.setpos("'<", { 0, start_line, start_col + 1, 0 })
  vim.fn.setpos("'>", { 0, end_line, end_col, 0 })
  vim.cmd.normal({ 'gv', bang = true })
end

---@param range lsp.Range
local function is_empty(range)
  return range.start.line == range['end'].line and range.start.character == range['end'].character
end

--- 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. `on_response` is called after the request is completed.
---
--- @param direction integer
--- @param on_response? fun(errors: table<integer, lsp.ResponseError>)
function M.selection_range(direction, on_response)
  validate('direction', direction, 'number')
  validate('callback', on_response, 'function', true)

  if selection_ranges then
    local new_index = selection_ranges.index + direction
    selection_ranges.index = math.min(#selection_ranges.ranges, math.max(1, new_index))

    select_range(selection_ranges.ranges[selection_ranges.index])
    if on_response then
      on_response({})
    end
    return
  end

  local method = 'textDocument/selectionRange'
  local client = lsp.get_clients({ method = method, bufnr = 0 })[1]
  if not client then
    vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN)
    if on_response then
      on_response({})
    end
    return
  end

  local position_params = util.make_position_params(0, client.offset_encoding)

  ---@type lsp.SelectionRangeParams

Review comment (suggestion):
```suggestion
  validate('on_response', on_response, 'function', true)
```

Code after:
  -- If the selection ends at column 0, adjust the position to the end of the previous line.
  if end_col == 0 then
    end_line = end_line - 1
    local end_line_text = api.nvim_buf_get_lines(0, end_line - 1, end_line, true)[1]
    end_col = #end_line_text
  end

  vim.fn.setpos("'<", { 0, start_line, start_col + 1, 0 })
  vim.fn.setpos("'>", { 0, end_line, end_col, 0 })
  vim.cmd.normal({ 'gv', bang = true })
end

---@param range lsp.Range
local function is_empty(range)
  return range.start.line == range['end'].line and range.start.character == range['end'].character
end

--- 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.
---
--- @param direction integer
--- @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
    selection_ranges.index = math.min(#selection_ranges.ranges, math.max(1, new_index))

    select_range(selection_ranges.ranges[selection_ranges.index])
    return
  end

  local method = 'textDocument/selectionRange'
  local client = lsp.get_clients({ method = method, bufnr = 0 })[1]
  if not client then
    vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN)
    return
  end

  local position_params = util.make_position_params(0, client.offset_encoding)

  ---@type lsp.SelectionRangeParams
  local params = {
    textDocument = position_params.textDocument,
    positions = { position_params.position },
  }

  timeout_ms = timeout_ms or 1000