// ---------------------------------------------------------------------------- // Misc // ---------------------------------------------------------------------------- /** * Insert a value into an array at the specified index * @param arr - the array * @param at - the index to insert at * @param value - what to add into the array */ export function arrayInsert( arr: Array, at: number, value: T | Array, ): Array { const insert = Array.isArray(value) ? value : [value]; if (at >= arr.length) { arr.push(...insert); return arr; } return [...arr.slice(0, at), ...insert, ...arr.slice(at)]; } /** * An empty function */ export const noop = () => {}; // ---------------------------------------------------------------------------- // Math // ---------------------------------------------------------------------------- /** * Subtract two numbers, returning a zero if the result is negative * @param l * @param s */ export function posSub(l: number, s: number): number { return minSub(l, s, 0); } /** * Subtract two numbers, returning at least the minimum specified * @param l * @param s * @param min */ export function minSub(l: number, s: number, min: number): number { return Math.max(l - s, min); } /** * Add two numbers, up to a max value * @param n1 * @param n2 * @param max */ export function maxAdd(n1: number, n2: number, max: number): number { return Math.min(n1 + n2, max); } // ---------------------------------------------------------------------------- // Strings // ---------------------------------------------------------------------------- /** * Get the codepoint of the first byte of a string. If the string * is empty, this will return 256 * * @param s - the string */ export function ord(s: string): number { if (s.length > 0) { return s.codePointAt(0)!; } return 256; } /** * Split a string by graphemes, not just bytes * * @param s - the string to split into 'characters' */ export function chars(s: string): string[] { return s.split(/(?:)/u); } /** * Get the 'character length' of a string, not its UTF16 byte count * * @param s - the string to check */ export function strlen(s: string): number { return chars(s).length; } /** * Are all the characters in the string in ASCII range? * * @param char - string to check */ export function isAscii(char: string): boolean { return chars(char).every((char) => ord(char) < 0x80); } /** * Is the one char in the string an ascii control character? * * @param char - a one character string to check */ export function isControl(char: string): boolean { const code = ord(char); return isAscii(char) && (code === 0x7f || code < 0x20); } /** * Get the key code for a ctrl chord * * @param char - a one character string */ export function ctrlKey(char: string): string { // This is the normal use case, of course if (isAscii(char)) { const point = ord(char); return String.fromCodePoint(point & 0x1f); } // If it's not ascii, just return the input key code return char; } /** * Trim a string to a max number of characters * @param s * @param maxLen */ export function truncate(s: string, maxLen: number): string { const chin = chars(s); if (maxLen >= chin.length) { return s; } return chin.slice(0, maxLen).join(''); }