// the native server must answer while the client keeps the pipe open and waits, as an editor // does: one message in, the answer out, before anything else is sent. an output buffer that // is only flushed at exit, or an end-of-input peek that waits for the next message, fails this. // usage: node tests/drivers/lsp_live.js path/to/zclsp const cp = require('child_process'); const p = cp.spawn(process.argv[2], []); let inbox = Buffer.alloc(0), step = 0, done = false; const frame = m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); }; const finish = (ok, what) => { if (done) return; done = true; p.kill(); console.log((ok ? 'ok ' : 'FAIL ') + what); process.exit(ok ? 0 : 1); }; const timer = setTimeout(() => finish(false, 'lsp live: no answer in 5 s while the pipe stayed open (step ' + step + ')'), 5000); p.stdout.on('data', d => { inbox = Buffer.concat([inbox, d]); const e = inbox.indexOf('\r\n\r\n'); if (e < 0) return; const n = parseInt(/Content-Length: (\d+)/.exec(inbox.slice(0, e).toString())[1], 10); if (inbox.length < e + 4 + n) return; const m = JSON.parse(inbox.slice(e + 4, e + 4 + n).toString()); inbox = inbox.slice(e + 4 + n); if (step === 0 && m.id === 1) { step = 1; // the answer came with the pipe still open: ask for something else p.stdin.write(frame({ jsonrpc: '2.0', id: 2, method: 'shutdown', params: null })); } else if (step === 1 && m.id === 2) { clearTimeout(timer); finish(true, 'lsp live: initialize and shutdown are answered while the pipe stays open'); } }); p.stdin.write(frame({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } }));