Skip to content

Commit

Permalink
readline: replace quadratic regex with linear one
Browse files Browse the repository at this point in the history
Simplify regular expression in _wordLeft and _deleteWordLeft readline
methods.

PR-URL: #26778
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
  • Loading branch information
Hakerh400 authored and targos committed Mar 30, 2019
1 parent 83c35dc commit e4e2b0c
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 2 deletions.
10 changes: 8 additions & 2 deletions lib/readline.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,11 @@ function commonPrefix(strings) {

Interface.prototype._wordLeft = function() {
if (this.cursor > 0) {
// Reverse the string and match a word near beginning
// to avoid quadratic time complexity
var leading = this.line.slice(0, this.cursor);
var match = leading.match(/(?:[^\w\s]+|\w+|)\s*$/);
var reversed = leading.split('').reverse().join('');
var match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
this._moveCursor(-match[0].length);
}
};
Expand Down Expand Up @@ -634,8 +637,11 @@ Interface.prototype._deleteRight = function() {

Interface.prototype._deleteWordLeft = function() {
if (this.cursor > 0) {
// Reverse the string and match a word near beginning
// to avoid quadratic time complexity
var leading = this.line.slice(0, this.cursor);
var match = leading.match(/(?:[^\w\s]+|\w+|)\s*$/);
var reversed = leading.split('').reverse().join('');
var match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
leading = leading.slice(0, leading.length - match[0].length);
this.line = leading + this.line.slice(this.cursor, this.line.length);
this.cursor = leading.length;
Expand Down
23 changes: 23 additions & 0 deletions test/parallel/test-readline-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -1272,3 +1272,26 @@ const crlfDelay = Infinity;
}), delay);
}
});

// Ensure that the _wordLeft method works even for large input
{
const input = new Readable({
read() {
this.push('\x1B[1;5D'); // CTRL + Left
this.push(null);
},
});
const output = new Writable({
write: common.mustCall((data, encoding, cb) => {
assert.strictEqual(rl.cursor, rl.line.length - 1);
cb();
}),
});
const rl = new readline.createInterface({
input: input,
output: output,
terminal: true,
});
rl.line = `a${' '.repeat(1e6)}a`;
rl.cursor = rl.line.length;
}

0 comments on commit e4e2b0c

Please sign in to comment.