Skip to content

Commit

Permalink
repl: Auto alignment for .editor mode
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
princejwesley committed Aug 27, 2016
1 parent f8f283b commit 5bef38b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 2 deletions.
8 changes: 8 additions & 0 deletions lib/readline.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function Interface(input, output, completer, terminal) {

this._sawReturn = false;
this.isCompletionEnabled = true;
this._sawKeyPress = false;
this._previousKey = null;

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

// if the trimmed line is empty then return the line
if (this.line.trim().length === 0) return this.line;

if (this.history.length === 0 || this.history[0] !== this.line) {
this.history.unshift(this.line);

Expand Down Expand Up @@ -947,6 +951,10 @@ function emitKeypressEvents(stream, iface) {
if (r) {
clearTimeout(timeoutId);

if (iface) {
iface._sawKeyPress = r.length === 1;
}

for (var i = 0; i < r.length; i++) {
if (r[i] === '\t' && typeof r[i + 1] === 'string' && iface) {
iface.isCompletionEnabled = false;
Expand Down
9 changes: 9 additions & 0 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,15 @@ function REPLServer(prompt,

if (self.editorMode) {
self.bufferedCommand += cmd + '\n';

// code alignment
const matches = self._sawKeyPress ? cmd.match(/^\s+/) : null;
if (matches) {
const prefix = matches[0];
self.inputStream.write(prefix);
self.line = prefix;
self.cursor = prefix.length;
}
self.memory(cmd);
return;
}
Expand Down
56 changes: 54 additions & 2 deletions test/parallel/test-repl-.editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const repl = require('repl');
// \u001b[3G - Moves the cursor to 3rd column
const terminalCode = '\u001b[1G\u001b[0J> \u001b[3G';

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

Expand Down Expand Up @@ -49,7 +49,59 @@ const tests = [
input: 'var i = 1;\ni + 3',
output: '\n4',
event: {ctrl: true, name: 'd'}
},
{
input: ' var i = 1;\ni + 3',
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 replServer = repl.start({
prompt: '> ',
terminal: true,
input: stream,
output: stream,
useColors: false
});

stream.emit('data', '.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: ' '
}
];

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

0 comments on commit 5bef38b

Please sign in to comment.