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.lua132
1 files changed, 109 insertions, 23 deletions
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index a75ab37508..ce8468aa8a 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -18,6 +18,40 @@ end
local M = {}
+local default_border = {
+ {"", "NormalFloat"},
+ {"", "NormalFloat"},
+ {"", "NormalFloat"},
+ {" ", "NormalFloat"},
+ {"", "NormalFloat"},
+ {"", "NormalFloat"},
+ {"", "NormalFloat"},
+ {" ", "NormalFloat"},
+}
+
+--@private
+-- Check the border given by opts or the default border for the additional
+-- size it adds to a float.
+--@returns size of border in height and width
+local function get_border_size(opts)
+ local border = opts and opts.border or default_border
+ local height = 0
+ local width = 0
+
+ if type(border) == 'string' then
+ -- 'single', 'double', etc.
+ height = 2
+ width = 2
+ else
+ height = height + vim.fn.strdisplaywidth(border[2][1]) -- top
+ height = height + vim.fn.strdisplaywidth(border[6][1]) -- bottom
+ width = width + vim.fn.strdisplaywidth(border[4][1]) -- right
+ width = width + vim.fn.strdisplaywidth(border[8][1]) -- left
+ end
+
+ return { height = height, width = width }
+end
+
--@private
local function split_lines(value)
return split(value, '\n', true)
@@ -357,8 +391,11 @@ end
--- Returns the range table for the difference between old and new lines
--@param old_lines table list of lines
--@param new_lines table list of lines
+--@param start_line_idx int line to begin search for first difference
+--@param end_line_idx int line to begin search for last difference
+--@param offset_encoding string encoding requested by language server
--@returns table start_line_idx and start_col_idx of range
-function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx)
+function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx, offset_encoding)
local start_line, start_char = first_difference(old_lines, new_lines, start_line_idx)
local end_line, end_char = last_difference(vim.list_slice(old_lines, start_line, #old_lines),
vim.list_slice(new_lines, start_line, #new_lines), start_char, end_line_idx)
@@ -373,10 +410,19 @@ function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx)
adj_end_char = #old_lines[#old_lines + end_line + 1] + end_char + 1
end
+ local _
+ if offset_encoding == "utf-16" then
+ _, start_char = vim.str_utfindex(old_lines[start_line], start_char - 1)
+ _, end_char = vim.str_utfindex(old_lines[#old_lines + end_line + 1], adj_end_char)
+ else
+ start_char = start_char - 1
+ end_char = adj_end_char
+ end
+
local result = {
range = {
- start = { line = start_line - 1, character = start_char - 1},
- ["end"] = { line = adj_end_line, character = adj_end_char}
+ start = { line = start_line - 1, character = start_char},
+ ["end"] = { line = adj_end_line, character = end_char}
},
text = text,
rangeLength = length + 1,
@@ -424,6 +470,7 @@ function M.apply_text_document_edit(text_document_edit, index)
-- `VersionedTextDocumentIdentifier`s version may be null
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
if should_check_version and (text_document.version
+ and text_document.version > 0
and M.buf_versions[bufnr]
and M.buf_versions[bufnr] > text_document.version) then
print("Buffer ", text_document.uri, " newer than edits.")
@@ -844,7 +891,7 @@ function M.make_floating_popup_options(width, height, opts)
else
anchor = anchor..'S'
height = math.min(lines_above, height)
- row = 0
+ row = -get_border_size(opts).height
end
if vim.fn.wincol() + width <= api.nvim_get_option('columns') then
@@ -863,9 +910,27 @@ function M.make_floating_popup_options(width, height, opts)
row = row + (opts.offset_y or 0),
style = 'minimal',
width = width,
+ border = opts.border or default_border,
}
end
+local function _should_add_to_tagstack(new_item)
+ local stack = vim.fn.gettagstack()
+
+ -- Check if we're at the bottom of the tagstack.
+ if stack.curidx <= 1 then return true end
+
+ local top_item = stack.items[stack.curidx-1]
+
+ -- Check if the item at the top of the tagstack is exactly the
+ -- same as the one we want to push.
+ if top_item.tagname ~= new_item.tagname then return true end
+ for i, v in ipairs(top_item.from) do
+ if v ~= new_item.from[i] then return true end
+ end
+ return false
+end
+
--- Jumps to a location.
---
--@param location (`Location`|`LocationLink`)
@@ -874,22 +939,36 @@ function M.jump_to_location(location)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
if uri == nil then return end
- local bufnr = vim.uri_to_bufnr(uri)
- -- Save position in jumplist
- vim.cmd "normal! m'"
- -- Push a new item into tagstack
- local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0}
- local items = {{tagname=vim.fn.expand('<cword>'), from=from}}
- vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't')
+ local from_bufnr = vim.fn.bufnr('%')
+ local from = {from_bufnr, vim.fn.line('.'), vim.fn.col('.'), 0}
+ local item = {tagname=vim.fn.expand('<cword>'), from=from}
+
+ -- Save position in jumplist
+ vim.cmd("mark '")
--- Jump to new location (adjusting for UTF-16 encoding of characters)
+ local bufnr = vim.uri_to_bufnr(uri)
api.nvim_set_current_buf(bufnr)
api.nvim_buf_set_option(0, 'buflisted', true)
local range = location.range or location.targetSelectionRange
local row = range.start.line
local col = get_line_byte_from_position(0, range.start)
+ -- This prevents the tagstack to be filled with items that provide
+ -- no motion when CTRL-T is pressed because they're both the source
+ -- and the destination.
+ local motionless =
+ bufnr == from_bufnr and
+ row+1 == from[2] and col+1 == from[3]
+ if not motionless and _should_add_to_tagstack(item) then
+ local winid = vim.fn.win_getid()
+ local items = {item}
+ vim.fn.settagstack(winid, {items=items}, 't')
+ end
+
+ -- Jump to new location
api.nvim_win_set_cursor(0, {row + 1, col})
+
return true
end
@@ -969,27 +1048,20 @@ function M.focusable_preview(unique_name, fn)
end)
end
---- Trims empty lines from input and pad left and right with spaces
+--- Trims empty lines from input and pad top and bottom with empty 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)
+function M._trim(contents, opts)
validate {
contents = { contents, 't' };
opts = { opts, 't', true };
}
opts = opts or {}
- local left_padding = (" "):rep(opts.pad_left or 1)
- local right_padding = (" "):rep(opts.pad_right or 1)
contents = M.trim_empty_lines(contents)
- for i, line in ipairs(contents) do
- contents[i] = string.format('%s%s%s', left_padding, line:gsub("\r", ""), right_padding)
- end
if opts.pad_top then
for _ = 1, opts.pad_top do
table.insert(contents, 1, "")
@@ -1066,8 +1138,8 @@ function M.fancy_floating_markdown(contents, opts)
end
end
end
- -- Clean up and add padding
- stripped = M._trim_and_pad(stripped, opts)
+ -- Clean up
+ stripped = M._trim(stripped, opts)
-- Compute size of float needed to show (wrapped) lines
opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0))
@@ -1170,6 +1242,20 @@ function M._make_floating_popup_size(contents, opts)
width = math.max(line_widths[i], width)
end
end
+
+ local border_width = get_border_size(opts).width
+ local screen_width = api.nvim_win_get_width(0)
+ width = math.min(width, screen_width)
+
+ -- make sure borders are always inside the screen
+ if width + border_width > screen_width then
+ width = width - (width + border_width - screen_width)
+ end
+
+ if wrap_at and wrap_at > width then
+ wrap_at = width
+ end
+
if max_width then
width = math.min(width, max_width)
wrap_at = math.min(wrap_at or max_width, max_width)
@@ -1223,7 +1309,7 @@ function M.open_floating_preview(contents, syntax, opts)
opts = opts or {}
-- Clean up input: trim empty lines from the end, pad
- contents = M._trim_and_pad(contents, opts)
+ contents = M._trim(contents, opts)
-- Compute size of float needed to show (wrapped) lines
opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0))