tty-strings
Version:
Tools for working with strings displayed in the terminal
108 lines • 4.65 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("./utils");
const splitChars_1 = require("./splitChars");
const stringLength_1 = require("./stringLength");
/**
* Insert, remove or replace characters from a string, similar to the native `Array.splice()` method,
* except that the start index and delete count refer to grapheme clusters within the string.
*
* @remarks
* String may contain ANSI escape codes; inserted content will adopt any ANSI styling applied to the character
* immediately preceding the insert point. ANSI control sequences that are not style or hyperlink sequences
* will be preserved in the output string.
*
* @example
* ```ts
* import { spliceChars } from 'tty-strings';
*
* spliceChars('à̰ b̸ ĉ̥', 2, 1, 'x͎͛ÿz̯̆'); // 'à̰ x͎͛ÿz̯̆ ĉ̥'
* ```
*
* @param string - Input string from which to remove, insert, or replace characters.
* @param start - Character index at which to begin splicing.
* @param deleteCount - Number of characters to remove from the string.
* @param insert - Optional string to be inserted at the index specified by the `start` parameter.
* @returns The modified input string.
*/
function spliceChars(string, start, deleteCount, insert = '') {
// splice start index
const startIndex = Math.max(start < 0 ? start + (0, stringLength_1.default)(string) : start, 0),
// splice end index
endIndex = startIndex + deleteCount,
// ansi escapes stack, items in the form [seq, isLink, close, [idx, afterStart]]
ansiStack = [];
// the result string
let result = '',
// character index
idx = 0,
// conventional string index
i = 0,
// start found flag
startFound = false,
// end found flag
endFound = false;
// match all ansi escape codes
for (const [chunk, isEscape] of (0, utils_1.parseAnsi)(String(string))) {
// check if chunk is an escape sequence
if (isEscape) {
// process this escape sequence
const closed = (0, utils_1.parseEscape)(ansiStack, chunk, [idx, startFound]);
// check if insert point has been reached or if chunk is not a SGR/hyperlink escape
if (closed === null || (!startFound && (idx < startIndex || insert))) {
result += chunk;
}
else if (closed.length) {
// add close sequences for any active escapes in the stack
result += (0, utils_1.closeEscapes)(closed.filter(([, , , [x, afterStart]]) => (!afterStart && (x < startIndex || insert))));
}
// increment conventional string index
i += chunk.length;
continue;
}
// check if the tail of the last chunk hit the end index
if (endFound) {
return result
// append any open escapes found after the insert point
+ (0, utils_1.openEscapes)(ansiStack.filter(([, , , [x, afterStart]]) => (afterStart || (x === startIndex && !insert))))
// add the rest of the string
+ string.slice(i);
}
// iterate through the characters in this chunk
for (const char of (0, splitChars_1.default)(chunk)) {
if (idx < startIndex) {
result += char;
}
else {
// check for the start of the splice
if (idx === startIndex && !startFound) {
result += insert;
startFound = true;
}
// check for end of the splice
if (idx === endIndex) {
return result
// append any open escapes found after the insert point
+ (0, utils_1.openEscapes)(ansiStack.filter(([, , , [x, afterStart]]) => (afterStart || (x === startIndex && !insert))))
// add the rest of the string
+ string.slice(i);
}
}
// increment char index
idx += 1;
// increment conventional string index
i += char.length;
}
// check if at the end of this chunk, we have hit the start index
if (idx === startIndex && !startFound) {
result += insert;
startFound = true;
}
// the end of this chunk hits the endIndex, set the `endFound` flag
if (idx === endIndex)
endFound = true;
}
return startFound ? result : result + insert;
}
exports.default = spliceChars;
//# sourceMappingURL=spliceChars.js.map
;