local LF = '\n' local strip_carriage_return = function( record ) if record:sub( -1 ) == '\r' then return record:sub( 1, -2 ) end return record end --- Creates a strict JSONL record splitter. --- Records are delimited by LF only. A trailing CR is stripped. --- Unicode line separators inside JSON strings never split a record. local new_splitter = function() local pending = '' --- @param chunk string? bytes received from the child process --- @return string[] records complete records contained in the chunk local feed = function( chunk ) local records = {} if chunk == nil or chunk == '' then return records end pending = pending .. chunk while true do local index = pending:find( LF, 1, true ) if not index then break end local record = strip_carriage_return( pending:sub( 1, index - 1 ) ) pending = pending:sub( index + 1 ) if record ~= '' then table.insert( records, record ) end end return records end --- @return string[] records the unterminated remainder, if any local finish = function() local remainder = strip_carriage_return( pending ) pending = '' if remainder == '' then return {} end return { remainder, } end local buffered = function() return pending end return { feed = feed, finish = finish, buffered = buffered, } end --- @param record string one complete JSONL record --- @return table? value decoded object, or nil when the record is not a JSON object --- @return string? error decode failure description local parse = function( record ) local ok, value = pcall( vim.json.decode, record, { luanil = { object = true, array = true, }, } ) if not ok then return nil, tostring( value ) end if type( value ) ~= 'table' then return nil, 'record is not a JSON object' end return value, nil end return { splitter = new_splitter, parse = parse, }