'use strict'; // Flags: --expose-internals const common = require('../common'); const assert = require('assert'); const repl = require('internal/repl'); const stream = require('stream'); class ActionStream extends stream.Stream { run(data) { const _iter = data[Symbol.iterator](); const doAction = () => { const next = _iter.next(); if (next.done) { // Close the repl. Note that it must have a clean prompt to do so. this.emit('keypress', '', { ctrl: true, name: 'd' }); return; } const action = next.value; if (typeof action === 'object') { this.emit('keypress', '', action); } setImmediate(doAction); }; doAction(); } write(chunk) { const chunkLines = chunk.toString('utf8').split('\n'); this.lines[this.lines.length - 1] += chunkLines[0]; if (chunkLines.length > 1) { this.lines.push(...chunkLines.slice(1)); } this.emit('line', this.lines[this.lines.length - 1]); return true; } resume() {} pause() {} } ActionStream.prototype.readable = true; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); { const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`); // Make sure the cursor is at the right places when pressing enter at the end of the first line. const checkResults = common.mustSucceed((r) => { r.write('let aaa = `I am a'); r.input.run([{ name: 'enter' }]); r.write('1111111111111'); r.input.run([{ name: 'enter' }]); r.write('22222222222222'); // The command is not complete yet. I can still edit it r.input.run([{ name: 'up' }]); r.input.run([{ name: 'up' }]); // I am on the first line for (let i = 0; i < 3; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the first line assert.strictEqual(r.cursor, 17); r.input.run([{ name: 'enter' }]); assert.strictEqual(r.cursor, 18); assert.strictEqual(r.line, 'let aaa = `I am a\n\n1111111111111\n22222222222222'); r.write('000'); r.input.run([{ name: 'down' }]); r.input.run([{ name: 'down' }]); // I am in the last line for (let i = 0; i < 5; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the last line r.write('`'); // Making the command complete r.input.run([{ name: 'enter' }]); // Issuing it assert.strictEqual(r.history.length, 1); assert.strictEqual(r.history[0], '22222222`222222\r1111111111111\r000\rlet aaa = `I am a'); }); repl.createInternalRepl( { NODE_REPL_HISTORY: historyPath }, { terminal: true, input: new ActionStream(), output: new stream.Writable({ write(chunk, _, next) { next(); } }), }, checkResults ); } { const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`); // Make sure the cursor is at the right places when pressing enter in the middle of the first line. const checkResults = common.mustSucceed((r) => { r.write('let bbb = `I am a'); r.input.run([{ name: 'enter' }]); r.write('1111111111111'); r.input.run([{ name: 'enter' }]); r.write('22222222222222'); // The command is not complete yet. I can still edit it r.input.run([{ name: 'up' }]); r.input.run([{ name: 'up' }]); // I am on the first line for (let i = 0; i < 3; i++) { r.input.run([{ name: 'left' }]); } // I am right after the string definition assert.strictEqual(r.cursor, 11); r.input.run([{ name: 'enter' }]); assert.strictEqual(r.cursor, 12); assert.strictEqual(r.line, 'let bbb = `\nI am a\n1111111111111\n22222222222222'); r.write('000'); r.input.run([{ name: 'enter' }]); r.input.run([{ name: 'down' }]); r.input.run([{ name: 'down' }]); // I am in the last line for (let i = 0; i < 14; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the last line r.write('`'); // Making the command complete r.input.run([{ name: 'enter' }]); // Issuing it assert.strictEqual(r.history.length, 1); assert.strictEqual(r.history[0], '22222222222222`\r1111111111111\rI am a\r000\rlet bbb = `'); }); repl.createInternalRepl( { NODE_REPL_HISTORY: historyPath }, { terminal: true, input: new ActionStream(), output: new stream.Writable({ write(chunk, _, next) { next(); } }), }, checkResults ); } { const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`); // Make sure the cursor is at the right places when pressing enter at the end of the second line. const checkResults = common.mustSucceed((r) => { r.write('let ccc = `I am a'); r.input.run([{ name: 'enter' }]); r.write('1111111111111'); r.input.run([{ name: 'enter' }]); r.write('22222222222222'); // The command is not complete yet. I can still edit it r.input.run([{ name: 'up' }]); // I am the end of second line assert.strictEqual(r.cursor, 31); r.input.run([{ name: 'enter' }]); assert.strictEqual(r.cursor, 32); assert.strictEqual(r.line, 'let ccc = `I am a\n1111111111111\n\n22222222222222'); r.write('000'); r.input.run([{ name: 'down' }]); // I am in the last line for (let i = 0; i < 11; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the last line r.write('`'); // Making the command complete r.input.run([{ name: 'enter' }]); // Issuing it assert.strictEqual(r.history.length, 1); assert.strictEqual(r.history[0], '22222222222222`\r000\r1111111111111\rlet ccc = `I am a'); }); repl.createInternalRepl( { NODE_REPL_HISTORY: historyPath }, { terminal: true, input: new ActionStream(), output: new stream.Writable({ write(chunk, _, next) { next(); } }), }, checkResults ); } { const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`); // Make sure the cursor is at the right places when pressing enter in the middle of the second line. const checkResults = common.mustSucceed((r) => { r.write('let ddd = `I am a'); r.input.run([{ name: 'enter' }]); r.write('1111111111111'); r.input.run([{ name: 'enter' }]); r.write('22222222222222'); // The command is not complete yet. I can still edit it r.input.run([{ name: 'up' }]); // I am the end of second line assert.strictEqual(r.cursor, 31); for (let i = 0; i < 6; i++) { r.input.run([{ name: 'left' }]); } // I am in the middle of the second line r.input.run([{ name: 'enter' }]); assert.strictEqual(r.cursor, 26); assert.strictEqual(r.line, 'let ddd = `I am a\n1111111\n111111\n22222222222222'); r.input.run([{ name: 'down' }]); // I am at the beginning of the last line for (let i = 0; i < 14; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the last line r.write('`'); // Making the command complete r.input.run([{ name: 'enter' }]); // Issuing it assert.strictEqual(r.history.length, 1); assert.strictEqual(r.history[0], '22222222222222`\r111111\r1111111\rlet ddd = `I am a'); }); repl.createInternalRepl( { NODE_REPL_HISTORY: historyPath }, { terminal: true, input: new ActionStream(), output: new stream.Writable({ write(chunk, _, next) { next(); } }), }, checkResults ); } { const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`); // Make sure the cursor is at the right places when pressing enter at the beginning of the third line. const checkResults = common.mustSucceed((r) => { r.write('let eee = `I am a'); r.input.run([{ name: 'enter' }]); r.write('1111111111111'); r.input.run([{ name: 'enter' }]); r.write('22222222222222'); // The command is not complete yet. I can still edit it for (let i = 0; i < 14; i++) { r.input.run([{ name: 'left' }]); } // I am at the beginning of the last line assert.strictEqual(r.cursor, 32); r.input.run([{ name: 'enter' }]); assert.strictEqual(r.cursor, 33); assert.strictEqual(r.line, 'let eee = `I am a\n1111111111111\n\n22222222222222'); r.input.run([{ name: 'up' }]); // I am the beginning of the new line r.write('000'); assert.strictEqual(r.cursor, 35); r.input.run([{ name: 'down' }]); // I am in the last line for (let i = 0; i < 11; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the last line r.write('`'); // Making the command complete r.input.run([{ name: 'enter' }]); // Issuing it assert.strictEqual(r.history.length, 1); assert.strictEqual(r.history[0], '22222222222222`\r000\r1111111111111\rlet eee = `I am a'); }); repl.createInternalRepl( { NODE_REPL_HISTORY: historyPath }, { terminal: true, input: new ActionStream(), output: new stream.Writable({ write(chunk, _, next) { next(); } }), }, checkResults ); } { const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`); // Make sure the cursor is at the right places when pressing enter in the middle of the third line // And executing the command while still in the middle of the multiline command const checkResults = common.mustSucceed((r) => { r.write('let fff = `I am a'); r.input.run([{ name: 'enter' }]); r.write('1111111111111'); r.input.run([{ name: 'enter' }]); r.write('22222222222222'); // The command is not complete yet. I can still edit it assert.strictEqual(r.cursor, 46); for (let i = 0; i < 6; i++) { r.input.run([{ name: 'left' }]); } // I am in the middle of the third line r.input.run([{ name: 'enter' }]); assert.strictEqual(r.cursor, 41); assert.strictEqual(r.line, 'let fff = `I am a\n1111111111111\n22222222\n222222'); r.input.run([{ name: 'down' }]); // I am at the beginning of the last line for (let i = 0; i < 6; i++) { r.input.run([{ name: 'right' }]); } // I am at the end of the last line r.write('`'); // Making the command complete r.input.run([{ name: 'up' }]); // I am not at the end of the last line r.input.run([{ name: 'enter' }]); // Issuing the command assert.strictEqual(r.history.length, 1); assert.strictEqual(r.history[0], '222222`\r22222222\r1111111111111\rlet fff = `I am a'); }); repl.createInternalRepl( { NODE_REPL_HISTORY: historyPath }, { terminal: true, input: new ActionStream(), output: new stream.Writable({ write(chunk, _, next) { next(); } }), }, checkResults ); }