Skip to content

Commit

Permalink
readline: strip ctrl chars for prompt width calc
Browse files Browse the repository at this point in the history
Use regular expression to strip vt ansi escape codes from display when
calulating prompt display width and cursor position

Fixes nodejs#3860 and nodejs#5628.
  • Loading branch information
OJezu authored and bnoordhuis committed Jun 17, 2013
1 parent 212e9cd commit ffcd8b9
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 4 deletions.
23 changes: 19 additions & 4 deletions lib/readline.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ Interface.prototype._getDisplayPos = function(str) {
var offset = 0;
var col = this.columns;
var code;
str = stripVTControlCharacters(str);
for (var i = 0, len = str.length; i < len; i++) {
code = codePointAt(str, i);
if (code >= 0x10000) { // surrogates
Expand All @@ -581,7 +582,7 @@ Interface.prototype._getDisplayPos = function(str) {
Interface.prototype._getCursorPos = function() {
var columns = this.columns;
var strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
var dispPos = this._getDisplayPos(strBeforeCursor);
var dispPos = this._getDisplayPos(stripVTControlCharacters(strBeforeCursor));
var cols = dispPos.cols;
var rows = dispPos.rows;
// If the cursor is on a full-width character which steps over the line,
Expand Down Expand Up @@ -921,9 +922,11 @@ exports.emitKeypressEvents = emitKeypressEvents;
*/

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

function emitKey(stream, s) {
var ch,
Expand Down Expand Up @@ -1207,6 +1210,7 @@ exports.clearScreenDown = clearScreenDown;

function getStringWidth(str) {
var width = 0;
str = stripVTControlCharacters(str);
for (var i = 0, len = str.length; i < len; i++) {
var code = codePointAt(str, i);
if (code >= 0x10000) { // surrogates
Expand Down Expand Up @@ -1289,3 +1293,14 @@ function codePointAt(str, index) {
return code;
}
exports.codePointAt = codePointAt;


/**
* Tries to remove all VT control characters. Use to estimate displayed
* string width. May be buggy due to not running a real state machine
*/
function stripVTControlCharacters(str) {
str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), '');
return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), '');
}
exports.stripVTControlCharacters = stripVTControlCharacters;
10 changes: 10 additions & 0 deletions test/simple/test-readline-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ FakeInput.prototype.end = function() {};
assert.equal(readline.getStringWidth('안녕하세요'), 10);
assert.equal(readline.getStringWidth('A\ud83c\ude00BC'), 5); // surrogate

// check if vt control chars are stripped
assert.equal(readline.stripVTControlCharacters('\u001b[31m> \u001b[39m'), '> ');
assert.equal(readline.stripVTControlCharacters('\u001b[31m> \u001b[39m> '), '> > ');
assert.equal(readline.stripVTControlCharacters('\u001b[31m\u001b[39m'), '');
assert.equal(readline.stripVTControlCharacters('> '), '> ');
assert.equal(readline.getStringWidth('\u001b[31m> \u001b[39m'), 2);
assert.equal(readline.getStringWidth('\u001b[31m> \u001b[39m> '), 4);
assert.equal(readline.getStringWidth('\u001b[31m\u001b[39m'), 0);
assert.equal(readline.getStringWidth('> '), 2);

assert.deepEqual(fi.listeners('end'), []);
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
});

0 comments on commit ffcd8b9

Please sign in to comment.