bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
194 lines (158 loc) • 4.14 kB
JavaScript
'use strict';
const stringWidth = require('string-width');
const stripAnsi = require('strip-ansi');
const ESCAPES = new Set([
'\u001B',
'\u009B'
]);
const END_CODE = 39;
const ESCAPE_CODES = new Map([
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ]
]);
const wrapAnsi = code => `${ESCAPES.values().next().value}[${code}m`;
// Calculate the length of words split on ' ', ignoring
// the extra characters added by ansi escape codes
const wordLengths = str => str.split(' ').map(s => stringWidth(s));
// Wrap a long word across multiple rows
// Ansi escape codes do not count towards length
const wrapWord = (rows, word, cols) => {
const arr = Array.from(word);
let insideEscape = false;
let visible = stringWidth(stripAnsi(rows[rows.length - 1]));
for (const item of arr.entries()) {
const i = item[0];
const char = item[1];
const charLength = stringWidth(char);
if (visible + charLength <= cols) {
rows[rows.length - 1] += char;
} else {
rows.push(char);
visible = 0;
}
if (ESCAPES.has(char)) {
insideEscape = true;
} else if (insideEscape && char === 'm') {
insideEscape = false;
continue;
}
if (insideEscape) {
continue;
}
visible += charLength;
if (visible === cols && i < arr.length - 1) {
rows.push('');
visible = 0;
}
}
// It's possible that the last row we copy over is only
// ansi escape characters, handle this edge-case
if (!visible && rows[rows.length - 1].length > 0 && rows.length > 1) {
rows[rows.length - 2] += rows.pop();
}
};
// The wrap-ansi module can be invoked
// in either 'hard' or 'soft' wrap mode
//
// 'hard' will never allow a string to take up more
// than cols characters
//
// 'soft' allows long words to expand past the column length
const exec = (str, cols, opts) => {
const options = opts || {};
if (str.trim() === '') {
return options.trim === false ? str : str.trim();
}
let pre = '';
let ret = '';
let escapeCode;
const lengths = wordLengths(str);
const words = str.split(' ');
const rows = [''];
for (const item of Array.from(words).entries()) {
const i = item[0];
const word = item[1];
rows[rows.length - 1] = options.trim === false ? rows[rows.length - 1] : rows[rows.length - 1].trim();
let rowLength = stringWidth(rows[rows.length - 1]);
if (rowLength || word === '') {
if (rowLength === cols && options.wordWrap === false) {
// If we start with a new word but the current row length equals the length of the columns, add a new row
rows.push('');
rowLength = 0;
}
rows[rows.length - 1] += ' ';
rowLength++;
}
// In 'hard' wrap mode, the length of a line is
// never allowed to extend past 'cols'
if (lengths[i] > cols && options.hard) {
if (rowLength) {
rows.push('');
}
wrapWord(rows, word, cols);
continue;
}
if (rowLength + lengths[i] > cols && rowLength > 0) {
if (options.wordWrap === false && rowLength < cols) {
wrapWord(rows, word, cols);
continue;
}
rows.push('');
}
if (rowLength + lengths[i] > cols && options.wordWrap === false) {
wrapWord(rows, word, cols);
continue;
}
rows[rows.length - 1] += word;
}
pre = rows.map(r => options.trim === false ? r : r.trim()).join('\n');
for (const item of Array.from(pre).entries()) {
const i = item[0];
const char = item[1];
ret += char;
if (ESCAPES.has(char)) {
const code = parseFloat(/\d[^m]*/.exec(pre.slice(i, i + 4)));
escapeCode = code === END_CODE ? null : code;
}
const code = ESCAPE_CODES.get(Number(escapeCode));
if (escapeCode && code) {
if (pre[i + 1] === '\n') {
ret += wrapAnsi(code);
} else if (char === '\n') {
ret += wrapAnsi(escapeCode);
}
}
}
return ret;
};
// For each newline, invoke the method separately
module.exports = (str, cols, opts) => {
return String(str)
.normalize()
.split('\n')
.map(line => exec(line, cols, opts))
.join('\n');
};