Skip to content

Commit b95aa2d

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>
1 parent 419a7da commit b95aa2d

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
@@ -577,8 +577,11 @@ function commonPrefix(strings) {
577577

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

635638
Interface.prototype._deleteWordLeft = function() {
636639
if (this.cursor > 0) {
640+
// Reverse the string and match a word near beginning
641+
// to avoid quadratic time complexity
637642
var leading = this.line.slice(0, this.cursor);
638-
var match = leading.match(/(?:[^\w\s]+|\w+|)\s*$/);
643+
var reversed = leading.split('').reverse().join('');
644+
var match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
639645
leading = leading.slice(0, leading.length - match[0].length);
640646
this.line = leading + this.line.slice(this.cursor, this.line.length);
641647
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)