refactor: better source code framework
This commit is contained in:
167
lua/Trans/wrapper/buffer.lua
Normal file
167
lua/Trans/wrapper/buffer.lua
Normal file
@@ -0,0 +1,167 @@
|
||||
local api, fn = vim.api, vim.fn
|
||||
|
||||
---@class buf
|
||||
---@field bufnr integer buffer handle
|
||||
---@field size integer buffer line count
|
||||
local buffer = {}
|
||||
|
||||
---Clear all content in buffer
|
||||
function buffer:wipe()
|
||||
api.nvim_buf_set_lines(self.bufnr, 0, -1, false, {})
|
||||
self.size = 0
|
||||
end
|
||||
|
||||
---delete buffer [_start, _end] line content [one index]
|
||||
---@param _start integer start line index
|
||||
---@param _end integer end line index
|
||||
function buffer:del(_start, _end)
|
||||
if not _start then
|
||||
fn.deletebufline(self.bufnr, '$')
|
||||
else
|
||||
_end = _end or _start
|
||||
fn.deletebufline(self.bufnr, _start, _end)
|
||||
end
|
||||
self.size = api.nvim_buf_line_count(self.bufnr)
|
||||
end
|
||||
|
||||
---Set buffer option
|
||||
---@param name string option name
|
||||
---@param value any option value
|
||||
function buffer:set(name, value)
|
||||
api.nvim_buf_set_option(self.bufnr, name, value)
|
||||
end
|
||||
|
||||
---get buffer option
|
||||
---@param name string option name
|
||||
---@return any
|
||||
function buffer:option(name)
|
||||
return api.nvim_buf_get_option(self.bufnr, name)
|
||||
end
|
||||
|
||||
function buffer:delete()
|
||||
api.nvim_buf_delete(self.bufnr, { force = true })
|
||||
end
|
||||
|
||||
---Set buffer load keymap
|
||||
---@param key string
|
||||
---@param operation function | string
|
||||
function buffer:map(key, operation)
|
||||
vim.keymap.set('n', key, operation, {
|
||||
buffer = self.bufnr,
|
||||
silent = true,
|
||||
})
|
||||
end
|
||||
|
||||
---Execute normal keycode in this buffer[no recursive]
|
||||
---@param key string key code
|
||||
function buffer:normal(key)
|
||||
api.nvim_buf_call(self.bufnr, function()
|
||||
vim.cmd([[normal! ]] .. key)
|
||||
end)
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
---@nodiscard
|
||||
function buffer:is_valid()
|
||||
return api.nvim_buf_is_valid(self.bufnr)
|
||||
end
|
||||
|
||||
---Get buffer [i, j] line content
|
||||
---@param i integer? start line index
|
||||
---@param j integer? end line index
|
||||
---@return string[]
|
||||
function buffer:lines(i, j)
|
||||
i = i and i - 1 or 0
|
||||
j = j and j - 1 or -1
|
||||
return api.nvim_buf_get_lines(self.bufnr, i, j, false)
|
||||
end
|
||||
|
||||
---Calculate buffer content display height
|
||||
---@param width integer
|
||||
---@return integer height
|
||||
function buffer:height(width)
|
||||
local size = self.size
|
||||
local lines = self:lines()
|
||||
local height = 0
|
||||
for i = 1, size do
|
||||
height = height + math.max(1, (math.ceil(lines[i]:width() / width)))
|
||||
end
|
||||
return height
|
||||
end
|
||||
|
||||
---Add|Set line content
|
||||
---@param nodes string|table|table[] string -> as line content | table -> as a node | table[] -> as node[]
|
||||
---@param index number? line number should be set[one index]
|
||||
function buffer:addline(nodes, index)
|
||||
local newsize = self.size + 1
|
||||
assert(index == nil or index <= newsize)
|
||||
index = index or newsize
|
||||
if index == newsize then
|
||||
self.size = newsize
|
||||
end
|
||||
|
||||
if type(nodes) == 'string' then
|
||||
self[index] = nodes
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local line = index - 1
|
||||
local bufnr = self.bufnr
|
||||
local col = 0
|
||||
if type(nodes[1]) == 'string' then
|
||||
self[index] = nodes[1]
|
||||
nodes:load(bufnr, line, col)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local strs = {}
|
||||
local num = #nodes
|
||||
for i = 1, num do
|
||||
strs[i] = nodes[i][1]
|
||||
end
|
||||
|
||||
self[index] = table.concat(strs)
|
||||
for i = 1, num do
|
||||
local node = nodes[i]
|
||||
node:load(bufnr, line, col)
|
||||
col = col + #node[1]
|
||||
end
|
||||
end
|
||||
|
||||
function buffer:init()
|
||||
self.bufnr = api.nvim_create_buf(false, false)
|
||||
self:set('filetype', 'Trans')
|
||||
self:set('buftype', 'nofile')
|
||||
self.size = 0
|
||||
end
|
||||
|
||||
---@private
|
||||
buffer.__index = function(self, key)
|
||||
local res = buffer[key]
|
||||
if res then
|
||||
return res
|
||||
|
||||
elseif type(key) == 'number' then
|
||||
return fn.getbufoneline(self.bufnr, key)
|
||||
|
||||
else
|
||||
error('invalid key' .. key)
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
buffer.__newindex = function(self, key, text)
|
||||
assert(key <= self.size + 1)
|
||||
fn.setbufline(self.bufnr, key, text)
|
||||
end
|
||||
|
||||
---buffer constructor
|
||||
---@return buf
|
||||
return function()
|
||||
return setmetatable({
|
||||
bufnr = -1,
|
||||
size = 0,
|
||||
}, buffer)
|
||||
end
|
223
lua/Trans/wrapper/window.lua
Normal file
223
lua/Trans/wrapper/window.lua
Normal file
@@ -0,0 +1,223 @@
|
||||
local api = vim.api
|
||||
local display = require('Trans.util.display')
|
||||
|
||||
---@class win
|
||||
---@field winid integer window handle
|
||||
---@field width integer
|
||||
---@field height integer
|
||||
---@field ns integer namespace for highlight
|
||||
---@field animation table window animation
|
||||
---@field buf buf buffer for attached
|
||||
local window = {}
|
||||
|
||||
---Change window attached buffer
|
||||
---@param buf buf
|
||||
function window:set_buf(buf)
|
||||
api.nvim_win_set_buf(self.winid, buf.bufnr)
|
||||
self.buf = buf
|
||||
end
|
||||
|
||||
---Check window valid
|
||||
---@return boolean
|
||||
function window:is_valid()
|
||||
return api.nvim_win_is_valid(self.winid)
|
||||
end
|
||||
|
||||
---Set window option
|
||||
---@param option string option name
|
||||
---@param value any
|
||||
function window:set(option, value)
|
||||
if self:is_valid() then
|
||||
api.nvim_win_set_option(self.winid, option, value)
|
||||
end
|
||||
end
|
||||
|
||||
---@param name string option name
|
||||
---@return any
|
||||
function window:option(name)
|
||||
return api.nvim_win_get_option(self.winid, name)
|
||||
end
|
||||
|
||||
---@param height integer
|
||||
function window:set_height(height)
|
||||
api.nvim_win_set_height(self.winid, height)
|
||||
self.height = height
|
||||
end
|
||||
|
||||
---@param width integer
|
||||
function window:set_width(width)
|
||||
api.nvim_win_set_width(self.winid, width)
|
||||
self.width = width
|
||||
end
|
||||
|
||||
---Expand window [width | height] value
|
||||
---@param opts table
|
||||
---|'field'string [width | height]
|
||||
---|'target'integer
|
||||
---@return function
|
||||
function window:expand(opts)
|
||||
self:lock()
|
||||
local field = opts.field
|
||||
local target = opts.target
|
||||
local cur = self[field]
|
||||
local times = math.abs(target - cur)
|
||||
|
||||
local wrap = self:option('wrap')
|
||||
self:set('wrap', false)
|
||||
local interval = opts.interval or self.animation.interval
|
||||
local method = api['nvim_win_set_' .. field]
|
||||
|
||||
local winid = self.winid
|
||||
local frame = target > cur and function(_, cur_times)
|
||||
method(winid, cur + cur_times)
|
||||
end or function(_, cur_times)
|
||||
method(winid, cur - cur_times)
|
||||
end
|
||||
|
||||
local run = display {
|
||||
times = times,
|
||||
frame = frame,
|
||||
interval = interval,
|
||||
}
|
||||
|
||||
run(function()
|
||||
self:set('wrap', wrap)
|
||||
self[field] = target
|
||||
self:unlock()
|
||||
end)
|
||||
return run
|
||||
end
|
||||
|
||||
---Close window
|
||||
---@return function run run until close done
|
||||
function window:try_close()
|
||||
local field = ({
|
||||
slid = 'width',
|
||||
fold = 'height',
|
||||
})[self.animation.close]
|
||||
|
||||
--- 播放动画
|
||||
local run = self:expand {
|
||||
field = field,
|
||||
target = 1,
|
||||
}
|
||||
run(function()
|
||||
api.nvim_win_close(self.winid, true)
|
||||
end)
|
||||
return run
|
||||
end
|
||||
|
||||
---lock window [open | close] operation
|
||||
function window:lock()
|
||||
while self.busy do
|
||||
vim.wait(50)
|
||||
end
|
||||
self.busy = true
|
||||
end
|
||||
|
||||
function window:unlock()
|
||||
self.busy = false
|
||||
end
|
||||
|
||||
---设置窗口本地的高亮组
|
||||
---@param name string 高亮组的名称
|
||||
---@param opts table 高亮选项
|
||||
function window:set_hl(name, opts)
|
||||
api.nvim_set_hl(self.ns, name, opts)
|
||||
end
|
||||
|
||||
---buffer:addline() helper function
|
||||
---@param node table
|
||||
---@return table node formatted node
|
||||
function window:center(node)
|
||||
local text = node[1]
|
||||
local width = text:width()
|
||||
local win_width = self.width
|
||||
local space = math.max(math.floor((win_width - width) / 2), 0)
|
||||
node[1] = (' '):rep(space) .. text
|
||||
return node
|
||||
end
|
||||
|
||||
---@private
|
||||
window.__index = window
|
||||
|
||||
---@class win_opts
|
||||
---@field buf buf buffer for attached
|
||||
---@field height integer
|
||||
---@field width integer
|
||||
---@field col integer
|
||||
---@field row integer
|
||||
---@field border string
|
||||
---@field title string | nil | table
|
||||
---@field relative string
|
||||
---@field ns integer namespace for highlight
|
||||
---@field zindex? integer
|
||||
---@field enter? boolean cursor should [enter] window
|
||||
---@field animation table window animation
|
||||
|
||||
---window constructor
|
||||
---@param opts win_opts
|
||||
---@return table
|
||||
---@return function
|
||||
return function(opts)
|
||||
assert(type(opts) == 'table')
|
||||
local ns = opts.ns
|
||||
local buf = opts.buf
|
||||
local col = opts.col
|
||||
local row = opts.row
|
||||
local title = opts.title
|
||||
local width = opts.width
|
||||
local enter = opts.enter or false
|
||||
local height = opts.height
|
||||
local border = opts.border
|
||||
local zindex = opts.zindex
|
||||
local relative = opts.relative
|
||||
local animation = opts.animation
|
||||
|
||||
local open = animation.open
|
||||
|
||||
local field = ({
|
||||
slid = 'width',
|
||||
fold = 'height',
|
||||
})[open]
|
||||
|
||||
local win_opt = {
|
||||
title_pos = nil,
|
||||
focusable = false,
|
||||
style = 'minimal',
|
||||
zindex = zindex,
|
||||
width = width,
|
||||
height = height,
|
||||
col = col,
|
||||
row = row,
|
||||
border = border,
|
||||
title = title,
|
||||
relative = relative,
|
||||
}
|
||||
|
||||
if field then
|
||||
win_opt[field] = 1
|
||||
end
|
||||
|
||||
if win_opt.title then
|
||||
win_opt.title_pos = 'center'
|
||||
end
|
||||
|
||||
local win = setmetatable({
|
||||
buf = buf,
|
||||
ns = ns,
|
||||
height = win_opt.height,
|
||||
width = win_opt.width,
|
||||
animation = animation,
|
||||
winid = api.nvim_open_win(buf.bufnr, enter, win_opt),
|
||||
}, window)
|
||||
|
||||
api.nvim_win_set_hl_ns(win.winid, win.ns)
|
||||
win:set_hl('Normal', { link = 'TransWin' })
|
||||
win:set_hl('FloatBorder', { link = 'TransBorder' })
|
||||
|
||||
return win, win:expand {
|
||||
field = field,
|
||||
target = opts[field],
|
||||
}
|
||||
end
|
Reference in New Issue
Block a user