1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 02:10:05 +08:00
SpaceVim/lua/spacevim/api/notify.lua
2024-05-21 11:25:51 +08:00

280 lines
7.8 KiB
Lua

--=============================================================================
-- notify.lua --- notift api for spacevim
-- Copyright (c) 2016-2023 Wang Shidong & Contributors
-- Author: Wang Shidong < wsdjeg@outlook.com >
-- URL: https://spacevim.org
-- License: GPLv3
--=============================================================================
local M = {}
M.__password = require('spacevim.api').import('password')
local empty = function(expr)
return vim.fn.empty(expr) == 1
end
local extend = function(t1, t2) -- {{{
for _, v in ipairs(t2) do
table.insert(t1, v)
end
end
local notifications = {}
M.message = {}
M.notification_width = 1
M.notify_max_width = 0
M.winid = -1
M.bufnr = -1
M.border = {}
M.border.winid = -1
M.border.bufnr = -1
-- M.borderchars = { '─', '│', '─', '│', '┌', '┐', '┘', '└' }
M.borderchars = { '', '', '', '', '', '', '', '' }
M.title = ''
M.winblend = 0
M.timeout = 3000
M.hashkey = ''
M.config = {}
M.notification_color = 'Normal'
---@param msg string|table<string> notification messages
---@param opts table notify options
--- - title: string, the notify title
function M.notify(msg, ...) -- {{{
local opts = select(1, ...) or {}
if M.is_list_of_string(msg) then
extend(M.message, msg)
elseif type(msg) == 'string' then
table.insert(M.message, msg)
end
if M.notify_max_width == 0 then
M.notify_max_width = vim.o.columns * 0.35
end
if type(opts) == 'string' then
M.notification_color = opts
elseif type(opts) == 'table' then
M.notification_color = opts.color or 'Normal'
end
if empty(M.hashkey) then
M.hashkey = M.__password.generate_simple(10)
end
M.redraw_windows()
vim.fn.setbufvar(M.bufnr, '&number', 0)
vim.fn.setbufvar(M.bufnr, '&relativenumber', 0)
vim.fn.setbufvar(M.bufnr, '&cursorline', 0)
vim.fn.setbufvar(M.bufnr, '&bufhidden', 'wipe')
vim.fn.setbufvar(M.border.bufnr, '&number', 0)
vim.fn.setbufvar(M.border.bufnr, '&relativenumber', 0)
vim.fn.setbufvar(M.border.bufnr, '&cursorline', 0)
vim.fn.setbufvar(M.border.bufnr, '&bufhidden', 'wipe')
notifications[M.hashkey] = M
M.increase_window()
if type(msg) == 'table' then
vim.fn.timer_start(M.timeout, M.close, { ['repeat'] = #msg })
else
vim.fn.timer_start(M.timeout, M.close, { ['repeat'] = 1 })
end
end
---@param msg table<string> # a string message list
---@return number
local function msg_real_len(msg)
local l = 0
for _, m in pairs(msg) do
l = l + #vim.split(m, '\n')
end
return l
end
function M.close_all() -- {{{
M.message = {}
if M.win_is_open then
vim.api.nvim_win_close(M.border.winid, true)
vim.api.nvim_win_close(M.winid, true)
end
if notifications[M.hashkey] then
notifications[M.hashkey] = nil
end
M.notification_width = 1
end
-- }}}
function M.win_is_open()
if M.winid > 0 then
return vim.api.nvim_win_is_valid(M.winid)
else
return false
end
end
-- }}}
function M.is_list_of_string(t) -- {{{
if type(t) == 'table' then
for _, v in pairs(t) do
if type(v) ~= 'string' then
return false
end
end
return true
end
return false
end
-- }}}
local function message_body(m) -- {{{
local b = {}
for _, v in pairs(m) do
extend(b, vim.split(v, '\n'))
end
return b
end
-- }}}
function M.redraw_windows()
if empty(M.message) then
return
end
M.begin_row = 2
local viml_notify = vim.fn['SpaceVim#api#notify#shared_notifys']()
for hashkey, _ in pairs(viml_notify) do
M.begin_row = M.begin_row + msg_real_len(viml_notify[hashkey].message) + 2
end
for hashkey, _ in pairs(notifications) do
if hashkey ~= M.hashkey then
M.begin_row = M.begin_row + msg_real_len(notifications[hashkey].message) + 2
else
break
end
end
if M.win_is_open() then
vim.api.nvim_win_set_config(M.winid, {
relative = 'editor',
width = M.notification_width,
height = msg_real_len(M.message),
row = M.begin_row + 1,
focusable = false,
col = vim.o.columns - M.notification_width - 1,
})
vim.api.nvim_win_set_config(M.border.winid, {
relative = 'editor',
width = M.notification_width + 2,
height = msg_real_len(M.message) + 2,
row = M.begin_row,
col = vim.o.columns - M.notification_width - 2,
focusable = false,
})
else
if vim.fn.bufexists(M.border.bufnr) ~= 1 then
M.border.bufnr = vim.api.nvim_create_buf(false, true)
end
if vim.fn.bufexists(M.bufnr) ~= 1 then
M.bufnr = vim.api.nvim_create_buf(false, true)
end
M.winid = vim.api.nvim_open_win(M.bufnr, false, {
relative = 'editor',
width = M.notification_width,
height = msg_real_len(M.message),
row = M.begin_row + 1,
col = vim.o.columns - M.notification_width - 1,
focusable = false,
noautocmd = true,
})
vim.api.nvim_win_set_option(M.winid, 'winhighlight', 'NormalFloat:Normal')
-- vim.api.nvim_win_set_option(M.winid, 'winhighlight', 'Search:' .. M.notification_color)
vim.fn.matchadd(M.notification_color, '.*', 10, -1, {
window = M.winid
})
M.border.winid = vim.api.nvim_open_win(M.border.bufnr, false, {
relative = 'editor',
width = M.notification_width + 2,
height = msg_real_len(M.message) + 2,
row = M.begin_row,
col = vim.o.columns - M.notification_width - 2,
focusable = false,
noautocmd = true,
})
-- vim.api.nvim_win_set_option(M.border.winid, 'winhighlight', 'Normal:VertSplit')
-- vim.api.nvim_win_set_option(M.border.winid, 'winhighlight', 'Search:VertSplit')
vim.fn.matchadd('VertSplit', '.*', 10, -1, {
window = M.border.winid
})
if
M.winblend > 0
and vim.fn.exists('&winblend') == 1
and vim.fn.exists('*nvim_win_set_option') == 1
then
vim.api.nvim_win_set_option(M.winid, 'winblend', M.winblend)
vim.api.nvim_win_set_option(M.border.winid, 'winblend', M.winblend)
end
end
vim.api.nvim_buf_set_lines(
M.border.bufnr,
0,
-1,
false,
M.draw_border(M.title, M.notification_width, msg_real_len(M.message))
)
vim.api.nvim_buf_set_lines(M.bufnr, 0, -1, false, message_body(M.message))
vim.api.nvim_win_set_cursor(M.winid, {1, 0})
vim.api.nvim_win_set_cursor(M.border.winid, {1, 0})
end
function M.increase_window()
if M.notification_width <= M.notify_max_width and M.win_is_open() then
M.notification_width = M.notification_width
+ vim.fn.min({
vim.fn.float2nr((M.notify_max_width - M.notification_width) * 1 / 20),
vim.fn.float2nr(M.notify_max_width),
})
vim.api.nvim_buf_set_lines(
M.border.bufnr,
0,
-1,
false,
M.draw_border(M.title, M.notification_width, msg_real_len(M.message))
)
M.redraw_windows()
vim.fn.timer_start(10, M.increase_window, { ['repeat'] = 1 })
end
end
function M.draw_border(title, width, height) -- {{{
local top = M.borderchars[5] .. vim.fn['repeat'](M.borderchars[1], width) .. M.borderchars[6]
local mid = M.borderchars[4] .. vim.fn['repeat'](' ', width) .. M.borderchars[2]
local bot = M.borderchars[8] .. vim.fn['repeat'](M.borderchars[3], width) .. M.borderchars[7]
-- local top = M.string_compose(top, 1, title)
local lines = { top }
extend(lines, vim.fn['repeat']({ mid }, height))
extend(lines, { bot })
return lines
end
-- }}}
function M.close(...) -- {{{
if not empty(M.message) then
table.remove(M.message, 1)
end
if #M.message == 0 then
if M.win_is_open() then
local ei = vim.o.eventignore
vim.o.eventignore = 'all'
vim.api.nvim_win_close(M.border.winid, true)
vim.api.nvim_win_close(M.winid, true)
vim.o.eventignore = ei
end
if notifications[M.hashkey] then
notifications[M.hashkey] = nil
end
M.notification_width = 1
end
for hashkey, _ in pairs(notifications) do
notifications[hashkey].redraw_windows()
end
end
-- }}}
return M