Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/pivi/bridge.lua

Raw
local edit = require 'bugabinga.pivi.edit'
local context = require 'bugabinga.pivi.context'
local activity = require 'bugabinga.pivi.activity'
local docs = require 'bugabinga.pivi.docs'

local failure = function( message )
  return { ok = false, error = tostring( message ), }
end

local ensure_buffer = function( path )
  local bufnr = edit.buffer_for( path )
  if bufnr then return bufnr, true end
  return nil, false
end

local actions = {}

--- Loaded buffers, including unsaved content, win over disk.
actions.read = function( request )
  if type( request.path ) ~= 'string' then return failure 'path is required' end
  local content, source = edit.read( request.path )
  if content == nil then return failure( 'cannot read ' .. request.path ) end
  return { ok = true, content = content, source = source, }
end

actions.write = function( request )
  if type( request.path ) ~= 'string' then return failure 'path is required' end
  if type( request.content ) ~= 'string' then return failure 'content is required' end

  local bufnr = ensure_buffer( request.path )
  if not bufnr then
    local ok, problem = pcall( vim.fn.writefile, vim.split( request.content, '\n', { plain = true, } ), request.path )
    if not ok then return failure( problem ) end
    return { ok = true, source = 'disk', }
  end

  local applied, joined = edit.set_content( bufnr, request.content )
  if not applied then return failure 'buffer write failed' end
  local persisted = edit.persist( bufnr )
  return { ok = true, source = 'buffer', joined = joined, persisted = persisted, }
end

actions.edit = function( request )
  if type( request.path ) ~= 'string' then return failure 'path is required' end
  if type( request.old_text ) ~= 'string' then return failure 'old_text is required' end
  if type( request.new_text ) ~= 'string' then return failure 'new_text is required' end

  local bufnr = ensure_buffer( request.path )
  if not bufnr then
    local content = edit.read( request.path )
    if content == nil then return failure( 'cannot read ' .. request.path ) end
    local first = content:find( request.old_text, 1, true )
    if not first then return failure 'old_text not found' end
    local replaced = content:sub( 1, first - 1 ) .. request.new_text .. content:sub( first + #request.old_text )
    local ok, problem = pcall( vim.fn.writefile, vim.split( replaced, '\n', { plain = true, } ), request.path )
    if not ok then return failure( problem ) end
    return { ok = true, source = 'disk', }
  end

  local applied, problem = edit.replace( bufnr, request.old_text, request.new_text )
  if not applied then return failure( problem ) end
  local persisted = edit.persist( bufnr )
  return { ok = true, source = 'buffer', joined = problem, persisted = persisted, }
end

actions.state = function()
  return { ok = true, state = context.editor(), }
end

actions.view = function()
  return { ok = true, view = context.visible(), }
end

--- Resolves help exactly as this Neovim would, plugin documentation included.
actions.help = function( request )
  if type( request.tag ) ~= 'string' or request.tag == '' then return failure 'tag is required' end
  local result = docs.read( request.tag )
  if not result.found then
    return { ok = true, found = false, tag = result.tag, candidates = result.candidates, }
  end
  return {
    ok = true,
    found = true,
    tag = result.tag,
    file = result.file,
    path = result.path,
    line = result.line,
    text = result.text,
  }
end

actions.lua = function( request )
  if type( request.code ) ~= 'string' then return failure 'code is required' end
  local chunk, problem = loadstring( request.code, 'pivi' )
  if not chunk then return failure( problem ) end
  local ok, result = pcall( chunk )
  if not ok then return failure( result ) end
  return { ok = true, result = vim.inspect( result ), }
end

actions.findings = function( request )
  local entries = activity.findings( request.items, request.title )
  return { ok = true, count = #entries, }
end

actions.diagnostics = function( request )
  if type( request.path ) ~= 'string' then return failure 'path is required' end
  local bufnr = ensure_buffer( request.path )
  if not bufnr then return failure 'file is not loaded in a buffer' end
  activity.diagnose( bufnr, request.items )
  return { ok = true, count = #( request.items or {} ), }
end

--- Single entry point used by the companion Pi extension.
--- @param request table action plus action specific fields
local handle = function( request )
  if type( request ) ~= 'table' then return failure 'request must be a table' end
  local action = actions[request.action]
  if not action then return failure( 'unknown action: ' .. tostring( request.action ) ) end
  local ok, result = pcall( action, request )
  if not ok then return failure( result ) end
  return result
end

return {
  handle = handle,
  actions = actions,
}