sandbox-js/node_modules/prompt-sync/index.js
2023-01-21 20:28:58 -05:00

244 lines
7.0 KiB
JavaScript

'use strict'
var fs = require('fs');
var stripAnsi = require('strip-ansi');
var term = 13; // carriage return
/**
* create -- sync function for reading user input from stdin
* @param {Object} config {
* sigint: {Boolean} exit on ^C
* autocomplete: {StringArray} function({String})
* history: {String} a history control object (see `prompt-sync-history`)
* }
* @returns {Function} prompt function
*/
// for ANSI escape codes reference see https://en.wikipedia.org/wiki/ANSI_escape_code
function create(config) {
config = config || {};
var sigint = config.sigint;
var eot = config.eot;
var autocomplete = config.autocomplete =
config.autocomplete || function(){return []};
var history = config.history;
prompt.history = history || {save: function(){}};
prompt.hide = function (ask) { return prompt(ask, {echo: ''}) };
return prompt;
/**
* prompt -- sync function for reading user input from stdin
* @param {String} ask opening question/statement to prompt for
* @param {String} value initial value for the prompt
* @param {Object} opts {
* echo: set to a character to be echoed, default is '*'. Use '' for no echo
* value: {String} initial value for the prompt
* ask: {String} opening question/statement to prompt for, does not override ask param
* autocomplete: {StringArray} function({String})
* }
*
* @returns {string} Returns the string input or (if sigint === false)
* null if user terminates with a ^C
*/
function prompt(ask, value, opts) {
var insert = 0, savedinsert = 0, res, i, savedstr;
opts = opts || {};
if (Object(ask) === ask) {
opts = ask;
ask = opts.ask;
} else if (Object(value) === value) {
opts = value;
value = opts.value;
}
ask = ask || '';
var echo = opts.echo;
var masked = 'echo' in opts;
autocomplete = opts.autocomplete || autocomplete;
var fd = (process.platform === 'win32') ?
process.stdin.fd :
fs.openSync('/dev/tty', 'rs');
var wasRaw = process.stdin.isRaw;
if (!wasRaw) { process.stdin.setRawMode && process.stdin.setRawMode(true); }
var buf = Buffer.alloc(3);
var str = '', character, read;
savedstr = '';
if (ask) {
process.stdout.write(ask);
}
var cycle = 0;
var prevComplete;
while (true) {
read = fs.readSync(fd, buf, 0, 3);
if (read > 1) { // received a control sequence
switch(buf.toString()) {
case '\u001b[A': //up arrow
if (masked) break;
if (!history) break;
if (history.atStart()) break;
if (history.atEnd()) {
savedstr = str;
savedinsert = insert;
}
str = history.prev();
insert = str.length;
process.stdout.write('\u001b[2K\u001b[0G' + ask + str);
break;
case '\u001b[B': //down arrow
if (masked) break;
if (!history) break;
if (history.pastEnd()) break;
if (history.atPenultimate()) {
str = savedstr;
insert = savedinsert;
history.next();
} else {
str = history.next();
insert = str.length;
}
process.stdout.write('\u001b[2K\u001b[0G'+ ask + str + '\u001b['+(insert+ask.length+1)+'G');
break;
case '\u001b[D': //left arrow
if (masked) break;
var before = insert;
insert = (--insert < 0) ? 0 : insert;
if (before - insert)
process.stdout.write('\u001b[1D');
break;
case '\u001b[C': //right arrow
if (masked) break;
insert = (++insert > str.length) ? str.length : insert;
process.stdout.write('\u001b[' + (insert+ask.length+1) + 'G');
break;
default:
if (buf.toString()) {
str = str + buf.toString();
str = str.replace(/\0/g, '');
insert = str.length;
promptPrint(masked, ask, echo, str, insert);
process.stdout.write('\u001b[' + (insert+ask.length+1) + 'G');
buf = Buffer.alloc(3);
}
}
continue; // any other 3 character sequence is ignored
}
// if it is not a control character seq, assume only one character is read
character = buf[read-1];
// catch a ^C and return null
if (character == 3){
process.stdout.write('^C\n');
fs.closeSync(fd);
if (sigint) process.exit(130);
process.stdin.setRawMode && process.stdin.setRawMode(wasRaw);
return null;
}
// catch a ^D and exit
if (character == 4) {
if (str.length == 0 && eot) {
process.stdout.write('exit\n');
process.exit(0);
}
}
// catch the terminating character
if (character == term) {
fs.closeSync(fd);
if (!history) break;
if (!masked && str.length) history.push(str);
history.reset();
break;
}
// catch a TAB and implement autocomplete
if (character == 9) { // TAB
res = autocomplete(str);
if (str == res[0]) {
res = autocomplete('');
} else {
prevComplete = res.length;
}
if (res.length == 0) {
process.stdout.write('\t');
continue;
}
var item = res[cycle++] || res[cycle = 0, cycle++];
if (item) {
process.stdout.write('\r\u001b[K' + ask + item);
str = item;
insert = item.length;
}
}
if (character == 127 || (process.platform == 'win32' && character == 8)) { //backspace
if (!insert) continue;
str = str.slice(0, insert-1) + str.slice(insert);
insert--;
process.stdout.write('\u001b[2D');
} else {
if ((character < 32 ) || (character > 126))
continue;
str = str.slice(0, insert) + String.fromCharCode(character) + str.slice(insert);
insert++;
};
promptPrint(masked, ask, echo, str, insert);
}
process.stdout.write('\n')
process.stdin.setRawMode && process.stdin.setRawMode(wasRaw);
return str || value || '';
};
function promptPrint(masked, ask, echo, str, insert) {
if (masked) {
process.stdout.write('\u001b[2K\u001b[0G' + ask + Array(str.length+1).join(echo));
} else {
process.stdout.write('\u001b[s');
if (insert == str.length) {
process.stdout.write('\u001b[2K\u001b[0G'+ ask + str);
} else {
if (ask) {
process.stdout.write('\u001b[2K\u001b[0G'+ ask + str);
} else {
process.stdout.write('\u001b[2K\u001b[0G'+ str + '\u001b[' + (str.length - insert) + 'D');
}
}
// Reposition the cursor to the right of the insertion point
var askLength = stripAnsi(ask).length;
process.stdout.write(`\u001b[${askLength+1+(echo==''? 0:insert)}G`);
}
}
};
module.exports = create;