Skip to content

Commit 5bef38b

Browse files
committed
repl: Auto alignment for .editor mode
When in `.editor` mode, current line whitespace prefixes are preserved in the subsequent line. User can hit backspace to clean the whitespace ```js node 🙈 ₹ node > .editor // Entering editor mode (^D to finish, ^C to cancel) function test() { console.log('tested!'); //On enter, cursor will be after 2 spaces _ ``` PR-URL: #8241 Reviewed-By: James M Snell <[email protected]>
1 parent f8f283b commit 5bef38b

File tree

3 files changed

+71
-2
lines changed

3 files changed

+71
-2
lines changed

lib/readline.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function Interface(input, output, completer, terminal) {
4141

4242
this._sawReturn = false;
4343
this.isCompletionEnabled = true;
44+
this._sawKeyPress = false;
4445
this._previousKey = null;
4546

4647
EventEmitter.call(this);
@@ -247,6 +248,9 @@ Interface.prototype._addHistory = function() {
247248
// if the history is disabled then return the line
248249
if (this.historySize === 0) return this.line;
249250

251+
// if the trimmed line is empty then return the line
252+
if (this.line.trim().length === 0) return this.line;
253+
250254
if (this.history.length === 0 || this.history[0] !== this.line) {
251255
this.history.unshift(this.line);
252256

@@ -947,6 +951,10 @@ function emitKeypressEvents(stream, iface) {
947951
if (r) {
948952
clearTimeout(timeoutId);
949953

954+
if (iface) {
955+
iface._sawKeyPress = r.length === 1;
956+
}
957+
950958
for (var i = 0; i < r.length; i++) {
951959
if (r[i] === '\t' && typeof r[i + 1] === 'string' && iface) {
952960
iface.isCompletionEnabled = false;

lib/repl.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,15 @@ function REPLServer(prompt,
471471

472472
if (self.editorMode) {
473473
self.bufferedCommand += cmd + '\n';
474+
475+
// code alignment
476+
const matches = self._sawKeyPress ? cmd.match(/^\s+/) : null;
477+
if (matches) {
478+
const prefix = matches[0];
479+
self.inputStream.write(prefix);
480+
self.line = prefix;
481+
self.cursor = prefix.length;
482+
}
474483
self.memory(cmd);
475484
return;
476485
}

test/parallel/test-repl-.editor.js

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const repl = require('repl');
99
// \u001b[3G - Moves the cursor to 3rd column
1010
const terminalCode = '\u001b[1G\u001b[0J> \u001b[3G';
1111

12-
function run(input, output, event) {
12+
function run({input, output, event}) {
1313
const stream = new common.ArrayStream();
1414
let found = '';
1515

@@ -49,7 +49,59 @@ const tests = [
4949
input: 'var i = 1;\ni + 3',
5050
output: '\n4',
5151
event: {ctrl: true, name: 'd'}
52+
},
53+
{
54+
input: ' var i = 1;\ni + 3',
55+
output: '\n4',
56+
event: {ctrl: true, name: 'd'}
57+
}
58+
];
59+
60+
tests.forEach(run);
61+
62+
// Auto code alignment for .editor mode
63+
function testCodeAligment({input, cursor = 0, line = ''}) {
64+
const stream = new common.ArrayStream();
65+
66+
const replServer = repl.start({
67+
prompt: '> ',
68+
terminal: true,
69+
input: stream,
70+
output: stream,
71+
useColors: false
72+
});
73+
74+
stream.emit('data', '.editor\n');
75+
input.split('').forEach((ch) => stream.emit('data', ch));
76+
// Test the content of current line and the cursor position
77+
assert.strictEqual(line, replServer.line);
78+
assert.strictEqual(cursor, replServer.cursor);
79+
80+
replServer.write('', {ctrl: true, name: 'd'});
81+
replServer.close();
82+
// Ensure that empty lines are not saved in history
83+
assert.notStrictEqual(replServer.history[0].trim(), '');
84+
}
85+
86+
const codeAlignmentTests = [
87+
{
88+
input: 'var i = 1;\n'
89+
},
90+
{
91+
input: ' var i = 1;\n',
92+
cursor: 2,
93+
line: ' '
94+
},
95+
{
96+
input: ' var i = 1;\n',
97+
cursor: 5,
98+
line: ' '
99+
},
100+
{
101+
input: ' var i = 1;\n var j = 2\n',
102+
cursor: 2,
103+
line: ' '
52104
}
53105
];
54106

55-
tests.forEach(({input, output, event}) => run(input, output, event));
107+
codeAlignmentTests.forEach(testCodeAligment);

0 commit comments

Comments
 (0)