--- Fake Pi child process for pivi specs. --- Never patches globals: pivi takes its spawner as an argument. local new = function() local channel = { writes = {}, killed = false, commands = {}, } channel.spawn = function( command, options ) channel.command = command channel.options = options channel.stdout = options.stdout channel.stderr = options.stderr channel.on_exit = options.on_exit return { write = function( _, data ) if data == nil then channel.stdin_closed = true return end table.insert( channel.writes, data ) for _, record in ipairs( vim.split( data, '\n', { plain = true, } ) ) do if record ~= '' then local ok, value = pcall( vim.json.decode, record ) if ok then table.insert( channel.commands, value ) end end end end, kill = function() channel.killed = true end, } end --- Emits raw bytes on the child's stdout. channel.emit = function( text ) channel.stdout( nil, text ) end --- Emits one frame as a complete JSONL record. channel.frame = function( value ) channel.emit( vim.json.encode( value ) .. '\n' ) end channel.stderr_emit = function( text ) channel.stderr( nil, text ) end channel.close = function( code ) channel.on_exit { code = code or 0, } end --- Waits for a predicate without sleeping blindly. channel.settle = function( predicate ) return vim.wait( 2000, predicate, 5 ) end --- Waits until every scheduled callback so far has run. channel.flush = function() local done = false vim.schedule( function() done = true end ) vim.wait( 2000, function() return done end, 1 ) end return channel end --- Runs the event loop briefly so callbacks scheduled by a finished test --- cannot surface after its stubs and buffers are gone. local drain = function( milliseconds ) vim.wait( milliseconds or 60, function() return false end, 5 ) end --- Closes every window except the first so repeated splits cannot exhaust space. local close_extra_windows = function() local wins = vim.api.nvim_tabpage_list_wins( 0 ) for index = 2, #wins do pcall( vim.api.nvim_win_close, wins[index], true ) end for _, tabpage in ipairs( vim.api.nvim_list_tabpages() ) do if tabpage ~= vim.api.nvim_get_current_tabpage() then pcall( vim.api.nvim_win_close, vim.api.nvim_tabpage_list_wins( tabpage )[1], true ) end end end return { new = new, drain = drain, close_extra_windows = close_extra_windows, }