aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/util.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp/util.lua')
-rw-r--r--runtime/lua/vim/lsp/util.lua402
1 files changed, 101 insertions, 301 deletions
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 9ed19b938d..3deec6d74e 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -5,50 +5,32 @@ local api = vim.api
local list_extend = vim.list_extend
local highlight = require 'vim.highlight'
+local npcall = vim.F.npcall
+local split = vim.split
+
+local _warned = {}
+local warn_once = function(message)
+ if not _warned[message] then
+ vim.api.nvim_err_writeln(message)
+ _warned[message] = true
+ end
+end
+
local M = {}
--- FIXME: DOC: Expose in vimdocs
---- 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 = {}
+-- TODO(remove-callbacks)
+M.diagnostics_by_buf = setmetatable({}, {
+ __index = function(_, bufnr)
+ warn_once("diagnostics_by_buf is deprecated. Use 'vim.lsp.diagnostic.get'")
+ return vim.lsp.diagnostic.get(bufnr)
+ end
+})
-local split = vim.split
--@private
local function split_lines(value)
return split(value, '\n', true)
end
---@private
-local function ok_or_nil(status, ...)
- if not status then return end
- return ...
-end
---@private
-local function npcall(fn, ...)
- return ok_or_nil(pcall(fn, ...))
-end
-
--- Replaces text in a range with new text.
---
--- CAUTION: Changes in-place!
@@ -121,10 +103,18 @@ local function get_line_byte_from_position(bufnr, position)
-- When on the first character, we can ignore the difference between byte and
-- character
if col > 0 then
+ if not api.nvim_buf_is_loaded(bufnr) then
+ vim.fn.bufload(bufnr)
+ end
+
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)
+ local ok, result = pcall(vim.str_byteindex, lines[1], col)
+
+ if ok then
+ return result
+ end
end
end
return col
@@ -700,13 +690,13 @@ end
--- Trims empty lines from input and pad left and right with spaces
---
---@param contents table of lines to trim and pad
---@param opts dictionary with optional fields
--- - pad_left number of columns to pad contents at left (default 1)
--- - pad_right number of columns to pad contents at right (default 1)
--- - pad_top number of lines to pad contents at top (default 0)
--- - pad_bottom number of lines to pad contents at bottom (default 0)
---@returns contents table of trimmed and padded lines
+---@param contents table of lines to trim and pad
+---@param opts dictionary with optional fields
+--- - pad_left number of columns to pad contents at left (default 1)
+--- - pad_right number of columns to pad contents at right (default 1)
+--- - pad_top number of lines to pad contents at top (default 0)
+--- - pad_bottom number of lines to pad contents at bottom (default 0)
+---@return contents table of trimmed and padded lines
function M._trim_and_pad(contents, opts)
validate {
contents = { contents, 't' };
@@ -742,19 +732,19 @@ end
--- regions to improve readability.
--- The result is shown in a floating preview.
---
---@param contents table of lines to show in window
---@param opts dictionary with optional fields
--- - height of floating window
--- - width of floating window
--- - wrap_at character to wrap at for computing height
--- - max_width maximal width of floating window
--- - max_height maximal height of floating window
--- - pad_left number of columns to pad contents at left
--- - pad_right number of columns to pad contents at right
--- - pad_top number of lines to pad contents at top
--- - pad_bottom number of lines to pad contents at bottom
--- - separator insert separator after code block
---@returns width,height size of float
+---@param contents table of lines to show in window
+---@param opts dictionary with optional fields
+--- - height of floating window
+--- - width of floating window
+--- - wrap_at character to wrap at for computing height
+--- - max_width maximal width of floating window
+--- - max_height maximal height of floating window
+--- - pad_left number of columns to pad contents at left
+--- - pad_right number of columns to pad contents at right
+--- - pad_top number of lines to pad contents at top
+--- - pad_bottom number of lines to pad contents at bottom
+--- - separator insert separator after code block
+---@returns width,height size of float
function M.fancy_floating_markdown(contents, opts)
validate {
contents = { contents, 't' };
@@ -971,170 +961,80 @@ function M.open_floating_preview(contents, filetype, opts)
return floating_bufnr, floating_winnr
end
+-- TODO(remove-callbacks)
do
- local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics")
- local reference_ns = api.nvim_create_namespace("vim_lsp_references")
- local sign_ns = 'vim_lsp_signs'
- local underline_highlight_name = "LspDiagnosticsUnderline"
- vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name))
- for kind, _ in pairs(protocol.DiagnosticSeverity) do
- if type(kind) == 'string' then
- vim.cmd(string.format("highlight default link %s%s %s", underline_highlight_name, kind, underline_highlight_name))
- end
+ --@deprecated
+ function M.get_severity_highlight_name(severity)
+ warn_once("vim.lsp.util.get_severity_highlight_name is deprecated.")
+ return vim.lsp.diagnostic._get_severity_highlight_name(severity)
end
- local severity_highlights = {}
+ --@deprecated
+ function M.buf_clear_diagnostics(bufnr, client_id)
+ warn_once("buf_clear_diagnostics is deprecated. Use vim.lsp.diagnostic.clear")
+ return vim.lsp.diagnostic.clear(bufnr, client_id)
+ end
- local severity_floating_highlights = {}
+ --@deprecated
+ function M.get_line_diagnostics()
+ warn_once("get_line_diagnostics is deprecated. Use vim.lsp.diagnostic.get_line_diagnostics")
- local default_severity_highlight = {
- [protocol.DiagnosticSeverity.Error] = { guifg = "Red" };
- [protocol.DiagnosticSeverity.Warning] = { guifg = "Orange" };
- [protocol.DiagnosticSeverity.Information] = { guifg = "LightBlue" };
- [protocol.DiagnosticSeverity.Hint] = { guifg = "LightGrey" };
- }
+ local bufnr = api.nvim_get_current_buf()
+ local line_nr = api.nvim_win_get_cursor(0)[1] - 1
- -- Initialize default severity highlights
- for severity, hi_info in pairs(default_severity_highlight) do
- local severity_name = protocol.DiagnosticSeverity[severity]
- local highlight_name = "LspDiagnostics"..severity_name
- local floating_highlight_name = highlight_name.."Floating"
- -- Try to fill in the foreground color with a sane default.
- local cmd_parts = {"highlight", "default", highlight_name}
- for k, v in pairs(hi_info) do
- table.insert(cmd_parts, k.."="..v)
- end
- api.nvim_command(table.concat(cmd_parts, ' '))
- api.nvim_command('highlight link ' .. highlight_name .. 'Sign ' .. highlight_name)
- api.nvim_command('highlight link ' .. highlight_name .. 'Floating ' .. highlight_name)
- severity_highlights[severity] = highlight_name
- severity_floating_highlights[severity] = floating_highlight_name
+ return vim.lsp.diagnostic.get_line_diagnostics(bufnr, line_nr)
end
- --- Clears diagnostics for a buffer.
- ---
- --@param bufnr (number) buffer id
- function M.buf_clear_diagnostics(bufnr)
- validate { bufnr = {bufnr, 'n', true} }
- bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
+ --@deprecated
+ function M.show_line_diagnostics()
+ warn_once("show_line_diagnostics is deprecated. Use vim.lsp.diagnostic.show_line_diagnostics")
- -- clear sign group
- vim.fn.sign_unplace(sign_ns, {buffer=bufnr})
+ local bufnr = api.nvim_get_current_buf()
+ local line_nr = api.nvim_win_get_cursor(0)[1] - 1
- -- clear virtual text namespace
- api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1)
+ return vim.lsp.diagnostic.show_line_diagnostics(bufnr, line_nr)
end
- --- Gets the name of a severity's highlight group.
- ---
- --@param severity A member of `vim.lsp.protocol.DiagnosticSeverity`
- --@returns (string) Highlight group name
- function M.get_severity_highlight_name(severity)
- return severity_highlights[severity]
+ --@deprecated
+ function M.buf_diagnostics_save_positions(bufnr, diagnostics, client_id)
+ warn_once("buf_diagnostics_save_positions is deprecated. Use vim.lsp.diagnostic.save")
+ return vim.lsp.diagnostic.save(diagnostics, bufnr, client_id)
end
- --- Gets list of diagnostics for the current line.
- ---
- --@returns (table) list of `Diagnostic` tables
- --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic
- function M.get_line_diagnostics()
- local bufnr = api.nvim_get_current_buf()
- local linenr = api.nvim_win_get_cursor(0)[1] - 1
-
- local buffer_diagnostics = M.diagnostics_by_buf[bufnr]
+ --@deprecated
+ function M.buf_diagnostics_get_positions(bufnr, client_id)
+ warn_once("buf_diagnostics_get_positions is deprecated. Use vim.lsp.diagnostic.get")
+ return vim.lsp.diagnostic.get(bufnr, client_id)
+ end
- if not buffer_diagnostics then
- return {}
- end
+ --@deprecated
+ function M.buf_diagnostics_underline(bufnr, diagnostics, client_id)
+ warn_once("buf_diagnostics_underline is deprecated. Use 'vim.lsp.diagnostic.set_underline'")
+ return vim.lsp.diagnostic.set_underline(diagnostics, bufnr, client_id)
+ end
- local diagnostics_by_line = M.diagnostics_group_by_line(buffer_diagnostics)
- return diagnostics_by_line[linenr] or {}
+ --@deprecated
+ function M.buf_diagnostics_virtual_text(bufnr, diagnostics, client_id)
+ warn_once("buf_diagnostics_virtual_text is deprecated. Use 'vim.lsp.diagnostic.set_virtual_text'")
+ return vim.lsp.diagnostic.set_virtual_text(diagnostics, bufnr, client_id)
end
- --- Displays the diagnostics for the current line in a floating hover
- --- window.
- function M.show_line_diagnostics()
- -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {})
- -- if #marks == 0 then
- -- return
- -- end
- local lines = {"Diagnostics:"}
- local highlights = {{0, "Bold"}}
- local line_diagnostics = M.get_line_diagnostics()
- if vim.tbl_isempty(line_diagnostics) then return end
-
- for i, diagnostic in ipairs(line_diagnostics) do
- -- for i, mark in ipairs(marks) do
- -- local mark_id = mark[1]
- -- local diagnostic = buffer_diagnostics[mark_id]
-
- -- TODO(ashkan) make format configurable?
- local prefix = string.format("%d. ", i)
- local hiname = severity_floating_highlights[diagnostic.severity]
- assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity))
- local message_lines = split_lines(diagnostic.message)
- table.insert(lines, prefix..message_lines[1])
- table.insert(highlights, {#prefix + 1, hiname})
- for j = 2, #message_lines do
- table.insert(lines, message_lines[j])
- table.insert(highlights, {0, hiname})
- end
- end
- local popup_bufnr, winnr = M.open_floating_preview(lines, 'plaintext')
- for i, hi in ipairs(highlights) do
- local prefixlen, hiname = unpack(hi)
- -- Start highlight after the prefix
- api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1)
- end
- return popup_bufnr, winnr
+ --@deprecated
+ function M.buf_diagnostics_signs(bufnr, diagnostics, client_id)
+ warn_once("buf_diagnostics_signs is deprecated. Use 'vim.lsp.diagnostics.set_signs'")
+ return vim.lsp.diagnostic.set_signs(diagnostics, bufnr, client_id)
end
- --- Saves diagnostics into vim.lsp.util.diagnostics_by_buf[{bufnr}].
- ---
- --@param bufnr (number) buffer id for which the diagnostics are for
- --@param diagnostics list of `Diagnostic`s received from the LSP server
- function M.buf_diagnostics_save_positions(bufnr, diagnostics)
- validate {
- bufnr = {bufnr, 'n', true};
- diagnostics = {diagnostics, 't', true};
- }
- if not diagnostics then return end
- bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
-
- if not M.diagnostics_by_buf[bufnr] then
- -- Clean up our data when the buffer unloads.
- api.nvim_buf_attach(bufnr, false, {
- on_detach = function(b)
- M.diagnostics_by_buf[b] = nil
- end
- })
- end
- M.diagnostics_by_buf[bufnr] = diagnostics
+ --@deprecated
+ function M.buf_diagnostics_count(kind, client_id)
+ warn_once("buf_diagnostics_count is deprecated. Use 'vim.lsp.diagnostic.get_count'")
+ return vim.lsp.diagnostic.get_count(vim.api.nvim_get_current_buf(), client_id, kind)
end
- --- Highlights a list of diagnostics in a buffer by underlining them.
- ---
- --@param bufnr (number) buffer id
- --@param diagnostics (list of `Diagnostic`s)
- function M.buf_diagnostics_underline(bufnr, diagnostics)
- for _, diagnostic in ipairs(diagnostics) do
- local start = diagnostic.range["start"]
- local finish = diagnostic.range["end"]
-
- local hlmap = {
- [protocol.DiagnosticSeverity.Error]='Error',
- [protocol.DiagnosticSeverity.Warning]='Warning',
- [protocol.DiagnosticSeverity.Information]='Information',
- [protocol.DiagnosticSeverity.Hint]='Hint',
- }
+end
- highlight.range(bufnr, diagnostic_ns,
- underline_highlight_name..hlmap[diagnostic.severity],
- {start.line, start.character},
- {finish.line, finish.character}
- )
- end
- end
+do --[[ References ]]
+ local reference_ns = api.nvim_create_namespace("vim_lsp_references")
--- Removes document highlights from a buffer.
---
@@ -1162,109 +1062,6 @@ do
highlight.range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos)
end
end
-
- --- Groups a list of diagnostics by line.
- ---
- --@param diagnostics (table) list of `Diagnostic`s
- --@returns (table) dictionary mapping lines to lists of diagnostics valid on
- ---those lines
- --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic
- function M.diagnostics_group_by_line(diagnostics)
- if not diagnostics then return end
- local diagnostics_by_line = {}
- for _, diagnostic in ipairs(diagnostics) do
- local start = diagnostic.range.start
- -- TODO: Are diagnostics only valid for a single line? I don't understand
- -- why this would be okay otherwise
- local line_diagnostics = diagnostics_by_line[start.line]
- if not line_diagnostics then
- line_diagnostics = {}
- diagnostics_by_line[start.line] = line_diagnostics
- end
- table.insert(line_diagnostics, diagnostic)
- end
- return diagnostics_by_line
- end
-
- --- Given a list of diagnostics, sets the corresponding virtual text for a
- --- buffer.
- ---
- --@param bufnr buffer id
- --@param diagnostics (table) list of `Diagnostic`s
- function M.buf_diagnostics_virtual_text(bufnr, diagnostics)
- if not diagnostics then
- return
- end
- local buffer_line_diagnostics = M.diagnostics_group_by_line(diagnostics)
- for line, line_diags in pairs(buffer_line_diagnostics) do
- local virt_texts = {}
- for i = 1, #line_diags - 1 do
- table.insert(virt_texts, {"■", severity_highlights[line_diags[i].severity]})
- end
- local last = line_diags[#line_diags]
- -- TODO(ashkan) use first line instead of subbing 2 spaces?
- table.insert(virt_texts, {"■ "..last.message:gsub("\r", ""):gsub("\n", " "), severity_highlights[last.severity]})
- api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {})
- end
- end
-
- --- Returns the number of diagnostics of given kind for current buffer.
- ---
- --- Useful for showing diagnostic counts in statusline. eg:
- ---
- --- <pre>
- --- function! LspStatus() abort
- --- let sl = ''
- --- if luaeval('not vim.tbl_isempty(vim.lsp.buf_get_clients(0))')
- --- let sl.='%#MyStatuslineLSP#E:'
- --- let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.util.buf_diagnostics_count([[Error]])")}'
- --- let sl.='%#MyStatuslineLSP# W:'
- --- let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.util.buf_diagnostics_count([[Warning]])")}'
- --- else
- --- let sl.='%#MyStatuslineLSPErrors#off'
- --- endif
- --- return sl
- --- endfunction
- --- let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus()
- --- </pre>
- ---
- --@param kind Diagnostic severity kind: See |vim.lsp.protocol.DiagnosticSeverity|
- --@returns Count of diagnostics
- function M.buf_diagnostics_count(kind)
- local bufnr = vim.api.nvim_get_current_buf()
- local diagnostics = M.diagnostics_by_buf[bufnr]
- if not diagnostics then return end
- local count = 0
- for _, diagnostic in pairs(diagnostics) do
- if protocol.DiagnosticSeverity[kind] == diagnostic.severity then
- count = count + 1
- end
- end
- return count
- end
-
- local diagnostic_severity_map = {
- [protocol.DiagnosticSeverity.Error] = "LspDiagnosticsErrorSign";
- [protocol.DiagnosticSeverity.Warning] = "LspDiagnosticsWarningSign";
- [protocol.DiagnosticSeverity.Information] = "LspDiagnosticsInformationSign";
- [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign";
- }
-
- --- Places signs for each diagnostic in the sign column.
- ---
- --- Sign characters can be customized with the following commands:
- ---
- --- <pre>
- --- sign define LspDiagnosticsErrorSign text=E texthl=LspDiagnosticsError linehl= numhl=
- --- sign define LspDiagnosticsWarningSign text=W texthl=LspDiagnosticsWarning linehl= numhl=
- --- sign define LspDiagnosticsInformationSign text=I texthl=LspDiagnosticsInformation linehl= numhl=
- --- sign define LspDiagnosticsHintSign text=H texthl=LspDiagnosticsHint linehl= numhl=
- --- </pre>
- function M.buf_diagnostics_signs(bufnr, diagnostics)
- for _, diagnostic in ipairs(diagnostics) do
- vim.fn.sign_place(0, sign_ns, diagnostic_severity_map[diagnostic.severity], bufnr, {lnum=(diagnostic.range.start.line+1)})
- end
- end
end
local position_sort = sort_by_key(function(v)
@@ -1561,6 +1358,9 @@ function M.character_offset(buf, row, col)
return str_utfindex(line, col)
end
+M._get_line_byte_from_position = get_line_byte_from_position
+M._warn_once = warn_once
+
M.buf_versions = {}
return M