tty-strings
Version:
Tools for working with strings displayed in the terminal
136 lines • 6.13 kB
JavaScript
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
;