refactor: window和content的关系由继承改为组合
This commit is contained in:
		
							
								
								
									
										167
									
								
								lua/Trans/content.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								lua/Trans/content.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | |||||||
|  | local api = vim.api | ||||||
|  | local content = { | ||||||
|  |     newline = function(self, value) | ||||||
|  |         if not self.modifiable then | ||||||
|  |             error('content can not add newline now') | ||||||
|  |         end | ||||||
|  |         self.size = self.size + 1 | ||||||
|  |         self.lines[self.size] = value | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     newhl = function(self, opt) | ||||||
|  |         if not self.modifiable then | ||||||
|  |             error('content can not add newline now') | ||||||
|  |         end | ||||||
|  |         self.hl_size = self.hl_size + 1 | ||||||
|  |         self.highlights[self.hl_size] = opt | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     center_line = function(self, text, highlight) | ||||||
|  |         vim.validate { | ||||||
|  |             text = { text, 's' } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         local space = math.floor((self.window.width - text:width()) / 2) | ||||||
|  |         local interval = (' '):rep(space) | ||||||
|  |         self:newline(interval .. text) | ||||||
|  |         if highlight then | ||||||
|  |             self:newhl { | ||||||
|  |                 name   = highlight, | ||||||
|  |                 line   = self.size - 1, | ||||||
|  |                 _start = space, | ||||||
|  |                 _end   = space + #text, | ||||||
|  |             } | ||||||
|  |         end | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     wipe = function(self) | ||||||
|  |         local clear = require('table.clear') | ||||||
|  |         clear(self.lines) | ||||||
|  |         clear(self.highlights) | ||||||
|  |         self.size = 0 | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     attach = function(self) | ||||||
|  |         if self.size == 0 then | ||||||
|  |             return | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         self.window:bufset('modifiable', true) | ||||||
|  |         local window = self.window | ||||||
|  |         local offset = self.offset | ||||||
|  |  | ||||||
|  |         api.nvim_buf_set_lines(window.bufnr, offset, offset + 1, true, self.lines) | ||||||
|  |  | ||||||
|  |         for _, hl in ipairs(self.highlights) do | ||||||
|  |             api.nvim_buf_add_highlight(window.bufnr, window.hl, hl.name, offset + hl.line, hl._start, hl._end) | ||||||
|  |         end | ||||||
|  |         self.window:bufset('modifiable', false) | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     actual_height = function(self) | ||||||
|  |         if self.window:option('wrap') then | ||||||
|  |             local height = 0 | ||||||
|  |             local width = self.window.width | ||||||
|  |             local lines = self.lines | ||||||
|  |             for i = 1, self.size do | ||||||
|  |                 height = height + math.max(1, (math.ceil(lines[i]:width() / width))) | ||||||
|  |             end | ||||||
|  |             return height | ||||||
|  |         else | ||||||
|  |             return self.size | ||||||
|  |         end | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     addline = function(self, newline, highlight) | ||||||
|  |         self:newline(newline) | ||||||
|  |         if highlight then | ||||||
|  |             self:newhl { | ||||||
|  |                 name = highlight, | ||||||
|  |                 line = self.size - 1, | ||||||
|  |                 _start = 0, | ||||||
|  |                 _end = -1, | ||||||
|  |             } | ||||||
|  |         end | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     items_wrap = function(self) | ||||||
|  |         local items = {} | ||||||
|  |         local size = 0 | ||||||
|  |         local width = 0 | ||||||
|  |  | ||||||
|  |         return { | ||||||
|  |             add_item = function(item, highlight) | ||||||
|  |                 size = size + 1 | ||||||
|  |                 items[size] = { item, highlight } | ||||||
|  |                 width = width + item:width() | ||||||
|  |             end, | ||||||
|  |  | ||||||
|  |             load = function() | ||||||
|  |                 assert(size > 1, 'no item need be loaded') | ||||||
|  |                 local space = math.floor((self.window.width - width) / (size - 1)) | ||||||
|  |                 assert(space > 0, 'try to expand window width') | ||||||
|  |                 local interval = (' '):rep(space) | ||||||
|  |                 local line = '' | ||||||
|  |  | ||||||
|  |                 local function load_item(idx) | ||||||
|  |                     local item = items[idx] | ||||||
|  |                     if item[2] then | ||||||
|  |                         self:newhl { | ||||||
|  |                             name = item[2], | ||||||
|  |                             line = self.size, -- NOTE : 此时还没插入新行, size ==> 行号(zero index) | ||||||
|  |                             _start = #line, | ||||||
|  |                             _end = #line + #item[1], | ||||||
|  |                         } | ||||||
|  |                     end | ||||||
|  |                     line = line .. item[1] | ||||||
|  |                 end | ||||||
|  |  | ||||||
|  |                 load_item(1) | ||||||
|  |                 for i = 2, size do | ||||||
|  |                     line = line .. interval | ||||||
|  |                     load_item(i) | ||||||
|  |                 end | ||||||
|  |  | ||||||
|  |                 self:newline(line) | ||||||
|  |             end | ||||||
|  |         } | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     line_wrap = function(self) | ||||||
|  |         self:newline('') | ||||||
|  |         local index = self.size | ||||||
|  |         return function(text, highlight) | ||||||
|  |             if highlight then | ||||||
|  |                 local _start = #self.lines[index] | ||||||
|  |                 local _end = _start + #text | ||||||
|  |                 self:newhl { | ||||||
|  |                     name = highlight, | ||||||
|  |                     line = index - 1, | ||||||
|  |                     _start = _start, | ||||||
|  |                     _end = _end, | ||||||
|  |                 } | ||||||
|  |             end | ||||||
|  |  | ||||||
|  |             self.lines[index] = self.lines[index] .. text | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ---content的构造函数 | ||||||
|  | ---@param window table 链接的窗口 | ||||||
|  | ---@return table 构造好的content | ||||||
|  | return function(window, offset) | ||||||
|  |     vim.validate { | ||||||
|  |         window = { window, 't' }, | ||||||
|  |     } | ||||||
|  |     return setmetatable({ | ||||||
|  |         modifiable = true, | ||||||
|  |         offset = offset or 0, | ||||||
|  |         window = window, | ||||||
|  |         size = 0, | ||||||
|  |         hl_size = 0, | ||||||
|  |         lines = {}, | ||||||
|  |         highlights = {}, | ||||||
|  |     }, { __index = content }) | ||||||
|  | end | ||||||
| @@ -1,12 +1,13 @@ | |||||||
| local conf = require('Trans').conf | local conf = require('Trans').conf | ||||||
| local icon = conf.icon | local icon = conf.icon | ||||||
|  |  | ||||||
| local m_window = require('Trans.window') | local m_window | ||||||
| local m_result | local m_result  | ||||||
|  | local m_content | ||||||
| local m_indent = '    ' | local m_indent = '    ' | ||||||
|  |  | ||||||
| local title = function(str) | local title = function(str) | ||||||
|     local wrapper = m_window.text_wrap() |     local wrapper = m_content:line_wrap() | ||||||
|     -- wrapper('', 'TransTitleRound') |     -- wrapper('', 'TransTitleRound') | ||||||
|     wrapper('', 'TransTitleRound') |     wrapper('', 'TransTitleRound') | ||||||
|     wrapper(str, 'TransTitle') |     wrapper(str, 'TransTitle') | ||||||
| @@ -61,7 +62,7 @@ end | |||||||
|  |  | ||||||
| local process = { | local process = { | ||||||
|     title = function() |     title = function() | ||||||
|         local line = m_window.line_wrap() |         local line = m_content:items_wrap() | ||||||
|         line.add_item( |         line.add_item( | ||||||
|             m_result.word, |             m_result.word, | ||||||
|             'TransWord' |             'TransWord' | ||||||
| @@ -94,12 +95,13 @@ local process = { | |||||||
|             end |             end | ||||||
|  |  | ||||||
|             for i = 1, size, 3 do |             for i = 1, size, 3 do | ||||||
|                 m_window.addline( |                 m_content:addline( | ||||||
|                     m_indent .. tags[i] .. interval .. (tags[i + 1] or '') .. interval .. (tags[i + 2] or ''), |                     m_indent .. tags[i] .. interval .. (tags[i + 1] or '') .. interval .. (tags[i + 2] or ''), | ||||||
|                     'TransTag' |                     'TransTag' | ||||||
|                 ) |                 ) | ||||||
|             end |             end | ||||||
|             m_window.addline('') |  | ||||||
|  |             m_content:addline('') | ||||||
|         end |         end | ||||||
|     end, |     end, | ||||||
|  |  | ||||||
| @@ -108,13 +110,13 @@ local process = { | |||||||
|             title('词性') |             title('词性') | ||||||
|  |  | ||||||
|             for pos in vim.gsplit(m_result.pos, '/', true) do |             for pos in vim.gsplit(m_result.pos, '/', true) do | ||||||
|                 m_window.addline( |                 m_content:addline( | ||||||
|                     m_indent .. pos_map[pos:sub(1, 1)] .. pos:sub(3) .. '%', |                     m_indent .. pos_map[pos:sub(1, 1)] .. pos:sub(3) .. '%', | ||||||
|                     'TransPos' |                     'TransPos' | ||||||
|                 ) |                 ) | ||||||
|             end |             end | ||||||
|  |  | ||||||
|             m_window.addline('') |             m_content:addline('') | ||||||
|         end |         end | ||||||
|     end, |     end, | ||||||
|  |  | ||||||
| @@ -124,13 +126,13 @@ local process = { | |||||||
|             local interval = '    ' |             local interval = '    ' | ||||||
|  |  | ||||||
|             for exc in vim.gsplit(m_result.exchange, '/', true) do |             for exc in vim.gsplit(m_result.exchange, '/', true) do | ||||||
|                 m_window.addline( |                 m_content:addline( | ||||||
|                     m_indent .. exchange_map[exc:sub(1, 1)] .. interval .. exc:sub(3), |                     m_indent .. exchange_map[exc:sub(1, 1)] .. interval .. exc:sub(3), | ||||||
|                     'TransExchange' |                     'TransExchange' | ||||||
|                 ) |                 ) | ||||||
|             end |             end | ||||||
|  |  | ||||||
|             m_window.addline('') |             m_content:addline('') | ||||||
|         end |         end | ||||||
|     end, |     end, | ||||||
|  |  | ||||||
| @@ -138,13 +140,13 @@ local process = { | |||||||
|         title('中文翻译') |         title('中文翻译') | ||||||
|  |  | ||||||
|         for trs in vim.gsplit(m_result.translation, '\n', true) do |         for trs in vim.gsplit(m_result.translation, '\n', true) do | ||||||
|             m_window.addline( |             m_content:addline( | ||||||
|                 m_indent .. trs, |                 m_indent .. trs, | ||||||
|                 'TransTranslation' |                 'TransTranslation' | ||||||
|             ) |             ) | ||||||
|         end |         end | ||||||
|  |  | ||||||
|         m_window.addline('') |         m_content:addline('') | ||||||
|     end, |     end, | ||||||
|  |  | ||||||
|     definition = function() |     definition = function() | ||||||
| @@ -153,18 +155,18 @@ local process = { | |||||||
|  |  | ||||||
|             for def in vim.gsplit(m_result.definition, '\n', true) do |             for def in vim.gsplit(m_result.definition, '\n', true) do | ||||||
|                 def = def:gsub('^%s+', '', 1) -- TODO :判断是否需要分割空格 |                 def = def:gsub('^%s+', '', 1) -- TODO :判断是否需要分割空格 | ||||||
|                 m_window.addline( |                 m_content:addline( | ||||||
|                     m_indent .. def, |                     m_indent .. def, | ||||||
|                     'TransDefinition' |                     'TransDefinition' | ||||||
|                 ) |                 ) | ||||||
|             end |             end | ||||||
|  |  | ||||||
|             m_window.addline('') |             m_content:addline('') | ||||||
|         end |         end | ||||||
|     end, |     end, | ||||||
|  |  | ||||||
|     failed = function() |     failed = function() | ||||||
|         m_window.addline( |         m_content:addline( | ||||||
|             icon.notfound .. m_indent .. '没有找到相关的翻译', |             icon.notfound .. m_indent .. '没有找到相关的翻译', | ||||||
|             'TransFailed' |             'TransFailed' | ||||||
|         ) |         ) | ||||||
| @@ -174,10 +176,11 @@ local process = { | |||||||
|  |  | ||||||
| local action = { | local action = { | ||||||
|     pageup = function() |     pageup = function() | ||||||
|         m_window.normal('gg') |         m_window:normal('gg') | ||||||
|     end, |     end, | ||||||
|  |  | ||||||
|     pagedown = function() |     pagedown = function() | ||||||
|         m_window.normal('G') |         m_window:normal('G') | ||||||
|     end, |     end, | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -200,8 +203,8 @@ return function(word) | |||||||
|         row      = 2, |         row      = 2, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     m_window = require("Trans.window")(false, opt) | ||||||
|     m_window.init(false, opt) |     m_content = m_window.content | ||||||
|  |  | ||||||
|     if m_result then |     if m_result then | ||||||
|         for _, field in ipairs(conf.order) do |         for _, field in ipairs(conf.order) do | ||||||
| @@ -210,24 +213,22 @@ return function(word) | |||||||
|     else |     else | ||||||
|         process.failed() |         process.failed() | ||||||
|     end |     end | ||||||
|  |     m_window:set('wrap', true) | ||||||
|  |  | ||||||
|     m_window.draw() |     m_window:draw(true) | ||||||
|     -- Auto Close |     -- Auto Close | ||||||
|     vim.api.nvim_create_autocmd( |     vim.api.nvim_create_autocmd( | ||||||
|         { --[[ 'InsertEnter', ]] 'CursorMoved', 'BufLeave', }, { |         { --[[ 'InsertEnter', ]] 'CursorMoved', 'BufLeave', }, { | ||||||
|         buffer = 0, |         buffer = 0, | ||||||
|         once = true, |         once = true, | ||||||
|         callback = function() |         callback = function() | ||||||
|             m_window.try_close(hover.animation) -- NOTE :maybe can be passed by uesr |             m_window:try_close(hover.animation) | ||||||
|         end, |         end, | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|     m_window.set('wrap', true) |  | ||||||
|     m_window.adjust() |  | ||||||
|  |  | ||||||
|     for act, key in pairs(hover.keymap) do |     for act, key in pairs(hover.keymap) do | ||||||
|         vim.keymap.set('n', key, function() |         vim.keymap.set('n', key, function() | ||||||
|             if m_window.is_open() then |             if m_window:is_open() then | ||||||
|                 action[act]() |                 action[act]() | ||||||
|             end |             end | ||||||
|         end) |         end) | ||||||
|   | |||||||
| @@ -1,31 +1,147 @@ | |||||||
| local api = vim.api | local api = vim.api | ||||||
| --- =================== Window Attributes ================================ |  | ||||||
| local M = { |  | ||||||
|     height     = 0, -- 窗口的当前的高度 |  | ||||||
|     size       = 0, -- 窗口的行数 |  | ||||||
|     width      = 0, -- 窗口的当前的宽度 |  | ||||||
|     lines      = {}, |  | ||||||
|     highlights = {}, |  | ||||||
|     winid      = -1, -- 窗口的handle |  | ||||||
|     bufnr      = -1, -- 窗口对应的buffer的handle |  | ||||||
|     hl         = api.nvim_create_namespace('TransWinHl'), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| -- M.<++>   --> <++> |  | ||||||
|  |  | ||||||
|  | ---@diagnostic disable-next-line: duplicate-set-field | ||||||
| function string:width() | function string:width() | ||||||
|     ---@diagnostic disable-next-line: param-type-mismatch |     ---@diagnostic disable-next-line: param-type-mismatch | ||||||
|     return vim.fn.strwidth(self) |     return vim.fn.strwidth(self) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- =================== Load Window Options ================================ | local window = { | ||||||
| M.init = function(entry, opts) |     ---设置窗口的选项 | ||||||
|  |     ---@param self table 需要设置的window | ||||||
|  |     ---@param option string 待设置的选项名 | ||||||
|  |     ---@param value any 选项的值 | ||||||
|  |     set = function(self, option, value) | ||||||
|  |         api.nvim_win_set_option(self.winid, option, value) | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---设置窗口的高度 | ||||||
|  |     ---@param self table 窗口类 | ||||||
|  |     ---@param height integer 设置的高度 | ||||||
|  |     set_height = function(self, height) | ||||||
|  |         api.nvim_win_set_height(self.winid, height) | ||||||
|  |         self.height = height | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---设置窗口的宽度 | ||||||
|  |     ---@param self table 窗口对象 | ||||||
|  |     ---@param width integer 设置的宽度 | ||||||
|  |     set_width = function(self, width) | ||||||
|  |         api.nvim_win_set_width(self.winid, width) | ||||||
|  |         self.width = width | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---设置窗口对应的buffer | ||||||
|  |     ---@param self table 同set类似 | ||||||
|  |     ---@param option string | ||||||
|  |     ---@param value any | ||||||
|  |     bufset = function(self, option, value) | ||||||
|  |         api.nvim_buf_set_option(self.bufnr, option, value) | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---查看**窗口**的选项 | ||||||
|  |     ---@param self table 窗口对象 | ||||||
|  |     ---@param name string 选项名 | ||||||
|  |     ---@return any 对应的值 | ||||||
|  |     ---@nodiscard | ||||||
|  |     option = function(self, name) | ||||||
|  |         return api.nvim_win_get_option(self.winid, name) | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---设置当前窗口内的键映射, **需要光标在该窗口内** | ||||||
|  |     ---@param self table 窗口对象 | ||||||
|  |     ---@param key string 映射的键位 | ||||||
|  |     ---@param operation any 执行的操作 | ||||||
|  |     map = function(self, key, operation) | ||||||
|  |         vim.keymap.set('n', key, operation, { | ||||||
|  |             buffer = self.bufnr, | ||||||
|  |             silent = true, | ||||||
|  |         }) | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---查看窗口是否是打开状态 | ||||||
|  |     ---@param self table window对象 | ||||||
|  |     ---@return boolean | ||||||
|  |     ---@nodiscard | ||||||
|  |     is_open = function(self) | ||||||
|  |         return self.winid > 0 and 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, | ||||||
|  |  | ||||||
|  |     ---**第一次**绘制窗口的内容 | ||||||
|  |     ---@param self table 窗口的对象 | ||||||
|  |     ---@param adjust boolean 是否需要调整窗口的高度和宽度 (只有窗口只有一行时才会调整宽度) | ||||||
|  |     draw = function(self, adjust) | ||||||
|  |         -- TODO : | ||||||
|  |         if self.title then | ||||||
|  |             self.title:attach() | ||||||
|  |         end | ||||||
|  |         self.content:attach() | ||||||
|  |  | ||||||
|  |         if adjust then | ||||||
|  |             local height = self.content:actual_height() + self.title:actual_height() | ||||||
|  |             if self.height > height then | ||||||
|  |                 self:set_height(height) | ||||||
|  |             end | ||||||
|  |  | ||||||
|  |             if self.content.size == 1 and self.title.size == 0 then | ||||||
|  |                 self:set_width(self.content.lines[1]:width()) | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---**重新绘制内容**(标题不变) | ||||||
|  |     ---@param self table 窗口对象 | ||||||
|  |     redraw = function(self) | ||||||
|  |         self.content:attach() | ||||||
|  |     end, | ||||||
|  |  | ||||||
|  |     ---安全的关闭窗口 | ||||||
|  |     ---@param self table 窗口对象 | ||||||
|  |     ---@param interval integer 动画的间隔 | ||||||
|  |     try_close = function(self, interval) | ||||||
|  |         vim.validate { | ||||||
|  |             interval = { interval, 'n' } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if self:is_open() then | ||||||
|  |             local function narrow() | ||||||
|  |                 if self.height > 1 then | ||||||
|  |                     self.height = self.height - 1 | ||||||
|  |                     api.nvim_win_set_height(self.winid, self.height) | ||||||
|  |                     vim.defer_fn(narrow, interval) | ||||||
|  |                 else | ||||||
|  |                     -- Wait animation done | ||||||
|  |                     vim.defer_fn(function() | ||||||
|  |                         api.nvim_win_close(self.winid, true) | ||||||
|  |                         self.winid = -1 | ||||||
|  |                     end, interval + 2) | ||||||
|  |                 end | ||||||
|  |             end | ||||||
|  |  | ||||||
|  |             narrow() | ||||||
|  |         end | ||||||
|  |     end, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ---窗口对象的构造器 | ||||||
|  | ---@param entry boolean 光标初始化时是否应该进入窗口 | ||||||
|  | ---@param option table 需要设置的选项 | ||||||
|  | ---@return table | ||||||
|  | ---@nodiscard | ||||||
|  | return function(entry, option) | ||||||
|     vim.validate { |     vim.validate { | ||||||
|         entry = { entry, 'b' }, |         entry = { entry, 'b' }, | ||||||
|         opts = { opts, 't' } |         option = { option, 't' }, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     local opt = { |     local opt = { | ||||||
|        relative  = nil, |         relative  = nil, | ||||||
|         width     = nil, |         width     = nil, | ||||||
|         height    = nil, |         height    = nil, | ||||||
|         border    = nil, |         border    = nil, | ||||||
| @@ -37,253 +153,43 @@ M.init = function(entry, opts) | |||||||
|         zindex    = 100, |         zindex    = 100, | ||||||
|         style     = 'minimal', |         style     = 'minimal', | ||||||
|     } |     } | ||||||
|  |     for k, v in pairs(option) do | ||||||
|     for k, v in pairs(opts) do |  | ||||||
|         opt[k] = v |         opt[k] = v | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     M.height = opt.height |     local bufnr = api.nvim_create_buf(false, true) | ||||||
|     M.width  = opt.width |     local winid = api.nvim_open_win(bufnr, entry, opt) | ||||||
|     M.bufnr  = api.nvim_create_buf(false, true) |  | ||||||
|     M.winid  = api.nvim_open_win(M.bufnr, entry, opt) |  | ||||||
|     M.set('winhl', 'Normal:TransWin,FloatBorder:TransBorder') |  | ||||||
|     M.bufset('bufhidden', 'wipe') |  | ||||||
|     M.bufset('filetype', 'Trans') |  | ||||||
|     M.wipe() |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |     local win = setmetatable({ | ||||||
| M.draw = function() |         title   = nil, | ||||||
|     -- TODO : |         content = nil, | ||||||
|     M.bufset('modifiable', true) |         winid   = winid, | ||||||
|     api.nvim_buf_set_lines(M.bufnr, 0, -1, false, M.lines) |         bufnr   = bufnr, | ||||||
|     for _, hl in ipairs(M.highlights) do |         width   = opt.width, | ||||||
|         api.nvim_buf_add_highlight(M.bufnr, M.hl, hl.name, hl.line, hl._start, hl._end) |         height  = opt.height, | ||||||
|     end |         hl      = api.nvim_create_namespace('TransWinHl'), | ||||||
|  |     }, | ||||||
|     M.bufset('modifiable', false) |         { __index = function(tbl, key) | ||||||
|     -- vim.pretty_print(M.highlights) |             if key == 'content' then | ||||||
| end |                 if tbl.title then | ||||||
|  |                     tbl.content = require('Trans.content')(tbl, tbl.title.size) | ||||||
| ---清空window的数据 |                     tbl.title.modifiable = false | ||||||
| M.wipe = function() |                 else | ||||||
|     M.size = 0 |                     tbl.content = require('Trans.content')(tbl) | ||||||
|     local clear = require('table.clear') |  | ||||||
|     clear(M.lines) |  | ||||||
|     clear(M.highlights) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| M.is_open = function() |  | ||||||
|     return M.winid > 0 and api.nvim_win_is_valid(M.winid) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ---安全的关闭窗口 |  | ||||||
| ---@param interval integer 窗口关闭动画的间隔 |  | ||||||
| M.try_close = function(interval) |  | ||||||
|     if M.is_open() then |  | ||||||
|         local function narrow() |  | ||||||
|             if M.height > 1 then |  | ||||||
|                 M.height = M.height - 1 |  | ||||||
|                 api.nvim_win_set_height(M.winid, M.height) |  | ||||||
|                 vim.defer_fn(narrow, interval) |  | ||||||
|             else |  | ||||||
|                 -- Wait animation done |  | ||||||
|                 vim.defer_fn(function() |  | ||||||
|                     api.nvim_win_close(M.winid, true) |  | ||||||
|                     M.winid = -1 |  | ||||||
|                 end, interval + 2) |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
|  |  | ||||||
|         narrow() |  | ||||||
|     end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| M.cur_height = function() |  | ||||||
|     if api.nvim_win_get_option(M.winid, 'wrap') then |  | ||||||
|         local height = 0 |  | ||||||
|         local width = M.width |  | ||||||
|         local lines = M.lines |  | ||||||
|  |  | ||||||
|         for i = 1, M.size do |  | ||||||
|             height = height + math.max(1, (math.ceil(lines[i]:width() / width))) |  | ||||||
|         end |  | ||||||
|         return height |  | ||||||
|  |  | ||||||
|     else |  | ||||||
|         return M.size |  | ||||||
|     end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| M.adjust = function() |  | ||||||
|     local cur_height = M.cur_height() |  | ||||||
|     if M.height > cur_height then |  | ||||||
|         api.nvim_win_set_height(M.winid, cur_height) |  | ||||||
|         M.height = cur_height |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     if M.size == 1 then |  | ||||||
|         api.nvim_win_set_width(M.winid, M.lines[1]:width()) |  | ||||||
|     end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ---- ============ Utility functions ============ |  | ||||||
| ---设置窗口选项 |  | ||||||
| ---@param option string 需要设置的窗口 |  | ||||||
| ---@param value any 需要设置的值 |  | ||||||
| M.set = function(option, value) |  | ||||||
|     api.nvim_win_set_option(M.winid, option, value) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| ---设置窗口对应buffer的选项 |  | ||||||
| ---@param option string 需要设置的窗口 |  | ||||||
| ---@param value any 需要设置的值 |  | ||||||
| M.bufset = function(option, value) |  | ||||||
|     api.nvim_buf_set_option(M.bufnr, option, value) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| M.normal = function(key) |  | ||||||
|     api.nvim_buf_call(M.bufnr, function() |  | ||||||
|         vim.cmd([[normal! ]] .. key) |  | ||||||
|     end) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| ---设置该窗口的本地的键映射(都为normal模式) |  | ||||||
| ---@param key string 映射的键 |  | ||||||
| ---@param operation any 执行的操作 |  | ||||||
| M.map = function(key, operation) |  | ||||||
|     -- api.nvim_buf_set_keymap(M.bufnr, 'n', key, operation, { silent = true, noremap = true, }) |  | ||||||
|     vim.keymap.set('n', key, operation, { |  | ||||||
|         silent = true, |  | ||||||
|         buffer = M.bufnr, |  | ||||||
|     }) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| --- =================== Window lines ================================ |  | ||||||
| local function insert_line(text) |  | ||||||
|     vim.validate { |  | ||||||
|         text = { text, 's' }, |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     M.size = M.size + 1 |  | ||||||
|     M.lines[M.size] = text |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ---向窗口中添加新行 |  | ||||||
| ---@param newline string 待添加的新行 |  | ||||||
| ---@param opt? table|string 可选的行属性: highlight, TODO : |  | ||||||
| M.addline = function(newline, opt) |  | ||||||
|     insert_line(newline) |  | ||||||
|  |  | ||||||
|     if type(opt) == 'string' then |  | ||||||
|         table.insert(M.highlights, { |  | ||||||
|             name = opt, |  | ||||||
|             line = M.size - 1, -- NOTE : 高亮的行号是以0为第一行 |  | ||||||
|             _start = 0, |  | ||||||
|             _end = -1, |  | ||||||
|         }) |  | ||||||
|         -- elseif type(opt) == 'table' then |  | ||||||
|         --     -- TODO : |  | ||||||
|         --     error('TODO') |  | ||||||
|     end |  | ||||||
| end |  | ||||||
|  |  | ||||||
| ---添加一行新的内容并居中 |  | ||||||
| ---@param text string 需要居中的文本 |  | ||||||
| ---@param highlight? string 可选的高亮组 |  | ||||||
| M.center = function(text, highlight) |  | ||||||
|     vim.validate { |  | ||||||
|         text = { text, 's' } |  | ||||||
|     } |  | ||||||
|     local space = math.floor((M.width - text:width()) / 2) |  | ||||||
|     local interval = (' '):rep(space) |  | ||||||
|     insert_line(interval .. text) |  | ||||||
|     if highlight then |  | ||||||
|         table.insert(M.highlights, { |  | ||||||
|             name = highlight, |  | ||||||
|             line = M.size - 1, |  | ||||||
|             _start = space, |  | ||||||
|             _end = space + #text, |  | ||||||
|         }) |  | ||||||
|     end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ---返回一个行的包装器: 具有 [add_item] [load] 方法 |  | ||||||
| ---能够添加item, 调用load格式化并载入window.lines |  | ||||||
| M.line_wrap = function() |  | ||||||
|     local items = {} |  | ||||||
|     local width = 0 -- 所有item的总width |  | ||||||
|     local size  = 0 -- item数目 |  | ||||||
|     return { |  | ||||||
|         add_item = function(item, highlight) |  | ||||||
|             size = size + 1 |  | ||||||
|             items[size] = { item, highlight } |  | ||||||
|             width = width + item:width() |  | ||||||
|         end, |  | ||||||
|  |  | ||||||
|         load = function() |  | ||||||
|             assert(size > 1, 'no item need be loaded') |  | ||||||
|             local space = math.floor((M.width - width) / (size - 1)) |  | ||||||
|             assert(space > 0, 'try to expand window width') |  | ||||||
|             local interval = (' '):rep(space) |  | ||||||
|             local value = '' |  | ||||||
|             local function load_item(idx) |  | ||||||
|                 local item = items[idx] |  | ||||||
|                 if item[2] then |  | ||||||
|                     table.insert(M.highlights, { |  | ||||||
|                         name = item[2], |  | ||||||
|                         line = M.size, -- NOTE : 此时还没插入新行, size ==> 行号(zero index) |  | ||||||
|                         _start = #value, |  | ||||||
|                         _end = #value + #item[1], |  | ||||||
|                     }) |  | ||||||
|                 end |                 end | ||||||
|                 value = value .. item[1] |                 return tbl.content | ||||||
|  |  | ||||||
|  |             elseif key == 'title' then | ||||||
|  |                 tbl.title = require('Trans.content')(tbl, 0) | ||||||
|  |                 return tbl.title | ||||||
|  |  | ||||||
|  |             else | ||||||
|  |                 return window[key] | ||||||
|             end |             end | ||||||
|  |         end }) | ||||||
|  |  | ||||||
|             load_item(1) |     win:set('winhl', 'Normal:TransWin,FloatBorder:TransBorder') | ||||||
|             for i = 2, size do |     win:bufset('bufhidden', 'wipe') | ||||||
|                 value = value .. interval |     win:bufset('filetype', 'Trans') | ||||||
|                 load_item(i) |     return win | ||||||
|             end |  | ||||||
|  |  | ||||||
|             insert_line(value) |  | ||||||
|         end |  | ||||||
|     } |  | ||||||
| end | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| M.text_wrap = function() |  | ||||||
|     insert_line('') |  | ||||||
|     local l = M.size |  | ||||||
|  |  | ||||||
|     return function(text, highlight) |  | ||||||
|         if highlight then |  | ||||||
|             local _start = #M.lines[l] |  | ||||||
|             local _end = _start + #text |  | ||||||
|             table.insert(M.highlights, { |  | ||||||
|                 name = highlight, |  | ||||||
|                 line = M.size - 1, |  | ||||||
|                 _start = _start, |  | ||||||
|                 _end = _end, |  | ||||||
|             }) |  | ||||||
|         end |  | ||||||
|  |  | ||||||
|         M.lines[l] = M.lines[l] .. text |  | ||||||
|     end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| --- =================== Window Highlights ================================ |  | ||||||
| --- TODO : add helpful function for highlights |  | ||||||
|  |  | ||||||
| return M |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user