mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 07:00:05 +08:00
280 lines
7.5 KiB
Lua
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
|