current commit
260415.17 · refactor(previewer): handle preview buffer cache
-- cache entry for preview buf

---rename bufnr to buf?
---@class fzf-lua.BcacheEntry
---@field bufnr integer
---@field tick? integer
---@field min_winopts? boolean
---@field pos? [integer, integer]|true cached cursor positions

---@class fzf-lua.Bcache
---@field private entries table<any, fzf-lua.BcacheEntry> cached preview entries
---@field private buffers table<integer, fzf-lua.BcacheEntry> cached bufnrs
---@field private bufdel fun(buf: integer) callback to delete a buffer when evicted from cache
local M = {}
M.__index = M

---@param bufdel fun(buf: integer)
---@return fzf-lua.Bcache
function M.new(bufdel)
  return setmetatable({ entries = {}, buffers = {}, bufdel = bufdel }, M)
end

---Prioritize entry.cache_key since override self:key_from_entry is not allowed now
---@param entry fzf-lua.buffer_or_file.Entry
---@return string?
local key_from_entry = function(entry)
  -- entry.do_not_cache -> entry.cache_key=false?
  if entry.do_not_cache then return nil end
  return entry.cache_key
      or entry.bufnr and ("bufnr:%d"):format(entry.bufnr)
      or entry.uri
      or entry.path
end

---@param entry fzf-lua.buffer_or_file.Entry
---@return fzf-lua.BcacheEntry?
function M:get(entry)
  return self.entries[key_from_entry(entry)]
end

---cache only buffer have key_from_entry
---@param entry fzf-lua.buffer_or_file.Entry
---@param buf integer buf should be valid
---@param min_winopts boolean?
function M:set(entry, buf, min_winopts)
  local key = key_from_entry(entry)
  if not key then return end
  local cached = self.entries[key]
  if cached and cached.bufnr == buf then
    cached.tick = entry.tick
    return
  elseif cached then
    self.bufdel(cached.bufnr)
  end
  ---@type fzf-lua.BcacheEntry
  local newcache = {
    bufnr = buf,
    tick = entry.tick,
    min_winopts = min_winopts,
    pos = true, -- reset scroll position
  }
  self.entries[key] = newcache
  self.buffers[buf] = newcache
  -- remove buffer auto-delete since it's now cached
  vim.bo[buf].bufhidden = "hide"
end

---@param entry fzf-lua.buffer_or_file.Entry
---@return fzf-lua.BcacheEntry?, boolean? (true: stale, false: valid, nil: not cached)
function M:check(entry)
  local cached = self.entries[key_from_entry(entry)]
  return cached, cached and entry.tick ~= cached.tick or nil
end

---nop if update on a un-managed buf
---@param buf integer
---@param pos [integer, integer]|true set/reset cursor position
function M:update_pos(buf, pos)
  local cached = self.buffers[buf]
  if cached then cached.pos = pos end
end

---nop if update on a un-managed buf
---@param buf integer
function M:reset_pos(buf)
  self:update_pos(buf, true)
end

---@param buf integer
---@return [integer, integer]|true?
function M:get_pos(buf)
  local cached = self.buffers[buf]
  return cached and cached.pos or nil
end

function M:clear()
  for _, entry in pairs(self.entries) do
    self.bufdel(entry.bufnr)
  end
  self.entries = {}
  self.buffers = {}
end

return M