Trans.nvim/lua/Trans/window.lua
2023-02-01 17:18:51 +08:00

249 lines
6.5 KiB
Lua

local api = vim.api
local new_content = require('Trans.content')
local new_animation = require('Trans.util.animation')
local busy = false
local function lock()
while busy do
vim.wait(50)
end
busy = true
end
---@class window
---@field winid integer 窗口的handle
---@field bufnr integer 窗口对应buffer的handle
---@field width integer 窗口当前的宽度
---@field height integer 窗口当前的高度
---@field hl integer 窗口highlight的namespace
---@field contents table[] 窗口内容的对象数组
---@type window
local window = {
set = function(self, option, value)
api.nvim_win_set_option(self.winid, option, value)
end,
set_height = function(self, height)
api.nvim_win_set_height(self.winid, height)
self.height = height
end,
set_width = function(self, width)
api.nvim_win_set_width(self.winid, width)
self.width = width
end,
bufset = function(self, option, value)
api.nvim_buf_set_option(self.bufnr, option, value)
end,
---@nodiscard
option = function(self, name)
return api.nvim_win_get_option(self.winid, name)
end,
map = function(self, key, operation)
vim.keymap.set('n', key, operation, {
buffer = self.bufnr,
silent = true,
})
end,
---@nodiscard
is_open = function(self)
return api.nvim_win_is_valid(self.winid)
end,
normal = function(self, key)
api.nvim_buf_call(self.bufnr, function()
vim.cmd([[normal! ]] .. key)
end)
end,
draw = function(self)
local offset = 0
for _, content in ipairs(self.contents) do
content:attach(offset)
offset = offset + content.size
end
end,
open = function(self, opts)
self:draw()
local wrap = self:option('wrap')
self:set('wrap', false)
opts = opts or {}
local animation = opts.animation or self.animation.open
local callback = function()
busy = false
self:set('wrap', wrap)
if opts.callback then
opts.callback()
end
end
lock()
if animation then
local interval = self.animation.interval
local field = ({
fold = 'height',
slid = 'width',
})[animation]
local method = api['nvim_win_set_' .. field]
local winid = self.winid
new_animation({
interval = interval,
times = self[field],
frame = function(_, times)
method(winid, times)
end,
callback = callback,
}):display()
else
callback()
end
end,
---安全的关闭窗口
try_close = function(self, opts)
opts = opts or {}
self:set('wrap', false)
if self:is_open() then
local callback = function()
api.nvim_win_close(self.winid, true)
self.winid = -1
busy = false
if opts.callback then
opts.callback()
end
if api.nvim_buf_is_valid(self.bufnr) and opts.wipeout then
api.nvim_buf_delete(self.bufnr, { force = true })
self.bufnr = -1
end
end
lock()
self.config = api.nvim_win_get_config(self.winid)
local animation = self.animation.close
if animation then
local interval = self.animation.interval
local field = ({
fold = 'height',
slid = 'width',
})[animation]
local target = self[field]
local method = api['nvim_win_set_' .. field]
local winid = self.winid
new_animation({
times = target,
frame = function(_, times)
method(winid, target - times)
end,
callback = callback,
interval = interval,
}):display()
else
callback()
end
end
end,
reopen = function(self, opts)
assert(self.bufnr ~= -1)
local entry = opts.entry or false
local win_opt = opts.win_opt or {}
local opt = opts.opt
self.config.win = nil
for k, v in pairs(win_opt) do
self.config[k] = v
end
self.winid = api.nvim_open_win(self.bufnr, entry, self.config)
self:open(opt)
end,
set_hl = function(self, name, opts)
api.nvim_set_hl(self.hl, name, opts)
end,
new_content = function(self)
local index = self.size + 1
self.size = index
self.contents[index] = new_content(self)
return self.contents[index]
end,
}
window.__index = window
---窗口对象的构造器
---@param entry boolean 光标初始化时是否应该进入窗口
---@param option table 需要设置的选项
---@return window win
---@nodiscard
return function(entry, option)
vim.validate {
entry = { entry, 'b' },
option = { option, 't' },
}
local opt = {
relative = option.relative,
width = option.width,
height = option.height,
border = option.border,
title = option.title,
col = option.col,
row = option.row,
title_pos = nil,
focusable = false,
zindex = option.zindex or 100,
style = 'minimal',
}
if opt.title then
opt.title_pos = 'center'
end
local bufnr = api.nvim_create_buf(false, true)
local ok, winid = pcall(api.nvim_open_win, bufnr, entry, opt)
if not ok then
error('open window faild: ' .. vim.inspect(opt))
end
local win
win = {
winid = winid,
bufnr = bufnr,
width = opt.width,
height = opt.height,
animation = option.animation,
hl = api.nvim_create_namespace('TransWinHl'),
size = 0,
contents = {}
}
---@diagnostic disable-next-line: param-type-mismatch
setmetatable(win, window)
win:bufset('filetype', 'Trans')
win:bufset('buftype', 'nofile')
api.nvim_win_set_hl_ns(win.winid, win.hl)
win:set_hl('Normal', { link = 'TransWin' })
win:set_hl('FloatBorder', { link = 'TransBorder' })
---@diagnostic disable-next-line: return-type-mismatch
return win
end