UNPKG

tty-strings

Version:

Tools for working with strings displayed in the terminal

136 lines 6.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sliceColumns = exports.sliceChars = void 0; const utils_1 = require("./utils"); const splitChars_1 = require("./splitChars"); const charWidths_1 = require("./charWidths"); const stringLength_1 = require("./stringLength"); const stringWidth_1 = require("./stringWidth"); function createSlicer(iterator, measureFn) { return (string, beginIndex = 0, endIndex = Infinity) => { // convert input to string if necessary // eslint-disable-next-line no-param-reassign if (typeof string !== 'string') string = String(string); // if either beginIndex or endIndex are negative, measure the string and adjust if (beginIndex < 0 || endIndex < 0) { // measure the string const n = measureFn(string); // adjust begin index if negative // eslint-disable-next-line no-param-reassign if (beginIndex < 0) beginIndex = n + beginIndex; // adjust end index if negative // eslint-disable-next-line no-param-reassign if (endIndex < 0) endIndex = n + endIndex; } // if slice span is <= 0, return an empty string if (beginIndex >= endIndex) return ''; // ansi escapes stack, items in the form [seq, isLink, close, idx] const ansiStack = []; // the result string let result = '', // current slice index idx = 0, // current stack index ax = -1, // queued ansi stack closings closedStack = []; // match all ansi escape codes for (const [chunk, isEscape] of (0, utils_1.parseAnsi)(string)) { // check if chunk is an escape sequence if (isEscape) { // process this escape sequence const closed = (0, utils_1.parseEscape)(ansiStack, chunk, idx); // add any newly closed sequences to the closed stack if (closed === null || closed === void 0 ? void 0 : closed.length) closedStack.unshift(...closed); continue; } // check if any unclosed sequences have accumulated if (closedStack.length) { // close acumulated ansi sequences result += (0, utils_1.closeEscapes)(closedStack.filter(([, , , cx]) => cx <= ax)); // reset the closed stack closedStack = []; } // store the value of the slice index at the outset of this chunk const sidx = idx; // iterate through the characters in this chunk for (const [char, span] of iterator(chunk)) { // check if we are currently within the desired slice if ((beginIndex < idx || (beginIndex === idx && span > 0)) && idx + span <= endIndex) { // check if the stack index is less than the slice index at the start of this chunk if (ax < sidx) { result += (0, utils_1.openEscapes)(ansiStack.filter(([, , , x]) => x > ax)); ax = sidx; } // add char to the result result += char; } // increment current slice index idx += span; // stop if the upper limit of the desired slice has been exceeded if (idx >= endIndex) { // close active items in the escape stack and return the result slice return result + (0, utils_1.closeEscapes)(ansiStack.filter(([, , , x]) => x <= ax)); } } } // close active items in the escape stack and return the result slice return result + (0, utils_1.closeEscapes)([...closedStack, ...ansiStack].filter(([, , , x]) => x <= ax)); }; } /** * Slice a string by character index. Behaves like the native `String.slice()`, except that indexes refer * to grapheme clusters within the string. Negative index values specify a position measured from the * character length of the string. * * @remarks * Input string may contain ANSI escape sequences. Style and hyperlink sequences that apply to the * sliced portion of the string will be preserved, while all other types of control sequences will be * ignored and will not be included in the output slice. * * @example * ```ts * import { sliceChars } from 'tty-strings'; * * const slice = sliceChars('🙈🙉🙊', 0, 2); // '🙈🙉' * ``` * * @param string - Input string to slice. * @param beginIndex - Character index (defaults to `0`) at which to begin the slice. * @param endIndex - Character index before which to end the slice. * @returns The sliced string. */ exports.sliceChars = createSlicer(function* iterator(str) { for (const char of (0, splitChars_1.default)(str)) yield [char, 1]; }, stringLength_1.default); /** * Slice a string by column index. Behaves like the native `String.slice()`, except that indexes account * for the visual width of each character. Negative index values specify a position measured from the * visual width of the string. * * @remarks * Input string may contain ANSI escape sequences. Style and hyperlink sequences that apply to the * sliced portion of the string will be preserved, while all other types of control sequences will be * ignored and will not be included in the output slice. * * @example * ```ts * import { sliceColumns } from 'tty-strings'; * * // '🙈', '🙉', and '🙊' are all full width characters * const slice = sliceColumns('🙈🙉🙊', 0, 2); // '🙈' * ``` * * @param string - Input string to slice. * @param beginIndex - Column index (defaults to `0`) at which to begin the slice. * @param endIndex - Column index before which to end the slice. * @returns The sliced string. */ exports.sliceColumns = createSlicer(charWidths_1.default, stringWidth_1.default); //# sourceMappingURL=sliceString.js.map