JaredMcAteer
JaredMcAteer

Reputation: 22536

Adding colors to terminal prompt results in large white space

I'm working on a simple cli script and wanted to add some color to the following code:

rl.question('Enter destination path: ', function(answer) {
     // ...                                                                                                                                
});                                                                                                                                  
rl.write('/home/' + user + '/bin');

Which displays in the terminal:

Enter destination path: /home/jmcateer/bin_

But I wanted to add some color to the prompt I did the following:

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

});                                                                                                                                  
rl.write('/home/' + user + '/bin');

And the command line prompt ended up displaying:

Enter destination path:                 /home/jmcateer/bin_

It works but there's a huge amount of white space I'd prefer weren't there. Does anyone have any ideas on how to deal with this?

Edit:

I can't delete the white space by backspacing through it... when I try to use the backspace key the white space jumps to the other end like so

Enter destination path:                 /home/jmcateer/bin_
Enter destination path: /home/jmcateer/bi                _
Enter destination path: /home/jmcateer/b                _
...
Enter destination path:                 _

At that point backspace has no effect.

Upvotes: 6

Views: 2476

Answers (3)

Xunnamius
Xunnamius

Reputation: 578

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: output of the above scripts

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

Upvotes: 2

Basil Crow
Basil Crow

Reputation: 316

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).

Upvotes: 5

JP Richardson
JP Richardson

Reputation: 39395

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

Upvotes: 0

Related Questions