From 5bef38b627cd9522d31d1f3ceae6655a06081ed0 Mon Sep 17 00:00:00 2001 From: Prince J Wesley Date: Wed, 24 Aug 2016 00:05:59 +0530 Subject: [PATCH] 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. 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: https://github.com/nodejs/node/pull/8241 Reviewed-By: James M Snell --- lib/readline.js | 8 +++++ lib/repl.js | 9 +++++ test/parallel/test-repl-.editor.js | 56 ++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index f7591b7cc1..e45fb2938e 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._sawKeyPress = false; this._previousKey = null; EventEmitter.call(this); @@ -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); @@ -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; diff --git a/lib/repl.js b/lib/repl.js index 1d678e6711..e93b710266 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 15765ad517..556662181f 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 = ''; @@ -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(({input, output, event}) => run(input, output, event)); +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: ' ' + } +]; + +codeAlignmentTests.forEach(testCodeAligment);