diff --git a/lib/readline.js b/lib/readline.js index 4eeefb227ca746..c4966e5aaf43f3 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -57,7 +57,8 @@ let StringDecoder; const kHistorySize = 30; const kMincrlfDelay = 100; // \r\n, \n, or \r followed by something other than \n -const lineEnding = /\r?\n|\r(?!\n)/; +const lineEndingWithSeparator = /(?<=\r?\n|\r(?!\n))/; +const lineEndingSeparatorInclusion = /[\r?\n|\r(?!\n)]$/; const KEYPRESS_DECODER = Symbol('keypress-decoder'); const ESCAPE_DECODER = Symbol('escape-decoder'); @@ -290,14 +291,14 @@ Interface.prototype.question = function(query, cb) { }; -Interface.prototype._onLine = function(line) { +Interface.prototype._onLine = function(line, separator = '\n') { if (this._questionCallback) { var cb = this._questionCallback; this._questionCallback = null; this.setPrompt(this._oldPrompt); cb(line); } else { - this.emit('line', line); + this.emit('line', line, separator); } }; @@ -425,7 +426,7 @@ Interface.prototype._normalWrite = function(b) { } // Run test() on the new string chunk, not on the entire line buffer. - var newPartContainsEnding = lineEnding.test(string); + var newPartContainsEnding = lineEndingWithSeparator.test(string); if (this._line_buffer) { string = this._line_buffer + string; @@ -435,12 +436,21 @@ Interface.prototype._normalWrite = function(b) { this._sawReturnAt = string.endsWith('\r') ? Date.now() : 0; // got one or more newlines; process into "line" events - var lines = string.split(lineEnding); + var lines = string.split(lineEndingWithSeparator); // either '' or (conceivably) the unfinished portion of the next line string = lines.pop(); this._line_buffer = string; - for (var n = 0; n < lines.length; n++) - this._onLine(lines[n]); + for (var n = 0; n < lines.length; n++) { + const _line = lines[n]; + if (lineEndingSeparatorInclusion.test(_line)) { + const lineWithoutSeparator = _line.slice(0, -1); + const separator = _line.slice(-1); + + this._onLine(lineWithoutSeparator, separator); + } else { + this._onLine(_line, '\n'); + } + } } else if (string) { // no newlines this time, save what we have for next time this._line_buffer = string; @@ -642,10 +652,10 @@ Interface.prototype.clearLine = function() { }; -Interface.prototype._line = function() { +Interface.prototype._line = function(separator = '\n') { var line = this._addHistory(); this.clearLine(); - this._onLine(line); + this._onLine(line, separator); }; @@ -924,7 +934,7 @@ Interface.prototype._ttyWrite = function(s, key) { switch (key.name) { case 'return': // carriage return, i.e. \r this._sawReturnAt = Date.now(); - this._line(); + this._line('\r'); break; case 'enter': diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index ec553832c42d68..5c3bf553ebfcb7 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -118,9 +118,10 @@ function isWarned(emitter) { { input: fi, output: fi, terminal: terminal } ); let called = false; - rli.on('line', function(line) { + rli.on('line', function(line, separator) { called = true; assert.strictEqual(line, 'asdf'); + assert.strictEqual(separator, '\n'); }); fi.emit('data', 'asdf\n'); assert.ok(called);