Use floating window for completion menus (#224)
* WIP * WIP * Fix #226 * Insert text * Emulate vim native * テキトウ * Tekito * Move scrollbar impl * aaa * Ignore unexpected event * fix * fix scroll * Refactor (conflict...) * Fix bug * Positive integer * Refactor a bit * Fix for pumheight=0 * fx * Improve matching highlight * Improve colorscheme handling * fmt * Add cmp.visible * Fix pum pos * ABBR_MARGIN * Fix cel calculation * up * refactor * fix * a * a * compat * Remove current completion state * Fix ghost text * Add feature toggle * highlight customization * Update * Add breaking change announcement * Add README.md * Remove unused function * extmark ephemeral ghost text * Support native comp * Fix docs pos * a * Remove if native menu visible * theme async * Improvement idea: option to disables insert on select item (#240) * use ghost text instead of insertion on prev/next item * add disables_insert_on_selection option * move disable_insert_on_select option as argumet on * update README * use an enum behavior to disable insert on select * Adopt contribution * Preselect * Improve * Change configuration option * a * Improve * Improve * Implement proper <C-e> behavior to native/custom * Support <C-c> maybe * Improve docs view * Improve * Avoid syntax leak * TODO: refactor * Fix * Revert win pos * fmt * ghost text remaining * Don't use italic by default * bottom * dedup by label * Ignore events * up * Hacky native view partial support * up * perf * improve * more cache * fmt * Fix format option * fmt * recheck * Fix * Improve * Improve * compat * implement redraw * improve * up * fmt/lint * immediate ghost text * source timeout * up * Support multibyte * disable highlight * up * improve * fmt * fmt * fix * fix * up * up * Use screenpos * Add undojoin check * Fix height * matcher bug * Fix dot-repeat * Remove undojoin * macro * Support dot-repeat * MacroSafe * Default item count is 200 * fmt Co-authored-by: Eric Puentes <eric.puentes@mercadolibre.com.co>
This commit is contained in:
@@ -26,19 +26,39 @@ async.throttle = function(fn, timeout)
|
||||
end
|
||||
timer:stop()
|
||||
|
||||
local delta = math.max(0, self.timeout - (vim.loop.now() - time))
|
||||
timer:start(
|
||||
delta,
|
||||
0,
|
||||
vim.schedule_wrap(function()
|
||||
time = nil
|
||||
fn(unpack(args))
|
||||
end)
|
||||
)
|
||||
local delta = math.max(1, self.timeout - (vim.loop.now() - time))
|
||||
timer:start(delta, 0, function()
|
||||
time = nil
|
||||
fn(unpack(args))
|
||||
end)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
---Timeout callback function
|
||||
---@param fn function
|
||||
---@param timeout number
|
||||
---@return function
|
||||
async.timeout = function(fn, timeout)
|
||||
local timer
|
||||
local done = false
|
||||
local callback = function(...)
|
||||
if not done then
|
||||
done = true
|
||||
timer:stop()
|
||||
timer:close()
|
||||
fn(...)
|
||||
end
|
||||
end
|
||||
timer = vim.loop.new_timer()
|
||||
timer:start(timeout, 0, function()
|
||||
callback()
|
||||
end)
|
||||
return callback
|
||||
end
|
||||
|
||||
---@alias cmp.AsyncDedup fun(callback: function): function
|
||||
|
||||
---Create deduplicated callback
|
||||
---@return function
|
||||
async.dedup = function()
|
||||
|
||||
33
lua/cmp/utils/binary.lua
Normal file
33
lua/cmp/utils/binary.lua
Normal file
@@ -0,0 +1,33 @@
|
||||
local binary = {}
|
||||
|
||||
---Insert item to list to ordered index
|
||||
---@param list any[]
|
||||
---@param item any
|
||||
---@param func fun(a: any, b: any): "1"|"-1"|"0"
|
||||
binary.insort = function(list, item, func)
|
||||
table.insert(list, binary.search(list, item, func), item)
|
||||
end
|
||||
|
||||
---Search suitable index from list
|
||||
---@param list any[]
|
||||
---@param item any
|
||||
---@param func fun(a: any, b: any): "1"|"-1"|"0"
|
||||
---@return number
|
||||
binary.search = function(list, item, func)
|
||||
local s = 1
|
||||
local e = #list
|
||||
while s <= e do
|
||||
local idx = math.floor((e + s) / 2)
|
||||
local diff = func(item, list[idx])
|
||||
if diff > 0 then
|
||||
s = idx + 1
|
||||
elseif diff < 0 then
|
||||
e = idx - 1
|
||||
else
|
||||
return idx + 1
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
return binary
|
||||
28
lua/cmp/utils/binary_spec.lua
Normal file
28
lua/cmp/utils/binary_spec.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
local binary = require('cmp.utils.binary')
|
||||
|
||||
describe('utils.binary', function()
|
||||
it('insort', function()
|
||||
local func = function(a, b)
|
||||
return a.score - b.score
|
||||
end
|
||||
local list = {}
|
||||
binary.insort(list, { id = 'a', score = 1 }, func)
|
||||
binary.insort(list, { id = 'b', score = 5 }, func)
|
||||
binary.insort(list, { id = 'c', score = 2.5 }, func)
|
||||
binary.insort(list, { id = 'd', score = 2 }, func)
|
||||
binary.insort(list, { id = 'e', score = 8 }, func)
|
||||
binary.insort(list, { id = 'g', score = 8 }, func)
|
||||
binary.insort(list, { id = 'h', score = 7 }, func)
|
||||
binary.insort(list, { id = 'i', score = 6 }, func)
|
||||
binary.insort(list, { id = 'j', score = 4 }, func)
|
||||
assert.are.equal(list[1].id, 'a')
|
||||
assert.are.equal(list[2].id, 'd')
|
||||
assert.are.equal(list[3].id, 'c')
|
||||
assert.are.equal(list[4].id, 'j')
|
||||
assert.are.equal(list[5].id, 'b')
|
||||
assert.are.equal(list[6].id, 'i')
|
||||
assert.are.equal(list[7].id, 'h')
|
||||
assert.are.equal(list[8].id, 'e')
|
||||
assert.are.equal(list[9].id, 'g')
|
||||
end)
|
||||
end)
|
||||
@@ -14,7 +14,7 @@ end
|
||||
cache.get = function(self, key)
|
||||
key = self:key(key)
|
||||
if self.entries[key] ~= nil then
|
||||
return unpack(self.entries[key])
|
||||
return self.entries[key]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
@@ -22,9 +22,9 @@ end
|
||||
---Set cache value explicitly
|
||||
---@param key string
|
||||
---@vararg any
|
||||
cache.set = function(self, key, ...)
|
||||
cache.set = function(self, key, value)
|
||||
key = self:key(key)
|
||||
self.entries[key] = { ... }
|
||||
self.entries[key] = value
|
||||
end
|
||||
|
||||
---Ensure value by callback
|
||||
@@ -33,9 +33,11 @@ end
|
||||
cache.ensure = function(self, key, callback)
|
||||
local value = self:get(key)
|
||||
if value == nil then
|
||||
self:set(key, callback())
|
||||
local v = callback()
|
||||
self:set(key, v)
|
||||
return v
|
||||
end
|
||||
return self:get(key)
|
||||
return value
|
||||
end
|
||||
|
||||
---Clear all cache entries
|
||||
|
||||
51
lua/cmp/utils/event.lua
Normal file
51
lua/cmp/utils/event.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
---@class cmp.Event
|
||||
---@field private events table<string, function[]>
|
||||
local event = {}
|
||||
|
||||
---Create vents
|
||||
event.new = function()
|
||||
local self = setmetatable({}, { __index = event })
|
||||
self.events = {}
|
||||
return self
|
||||
end
|
||||
|
||||
---Add event listener
|
||||
---@param name string
|
||||
---@param callback function
|
||||
---@return function
|
||||
event.on = function(self, name, callback)
|
||||
if not self.events[name] then
|
||||
self.events[name] = {}
|
||||
end
|
||||
table.insert(self.events[name], callback)
|
||||
return function()
|
||||
self:off(name, callback)
|
||||
end
|
||||
end
|
||||
|
||||
---Remove event listener
|
||||
---@param name string
|
||||
---@param callback function
|
||||
event.off = function(self, name, callback)
|
||||
for i, callback_ in ipairs(self.events[name] or {}) do
|
||||
if callback_ == callback then
|
||||
table.remove(self.events[name], i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Remove all events
|
||||
event.clear = function(self)
|
||||
self.events = {}
|
||||
end
|
||||
|
||||
---Emit event
|
||||
---@param name string
|
||||
event.emit = function(self, name, ...)
|
||||
for _, callback in ipairs(self.events[name] or {}) do
|
||||
callback(...)
|
||||
end
|
||||
end
|
||||
|
||||
return event
|
||||
46
lua/cmp/utils/highlight.lua
Normal file
46
lua/cmp/utils/highlight.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
local highlight = {}
|
||||
|
||||
highlight.keys = {
|
||||
'gui',
|
||||
'guifg',
|
||||
'guibg',
|
||||
'cterm',
|
||||
'ctermfg',
|
||||
'ctermbg',
|
||||
}
|
||||
|
||||
highlight.inherit = function(name, source, override)
|
||||
local cmd = ('highlight! default %s'):format(name)
|
||||
for _, key in ipairs(highlight.keys) do
|
||||
if override[key] then
|
||||
cmd = cmd .. (' %s=%s'):format(key, override[key])
|
||||
else
|
||||
local v = highlight.get(source, key)
|
||||
v = v == '' and 'NONE' or v
|
||||
cmd = cmd .. (' %s=%s'):format(key, v)
|
||||
end
|
||||
end
|
||||
vim.cmd(cmd)
|
||||
end
|
||||
|
||||
highlight.get = function(source, key)
|
||||
if key == 'gui' or key == 'cterm' then
|
||||
local ui = {}
|
||||
for _, k in ipairs({ 'bold', 'italic', 'reverse', 'inverse', 'standout', 'underline', 'undercurl', 'strikethrough' }) do
|
||||
if vim.fn.synIDattr(vim.fn.hlID(source), k, key) == 1 then
|
||||
table.insert(ui, k)
|
||||
end
|
||||
end
|
||||
return table.concat(ui, ',')
|
||||
elseif key == 'guifg' then
|
||||
return vim.fn.synIDattr(vim.fn.hlID(source), 'fg#', 'gui')
|
||||
elseif key == 'guibg' then
|
||||
return vim.fn.synIDattr(vim.fn.hlID(source), 'bg#', 'gui')
|
||||
elseif key == 'ctermfg' then
|
||||
return vim.fn.synIDattr(vim.fn.hlID(source), 'fg', 'term')
|
||||
elseif key == 'ctermbg' then
|
||||
return vim.fn.synIDattr(vim.fn.hlID(source), 'bg', 'term')
|
||||
end
|
||||
end
|
||||
|
||||
return highlight
|
||||
@@ -149,8 +149,12 @@ misc.set(_G, { 'cmp', 'utils', 'keymap', 'listen', 'run' }, function(mode, keys)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local fallback = keymap.listen.cache:get({ mode, bufnr, keys }).fallback
|
||||
local callback = keymap.listen.cache:get({ mode, bufnr, keys }).callback
|
||||
local done = false
|
||||
callback(keys, function()
|
||||
keymap.feedkeys(keymap.t(fallback), 'i')
|
||||
if not done then
|
||||
done = true
|
||||
keymap.feedkeys(keymap.t(fallback), 'i')
|
||||
end
|
||||
end)
|
||||
return keymap.t('<Ignore>')
|
||||
end)
|
||||
|
||||
@@ -35,7 +35,7 @@ end
|
||||
---@return T
|
||||
misc.merge = function(v1, v2)
|
||||
local merge1 = type(v1) == 'table' and (not vim.tbl_islist(v1) or vim.tbl_isempty(v1))
|
||||
local merge2 = type(v2) == 'table' and (not vim.tbl_islist(v1) or vim.tbl_isempty(v1))
|
||||
local merge2 = type(v2) == 'table' and (not vim.tbl_islist(v2) or vim.tbl_isempty(v2))
|
||||
if merge1 and merge2 then
|
||||
local new_tbl = {}
|
||||
for k, v in pairs(v2) do
|
||||
|
||||
@@ -15,6 +15,8 @@ INVALID_CHARS[string.byte('\t')] = true
|
||||
INVALID_CHARS[string.byte('\n')] = true
|
||||
INVALID_CHARS[string.byte('\r')] = true
|
||||
|
||||
local NR_BYTE = string.byte('\n')
|
||||
|
||||
local PAIR_CHARS = {}
|
||||
PAIR_CHARS[string.byte('[')] = string.byte(']')
|
||||
PAIR_CHARS[string.byte('(')] = string.byte(')')
|
||||
@@ -72,24 +74,6 @@ str.strikethrough = function(text)
|
||||
return buffer
|
||||
end
|
||||
|
||||
---omit
|
||||
---@param text string
|
||||
---@param width number
|
||||
---@return string
|
||||
str.omit = function(text, width)
|
||||
if width == 0 then
|
||||
return ''
|
||||
end
|
||||
|
||||
if not text then
|
||||
text = ''
|
||||
end
|
||||
if #text > width then
|
||||
return string.sub(text, 1, width + 1) .. '...'
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
---trim
|
||||
---@param text string
|
||||
---@return string
|
||||
@@ -136,21 +120,12 @@ str.get_word = function(text, stop_char)
|
||||
return text
|
||||
end
|
||||
|
||||
---Get character length.
|
||||
---@param text string
|
||||
---@param s number
|
||||
---@param e number
|
||||
---@return number
|
||||
str.chars = function(text, s, e)
|
||||
return vim.fn.strchars(string.sub(text, s, e))
|
||||
end
|
||||
|
||||
---Oneline
|
||||
---@param text string
|
||||
---@return string
|
||||
str.oneline = function(text)
|
||||
for i = 1, #text do
|
||||
if string.byte(text, i) == string.byte('\n', 1) then
|
||||
if string.byte(text, i) == NR_BYTE then
|
||||
return string.sub(text, 1, i - 1)
|
||||
end
|
||||
end
|
||||
|
||||
256
lua/cmp/utils/window.lua
Normal file
256
lua/cmp/utils/window.lua
Normal file
@@ -0,0 +1,256 @@
|
||||
local cache = require('cmp.utils.cache')
|
||||
local misc = require('cmp.utils.misc')
|
||||
|
||||
---@class cmp.WindowStyle
|
||||
---@field public relative string
|
||||
---@field public row number
|
||||
---@field public col number
|
||||
---@field public width number
|
||||
---@field public height number
|
||||
---@field public zindex number|nil
|
||||
|
||||
---@class cmp.Window
|
||||
---@field public buf number
|
||||
---@field public win number|nil
|
||||
---@field public sbuf1 number
|
||||
---@field public swin1 number|nil
|
||||
---@field public sbuf2 number
|
||||
---@field public swin2 number|nil
|
||||
---@field public style cmp.WindowStyle
|
||||
---@field public opt table<string, any>
|
||||
---@field public cache cmp.Cache
|
||||
local window = {}
|
||||
|
||||
---new
|
||||
---@return cmp.Window
|
||||
window.new = function()
|
||||
local self = setmetatable({}, { __index = window })
|
||||
self.buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_option(self.buf, 'undolevels', -1)
|
||||
vim.api.nvim_buf_set_option(self.buf, 'buftype', 'nofile')
|
||||
self.win = nil
|
||||
self.style = {}
|
||||
self.sbuf1 = vim.api.nvim_create_buf(false, true)
|
||||
self.swin1 = nil
|
||||
self.sbuf2 = vim.api.nvim_create_buf(false, true)
|
||||
self.swin2 = nil
|
||||
self.cache = cache.new()
|
||||
self.opt = {}
|
||||
self.id = 0
|
||||
return self
|
||||
end
|
||||
|
||||
---Set window option.
|
||||
---NOTE: If the window already visible, immediately applied to it.
|
||||
---@param key string
|
||||
---@param value any
|
||||
window.option = function(self, key, value)
|
||||
if value == nil then
|
||||
return self.opt[key]
|
||||
end
|
||||
|
||||
self.opt[key] = value
|
||||
if self:visible() then
|
||||
vim.api.nvim_win_set_option(self.win, key, value)
|
||||
end
|
||||
end
|
||||
|
||||
---Set style.
|
||||
---@param style cmp.WindowStyle
|
||||
window.set_style = function(self, style)
|
||||
if vim.o.columns and vim.o.columns <= style.col + style.width then
|
||||
style.width = vim.o.columns - style.col - 1
|
||||
end
|
||||
if vim.o.lines and vim.o.lines <= style.row + style.height then
|
||||
style.height = vim.o.lines - style.row - 1
|
||||
end
|
||||
self.style = style
|
||||
self.style.zindex = self.style.zindex or 1
|
||||
end
|
||||
|
||||
---Open window
|
||||
---@param style cmp.WindowStyle
|
||||
window.open = function(self, style)
|
||||
self.id = self.id + 1
|
||||
|
||||
if style then
|
||||
self:set_style(style)
|
||||
end
|
||||
|
||||
if self.style.width < 1 or self.style.height < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
||||
vim.api.nvim_win_set_config(self.win, self.style)
|
||||
else
|
||||
local s = misc.copy(self.style)
|
||||
s.noautocmd = true
|
||||
self.win = vim.api.nvim_open_win(self.buf, false, s)
|
||||
for k, v in pairs(self.opt) do
|
||||
vim.api.nvim_win_set_option(self.win, k, v)
|
||||
end
|
||||
end
|
||||
self:update()
|
||||
end
|
||||
|
||||
---Update
|
||||
window.update = function(self)
|
||||
if self:has_scrollbar() then
|
||||
local total = self:get_content_height()
|
||||
local info = self:info()
|
||||
local bar_height = math.ceil(info.height * (info.height / total))
|
||||
local bar_offset = math.min(info.height - bar_height, math.floor(info.height * (vim.fn.getwininfo(self.win)[1].topline / total)))
|
||||
local style1 = {}
|
||||
style1.relative = 'editor'
|
||||
style1.style = 'minimal'
|
||||
style1.width = 1
|
||||
style1.height = info.height
|
||||
style1.row = info.row
|
||||
style1.col = info.col + info.width - (info.has_scrollbar and 1 or 0)
|
||||
style1.zindex = (self.style.zindex and (self.style.zindex + 1) or 1)
|
||||
if self.swin1 and vim.api.nvim_win_is_valid(self.swin1) then
|
||||
vim.api.nvim_win_set_config(self.swin1, style1)
|
||||
else
|
||||
style1.noautocmd = true
|
||||
self.swin1 = vim.api.nvim_open_win(self.sbuf1, false, style1)
|
||||
vim.api.nvim_win_set_option(self.swin1, 'winhighlight', 'Normal:PmenuSbar,NormalNC:PmenuSbar,NormalFloat:PmenuSbar')
|
||||
end
|
||||
local style2 = {}
|
||||
style2.relative = 'editor'
|
||||
style2.style = 'minimal'
|
||||
style2.width = 1
|
||||
style2.height = bar_height
|
||||
style2.row = info.row + bar_offset
|
||||
style2.col = info.col + info.width - (info.has_scrollbar and 1 or 0)
|
||||
style2.zindex = (self.style.zindex and (self.style.zindex + 2) or 2)
|
||||
if self.swin2 and vim.api.nvim_win_is_valid(self.swin2) then
|
||||
vim.api.nvim_win_set_config(self.swin2, style2)
|
||||
else
|
||||
style2.noautocmd = true
|
||||
self.swin2 = vim.api.nvim_open_win(self.sbuf2, false, style2)
|
||||
vim.api.nvim_win_set_option(self.swin2, 'winhighlight', 'Normal:PmenuThumb,NormalNC:PmenuThumb,NormalFloat:PmenuThumb')
|
||||
end
|
||||
else
|
||||
if self.swin1 and vim.api.nvim_win_is_valid(self.swin1) then
|
||||
vim.api.nvim_win_close(self.swin1, false)
|
||||
self.swin1 = nil
|
||||
end
|
||||
if self.swin2 and vim.api.nvim_win_is_valid(self.swin2) then
|
||||
vim.api.nvim_win_close(self.swin2, false)
|
||||
self.swin2 = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Close window
|
||||
window.close = function(self)
|
||||
local id = self.id
|
||||
vim.schedule(function()
|
||||
if id == self.id then
|
||||
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
||||
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
||||
vim.api.nvim_win_close(self.win, true)
|
||||
self.win = nil
|
||||
end
|
||||
if self.swin1 and vim.api.nvim_win_is_valid(self.swin1) then
|
||||
vim.api.nvim_win_close(self.swin1, false)
|
||||
self.swin1 = nil
|
||||
end
|
||||
if self.swin2 and vim.api.nvim_win_is_valid(self.swin2) then
|
||||
vim.api.nvim_win_close(self.swin2, false)
|
||||
self.swin2 = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---Return the window is visible or not.
|
||||
window.visible = function(self)
|
||||
return self.win and vim.api.nvim_win_is_valid(self.win)
|
||||
end
|
||||
|
||||
---Return the scrollbar will shown or not.
|
||||
window.has_scrollbar = function(self)
|
||||
return (self.style.height or 0) < self:get_content_height()
|
||||
end
|
||||
|
||||
---Return win info.
|
||||
window.info = function(self)
|
||||
local border_width = self:get_border_width()
|
||||
local has_scrollbar = self:has_scrollbar()
|
||||
return {
|
||||
row = self.style.row,
|
||||
col = self.style.col,
|
||||
width = self.style.width + border_width + (has_scrollbar and 1 or 0),
|
||||
height = self.style.height,
|
||||
border_width = border_width,
|
||||
has_scrollbar = has_scrollbar,
|
||||
}
|
||||
end
|
||||
|
||||
---Get border width
|
||||
---@return number
|
||||
window.get_border_width = function(self)
|
||||
local border = self.style.border
|
||||
if type(border) == 'table' then
|
||||
local new_border = {}
|
||||
while #new_border < 8 do
|
||||
for _, b in ipairs(border) do
|
||||
table.insert(new_border, b)
|
||||
end
|
||||
end
|
||||
border = new_border
|
||||
end
|
||||
|
||||
local w = 0
|
||||
if border then
|
||||
if type(border) == 'string' then
|
||||
if border == 'single' then
|
||||
w = 2
|
||||
elseif border == 'solid' then
|
||||
w = 2
|
||||
elseif border == 'double' then
|
||||
w = 2
|
||||
elseif border == 'rounded' then
|
||||
w = 2
|
||||
elseif border == 'shadow' then
|
||||
w = 1
|
||||
end
|
||||
elseif type(border) == 'table' then
|
||||
local b4 = type(border[4]) == 'table' and border[4][1] or border[4]
|
||||
if #b4 > 0 then
|
||||
w = w + 1
|
||||
end
|
||||
local b8 = type(border[8]) == 'table' and border[8][1] or border[8]
|
||||
if #b8 > 0 then
|
||||
w = w + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return w
|
||||
end
|
||||
|
||||
---Get scroll height.
|
||||
---@return number
|
||||
window.get_content_height = function(self)
|
||||
if not self:option('wrap') then
|
||||
return vim.api.nvim_buf_line_count(self.buf)
|
||||
end
|
||||
|
||||
return self.cache:ensure({
|
||||
'get_content_height',
|
||||
self.style.width,
|
||||
self.buf,
|
||||
vim.api.nvim_buf_get_changedtick(self.buf),
|
||||
}, function()
|
||||
local height = 0
|
||||
for _, text in ipairs(vim.api.nvim_buf_get_lines(self.buf, 0, -1, false)) do
|
||||
height = height + math.ceil(math.max(1, vim.fn.strdisplaywidth(text)) / self.style.width)
|
||||
end
|
||||
return height
|
||||
end)
|
||||
end
|
||||
|
||||
return window
|
||||
Reference in New Issue
Block a user