local capture = require 'bugabinga.pivi.capture' describe( 'pivi capture', function() it( 'packs styles into cell-coordinate runs without losing Unicode or whitespace', function() local cells = { { '界', 1, { foreground = 123, bold = true, }, }, { '', 1, { foreground = 123, bold = true, }, }, { 'é', 2, { background = 456, }, }, { ' ', 2, { background = 456, }, }, } local grid = capture.encode( 4, 2, function( _, col ) return unpack( cells[col] ) end ) assert.equal( '界é ', grid.rows[1].text ) assert.same( grid.rows[1], grid.rows[2] ) assert.same( { { col = 1, width = 2, style = '1', }, { col = 3, width = 2, style = '2', }, }, grid.rows[1].runs ) assert.same( { foreground = 123, bold = true, }, grid.styles['1'] ) assert.same( { background = 456, }, grid.styles['2'] ) assert.equal( 4, grid.width ) assert.equal( 2, grid.height ) end ) it( 'starts a new run when a style reappears and resets runs per row', function() local grid = capture.encode( 3, 2, function( _, col ) return ' ', col == 2 and 2 or 1, {} end ) assert.equal( 3, #grid.rows[1].runs ) assert.equal( 1, grid.rows[2].runs[1].col ) assert.equal( ' ', grid.rows[2].text ) end ) it( 'decodes composed UI events identically across arbitrary byte boundaries', function() local events = { { 'grid_resize', { 1, 4, 2, }, }, { 'default_colors_set', { 123, 456, 789, 1, 2, }, }, { 'hl_attr_define', { 1, { foreground = 123, }, { foreground = 7, }, {} }, }, { 'grid_line', { 1, 0, 0, { { '界', 1, }, { '', }, { 'é', }, { ' ', }, }, false, }, }, { 'grid_line', { 1, 1, 0, { { 'x', 1, 4, }, }, false, }, }, { 'grid_cursor_goto', { 1, 0, 2, }, }, { 'flush', }, } local bytes = vim.mpack.encode( { 2, 'redraw', events, } ) .. vim.mpack.encode( { 1, 1, vim.NIL, vim.NIL, } ) local expected = { cells = {}, styles = {}, } capture.decode( expected, vim.mpack.Unpacker(), bytes ) assert.is_true( expected.ready ) assert.same( { row = 1, col = 3, }, expected.cursor ) assert.same( { foreground = 7, }, expected.styles[1].cterm ) assert.equal( 'x', expected.cells[2][4][1] ) for size = 1, #bytes do local state, unpacker = { cells = {}, styles = {}, }, vim.mpack.Unpacker() for start = 1, #bytes, size do capture.decode( state, unpacker, bytes:sub( start, start + size - 1 ) ) end assert.same( expected, state ) end for seed = 1, 50 do local state, unpacker = { cells = {}, styles = {}, }, vim.mpack.Unpacker() local offset, value = 1, seed while offset <= #bytes do value = ( value * 48271 ) % 2147483647 local size = value % 23 + 1 capture.decode( state, unpacker, bytes:sub( offset, offset + size - 1 ) ) offset = offset + size end assert.same( expected, state ) end end ) it( 'applies scroll, clear and resize events and reports UI attach errors', function() local state = { cells = {}, styles = {}, } capture.consume( state, { { 'grid_resize', { 1, 2, 2, }, }, { 'grid_line', { 1, 0, 0, { { 'a', 0, 2, }, }, false, }, }, { 'grid_line', { 1, 1, 0, { { 'b', 0, 2, }, }, false, }, }, { 'grid_scroll', { 1, 0, 2, 0, 2, 1, 0, }, }, } ) assert.equal( 'b', state.cells[1][1][1] ) assert.equal( ' ', state.cells[2][1][1] ) capture.consume( state, { { 'grid_clear', { 1, }, }, } ) assert.equal( ' ', state.cells[1][1][1] ) capture.consume( state, { { 'grid_resize', { 1, 1, 1, }, }, } ) assert.same( { { { ' ', 0, }, }, }, state.cells ) capture.decode( state, vim.mpack.Unpacker(), vim.mpack.encode( { 1, 1, { 0, 'denied', }, vim.NIL, } ) ) assert.is_truthy( state.error:find( 'denied', 1, true ) ) end ) it( 'refuses to present headless buffer text as a rendered UI', function() assert.equal( 0, #vim.api.nvim_list_uis() ) local result = require( 'bugabinga.pivi.bridge' ).handle { action = 'capture', } assert.is_false( result.ok ) assert.is_truthy( result.error:find( 'attached UI', 1, true ) ) end ) end )