Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/pivi_status.lua

Raw
--- Statusline consumer for pivi.
--- It reads the published cache only and never requires pivi, so the
--- statusline keeps working when pivi is absent, unloaded, or broken.
local statusline = require 'bugabinga.statusline'
local auto = require 'std.auto'
local glyph = require 'std.icon'

-- The progress frames the rest of the configuration already animates with.
local SPINNER = {
  glyph.progress_0, glyph.progress_1, glyph.progress_2, glyph.progress_3, glyph.progress_4,
  glyph.progress_5, glyph.progress_6, glyph.progress_7, glyph.progress_8, glyph.progress_9,
}
local INTERVAL = 90

local state = {
  frame = 1,
  timer = nil,
}

local sessions = function()
  local value = vim.g.pivi
  if type( value ) ~= 'table' then return nil, {} end
  return value.active, value.sessions or {}
end

local busy = function( entry )
  return entry ~= nil
    and entry.connection ~= 'stopped'
    and ( entry.run == 'running' or entry.run == 'retrying' or entry.compacting == true )
end

local pending = function( entry )
  if not entry or type( entry.queue ) ~= 'table' then return 0 end
  return #( entry.queue.steering or {} ) + #( entry.queue.follow_up or {} )
end

local label = function( entry )
  if entry.error then
    return '%#ErrorMsg#' .. glyph.error .. ' ' .. ( entry.tool and entry.tool.name or 'error' ) .. '%*'
  end
  if entry.compacting then return '%#WarningMsg#π ' .. glyph.lazy .. ' compacting%*' end
  if entry.run == 'retrying' then return '%#WarningMsg#π ' .. glyph.warn .. ' retry%*' end
  if entry.tool then return '%#DiffChange#π ' .. tostring( entry.tool.name ) .. '%*' end
  if entry.run == 'running' then return '%#DiffChange#π ' .. glyph.star .. ' thinking%*' end
  return 'π'
end

local stop_spinner = function()
  if not state.timer then return end
  state.timer:stop()
  state.timer:close()
  state.timer = nil
end

local start_spinner = function()
  if state.timer then return end
  state.timer = vim.uv.new_timer()
  state.timer:start( INTERVAL, INTERVAL, vim.schedule_wrap( function()
    state.frame = state.frame % #SPINNER + 1
    vim.cmd.redrawstatus()
  end ) )
end

--- Cached only: evaluating the statusline never talks to Pi.
local component = function()
  local active, all = sessions()
  if not active then return '' end

  local entry = all[active]
  if not entry then return '' end

  local pieces = { label( entry ), }
  if busy( entry ) then table.insert( pieces, 1, SPINNER[state.frame] ) end

  local waiting = pending( entry )
  if waiting > 0 then table.insert( pieces, '+' .. tostring( waiting ) ) end

  return table.concat( pieces, ' ' )
end

statusline.register( component )

auto 'pivi_statusline' {
  events = 'User',
  pattern = 'PiviStatus',
  description = 'Animate the pivi statusline while a run is active.',
  command = function()
    local active, all = sessions()
    local entry = active and all[active] or nil
    if busy( entry ) then
      start_spinner()
    else
      stop_spinner()
    end
    vim.cmd.redrawstatus()
  end,
}

auto 'pivi_activity' {
  events = 'User',
  pattern = 'PiviActivity',
  description = 'Announce agent findings that landed in a native list.',
  command = function( event )
    local data = event.data
    if type( data ) ~= 'table' or data.kind ~= 'findings' then return end
    local count = tonumber( data.count ) or 0
    if count == 0 then return end
    vim.notify(
      ( 'π %d finding%s in the quickfix list' ):format( count, count == 1 and '' or 's' ),
      vim.log.levels.INFO
    )
  end,
}

_G.BugabingaPiviStatus = {
  component = component,
  busy = busy,
  pending = pending,
  label = label,
}

return _G.BugabingaPiviStatus