1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 07:00:05 +08:00
SpaceVim/bundle/deoplete-lsp/lua/util.lua
2021-10-05 15:13:10 +08:00

280 lines
7.5 KiB
Lua

--
--------------------------------------------------------------------------------
-- File: util.lua
--------------------------------------------------------------------------------
--
-- https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/util.lua
--
-- Don't like some conventions here but minimal modifications will make easier to compare with original
local vim = vim
local validate = vim.validate
local api = vim.api
local M = {}
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.make_floating_popup_options(width, height, opts)
validate {
opts = { opts, 't', true };
}
opts = opts or {}
validate {
["opts.offset_x"] = { opts.offset_x, 'n', true };
["opts.offset_y"] = { opts.offset_y, 'n', true };
}
local lines_above = vim.fn.winline() - 1
local lines_below = vim.fn.winheight(0) - lines_above
local col
if lines_above < lines_below then
height = math.min(lines_below, height)
else
height = math.min(lines_above, height)
end
if opts.align == 'right' then
col = opts.col + opts.width
else
col = opts.col - width - 1
end
return {
col = col,
height = height,
relative = 'editor',
row = opts.row,
focusable = false,
style = 'minimal',
width = width,
}
end
local function find_window_by_var(name, value)
for _, win in ipairs(api.nvim_list_wins()) do
if npcall(api.nvim_win_get_var, win, name) == value then
return win
end
end
end
function M.focusable_float(unique_name, fn)
if npcall(api.nvim_win_get_var, 0, unique_name) then
return api.nvim_command("wincmd p")
end
local bufnr = api.nvim_get_current_buf()
do
local win = find_window_by_var(unique_name, bufnr)
if win then
api.nvim_win_close(win, true)
end
end
local pbufnr, pwinnr = fn()
if pbufnr then
api.nvim_win_set_var(pwinnr, unique_name, bufnr)
return pbufnr, pwinnr
end
end
-- Convert markdown into syntax highlighted regions by stripping the code
-- blocks and converting them into highlighted code.
-- This will by default insert a blank line separator after those code block
-- regions to improve readability.
function M.fancy_floating_markdown(contents, opts)
local pad_left = opts and opts.pad_left
local pad_right = opts and opts.pad_right
local stripped = {}
local highlights = {}
local max_width
if opts.align == 'right' then
local columns = api.nvim_get_option('columns')
max_width = columns - opts.col - opts.width
else
max_width = opts.col - 1
end
-- Didn't have time to investigate but this fixes popup offset by one display issue
max_width = max_width - pad_left - pad_right
do
local i = 1
while i <= #contents do
local line = contents[i]
local ft = line:match("^```([a-zA-Z0-9_]*)$")
if ft then
local start = #stripped
i = i + 1
while i <= #contents do
line = contents[i]
if line == "```" then
i = i + 1
break
end
if #line > max_width then
while #line > max_width do
local trimmed_line = string.sub(line, 1, max_width)
local index = trimmed_line:reverse():find(" ")
if index == nil or index > #trimmed_line/2 then
break
else
table.insert(stripped, string.sub(line, 1, max_width-index))
line = string.sub(line, max_width-index+2, #line)
end
end
table.insert(stripped, line)
else
table.insert(stripped, line)
end
i = i + 1
end
table.insert(highlights, {
ft = ft;
start = start + 1;
finish = #stripped + 1 - 1
})
else
if #line > max_width then
while #line > max_width do
local trimmed_line = string.sub(line, 1, max_width)
-- local index = math.max(trimmed_line:reverse():find(" "), trimmed_line:reverse():find("/"))
local index = trimmed_line:reverse():find(" ")
if index == nil or index > #trimmed_line/2 then
break
else
table.insert(stripped, string.sub(line, 1, max_width-index))
line = string.sub(line, max_width-index+2, #line)
end
end
table.insert(stripped, line)
else
table.insert(stripped, line)
end
i = i + 1
end
end
end
local width = 0
for i, v in ipairs(stripped) do
v = v:gsub("\r", "")
if pad_left then v = (" "):rep(pad_left)..v end
if pad_right then v = v..(" "):rep(pad_right) end
stripped[i] = v
width = math.max(width, #v)
end
if opts.align == 'right' then
local columns = api.nvim_get_option('columns')
if opts.col + opts.row + width > columns then
width = columns - opts.col - opts.width - 1
end
else
if width > opts.col then
width = opts.col - 1
end
end
local insert_separator = true
if insert_separator then
for i, h in ipairs(highlights) do
h.start = h.start + i - 1
h.finish = h.finish + i - 1
if h.finish + 1 <= #stripped then
table.insert(stripped, h.finish + 1, string.rep("", width))
end
end
end
-- Make the floating window.
local height = #stripped
local bufnr = api.nvim_create_buf(false, true)
local winnr
if vim.g.completion_docked_hover == 1 then
if height > vim.g.completion_docked_maximum_size then
height = vim.g.completion_docked_maximum_size
elseif height < vim.g.completion_docked_minimum_size then
height = vim.g.completion_docked_minimum_size
end
local row
if vim.fn.winline() > api.nvim_get_option('lines')/2 then
row = 0
else
row = api.nvim_get_option('lines') - height
end
winnr = api.nvim_open_win(bufnr, false, {
col = 0,
height = height,
relative = 'editor',
row = row,
focusable = true,
style = 'minimal',
width = api.nvim_get_option('columns'),
})
else
local opt = M.make_floating_popup_options(width, height, opts)
if opt.width <= 0 then return end
winnr = api.nvim_open_win(bufnr, false, opt)
end
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped)
local cwin = vim.api.nvim_get_current_win()
vim.api.nvim_set_current_win(winnr)
vim.cmd("ownsyntax markdown")
local idx = 1
local function highlight_region(ft, start, finish)
if ft == '' then return end
local name = ft..idx
idx = idx + 1
local lang = "@"..ft:upper()
-- TODO(ashkan): better validation before this.
if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then
return
end
vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s", name, start, finish + 1, lang))
end
for _, h in ipairs(highlights) do
highlight_region(h.ft, h.start, h.finish)
end
vim.api.nvim_set_current_win(cwin)
return bufnr, winnr
end
local str_utfindex = vim.str_utfindex
local function make_position_param()
local row, col = unpack(api.nvim_win_get_cursor(0))
row = row - 1
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
col = str_utfindex(line, col)
return { line = row; character = col; }
end
function M.make_position_params()
return {
textDocument = M.make_text_document_params();
position = make_position_param()
}
end
function M.make_text_document_params()
return { uri = vim.uri_from_bufnr(0) }
end
return M