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.
This commit is contained in:
parent
212e9cd8c9
commit
ffcd8b94c2
@ -557,6 +557,7 @@ Interface.prototype._getDisplayPos = function(str) {
|
|||||||
var offset = 0;
|
var offset = 0;
|
||||||
var col = this.columns;
|
var col = this.columns;
|
||||||
var code;
|
var code;
|
||||||
|
str = stripVTControlCharacters(str);
|
||||||
for (var i = 0, len = str.length; i < len; i++) {
|
for (var i = 0, len = str.length; i < len; i++) {
|
||||||
code = codePointAt(str, i);
|
code = codePointAt(str, i);
|
||||||
if (code >= 0x10000) { // surrogates
|
if (code >= 0x10000) { // surrogates
|
||||||
@ -581,7 +582,7 @@ Interface.prototype._getDisplayPos = function(str) {
|
|||||||
Interface.prototype._getCursorPos = function() {
|
Interface.prototype._getCursorPos = function() {
|
||||||
var columns = this.columns;
|
var columns = this.columns;
|
||||||
var strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
|
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 cols = dispPos.cols;
|
||||||
var rows = dispPos.rows;
|
var rows = dispPos.rows;
|
||||||
// If the cursor is on a full-width character which steps over the line,
|
// If the cursor is on a full-width character which steps over the line,
|
||||||
@ -921,9 +922,11 @@ exports.emitKeypressEvents = emitKeypressEvents;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Regexes used for ansi escape code splitting
|
// Regexes used for ansi escape code splitting
|
||||||
var metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
|
var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
|
||||||
var functionKeyCodeRe =
|
var metaKeyCodeRe = new RegExp('^' + metaKeyCodeReAnywhere.source + '$');
|
||||||
/^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
|
var functionKeyCodeReAnywhere =
|
||||||
|
/(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
|
||||||
|
var functionKeyCodeRe = new RegExp('^' + functionKeyCodeReAnywhere.source);
|
||||||
|
|
||||||
function emitKey(stream, s) {
|
function emitKey(stream, s) {
|
||||||
var ch,
|
var ch,
|
||||||
@ -1207,6 +1210,7 @@ exports.clearScreenDown = clearScreenDown;
|
|||||||
|
|
||||||
function getStringWidth(str) {
|
function getStringWidth(str) {
|
||||||
var width = 0;
|
var width = 0;
|
||||||
|
str = stripVTControlCharacters(str);
|
||||||
for (var i = 0, len = str.length; i < len; i++) {
|
for (var i = 0, len = str.length; i < len; i++) {
|
||||||
var code = codePointAt(str, i);
|
var code = codePointAt(str, i);
|
||||||
if (code >= 0x10000) { // surrogates
|
if (code >= 0x10000) { // surrogates
|
||||||
@ -1289,3 +1293,14 @@ function codePointAt(str, index) {
|
|||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
exports.codePointAt = codePointAt;
|
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;
|
||||||
|
@ -192,6 +192,16 @@ FakeInput.prototype.end = function() {};
|
|||||||
assert.equal(readline.getStringWidth('안녕하세요'), 10);
|
assert.equal(readline.getStringWidth('안녕하세요'), 10);
|
||||||
assert.equal(readline.getStringWidth('A\ud83c\ude00BC'), 5); // surrogate
|
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('end'), []);
|
||||||
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
|
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user