Adding colors to terminal prompt results in large white space

a 夏天 提交于 2019-12-03 16:57:11

When you call rl.setPrompt(prompt, length) without its second argument, the internal _promptLength variable is set to the length of the prompt string; ANSI X3.64 escape sequences are not interpreted. The internal _getCursorPos method computes the cursor position from _promptLength][3]; as such, the inclusion of the escape sequences in the length results in the cursor being positioned further away than it should be.

To resolve this problem fully, Node's readline library should parse the ANSI escape sequences when setting _promptLength. To work around this problem, you can manually calculate the length of the prompt string without the escape sequences and pass that as the second argument to rl.setPrompt(prompt, length).

I also ran into a similar issue. Basil Crow is correct in his excellent answer on the cause of the problem in that it is indeed the ANSI escape sequences that are causing the length glitch; however, Interface.setPrompt() is not just the problem—it is the solution!

It seems that this misreading of the length (something I artfully avoided while playing around in bash) affects the entire Interface object's writeout process, i.e. anything that calls Interface.setPrompt() in any capacity will be afflicted when the length parameter is left not specified.

In order to overcome this problem, you can do one of two things:


Redefine Interface.setPrompt() to always specify a length for prompt output so that methods like Interface.question() function properly again:

// I'm using the 'color' npm library for the sake of convenience. It is not required
// and you can use normal regex to strip color codes and what not.
var colors   = require('colors'),
    readline = require('readline');

var rl = readline.createInterface(process.stdin, process.stdout);

/* Overcome some bugs in the Nodejs readline implementation */
rl._setPrompt = rl.setPrompt;
rl.setPrompt = function(prompt, length)
{
    rl._setPrompt(prompt, length ? length : prompt.split(/[\r\n]/).pop().stripColors.length);
};

var str = '[' + '?'.green + '] Blackbeard walks under the black flag with a ____? ';

rl.question(str, function(answer)
{
    var answ = 'scallywag swagger';
    console.log(
        'You answered "' + ((answer == answ) ? answer.green : answer.red)
        + '". The correct answer is', '"' + answ.green + '".');
});


Redefine Interface.write() to use Interface.setPrompt() passing both the writeout string and the true length of the string to the setPrompt method:

var colors   = require('colors'),
    readline = require('readline');

var rl = readline.createInterface(process.stdin, process.stdout);

/* Overcome some bugs in the Nodejs readline implementation */
rl._write = rl.write; 
rl.write = function(d, key)
{
    // "key" functionality is lost, but if you care enough you can add it back
    rl.setPrompt(d, d.split(/[\r\n]/).pop().stripColors.length);
    rl.prompt(true);
};

var str = '[' + '?'.green + '] Blackbeard walks under the black flag with a ____? ';
rl.write(str);

rl.on('line', function(answer)
{
    var answ = 'scallywag swagger';
    console.log(
        'You answered "' + ((answer == answ) ? answer.green : answer.red)
        + '". The correct answer is', '"' + answ.green + '".');
    rl.prompt(true);
});


The results for both are the same:

Or you can do both. Or you can modify the readline.Interface.prototype directly (and have your fix applied globally) instead of the object instances themselves. Lots of options here.

Hope this helps someone!

EDIT—See also: https://github.com/joyent/node/issues/3860

Sure, you'll want to modify your rl.write string with the CSI sequence n D where n is the number of characters to move your cursor back.

Here is a snippet to experiment with:

var rl = require('readline').createInterface({input: process.stdin, output: process.stdout});

rl.question('\u001b[1;36mEnter destination path: \u001b[0m', function(answer) {

});                                                                                               
rl.write('\u001b[11 D/home/jp/bin');

Notice the 11 and the D in the last line? The D stands for the number of characters to move back. 11 is obviously then the number of characters.

See this for all of the fun terminal codes: http://en.wikipedia.org/wiki/ANSI_escape_code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!