1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 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: {} } }));
|