Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: repl add .load-editor feature #14503

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to have an example

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean add an example in doc?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. For instance, I presume there's a way of providing the file name to load but that's not captured here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just how .load does it should be fine, the second line.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I have updated doc file

`> .load-editor ./file/to/load.js`

<!-- 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 = common.mustNotCall('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);