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, }