| '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. |
| // If the cursor is at the end of a long line and the down key is pressed, |
| // Move the cursor to the end of the next line, if shorter. |
| const checkResults = common.mustSucceed((r) => { |
| r.write('let str = `'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('111'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('22222222222222'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('3`'); |
| r.input.run([{ name: 'enter' }]); |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 33); |
| |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 18); |
| |
| for (let i = 0; i < 5; i++) { |
| r.input.run([{ name: 'right' }]); |
| } |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 15); |
| r.input.run([{ name: 'up' }]); |
| |
| for (let i = 0; i < 4; i++) { |
| r.input.run([{ name: 'right' }]); |
| } |
| assert.strictEqual(r.cursor, 11); |
| |
| r.input.run([{ name: 'down' }]); |
| assert.strictEqual(r.cursor, 15); |
| |
| r.input.run([{ name: 'down' }]); |
| assert.strictEqual(r.cursor, 27); |
| |
| r.close(); |
| }); |
| |
| 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. |
| // This is testing cursor clamping and restoring when moving up and down from long lines. |
| const checkResults = common.mustSucceed((r) => { |
| r.write('let ddd = `000'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('1111111111111'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('22222'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('2222'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('22222'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('33333333`'); |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 45); |
| |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 39); |
| |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 34); |
| |
| r.input.run([{ name: 'up' }]); |
| assert.strictEqual(r.cursor, 24); |
| |
| r.input.run([{ name: 'right' }]); |
| // This is to reach a cursor pos which is much higher than the line we want to go to, |
| // So we can check that the cursor is clamped to the end of the line. |
| r.input.run([{ name: 'right' }]); |
| |
| r.input.run([{ name: 'down' }]); |
| assert.strictEqual(r.cursor, 34); |
| |
| r.input.run([{ name: 'down' }]); |
| assert.strictEqual(r.cursor, 39); |
| |
| r.input.run([{ name: 'down' }]); |
| assert.strictEqual(r.cursor, 45); |
| |
| r.input.run([{ name: 'down' }]); |
| assert.strictEqual(r.cursor, 55); |
| |
| r.close(); |
| }); |
| |
| 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)}`); |
| // If the last command errored and the user is trying to edit it, |
| // The errored line should be removed from history |
| const checkResults = common.mustSucceed((r) => { |
| r.write('let lineWithMistake = `I have some'); |
| r.input.run([{ name: 'enter' }]); |
| r.write('problem with` my syntax\''); |
| r.input.run([{ name: 'enter' }]); |
| r.input.run([{ name: 'up' }]); |
| r.input.run([{ name: 'backspace' }]); |
| r.write('`'); |
| for (let i = 0; i < 11; i++) { |
| r.input.run([{ name: 'left' }]); |
| } |
| r.input.run([{ name: 'backspace' }]); |
| r.input.run([{ name: 'enter' }]); |
| |
| assert.strictEqual(r.history.length, 1); |
| // Check that the line is properly set in the history structure |
| assert.strictEqual(r.history[0], 'problem with my syntax`\rlet lineWithMistake = `I have some'); |
| assert.strictEqual(r.line, ''); |
| |
| r.input.run([{ name: 'up' }]); |
| // Check that the line is properly displayed |
| assert.strictEqual(r.line, 'let lineWithMistake = `I have some\nproblem with my syntax`'); |
| |
| r.close(); |
| }); |
| |
| 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)}`); |
| const outputBuffer = []; |
| |
| // Test that the REPL preview is properly shown on multiline commands |
| // And deleted when enter is pressed |
| const checkResults = common.mustSucceed((r) => { |
| r.write('Array(100).fill('); |
| r.input.run([{ name: 'enter' }]); |
| r.write('123'); |
| r.input.run([{ name: 'enter' }]); |
| r.write(')'); |
| r.input.run([{ name: 'enter' }]); |
| r.input.run([{ name: 'up' }]); |
| r.input.run([{ name: 'up' }]); |
| |
| assert.deepStrictEqual(r.last, new Array(100).fill(123)); |
| r.input.run([{ name: 'enter' }]); |
| assert.strictEqual(outputBuffer.includes('[\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,\n' + |
| ' 123\n' + |
| ']\n'), true); |
| |
| r.close(); |
| }); |
| |
| repl.createInternalRepl( |
| { NODE_REPL_HISTORY: historyPath }, |
| { |
| preview: true, |
| terminal: true, |
| input: new ActionStream(), |
| output: new stream.Writable({ |
| write(chunk, _, next) { |
| // Store each chunk in the buffer |
| outputBuffer.push(chunk.toString()); |
| next(); |
| } |
| }), |
| }, |
| checkResults |
| ); |
| } |