local protocol = require 'vim.lsp.protocol'
local vim = vim
local validate = vim.validate
local api = vim.api
local list_extend = vim.list_extend
local highlight = require 'vim.highlight'
local M = {}
--- Diagnostics received from the server via `textDocument/publishDiagnostics`
-- by buffer.
--
-- {<bufnr>: {diagnostics}}
--
-- This contains only entries for active buffers. Entries for detached buffers
-- are discarded.
--
-- If you override the `textDocument/publishDiagnostic` callback,
-- this will be empty unless you call `buf_diagnostics_save_positions`.
--
--
-- Diagnostic is:
--
-- {
-- range: Range
-- message: string
-- severity?: DiagnosticSeverity
-- code?: number | string
-- source?: string
-- tags?: DiagnosticTag[]
-- relatedInformation?: DiagnosticRelatedInformation[]
-- }
M.diagnostics_by_buf = {}
local split = vim.split
local function split_lines(value)
return split(value, '\n', true)
end
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
function M.set_lines(lines, A, B, new_lines)
-- 0-indexing to 1-indexing
local i_0 = A[1] + 1
-- If it extends past the end, truncate it to the end. This is because the
-- way the LSP describes the range including the last newline is by
-- specifying a line number after what we would call the last line.
local i_n = math.min(B[1] + 1, #lines)
if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then
error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines})
end
local prefix = ""
local suffix = lines[i_n]:sub(B[2]+1)
if A[2] > 0 then
prefix = lines[i_0]:sub(1, A[2])
end
local n = i_n - i_0 + 1
if n ~= #new_lines then
for _ = 1, n - #new_lines do table.remove(lines, i_0) end
for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end
end
for i = 1, #new_lines do
lines[i - 1 + i_0] = new_lines[i]
end
if #suffix > 0 then
local i = i_0 + #new_lines - 1
lines[i] = lines[i]..suffix
end
if #prefix > 0 then
lines[i_0] = prefix..lines[i_0]
end
return lines
end
local function sort_by_key(fn)
return function(a,b)
local ka, kb = fn(a), fn(b)
assert(#ka == #kb)
for i = 1, #ka do
if ka[i] ~= kb[i] then
return ka[i] < kb[i]
end
end
-- every value must have been equal here, which means it's not less than.
return false
end
end
local edit_sort_key = sort_by_key(function(e)
return {e.A[1], e.A[2], e.i}
end)
--- 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
-- 1-indexed
local function get_line_byte_from_position(bufnr, position)
-- LSP's line and characters are 0-indexed
-- Vim's line and columns are 1-indexed
local col = position.character
-- When on the first character, we can ignore the difference between byte and
-- character
if col > 0 then
local line = position.line
local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false)
if #lines > 0 then
return vim.str_byteindex(lines[1], col)
end
end
return col
end
function M.apply_text_edits(text_edits, bufnr)
if not next(text_edits) then return end
if not
|