Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/pivi/detail.lua

Raw
local entry = require 'bugabinga.pivi.entry'
local transcript = require 'bugabinga.pivi.transcript'
local navigate = require 'bugabinga.pivi.navigate'

local MAX_RESULT_LINES = 60

local TITLES = {
  prompt = 'submission',
  answer = 'answer',
  tool = 'tool call',
  error = 'error',
}

--- One readable value.
--- A JSON fence would be concealed by the markdown rendering of the float, so
--- arguments are listed rather than encoded.
local readable = function( value )
  if type( value ) == 'string' then return value end
  if type( value ) ~= 'table' then return tostring( value ) end
  return ( vim.inspect( value, { newline = ' ', indent = '', } ) )
end

local result_text = function( result )
  if type( result ) ~= 'table' then return nil end
  local pieces = {}
  for _, block in ipairs( result.content or {} ) do
    if block.type == 'text' and type( block.text ) == 'string' then
      table.insert( pieces, block.text )
    end
  end
  local joined = vim.trim( table.concat( pieces, '\n' ) )
  if joined == '' then return nil end
  return joined
end

--- Everything known about one entry, as readable text.
local describe = function( bufnr, item )
  if not item then return {} end
  local meta = item.meta or {}
  local lines = { '# ' .. ( TITLES[item.kind] or item.kind ), '', }

  if meta.tool then table.insert( lines, '- tool: `' .. tostring( meta.tool ) .. '`' ) end
  if meta.failed ~= nil then
    table.insert( lines, '- outcome: ' .. ( meta.failed and 'failed' or 'succeeded' ) )
  end

  local location = navigate.location_of( item )
  if location then
    local place = location.path
    if location.line then place = place .. ':' .. tostring( location.line ) end
    table.insert( lines, '- location: `' .. place .. '`' )
  end

  table.insert( lines, '- lines: ' .. tostring( item.first ) .. '-' .. tostring( item.last ) )

  if type( meta.args ) == 'table' and next( meta.args ) ~= nil then
    table.insert( lines, '' )
    table.insert( lines, '## arguments' )
    table.insert( lines, '' )

    local names = vim.tbl_keys( meta.args )
    table.sort( names, function( left, right ) return tostring( left ) < tostring( right ) end )
    for _, name in ipairs( names ) do
      table.insert( lines, '- ' .. tostring( name ) .. ': ' .. readable( meta.args[name] ) )
    end
  end

  if meta.error then
    vim.list_extend( lines, { '', '## error', '', tostring( meta.error ), } )
  end

  local produced = result_text( meta.result )
  if produced then
    local shown = vim.split( produced, '\n', { plain = true, } )
    local truncated = #shown > MAX_RESULT_LINES
    if truncated then shown = vim.list_slice( shown, 1, MAX_RESULT_LINES ) end
    vim.list_extend( lines, { '', '## result', '', } )
    vim.list_extend( lines, shown )
    if truncated then
      table.insert( lines, '' )
      table.insert( lines, '_the entry itself holds the rest_' )
    end
  end

  if item.kind ~= 'tool' and not meta.error then
    local content = vim.api.nvim_buf_get_lines( bufnr, item.first - 1, item.last, false )
    vim.list_extend( lines, { '', '## text', '', } )
    vim.list_extend( lines, content )
  end

  return lines
end

--- Inspects the entry under the cursor without leaving the transcript.
--- @return number? bufnr the floating buffer
--- @return number? winid the floating window
local inspect = function( bufnr, line_number )
  local item = entry.at( bufnr, line_number or vim.api.nvim_win_get_cursor( 0 )[1] )
  if not item then return nil end

  local lines = describe( bufnr, item )
  if #lines == 0 then return nil end

  -- A nameless buffer makes a statusline fall back to the working directory,
  -- so even a transient float says what it is.
  local ok, float, window = pcall( vim.lsp.util.open_floating_preview, lines, 'markdown', {
    border = 'rounded',
    focus = false,
    focusable = true,
    max_width = 100,
    max_height = 30,
  } )
  if not ok then return nil end

  if float and vim.api.nvim_buf_is_valid( float ) then
    pcall( vim.api.nvim_buf_set_name, float, 'pivi://' .. ( item.meta.tool or item.kind ) .. '/detail' )
  end

  return float, window
end

local bind = function( bufnr )
  vim.keymap.set( 'n', '<Plug>(pivi-inspect-entry)', function() inspect( bufnr ) end,
    { buffer = bufnr, desc = 'pivi inspect the entry', } )
end

transcript.on_buffer( bind )

return {
  describe = describe,
  inspect = inspect,
  bind = bind,
}