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