Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit ffcd8b9

Browse files
OJezubnoordhuis
authored andcommitted
readline: strip ctrl chars for prompt width calc
Use regular expression to strip vt ansi escape codes from display when calulating prompt display width and cursor position Fixes #3860 and #5628.
1 parent 212e9cd commit ffcd8b9

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

lib/readline.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ Interface.prototype._getDisplayPos = function(str) {
557557
var offset = 0;
558558
var col = this.columns;
559559
var code;
560+
str = stripVTControlCharacters(str);
560561
for (var i = 0, len = str.length; i < len; i++) {
561562
code = codePointAt(str, i);
562563
if (code >= 0x10000) { // surrogates
@@ -581,7 +582,7 @@ Interface.prototype._getDisplayPos = function(str) {
581582
Interface.prototype._getCursorPos = function() {
582583
var columns = this.columns;
583584
var strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
584-
var dispPos = this._getDisplayPos(strBeforeCursor);
585+
var dispPos = this._getDisplayPos(stripVTControlCharacters(strBeforeCursor));
585586
var cols = dispPos.cols;
586587
var rows = dispPos.rows;
587588
// If the cursor is on a full-width character which steps over the line,
@@ -921,9 +922,11 @@ exports.emitKeypressEvents = emitKeypressEvents;
921922
*/
922923

923924
// Regexes used for ansi escape code splitting
924-
var metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
925-
var functionKeyCodeRe =
926-
/^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
925+
var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
926+
var metaKeyCodeRe = new RegExp('^' + metaKeyCodeReAnywhere.source + '$');
927+
var functionKeyCodeReAnywhere =
928+
/(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
929+
var functionKeyCodeRe = new RegExp('^' + functionKeyCodeReAnywhere.source);
927930

928931
function emitKey(stream, s) {
929932
var ch,
@@ -1207,6 +1210,7 @@ exports.clearScreenDown = clearScreenDown;
12071210

12081211
function getStringWidth(str) {
12091212
var width = 0;
1213+
str = stripVTControlCharacters(str);
12101214
for (var i = 0, len = str.length; i < len; i++) {
12111215
var code = codePointAt(str, i);
12121216
if (code >= 0x10000) { // surrogates
@@ -1289,3 +1293,14 @@ function codePointAt(str, index) {
12891293
return code;
12901294
}
12911295
exports.codePointAt = codePointAt;
1296+
1297+
1298+
/**
1299+
* Tries to remove all VT control characters. Use to estimate displayed
1300+
* string width. May be buggy due to not running a real state machine
1301+
*/
1302+
function stripVTControlCharacters(str) {
1303+
str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), '');
1304+
return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), '');
1305+
}
1306+
exports.stripVTControlCharacters = stripVTControlCharacters;

test/simple/test-readline-interface.js

+10
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,16 @@ FakeInput.prototype.end = function() {};
192192
assert.equal(readline.getStringWidth('안녕하세요'), 10);
193193
assert.equal(readline.getStringWidth('A\ud83c\ude00BC'), 5); // surrogate
194194

195+
// check if vt control chars are stripped
196+
assert.equal(readline.stripVTControlCharacters('\u001b[31m> \u001b[39m'), '> ');
197+
assert.equal(readline.stripVTControlCharacters('\u001b[31m> \u001b[39m> '), '> > ');
198+
assert.equal(readline.stripVTControlCharacters('\u001b[31m\u001b[39m'), '');
199+
assert.equal(readline.stripVTControlCharacters('> '), '> ');
200+
assert.equal(readline.getStringWidth('\u001b[31m> \u001b[39m'), 2);
201+
assert.equal(readline.getStringWidth('\u001b[31m> \u001b[39m> '), 4);
202+
assert.equal(readline.getStringWidth('\u001b[31m\u001b[39m'), 0);
203+
assert.equal(readline.getStringWidth('> '), 2);
204+
195205
assert.deepEqual(fi.listeners('end'), []);
196206
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
197207
});

0 commit comments

Comments
 (0)