@visulima/string
Version:
Functions for manipulating strings.
420 lines (415 loc) • 14 kB
JavaScript
'use strict';
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const constants = require('./packem_shared/constants-nihvIvk5.cjs');
const getStringWidth = require('./get-string-width.cjs');
const LRUCache = require('./packem_shared/LRUCache-Crb-m7cw.cjs');
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
const segmentCache = new LRUCache(100);
const defaultSegmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
const ANSI_RESET_CODES = /* @__PURE__ */ new Set([
"0",
// Full reset
"22",
// Bold reset
"23",
// Italic reset
"24",
// Underline reset
"27",
// Inverse reset
"28",
// Hidden reset
"29",
// Strikethrough reset
"39",
// Foreground color reset
"49"
// Background color reset
]);
const STYLE_CODES = /* @__PURE__ */ new Set([
"1",
// Bold
"3",
// Italic
"4",
// Underline
"7",
// Inverse
"8",
// Hidden
"9"
// Strikethrough
]);
const FORMAT_RESET_CODES = /* @__PURE__ */ new Map([
["22", "1"],
// Bold reset
["23", "3"],
// Italic reset
["24", "4"],
// Underline reset
["27", "7"],
// Inverse reset
["28", "8"],
// Hidden reset
["29", "9"]
// Strikethrough reset
]);
const isForegroundColor = /* @__PURE__ */ __name((code) => code >= "30" && code <= "37" || code >= "90" && code <= "97" || code.startsWith("38;"), "isForegroundColor");
const isBackgroundColor = /* @__PURE__ */ __name((code) => code >= "40" && code <= "47" || code >= "100" && code <= "107" || code.startsWith("48;"), "isBackgroundColor");
const FORMAT_STYLES = [
{ close: "\x1B[22m", open: "\x1B[1m" },
// Bold
{ close: "\x1B[23m", open: "\x1B[3m" },
// Italic
{ close: "\x1B[24m", open: "\x1B[4m" },
// Underline
{ close: "\x1B[27m", open: "\x1B[7m" },
// Inverse
{ close: "\x1B[28m", open: "\x1B[8m" },
// Hidden
{ close: "\x1B[29m", open: "\x1B[9m" }
// Strikethrough
];
const findFirstPositionOfAny = /* @__PURE__ */ __name((input, substrings) => {
let firstPos = -1;
for (const substring of substrings) {
const pos = input.indexOf(substring);
if (pos >= 0 && (firstPos === -1 || pos < firstPos)) {
firstPos = pos;
}
}
return firstPos;
}, "findFirstPositionOfAny");
const processIntoStyledSegments = /* @__PURE__ */ __name((input, options) => {
if (!input.includes("\x1B")) {
return [
{
after: "",
before: "",
content: input,
visibleLength: getStringWidth.getStringWidth(input, options.width)
}
];
}
const cachedResult = segmentCache.get(input);
if (cachedResult) {
return cachedResult;
}
const parts = [];
const matches = [];
let lastIndex = 0;
let match;
constants.RE_ANSI.lastIndex = 0;
while ((match = constants.RE_ANSI.exec(input)) !== null) {
const matchedText = match[0];
parts.push(input.slice(lastIndex, match.index));
matches.push(matchedText);
lastIndex = match.index + matchedText.length;
}
if (lastIndex < input.length) {
parts.push(input.slice(lastIndex));
}
const closingSequences = [];
for (const ansi of matches) {
if (ansi === "\x1B[0m" || ansi === "\x1B]8;;\x07") {
closingSequences.push(ansi);
continue;
}
if (ansi.startsWith("\x1B[") && ansi.endsWith("m")) {
const code = ansi.slice(2, -1);
if (ANSI_RESET_CODES.has(code)) {
closingSequences.push(ansi);
continue;
}
}
}
const activeStylesSet = /* @__PURE__ */ new Set();
const segments = [];
for (const [index, text] of parts.entries()) {
if (index > 0 && matches[index - 1]) {
const ansi = matches[index - 1];
if (ansi === "\x1B[0m") {
activeStylesSet.clear();
} else if (ansi === "\x1B]8;;\x07") {
for (const style of activeStylesSet) {
if (style.startsWith("\x1B]8;;") && !style.endsWith("\x1B]8;;\x07")) {
activeStylesSet.delete(style);
break;
}
}
} else if (ansi.startsWith("\x1B[") && ansi.endsWith("m")) {
const code = ansi.slice(2, -1);
if (code === "39") {
for (const style of activeStylesSet) {
if (style.startsWith("\x1B[") && style.endsWith("m")) {
const styleCode = style.slice(2, -1);
if (isForegroundColor(styleCode)) {
activeStylesSet.delete(style);
break;
}
}
}
} else if (code === "49") {
for (const style of activeStylesSet) {
if (style.startsWith("\x1B[") && style.endsWith("m")) {
const styleCode = style.slice(2, -1);
if (isBackgroundColor(styleCode)) {
activeStylesSet.delete(style);
break;
}
}
}
} else if (FORMAT_RESET_CODES.has(code)) {
const targetCode = FORMAT_RESET_CODES.get(code);
for (const style of activeStylesSet) {
if (style === `\x1B[${targetCode}m`) {
activeStylesSet.delete(style);
break;
}
}
} else {
if (isForegroundColor(code)) {
for (const style of activeStylesSet) {
if (style.startsWith("\x1B[") && style.endsWith("m")) {
const styleCode = style.slice(2, -1);
if (isForegroundColor(styleCode)) {
activeStylesSet.delete(style);
break;
}
}
}
} else if (isBackgroundColor(code)) {
for (const style of activeStylesSet) {
if (style.startsWith("\x1B[") && style.endsWith("m")) {
const styleCode = style.slice(2, -1);
if (isBackgroundColor(styleCode)) {
activeStylesSet.delete(style);
break;
}
}
}
} else if (STYLE_CODES.has(code)) {
const targetStyle = `\x1B[${code}m`;
if (activeStylesSet.has(targetStyle)) {
activeStylesSet.delete(targetStyle);
}
}
activeStylesSet.add(ansi);
}
} else if (ansi.startsWith("\x1B]8;;") && !ansi.endsWith("\x1B]8;;\x07")) {
for (const style of activeStylesSet) {
if (style.startsWith("\x1B]8;;") && !style.endsWith("\x1B]8;;\x07")) {
activeStylesSet.delete(style);
break;
}
}
activeStylesSet.add(ansi);
}
}
if (text === "") {
continue;
}
let closingSequence = "";
if (activeStylesSet.size > 0) {
if (closingSequences.includes("\x1B[0m")) {
closingSequence = "\x1B[0m";
} else {
const closingParts = [];
let needsHyperlinkClose = false;
for (const style of activeStylesSet) {
if (style.startsWith("\x1B]8;;") && !style.endsWith("\x1B]8;;\x07")) {
needsHyperlinkClose = true;
break;
}
}
if (needsHyperlinkClose && closingSequences.includes("\x1B]8;;\x07")) {
closingParts.push("\x1B]8;;\x07");
}
let needsForegroundClose = false;
let needsBackgroundClose = false;
for (const style of activeStylesSet) {
if (style.startsWith("\x1B[") && style.endsWith("m")) {
const styleCode = style.slice(2, -1);
if (isForegroundColor(styleCode)) {
needsForegroundClose = true;
} else if (isBackgroundColor(styleCode)) {
needsBackgroundClose = true;
}
}
}
if (needsForegroundClose && needsBackgroundClose) {
const fgOpenPos = findFirstPositionOfAny(input, [
"\x1B[3",
// Basic colors 30-37
"\x1B[9",
// Bright colors 90-97
"\x1B[38;"
// RGB/256 colors
]);
const bgOpenPos = findFirstPositionOfAny(input, [
"\x1B[4",
// Basic colors 40-47
"\x1B[10",
// Bright colors 100-107
"\x1B[48;"
// RGB/256 colors
]);
if (fgOpenPos >= 0 && bgOpenPos >= 0) {
if (fgOpenPos > bgOpenPos) {
closingParts.push("\x1B[39m", "\x1B[49m");
} else {
closingParts.push("\x1B[49m", "\x1B[39m");
}
} else {
closingParts.push("\x1B[39m", "\x1B[49m");
}
} else {
if (needsForegroundClose) {
closingParts.push("\x1B[39m");
}
if (needsBackgroundClose) {
closingParts.push("\x1B[49m");
}
}
if (needsForegroundClose && needsBackgroundClose && input.includes("\x1B[39m") && input.includes("\x1B[49m")) {
if (input.indexOf("\x1B[39m") < input.indexOf("\x1B[49m")) {
closingParts.length -= needsForegroundClose && needsBackgroundClose ? 2 : needsForegroundClose || needsBackgroundClose ? 1 : 0;
closingParts.push("\x1B[39m", "\x1B[49m");
} else if (input.indexOf("\x1B[49m") < input.indexOf("\x1B[39m")) {
closingParts.length -= needsForegroundClose && needsBackgroundClose ? 2 : needsForegroundClose || needsBackgroundClose ? 1 : 0;
closingParts.push("\x1B[49m", "\x1B[39m");
}
}
const activeFormatStyles = FORMAT_STYLES.filter((style) => activeStylesSet.has(style.open)).map((style) => {
return {
position: input.indexOf(style.open),
style
};
}).filter((item) => item.position >= 0).sort((a, b) => b.position - a.position);
for (const item of activeFormatStyles) {
if (closingSequences.includes(item.style.close)) {
closingParts.push(item.style.close);
}
}
closingSequence = closingParts.join("");
}
}
const activeStylesString = [...activeStylesSet].join("");
segments.push({
after: closingSequence,
before: activeStylesString,
content: text,
visibleLength: getStringWidth.getStringWidth(text, {
fullWidth: 1,
wideWidth: 1
})
});
}
segmentCache.set(input, segments);
return segments;
}, "processIntoStyledSegments");
const fastSlice = /* @__PURE__ */ __name((inputString, startIndex, endIndex, options) => {
const graphemes = [...options.segmenter.segment(inputString)];
const result = [];
let currentWidth = 0;
let startFound = false;
for (const grapheme of graphemes) {
const graphemeWidth = getStringWidth.getStringWidth(grapheme.segment, options.width);
const positionBefore = currentWidth;
const positionAfter = currentWidth + graphemeWidth;
if (!startFound) {
if (positionBefore < startIndex) {
currentWidth = positionAfter;
continue;
} else {
startFound = true;
}
}
if (positionBefore >= endIndex) {
break;
}
if (positionAfter > endIndex) {
break;
}
result.push(grapheme.segment);
currentWidth = positionAfter;
}
return result.join("");
}, "fastSlice");
const slice = /* @__PURE__ */ __name((inputString, startIndex = 0, endIndex = Number.MAX_SAFE_INTEGER, options = {}) => {
const config = {
segmenter: defaultSegmenter,
...options
};
if (startIndex >= endIndex || inputString === "") {
return "";
}
const inputStringWidth = getStringWidth.getStringWidth(inputString, config.width);
if (startIndex === 0 && endIndex >= inputStringWidth) {
return inputString;
}
if (startIndex < 0 || endIndex < 0) {
throw new RangeError("Negative indices aren't supported");
}
if (!inputString.includes("\x1B")) {
if (/^[\u0000-\u007F]*$/.test(inputString)) {
return inputString.slice(startIndex, endIndex);
}
return fastSlice(inputString, startIndex, endIndex, config);
}
constants.RE_VALID_ANSI_PAIRS.lastIndex = 0;
constants.RE_VALID_HYPERLINKS.lastIndex = 0;
if (!constants.RE_VALID_ANSI_PAIRS.test(inputString) && !constants.RE_VALID_HYPERLINKS.test(inputString)) {
return inputString;
}
const segments = processIntoStyledSegments(inputString, config);
if (segments.length === 0) {
return "";
}
let currentPos = 0;
const visibleSegments = [];
for (const [index, segment] of segments.entries()) {
const segmentStart = currentPos;
const segmentEnd = currentPos + segment.visibleLength;
if (segmentEnd > startIndex && segmentStart < endIndex) {
const visibleStart = Math.max(0, startIndex - segmentStart);
const visibleEnd = Math.min(segment.visibleLength, endIndex - segmentStart);
visibleSegments.push({
end: visibleEnd,
index,
segment,
start: visibleStart
});
}
currentPos = segmentEnd;
}
if (visibleSegments.length === 0) {
return "";
}
const resultParts = [];
const firstSegment = visibleSegments[0].segment;
resultParts.push(firstSegment.before);
for (let index = 0; index < visibleSegments.length; index++) {
const { end, segment, start } = visibleSegments[index];
if (start === 0 && end === segment.visibleLength) {
resultParts.push(segment.content);
} else {
const graphemes = [...config.segmenter.segment(segment.content)];
resultParts.push(
graphemes.slice(start, end).map((entry) => entry.segment).join("")
);
}
if (index < visibleSegments.length - 1) {
const nextSegment = visibleSegments[index + 1].segment;
const segmentAfter = segment.after;
const nextBefore = nextSegment.before;
if (segmentAfter !== nextBefore && (segmentAfter || nextBefore)) {
resultParts.push(segmentAfter, nextBefore);
}
}
}
resultParts.push(visibleSegments.at(-1).segment.after);
return resultParts.join("");
}, "slice");
exports.slice = slice;