mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	fix(lsp): strictly enforce passing offset encoding (#17049)
This removes the "fallback" to utf-16 in many of our helper functions. We should always explicitly pass these around when possible except in two locations: * generating params with help utilities called by buf.lua functions * the buf.lua functions themselves Anything that is called by the handler should be passed the offset encoding.
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							e7cd811567
						
					
				
				
					commit
					bc722c8a74
				
			@@ -503,7 +503,7 @@ local function on_code_action_results(results, ctx)
 | 
			
		||||
  ---@private
 | 
			
		||||
  local function apply_action(action, client)
 | 
			
		||||
    if action.edit then
 | 
			
		||||
      util.apply_workspace_edit(action.edit)
 | 
			
		||||
      util.apply_workspace_edit(action.edit, client.offset_encoding)
 | 
			
		||||
    end
 | 
			
		||||
    if action.command then
 | 
			
		||||
      local command = type(action.command) == 'table' and action.command or action
 | 
			
		||||
 
 | 
			
		||||
@@ -111,13 +111,15 @@ M['client/registerCapability'] = function(_, _, ctx)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
 | 
			
		||||
M['workspace/applyEdit'] = function(_, workspace_edit)
 | 
			
		||||
M['workspace/applyEdit'] = function(_, workspace_edit, ctx)
 | 
			
		||||
  if not workspace_edit then return end
 | 
			
		||||
  -- TODO(ashkan) Do something more with label?
 | 
			
		||||
  local client_id = ctx.client_id
 | 
			
		||||
  local client = vim.lsp.get_client_by_id(client_id)
 | 
			
		||||
  if workspace_edit.label then
 | 
			
		||||
    print("Workspace edit", workspace_edit.label)
 | 
			
		||||
  end
 | 
			
		||||
  local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit)
 | 
			
		||||
  local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
 | 
			
		||||
  return {
 | 
			
		||||
    applied = status;
 | 
			
		||||
    failureReason = result;
 | 
			
		||||
@@ -159,6 +161,28 @@ M['textDocument/codeLens'] = function(...)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
 | 
			
		||||
M['textDocument/references'] =function(_, result, ctx, config)
 | 
			
		||||
  if not result or vim.tbl_isempty(result) then
 | 
			
		||||
    vim.notify('No references found')
 | 
			
		||||
  else
 | 
			
		||||
    config = config or {}
 | 
			
		||||
    if config.loclist then
 | 
			
		||||
      vim.fn.setloclist(0, {}, ' ', {
 | 
			
		||||
        title = 'Language Server';
 | 
			
		||||
        items = util.locations_to_items(result, ctx.offset_encoding);
 | 
			
		||||
      })
 | 
			
		||||
      api.nvim_command("lopen")
 | 
			
		||||
    else
 | 
			
		||||
      vim.fn.setqflist({}, ' ', {
 | 
			
		||||
        title = 'Language Server';
 | 
			
		||||
        items = util.locations_to_items(result, ctx.offset_encoding);
 | 
			
		||||
      })
 | 
			
		||||
      api.nvim_command("botright copen")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---@private
 | 
			
		||||
--- Return a function that converts LSP responses to list items and opens the list
 | 
			
		||||
@@ -194,9 +218,6 @@ local function response_to_list(map_result, entity)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
 | 
			
		||||
M['textDocument/references'] = response_to_list(util.locations_to_items, 'references')
 | 
			
		||||
 | 
			
		||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
 | 
			
		||||
M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols')
 | 
			
		||||
 | 
			
		||||
@@ -277,19 +298,23 @@ local function location_handler(_, result, ctx, _)
 | 
			
		||||
    local _ = log.info() and log.info(ctx.method, 'No location found')
 | 
			
		||||
    return nil
 | 
			
		||||
  end
 | 
			
		||||
  local client = vim.lsp.get_client_by_id(ctx.client_id)
 | 
			
		||||
 | 
			
		||||
  -- textDocument/definition can return Location or Location[]
 | 
			
		||||
  -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
 | 
			
		||||
 | 
			
		||||
  if vim.tbl_islist(result) then
 | 
			
		||||
    util.jump_to_location(result[1])
 | 
			
		||||
    util.jump_to_location(result[1], client.offset_encoding)
 | 
			
		||||
 | 
			
		||||
    if #result > 1 then
 | 
			
		||||
      vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)})
 | 
			
		||||
      vim.fn.setqflist({}, ' ', {
 | 
			
		||||
        title = 'LSP locations',
 | 
			
		||||
        items = util.locations_to_items(result, client.offset_encoding)
 | 
			
		||||
      })
 | 
			
		||||
      api.nvim_command("copen")
 | 
			
		||||
    end
 | 
			
		||||
  else
 | 
			
		||||
    util.jump_to_location(result)
 | 
			
		||||
    util.jump_to_location(result, client.offset_encoding)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -277,7 +277,7 @@ end
 | 
			
		||||
---@private
 | 
			
		||||
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
 | 
			
		||||
--- Returns a zero-indexed column, since set_lines() does the conversion to
 | 
			
		||||
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
 | 
			
		||||
---@param offset_encoding string utf-8|utf-16|utf-32
 | 
			
		||||
--- 1-indexed
 | 
			
		||||
local function get_line_byte_from_position(bufnr, position, offset_encoding)
 | 
			
		||||
  -- LSP's line and characters are 0-indexed
 | 
			
		||||
@@ -360,15 +360,14 @@ end
 | 
			
		||||
--- Applies a list of text edits to a buffer.
 | 
			
		||||
---@param text_edits table list of `TextEdit` objects
 | 
			
		||||
---@param bufnr number Buffer id
 | 
			
		||||
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to encoding of first client of `bufnr`
 | 
			
		||||
---@param offset_encoding string utf-8|utf-16|utf-32 defaults to encoding of first client of `bufnr`
 | 
			
		||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
 | 
			
		||||
function M.apply_text_edits(text_edits, bufnr, offset_encoding)
 | 
			
		||||
  validate {
 | 
			
		||||
    text_edits = { text_edits, 't', false };
 | 
			
		||||
    bufnr = { bufnr, 'number', false };
 | 
			
		||||
    offset_encoding = { offset_encoding, 'string', true };
 | 
			
		||||
    offset_encoding = { offset_encoding, 'string', false };
 | 
			
		||||
  }
 | 
			
		||||
  offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
 | 
			
		||||
  if not next(text_edits) then return end
 | 
			
		||||
  if not api.nvim_buf_is_loaded(bufnr) then
 | 
			
		||||
    vim.fn.bufload(bufnr)
 | 
			
		||||
@@ -517,9 +516,12 @@ end
 | 
			
		||||
---@param text_document_edit table: a `TextDocumentEdit` object
 | 
			
		||||
---@param index number: Optional index of the edit, if from a list of edits (or nil, if not from a list)
 | 
			
		||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
 | 
			
		||||
function M.apply_text_document_edit(text_document_edit, index)
 | 
			
		||||
function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
 | 
			
		||||
  local text_document = text_document_edit.textDocument
 | 
			
		||||
  local bufnr = vim.uri_to_bufnr(text_document.uri)
 | 
			
		||||
  if offset_encoding == nil then
 | 
			
		||||
    vim.notify_once("apply_text_document_edit must be called with valid offset encoding", vim.log.levels.WARN)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  -- For lists of text document edits,
 | 
			
		||||
  -- do not check the version after the first edit.
 | 
			
		||||
@@ -538,7 +540,7 @@ function M.apply_text_document_edit(text_document_edit, index)
 | 
			
		||||
    return
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  M.apply_text_edits(text_document_edit.edits, bufnr)
 | 
			
		||||
  M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Parses snippets in a completion entry.
 | 
			
		||||
@@ -737,7 +739,10 @@ end
 | 
			
		||||
---
 | 
			
		||||
---@param workspace_edit (table) `WorkspaceEdit`
 | 
			
		||||
--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
 | 
			
		||||
function M.apply_workspace_edit(workspace_edit)
 | 
			
		||||
function M.apply_workspace_edit(workspace_edit, offset_encoding)
 | 
			
		||||
  if offset_encoding == nil then
 | 
			
		||||
    vim.notify_once("apply_workspace_edit must be called with valid offset encoding", vim.log.levels.WARN)
 | 
			
		||||
  end
 | 
			
		||||
  if workspace_edit.documentChanges then
 | 
			
		||||
    for idx, change in ipairs(workspace_edit.documentChanges) do
 | 
			
		||||
      if change.kind == "rename" then
 | 
			
		||||
@@ -753,7 +758,7 @@ function M.apply_workspace_edit(workspace_edit)
 | 
			
		||||
      elseif change.kind then
 | 
			
		||||
        error(string.format("Unsupported change: %q", vim.inspect(change)))
 | 
			
		||||
      else
 | 
			
		||||
        M.apply_text_document_edit(change, idx)
 | 
			
		||||
        M.apply_text_document_edit(change, idx, offset_encoding)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    return
 | 
			
		||||
@@ -984,12 +989,16 @@ end
 | 
			
		||||
 | 
			
		||||
--- Jumps to a location.
 | 
			
		||||
---
 | 
			
		||||
---@param location (`Location`|`LocationLink`)
 | 
			
		||||
---@param location table (`Location`|`LocationLink`)
 | 
			
		||||
---@param offset_encoding string utf-8|utf-16|utf-32 (required)
 | 
			
		||||
---@returns `true` if the jump succeeded
 | 
			
		||||
function M.jump_to_location(location)
 | 
			
		||||
function M.jump_to_location(location, offset_encoding)
 | 
			
		||||
  -- location may be Location or LocationLink
 | 
			
		||||
  local uri = location.uri or location.targetUri
 | 
			
		||||
  if uri == nil then return end
 | 
			
		||||
  if offset_encoding == nil then
 | 
			
		||||
    vim.notify_once("jump_to_location must be called with valid offset encoding", vim.log.levels.WARN)
 | 
			
		||||
  end
 | 
			
		||||
  local bufnr = vim.uri_to_bufnr(uri)
 | 
			
		||||
  -- Save position in jumplist
 | 
			
		||||
  vim.cmd "normal! m'"
 | 
			
		||||
@@ -1004,7 +1013,7 @@ function M.jump_to_location(location)
 | 
			
		||||
  api.nvim_buf_set_option(bufnr, 'buflisted', true)
 | 
			
		||||
  local range = location.range or location.targetSelectionRange
 | 
			
		||||
  local row = range.start.line
 | 
			
		||||
  local col = get_line_byte_from_position(bufnr, range.start)
 | 
			
		||||
  local col = get_line_byte_from_position(bufnr, range.start, offset_encoding)
 | 
			
		||||
  api.nvim_win_set_cursor(0, {row + 1, col})
 | 
			
		||||
  -- Open folds under the cursor
 | 
			
		||||
  vim.cmd("normal! zv")
 | 
			
		||||
@@ -1513,11 +1522,13 @@ do --[[ References ]]
 | 
			
		||||
  ---
 | 
			
		||||
  ---@param bufnr number Buffer id
 | 
			
		||||
  ---@param references table List of `DocumentHighlight` objects to highlight
 | 
			
		||||
  ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to `offset_encoding` of first client of `bufnr`
 | 
			
		||||
  ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32".
 | 
			
		||||
  ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
 | 
			
		||||
  function M.buf_highlight_references(bufnr, references, offset_encoding)
 | 
			
		||||
    validate { bufnr = {bufnr, 'n', true} }
 | 
			
		||||
    offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
 | 
			
		||||
    validate {
 | 
			
		||||
      bufnr = {bufnr, 'n', true},
 | 
			
		||||
      offset_encoding = { offset_encoding, 'string', false };
 | 
			
		||||
    }
 | 
			
		||||
    for _, reference in ipairs(references) do
 | 
			
		||||
      local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"]
 | 
			
		||||
      local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"]
 | 
			
		||||
@@ -1550,9 +1561,14 @@ end)
 | 
			
		||||
--- The result can be passed to the {list} argument of |setqflist()| or
 | 
			
		||||
--- |setloclist()|.
 | 
			
		||||
---
 | 
			
		||||
---@param locations (table) list of `Location`s or `LocationLink`s
 | 
			
		||||
---@param locations table list of `Location`s or `LocationLink`s
 | 
			
		||||
---@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32
 | 
			
		||||
---@returns (table) list of items
 | 
			
		||||
function M.locations_to_items(locations)
 | 
			
		||||
function M.locations_to_items(locations, offset_encoding)
 | 
			
		||||
  if offset_encoding == nil then
 | 
			
		||||
    vim.notify_once("locations_to_items must be called with valid offset encoding", vim.log.levels.WARN)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local items = {}
 | 
			
		||||
  local grouped = setmetatable({}, {
 | 
			
		||||
    __index = function(t, k)
 | 
			
		||||
@@ -1592,7 +1608,7 @@ function M.locations_to_items(locations)
 | 
			
		||||
      local pos = temp.start
 | 
			
		||||
      local row = pos.line
 | 
			
		||||
      local line = lines[row] or ""
 | 
			
		||||
      local col = pos.character
 | 
			
		||||
      local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
 | 
			
		||||
      table.insert(items, {
 | 
			
		||||
        filename = filename,
 | 
			
		||||
        lnum = row + 1,
 | 
			
		||||
@@ -1776,7 +1792,13 @@ function M._get_offset_encoding(bufnr)
 | 
			
		||||
  local offset_encoding
 | 
			
		||||
 | 
			
		||||
  for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do
 | 
			
		||||
    local this_offset_encoding = client.offset_encoding or "utf-16"
 | 
			
		||||
    if client.offset_encoding == nil then
 | 
			
		||||
      vim.notify_once(
 | 
			
		||||
        string.format("Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.", client.id),
 | 
			
		||||
        vim.log.levels.ERROR
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
    local this_offset_encoding = client.offset_encoding
 | 
			
		||||
    if not offset_encoding then
 | 
			
		||||
      offset_encoding = this_offset_encoding
 | 
			
		||||
    elseif offset_encoding ~= this_offset_encoding then
 | 
			
		||||
@@ -1905,7 +1927,9 @@ end
 | 
			
		||||
---@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf}
 | 
			
		||||
function M.character_offset(buf, row, col, offset_encoding)
 | 
			
		||||
  local line = get_line(buf, row)
 | 
			
		||||
  offset_encoding = offset_encoding or M._get_offset_encoding(buf)
 | 
			
		||||
  if offset_encoding == nil then
 | 
			
		||||
    vim.notify_once("character_offset must be called with valid offset encoding", vim.log.levels.WARN)
 | 
			
		||||
  end
 | 
			
		||||
  -- If the col is past the EOL, use the line length.
 | 
			
		||||
  if col > #line then
 | 
			
		||||
    return _str_utfindex_enc(line, nil, offset_encoding)
 | 
			
		||||
 
 | 
			
		||||
@@ -1291,7 +1291,7 @@ describe('LSP', function()
 | 
			
		||||
        make_edit(2, 0, 2, 2, {"3"});
 | 
			
		||||
        make_edit(3, 2, 3, 4, {""});
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
      eq({
 | 
			
		||||
        '123First line of text';
 | 
			
		||||
        '2econd line of text';
 | 
			
		||||
@@ -1311,7 +1311,7 @@ describe('LSP', function()
 | 
			
		||||
        make_edit(3, #'', 3, #"Fourth", {"another line of text", "before this"});
 | 
			
		||||
        make_edit(3, #'Fourth', 3, #"Fourth line of text", {"!"});
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
      eq({
 | 
			
		||||
        '';
 | 
			
		||||
        '123';
 | 
			
		||||
@@ -1335,7 +1335,7 @@ describe('LSP', function()
 | 
			
		||||
        make_edit(3, #"Fourth", 3, #'', {"another line of text", "before this"});
 | 
			
		||||
        make_edit(3, #"Fourth line of text", 3, #'Fourth', {"!"});
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
      eq({
 | 
			
		||||
        '';
 | 
			
		||||
        '123';
 | 
			
		||||
@@ -1352,7 +1352,7 @@ describe('LSP', function()
 | 
			
		||||
      local edits = {
 | 
			
		||||
        make_edit(4, 3, 4, 4, {"ä"});
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
      eq({
 | 
			
		||||
        'First line of text';
 | 
			
		||||
        'Second line of text';
 | 
			
		||||
@@ -1365,7 +1365,7 @@ describe('LSP', function()
 | 
			
		||||
      local edits = {
 | 
			
		||||
        make_edit(5, 0, 5, 0, "foobar");
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
      eq({
 | 
			
		||||
        'First line of text';
 | 
			
		||||
        'Second line of text';
 | 
			
		||||
@@ -1380,7 +1380,7 @@ describe('LSP', function()
 | 
			
		||||
        make_edit(4, 0, 5, 0, "");
 | 
			
		||||
        make_edit(5, 0, 5, 0, "foobar");
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
      eq({
 | 
			
		||||
        'First line of text';
 | 
			
		||||
        'Second line of text';
 | 
			
		||||
@@ -1396,7 +1396,7 @@ describe('LSP', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(1, 0, 1, 19, 'Second line of text')
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({
 | 
			
		||||
          'First line of text';
 | 
			
		||||
          'Second line of text';
 | 
			
		||||
@@ -1413,7 +1413,7 @@ describe('LSP', function()
 | 
			
		||||
          make_edit(1, 0, 1, 6, ''),
 | 
			
		||||
          make_edit(1, 6, 1, 19, '')
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({
 | 
			
		||||
          'First line of text';
 | 
			
		||||
          '';
 | 
			
		||||
@@ -1430,7 +1430,7 @@ describe('LSP', function()
 | 
			
		||||
          make_edit(1, 0, 1, 6, ''),
 | 
			
		||||
          make_edit(0, 18, 5, 0, '')
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({
 | 
			
		||||
          'First line of text';
 | 
			
		||||
        }, buf_lines(1))
 | 
			
		||||
@@ -1442,7 +1442,7 @@ describe('LSP', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(1, 0, 2, 0, '')
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({
 | 
			
		||||
          'First line of text';
 | 
			
		||||
          'Third line of text';
 | 
			
		||||
@@ -1457,7 +1457,7 @@ describe('LSP', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(1, 7, 1, 11, '')
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({
 | 
			
		||||
          'First line of text';
 | 
			
		||||
          'Second  of text';
 | 
			
		||||
@@ -1473,7 +1473,7 @@ describe('LSP', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(0, 11, 1, 12, '')
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({
 | 
			
		||||
          'First line of text';
 | 
			
		||||
          'Third line of text';
 | 
			
		||||
@@ -1489,21 +1489,21 @@ describe('LSP', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(0, 0, 5, 0, {"All replaced"});
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({'All replaced'}, buf_lines(1))
 | 
			
		||||
      end)
 | 
			
		||||
      it('applies edits when the end line is 2 larger than vim\'s', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(0, 0, 6, 0, {"All replaced"});
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({'All replaced'}, buf_lines(1))
 | 
			
		||||
      end)
 | 
			
		||||
      it('applies edits with a column offset', function()
 | 
			
		||||
        local edits = {
 | 
			
		||||
          make_edit(0, 0, 5, 2, {"All replaced"});
 | 
			
		||||
        }
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
 | 
			
		||||
        exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16")
 | 
			
		||||
        eq({'All replaced'}, buf_lines(1))
 | 
			
		||||
      end)
 | 
			
		||||
    end)
 | 
			
		||||
@@ -1531,7 +1531,7 @@ describe('LSP', function()
 | 
			
		||||
      ]]
 | 
			
		||||
    end)
 | 
			
		||||
    it('correctly goes ahead with the edit if all is normal', function()
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(5))
 | 
			
		||||
      exec_lua("vim.lsp.util.apply_text_document_edit(..., nil, 'utf-16')", text_document_edit(5))
 | 
			
		||||
      eq({
 | 
			
		||||
        'First ↥ 🤦 🦄 line of text';
 | 
			
		||||
        '2nd line of 语text';
 | 
			
		||||
@@ -1543,7 +1543,7 @@ describe('LSP', function()
 | 
			
		||||
        local bufnr = select(1, ...)
 | 
			
		||||
        local text_edit = select(2, ...)
 | 
			
		||||
        vim.lsp.util.buf_versions[bufnr] = 10
 | 
			
		||||
        vim.lsp.util.apply_text_document_edit(text_edit)
 | 
			
		||||
        vim.lsp.util.apply_text_document_edit(text_edit, nil, 'utf-16')
 | 
			
		||||
      ]], target_bufnr, text_document_edit(0))
 | 
			
		||||
      eq({
 | 
			
		||||
        'First ↥ 🤦 🦄 line of text';
 | 
			
		||||
@@ -1556,7 +1556,7 @@ describe('LSP', function()
 | 
			
		||||
          local args = {...}
 | 
			
		||||
          local versionedBuf = args[2]
 | 
			
		||||
          vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion
 | 
			
		||||
          vim.lsp.util.apply_text_document_edit(args[1])
 | 
			
		||||
          vim.lsp.util.apply_text_document_edit(args[1], nil, 'utf-16')
 | 
			
		||||
        ]], edit, versionedBuf)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@@ -1582,17 +1582,36 @@ describe('LSP', function()
 | 
			
		||||
 | 
			
		||||
  describe('workspace_apply_edit', function()
 | 
			
		||||
    it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function()
 | 
			
		||||
      local expected = {
 | 
			
		||||
        applied = true;
 | 
			
		||||
        failureReason = nil;
 | 
			
		||||
      local expected_handlers = {
 | 
			
		||||
        {NIL, {}, {method="test", client_id=1}};
 | 
			
		||||
      }
 | 
			
		||||
      test_rpc_server {
 | 
			
		||||
        test_name = "basic_init";
 | 
			
		||||
        on_init = function(client, _)
 | 
			
		||||
          client.stop()
 | 
			
		||||
        end;
 | 
			
		||||
        -- If the program timed out, then code will be nil.
 | 
			
		||||
        on_exit = function(code, signal)
 | 
			
		||||
          eq(0, code, "exit code", fake_lsp_logfile)
 | 
			
		||||
          eq(0, signal, "exit signal", fake_lsp_logfile)
 | 
			
		||||
        end;
 | 
			
		||||
        -- Note that NIL must be used here.
 | 
			
		||||
        -- on_handler(err, method, result, client_id)
 | 
			
		||||
        on_handler = function(...)
 | 
			
		||||
          local expected = {
 | 
			
		||||
            applied = true;
 | 
			
		||||
            failureReason = nil;
 | 
			
		||||
          }
 | 
			
		||||
          eq(expected, exec_lua [[
 | 
			
		||||
            local apply_edit = {
 | 
			
		||||
              label = nil;
 | 
			
		||||
              edit = {};
 | 
			
		||||
            }
 | 
			
		||||
            return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit, {client_id = TEST_RPC_CLIENT_ID})
 | 
			
		||||
          ]])
 | 
			
		||||
          eq(table.remove(expected_handlers), {...})
 | 
			
		||||
        end;
 | 
			
		||||
      }
 | 
			
		||||
      eq(expected, exec_lua [[
 | 
			
		||||
        local apply_edit = {
 | 
			
		||||
          label = nil;
 | 
			
		||||
          edit = {};
 | 
			
		||||
        }
 | 
			
		||||
        return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit)
 | 
			
		||||
      ]])
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
@@ -1666,7 +1685,7 @@ describe('LSP', function()
 | 
			
		||||
        local workspace_edits = args[1]
 | 
			
		||||
        local target_bufnr = args[2]
 | 
			
		||||
 | 
			
		||||
        vim.lsp.util.apply_workspace_edit(workspace_edits)
 | 
			
		||||
        vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16')
 | 
			
		||||
 | 
			
		||||
        return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
 | 
			
		||||
      ]], make_workspace_edit(edits), target_bufnr))
 | 
			
		||||
@@ -1688,7 +1707,7 @@ describe('LSP', function()
 | 
			
		||||
        local workspace_edits = args[1]
 | 
			
		||||
        local target_bufnr = args[2]
 | 
			
		||||
 | 
			
		||||
        vim.lsp.util.apply_workspace_edit(workspace_edits)
 | 
			
		||||
        vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16')
 | 
			
		||||
 | 
			
		||||
        return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
 | 
			
		||||
      ]], make_workspace_edit(edits), target_bufnr))
 | 
			
		||||
@@ -1705,7 +1724,7 @@ describe('LSP', function()
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
 | 
			
		||||
      eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
 | 
			
		||||
    end)
 | 
			
		||||
    it('createFile does not touch file if it exists and ignoreIfExists is set', function()
 | 
			
		||||
@@ -1723,7 +1742,7 @@ describe('LSP', function()
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
 | 
			
		||||
      eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
 | 
			
		||||
      eq('Dummy content', read_file(tmpfile))
 | 
			
		||||
    end)
 | 
			
		||||
@@ -1743,7 +1762,7 @@ describe('LSP', function()
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
 | 
			
		||||
      exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
 | 
			
		||||
      eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
 | 
			
		||||
      eq('', read_file(tmpfile))
 | 
			
		||||
    end)
 | 
			
		||||
@@ -1764,7 +1783,7 @@ describe('LSP', function()
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit))
 | 
			
		||||
      eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16'))
 | 
			
		||||
      eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
 | 
			
		||||
      eq(false, exec_lua('return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))', tmpfile))
 | 
			
		||||
    end)
 | 
			
		||||
@@ -1925,7 +1944,7 @@ describe('LSP', function()
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
        return vim.lsp.util.locations_to_items(locations)
 | 
			
		||||
        return vim.lsp.util.locations_to_items(locations, 'utf-16')
 | 
			
		||||
      ]]
 | 
			
		||||
      eq(expected, actual)
 | 
			
		||||
    end)
 | 
			
		||||
@@ -1955,7 +1974,7 @@ describe('LSP', function()
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
        return vim.lsp.util.locations_to_items(locations)
 | 
			
		||||
        return vim.lsp.util.locations_to_items(locations, 'utf-16')
 | 
			
		||||
      ]]
 | 
			
		||||
      eq(expected, actual)
 | 
			
		||||
    end)
 | 
			
		||||
@@ -2259,7 +2278,7 @@ describe('LSP', function()
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    local jump = function(msg)
 | 
			
		||||
      eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg))
 | 
			
		||||
      eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg, "utf-16"))
 | 
			
		||||
      eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]])
 | 
			
		||||
      return {
 | 
			
		||||
        line = exec_lua[[return vim.fn.line('.')]],
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user