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