From f8ce89d978c68cb6ce277706c1b047ba4d195c8b Mon Sep 17 00:00:00 2001 From: Prince J Wesley Date: Wed, 24 Aug 2016 00:05:59 +0530 Subject: [PATCH 1/3] repl: Auto alignment for .editor mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When in `.editor` mode, current line whitespace prefixes are preserved in the subsequent line. ```js node 🙈 ₹ node > .editor // Entering editor mode (^D to finish, ^C to cancel) function test() { console.log('tested!'); //On enter, cursor will be at 2 position _ ``` --- lib/readline.js | 9 ++++++ lib/repl.js | 9 ++++++ test/parallel/test-repl-.editor.js | 47 ++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/lib/readline.js b/lib/readline.js index f7591b7cc1663b..e92268f3baf66c 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -41,6 +41,7 @@ function Interface(input, output, completer, terminal) { this._sawReturn = false; this.isCompletionEnabled = true; + this.isKeyPressed = false; this._previousKey = null; EventEmitter.call(this); @@ -247,6 +248,10 @@ Interface.prototype._addHistory = function() { // if the history is disabled then return the line if (this.historySize === 0) return this.line; + const line = this.line.replace(/^\s+|\s+$/, ''); + // if the trimmed line is empty then return the line + if (line.length === 0) return this.line; + if (this.history.length === 0 || this.history[0] !== this.line) { this.history.unshift(this.line); @@ -947,6 +952,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; diff --git a/lib/repl.js b/lib/repl.js index 1d678e67114c09..e93b71026656b2 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -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; } diff --git a/test/parallel/test-repl-.editor.js b/test/parallel/test-repl-.editor.js index 15765ad517d72a..241349b1b9e027 100644 --- a/test/parallel/test-repl-.editor.js +++ b/test/parallel/test-repl-.editor.js @@ -49,7 +49,54 @@ 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(({input, output, event}) => run(input, output, event)); + +// 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)); + replServer.close(); + assert.strictEqual(line, replServer.line); + assert.strictEqual(cursor, replServer.cursor); +} + +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); From 27222679453845cbb646d17f0975418454baf86f Mon Sep 17 00:00:00 2001 From: Prince J Wesley Date: Wed, 24 Aug 2016 00:48:45 +0530 Subject: [PATCH 2/3] typo _sawKeyPress --- lib/readline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/readline.js b/lib/readline.js index e92268f3baf66c..07a10c93784e09 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -41,7 +41,7 @@ function Interface(input, output, completer, terminal) { this._sawReturn = false; this.isCompletionEnabled = true; - this.isKeyPressed = false; + this._sawKeyPress = false; this._previousKey = null; EventEmitter.call(this); From 8ca02d560ddf2b58261c000da14ea44eb4ed4230 Mon Sep 17 00:00:00 2001 From: Prince J Wesley Date: Wed, 24 Aug 2016 07:44:10 +0530 Subject: [PATCH 3/3] Use trim() and test for no empty line history --- lib/readline.js | 3 +-- test/parallel/test-repl-.editor.js | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index 07a10c93784e09..e45fb2938e9bc0 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -248,9 +248,8 @@ Interface.prototype._addHistory = function() { // if the history is disabled then return the line if (this.historySize === 0) return this.line; - const line = this.line.replace(/^\s+|\s+$/, ''); // if the trimmed line is empty then return the line - if (line.length === 0) return this.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); diff --git a/test/parallel/test-repl-.editor.js b/test/parallel/test-repl-.editor.js index 241349b1b9e027..556662181f7831 100644 --- a/test/parallel/test-repl-.editor.js +++ b/test/parallel/test-repl-.editor.js @@ -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 = ''; @@ -57,7 +57,7 @@ const tests = [ } ]; -tests.forEach(({input, output, event}) => run(input, output, event)); +tests.forEach(run); // Auto code alignment for .editor mode function testCodeAligment({input, cursor = 0, line = ''}) { @@ -73,9 +73,14 @@ function testCodeAligment({input, cursor = 0, line = ''}) { stream.emit('data', '.editor\n'); input.split('').forEach((ch) => stream.emit('data', ch)); - replServer.close(); + // 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 = [