Skip to content

Commit 78fad32

Browse files
Hakerh400BethGriggs
authored andcommitted
readline: replace quadratic regex with linear one
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> Signed-off-by: Beth Griggs <Bethany.Griggs@uk.ibm.com>
1 parent e5181f8 commit 78fad32

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

lib/readline.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -569,8 +569,11 @@ function commonPrefix(strings) {
569569

570570
Interface.prototype._wordLeft = function() {
571571
if (this.cursor > 0) {
572+
// Reverse the string and match a word near beginning
573+
// to avoid quadratic time complexity
572574
var leading = this.line.slice(0, this.cursor);
573-
var match = leading.match(/(?:[^\w\s]+|\w+|)\s*$/);
575+
var reversed = leading.split('').reverse().join('');
576+
var match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
574577
this._moveCursor(-match[0].length);
575578
}
576579
};
@@ -626,8 +629,11 @@ Interface.prototype._deleteRight = function() {
626629

627630
Interface.prototype._deleteWordLeft = function() {
628631
if (this.cursor > 0) {
632+
// Reverse the string and match a word near beginning
633+
// to avoid quadratic time complexity
629634
var leading = this.line.slice(0, this.cursor);
630-
var match = leading.match(/(?:[^\w\s]+|\w+|)\s*$/);
635+
var reversed = leading.split('').reverse().join('');
636+
var match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
631637
leading = leading.slice(0, leading.length - match[0].length);
632638
this.line = leading + this.line.slice(this.cursor, this.line.length);
633639
this.cursor = leading.length;

test/parallel/test-readline-interface.js

+23
Original file line numberDiff line numberDiff line change
@@ -1272,3 +1272,26 @@ const crlfDelay = Infinity;
12721272
}), delay);
12731273
}
12741274
});
1275+
1276+
// Ensure that the _wordLeft method works even for large input
1277+
{
1278+
const input = new Readable({
1279+
read() {
1280+
this.push('\x1B[1;5D'); // CTRL + Left
1281+
this.push(null);
1282+
},
1283+
});
1284+
const output = new Writable({
1285+
write: common.mustCall((data, encoding, cb) => {
1286+
assert.strictEqual(rl.cursor, rl.line.length - 1);
1287+
cb();
1288+
}),
1289+
});
1290+
const rl = new readline.createInterface({
1291+
input: input,
1292+
output: output,
1293+
terminal: true,
1294+
});
1295+
rl.line = `a${' '.repeat(1e6)}a`;
1296+
rl.cursor = rl.line.length;
1297+
}

0 commit comments

Comments
 (0)