--- Manages creating/updating scrollbar gutter and thumb windows --- @class blink.cmp.ScrollbarWin --- @field enable_gutter boolean --- @field thumb_win? number --- @field gutter_win? number --- @field buf? number --- --- @field new fun(opts: blink.cmp.ScrollbarConfig): blink.cmp.ScrollbarWin --- @field is_visible fun(self: blink.cmp.ScrollbarWin): boolean --- @field show_thumb fun(self: blink.cmp.ScrollbarWin, geometry: blink.cmp.ScrollbarGeometry) --- @field show_gutter fun(self: blink.cmp.ScrollbarWin, geometry: blink.cmp.ScrollbarGeometry) --- @field hide_thumb fun(self: blink.cmp.ScrollbarWin) --- @field hide_gutter fun(self: blink.cmp.ScrollbarWin) --- @field hide fun(self: blink.cmp.ScrollbarWin) --- @field _make_win fun(self: blink.cmp.ScrollbarWin, geometry: blink.cmp.ScrollbarGeometry, hl_group: string): number --- @field redraw_if_needed fun(self: blink.cmp.ScrollbarWin) --- @type blink.cmp.ScrollbarWin --- @diagnostic disable-next-line: missing-fields local scrollbar_win = {} function scrollbar_win.new(opts) return setmetatable(opts, { __index = scrollbar_win }) end function scrollbar_win:is_visible() return self.thumb_win ~= nil and vim.api.nvim_win_is_valid(self.thumb_win) end function scrollbar_win:show_thumb(geometry) -- create window if it doesn't exist if self.thumb_win == nil or not vim.api.nvim_win_is_valid(self.thumb_win) then self.thumb_win = self:_make_win(geometry, 'BlinkCmpScrollBarThumb') else -- update with the geometry local thumb_existing_config = vim.api.nvim_win_get_config(self.thumb_win) local thumb_config = vim.tbl_deep_extend('force', thumb_existing_config, geometry) vim.api.nvim_win_set_config(self.thumb_win, thumb_config) end self:redraw_if_needed() end function scrollbar_win:show_gutter(geometry) if not self.enable_gutter then return end -- create window if it doesn't exist if self.gutter_win == nil or not vim.api.nvim_win_is_valid(self.gutter_win) then self.gutter_win = self:_make_win(geometry, 'BlinkCmpScrollBarGutter') else -- update with the geometry local gutter_existing_config = vim.api.nvim_win_get_config(self.gutter_win) local gutter_config = vim.tbl_deep_extend('force', gutter_existing_config, geometry) vim.api.nvim_win_set_config(self.gutter_win, gutter_config) end self:redraw_if_needed() end function scrollbar_win:hide_thumb() if self.thumb_win and vim.api.nvim_win_is_valid(self.thumb_win) then vim.api.nvim_win_close(self.thumb_win, true) self.thumb_win = nil self:redraw_if_needed() end end function scrollbar_win:hide_gutter() if self.gutter_win and vim.api.nvim_win_is_valid(self.gutter_win) then vim.api.nvim_win_close(self.gutter_win, true) self.gutter_win = nil self:redraw_if_needed() end end function scrollbar_win:hide() self:hide_thumb() self:hide_gutter() end function scrollbar_win:_make_win(geometry, hl_group) if self.buf == nil or not vim.api.nvim_buf_is_valid(self.buf) then self.buf = vim.api.nvim_create_buf(false, true) end local win_config = vim.tbl_deep_extend('force', geometry, { style = 'minimal', focusable = false, noautocmd = true, }) local win = vim.api.nvim_open_win(self.buf, false, win_config) vim.api.nvim_set_option_value('winhighlight', 'Normal:' .. hl_group .. ',EndOfBuffer:' .. hl_group, { win = win }) return win end local redraw_queued = false function scrollbar_win:redraw_if_needed() if redraw_queued or vim.api.nvim_get_mode().mode ~= 'c' then return end redraw_queued = true vim.schedule(function() redraw_queued = false if self.gutter_win ~= nil and vim.api.nvim_win_is_valid(self.gutter_win) then vim.api.nvim__redraw({ win = self.gutter_win, flush = true }) end if self.thumb_win ~= nil and vim.api.nvim_win_is_valid(self.thumb_win) then vim.api.nvim__redraw({ win = self.thumb_win, flush = true }) end end) end return scrollbar_win