Skip to content

Commit

Permalink
lib: repl for nodejs#14022
Browse files Browse the repository at this point in the history
  • Loading branch information
robbinhan committed Aug 15, 2017
1 parent cde272a commit cd1aa37
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 1 deletion.
1 change: 1 addition & 0 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The following special commands are supported by all REPL instances:
* `.load` - Load a file into the current REPL session.
`> .load ./file/to/load.js`
* `.editor` - Enter editor mode (`<ctrl>-D` to finish, `<ctrl>-C` to cancel)
* `.load-editor` - Load a file and enter editor mode (`<ctrl>-D` to finish, `<ctrl>-C` to cancel)

<!-- eslint-skip -->
```js
Expand Down
29 changes: 29 additions & 0 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,35 @@ function defineDefaultCommands(repl) {
'// Entering editor mode (^D to finish, ^C to cancel)\n');
}
});

repl.defineCommand('load-editor', {
help: 'Load a file and enter editor mode',
action: function(file) {
if (!this.terminal) return;
this.editorMode = true;
REPLServer.super_.prototype.setPrompt.call(this, '');
this.outputStream.write(
'// Entering editor mode (^D to finish, ^C to cancel)\n');
try {
var stats = fs.statSync(file);
if (stats && stats.isFile()) {
var data = fs.readFileSync(file, 'utf8');
var lines = data.split('\n');
this.displayPrompt();
for (var n = 0; n < lines.length; n++) {
if (lines[n])
this.write(`${lines[n]}\n`);
}
} else {
this.outputStream.write('Failed to load:' + file +
' is not a valid file\n');
}
} catch (e) {
this.outputStream.write('Failed to load:' + file + '\n');
}
this.displayPrompt();
}
});
}

function regexpEscape(s) {
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-repl-definecommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ r.defineCommand('say2', function() {
});

inputStream.write('.help\n');
assert(/\n\.say1 help for say1\n/.test(output),
assert(/\n\.say1 help for say1\n/.test(output),
'help for say1 not present');
assert(/\n\.say2\n/.test(output), 'help for say2 not present');
inputStream.write('.say1 node developer\n');
Expand Down
112 changes: 112 additions & 0 deletions test/parallel/test-repl-load-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const repl = require('repl');

// \u001b[1G - Moves the cursor to 1st column
// \u001b[0J - Clear screen
// \u001b[3G - Moves the cursor to 3rd column
const terminalCode = '\u001b[1G\u001b[0J> \u001b[3G';
const terminalCodeRegex = new RegExp(terminalCode.replace(/\[/g, '\\['), 'g');

function run({ input, filename, errormsg, output, event,
checkTerminalCodes = true }) {
const stream = new common.ArrayStream();
let found = '';

stream.write = (msg) => found += msg.replace('\r', '');

let expected = `${terminalCode}.load-editor ${filename}\n` +
'// Entering editor mode (^D to finish, ^C to cancel)\n' +
`${errormsg}${input}${output}\n${terminalCode}`;

const replServer = repl.start({
prompt: '> ',
terminal: true,
input: stream,
output: stream,
useColors: false
});

stream.emit('data', `.load-editor ${filename}\n`);
stream.emit('data', input);
replServer.write('', event);
replServer.close();

if (!checkTerminalCodes) {
found = found.replace(terminalCodeRegex, '').replace(/\n/g, '');
expected = expected.replace(terminalCodeRegex, '').replace(/\n/g, '');
}

assert.strictEqual(found, expected);
}

const tests = [
{
input: '',
filename: 'notexistfile.js',
errormsg: `Failed to load:notexistfile.js\n${terminalCode}\n`,
output: '(To exit, press ^C again or type .exit)',
event: { ctrl: true, name: 'c' }
},
{
input: 'var i = 1;\ni + 3',
filename: 'notexistfile.js',
errormsg: `Failed to load:notexistfile.js\n${terminalCode}`,
output: '\n4',
event: { ctrl: true, name: 'd' }
}
];

tests.forEach(run);

// Auto code alignment for .editor mode
function testCodeAligment({ input, cursor = 0, line = '' }) {
const stream = new common.ArrayStream();
const outputStream = new common.ArrayStream();

stream.write = () => { throw new Error('Writing not allowed!'); };

const replServer = repl.start({
prompt: '> ',
terminal: true,
input: stream,
output: outputStream,
useColors: false
});

stream.emit('data', '.load-editor\n');
input.split('').forEach((ch) => stream.emit('data', ch));
// Test the content of current line and the cursor position
assert.strictEqual(line, replServer.line);
assert.strictEqual(cursor, replServer.cursor);

replServer.write('', { ctrl: true, name: 'd' });
replServer.close();
// Ensure that empty lines are not saved in history
assert.notStrictEqual(replServer.history[0].trim(), '');
}

const codeAlignmentTests = [
{
input: 'var i = 1;\n'
},
{
input: ' var i = 1;\n',
cursor: 2,
line: ' '
},
{
input: ' var i = 1;\n',
cursor: 5,
line: ' '
},
{
input: ' var i = 1;\n var j = 2\n',
cursor: 2,
line: ' '
}
];

codeAlignmentTests.forEach(testCodeAligment);

0 comments on commit cd1aa37

Please sign in to comment.