CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
index.js469 linesDownload Raw Back to wrap-ansi
1import stringWidth from 'string-width';2import stripAnsi from 'strip-ansi';3import ansiStyles from 'ansi-styles';4 5const ANSI_ESCAPE = '\u001B';6const ANSI_ESCAPE_CSI = '\u009B';7const ESCAPES = new Set([8	ANSI_ESCAPE,9	ANSI_ESCAPE_CSI,10]);11 12const ANSI_ESCAPE_BELL = '\u0007';13const ANSI_CSI = '[';14const ANSI_OSC = ']';15const ANSI_SGR_TERMINATOR = 'm';16const ANSI_SGR_RESET = 0;17const ANSI_SGR_RESET_FOREGROUND = 39;18const ANSI_SGR_RESET_BACKGROUND = 49;19const ANSI_SGR_RESET_UNDERLINE_COLOR = 59;20const ANSI_SGR_FOREGROUND_EXTENDED = 38;21const ANSI_SGR_BACKGROUND_EXTENDED = 48;22const ANSI_SGR_UNDERLINE_COLOR_EXTENDED = 58;23const ANSI_SGR_COLOR_MODE_256 = 5;24const ANSI_SGR_COLOR_MODE_RGB = 2;25const ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;26const ANSI_ESCAPE_REGEX = new RegExp(`^\\u001B(?:\\${ANSI_CSI}(?<sgr>[0-9;]*)${ANSI_SGR_TERMINATOR}|${ANSI_ESCAPE_LINK}(?<uri>[^\\u0007\\u001B]*)(?:\\u0007|\\u001B\\\\))`);27const ANSI_ESCAPE_CSI_REGEX = new RegExp(`^\\u009B(?<sgr>[0-9;]*)${ANSI_SGR_TERMINATOR}`);28const ANSI_SGR_MODIFIER_CLOSE_CODES = new Set(ansiStyles.codes.values());29ANSI_SGR_MODIFIER_CLOSE_CODES.delete(ANSI_SGR_RESET);30 31const segmenter = new Intl.Segmenter();32const getGraphemes = string => Array.from(segmenter.segment(string), ({segment}) => segment);33const TAB_SIZE = 8;34 35const wrapAnsiCode = code => `${ANSI_ESCAPE}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;36const wrapAnsiHyperlink = url => `${ANSI_ESCAPE}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;37 38const getSgrTokens = sgrParameters => {39	const codes = sgrParameters.split(';').map(sgrParameter => sgrParameter === '' ? ANSI_SGR_RESET : Number.parseInt(sgrParameter, 10));40	const sgrTokens = [];41 42	for (let index = 0; index < codes.length; index++) {43		const code = codes[index];44 45		if (!Number.isFinite(code)) {46			continue;47		}48 49		if (50			(51				code === ANSI_SGR_FOREGROUND_EXTENDED52				|| code === ANSI_SGR_BACKGROUND_EXTENDED53				|| code === ANSI_SGR_UNDERLINE_COLOR_EXTENDED54			)55		) {56			if (index + 1 >= codes.length) {57				break;58			}59 60			const mode = codes[index + 1];61 62			if (mode === ANSI_SGR_COLOR_MODE_256 && Number.isFinite(codes[index + 2])) {63				sgrTokens.push([code, mode, codes[index + 2]]);64				index += 2;65				continue;66			}67 68			const red = codes[index + 2];69			const green = codes[index + 3];70			const blue = codes[index + 4];71			if (72				mode === ANSI_SGR_COLOR_MODE_RGB73				&& Number.isFinite(red)74				&& Number.isFinite(green)75				&& Number.isFinite(blue)76			) {77				sgrTokens.push([code, mode, red, green, blue]);78				index += 4;79				continue;80			}81 82			break;83		}84 85		sgrTokens.push([code]);86	}87 88	return sgrTokens;89};90 91const removeActiveStyle = (activeStyles, family) => {92	const activeStyleIndex = activeStyles.findIndex(activeStyle => activeStyle.family === family);93 94	if (activeStyleIndex !== -1) {95		activeStyles.splice(activeStyleIndex, 1);96	}97};98 99const upsertActiveStyle = (activeStyles, nextActiveStyle) => {100	removeActiveStyle(activeStyles, nextActiveStyle.family);101	activeStyles.push(nextActiveStyle);102};103 104const removeModifierStylesByClose = (activeStyles, closeCode) => {105	for (let index = activeStyles.length - 1; index >= 0; index--) {106		const activeStyle = activeStyles[index];107		if (activeStyle.family.startsWith('modifier-') && activeStyle.close === closeCode) {108			activeStyles.splice(index, 1);109		}110	}111};112 113const getColorStyle = (code, sgrToken) => {114	if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97) || (code === ANSI_SGR_FOREGROUND_EXTENDED && sgrToken.length > 1)) {115		return {116			family: 'foreground',117			open: sgrToken.join(';'),118			close: ANSI_SGR_RESET_FOREGROUND,119		};120	}121 122	if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107) || (code === ANSI_SGR_BACKGROUND_EXTENDED && sgrToken.length > 1)) {123		return {124			family: 'background',125			open: sgrToken.join(';'),126			close: ANSI_SGR_RESET_BACKGROUND,127		};128	}129 130	if (code === ANSI_SGR_UNDERLINE_COLOR_EXTENDED && sgrToken.length > 1) {131		return {132			family: 'underlineColor',133			open: sgrToken.join(';'),134			close: ANSI_SGR_RESET_UNDERLINE_COLOR,135		};136	}137};138 139const applySgrResetCode = (code, activeStyles) => {140	if (code === ANSI_SGR_RESET) {141		activeStyles.length = 0;142		return true;143	}144 145	if (code === ANSI_SGR_RESET_FOREGROUND) {146		removeActiveStyle(activeStyles, 'foreground');147		return true;148	}149 150	if (code === ANSI_SGR_RESET_BACKGROUND) {151		removeActiveStyle(activeStyles, 'background');152		return true;153	}154 155	if (code === ANSI_SGR_RESET_UNDERLINE_COLOR) {156		removeActiveStyle(activeStyles, 'underlineColor');157		return true;158	}159 160	if (ANSI_SGR_MODIFIER_CLOSE_CODES.has(code)) {161		removeModifierStylesByClose(activeStyles, code);162		return true;163	}164 165	return false;166};167 168const applySgrToken = (sgrToken, activeStyles) => {169	const [code] = sgrToken;170 171	if (applySgrResetCode(code, activeStyles)) {172		return;173	}174 175	const colorStyle = getColorStyle(code, sgrToken);176	if (colorStyle) {177		upsertActiveStyle(activeStyles, colorStyle);178		return;179	}180 181	const close = ansiStyles.codes.get(code);182	if (close !== undefined && close !== ANSI_SGR_RESET) {183		upsertActiveStyle(activeStyles, {184			family: `modifier-${code}`,185			open: sgrToken.join(';'),186			close,187		});188	}189};190 191const applySgrParameters = (sgrParameters, activeStyles) => {192	for (const sgrToken of getSgrTokens(sgrParameters)) {193		applySgrToken(sgrToken, activeStyles);194	}195};196 197const applySgrResets = (sgrParameters, activeStyles) => {198	for (const sgrToken of getSgrTokens(sgrParameters)) {199		const [code] = sgrToken;200		applySgrResetCode(code, activeStyles);201	}202};203 204const applyLeadingSgrResets = (string, activeStyles) => {205	let remainder = string;206 207	while (remainder.length > 0) {208		if (remainder.startsWith(ANSI_ESCAPE) && remainder[1] !== '\\') {209			const match = ANSI_ESCAPE_REGEX.exec(remainder);210			if (!match) {211				break;212			}213 214			if (match.groups.sgr !== undefined) {215				applySgrResets(match.groups.sgr, activeStyles);216			}217 218			remainder = remainder.slice(match[0].length);219			continue;220		}221 222		if (remainder.startsWith(ANSI_ESCAPE_CSI)) {223			const match = ANSI_ESCAPE_CSI_REGEX.exec(remainder);224			if (!match || match.groups.sgr === undefined) {225				break;226			}227 228			applySgrResets(match.groups.sgr, activeStyles);229			remainder = remainder.slice(match[0].length);230			continue;231		}232 233		break;234	}235};236 237const getClosingSgrSequence = activeStyles => [...activeStyles].reverse().map(activeStyle => wrapAnsiCode(activeStyle.close)).join('');238const getOpeningSgrSequence = activeStyles => activeStyles.map(activeStyle => wrapAnsiCode(activeStyle.open)).join('');239 240// Calculate the length of words split on ' ', ignoring241// the extra characters added by ANSI escape codes242const wordLengths = string => string.split(' ').map(word => stringWidth(word));243 244// Wrap a long word across multiple rows245// ANSI escape codes do not count towards length246const wrapWord = (rows, word, columns) => {247	const characters = getGraphemes(word);248 249	let isInsideEscape = false;250	let isInsideLinkEscape = false;251	let visible = stringWidth(stripAnsi(rows.at(-1)));252 253	for (const [index, character] of characters.entries()) {254		const characterLength = stringWidth(character);255 256		if (visible + characterLength <= columns) {257			rows[rows.length - 1] += character;258		} else {259			rows.push(character);260			visible = 0;261		}262 263		if (ESCAPES.has(character) && !(isInsideLinkEscape && character === ANSI_ESCAPE && characters[index + 1] === '\\')) {264			isInsideEscape = true;265 266			const ansiEscapeLinkCandidate = characters.slice(index + 1, index + 1 + ANSI_ESCAPE_LINK.length).join('');267			isInsideLinkEscape = ansiEscapeLinkCandidate === ANSI_ESCAPE_LINK;268		}269 270		if (isInsideEscape) {271			if (isInsideLinkEscape) {272				if (273					character === ANSI_ESCAPE_BELL274					|| (character === '\\' && index > 0 && characters[index - 1] === ANSI_ESCAPE) // ST terminator (ESC \)275				) {276					isInsideEscape = false;277					isInsideLinkEscape = false;278				}279			} else if (character === ANSI_SGR_TERMINATOR) {280				isInsideEscape = false;281			}282 283			continue;284		}285 286		visible += characterLength;287 288		if (visible === columns && index < characters.length - 1) {289			rows.push('');290			visible = 0;291		}292	}293 294	// It's possible that the last row we copy over is only295	// ANSI escape characters, handle this edge-case296	if (!visible && rows.at(-1).length > 0 && rows.length > 1) {297		rows[rows.length - 2] += rows.pop();298	}299};300 301// Trims spaces from a string ignoring invisible sequences302const stringVisibleTrimSpacesRight = string => {303	const words = string.split(' ');304	let last = words.length;305 306	while (last > 0) {307		if (stringWidth(words[last - 1]) > 0) {308			break;309		}310 311		last--;312	}313 314	if (last === words.length) {315		return string;316	}317 318	return words.slice(0, last).join(' ') + words.slice(last).join('');319};320 321const expandTabs = line => {322	if (!line.includes('\t')) {323		return line;324	}325 326	const segments = line.split('\t');327	let visible = 0;328	let expandedLine = '';329 330	for (const [index, segment] of segments.entries()) {331		expandedLine += segment;332		visible += stringWidth(segment);333 334		if (index < segments.length - 1) {335			const spaces = TAB_SIZE - (visible % TAB_SIZE);336			expandedLine += ' '.repeat(spaces);337			visible += spaces;338		}339	}340 341	return expandedLine;342};343 344// The wrap-ansi module can be invoked in either 'hard' or 'soft' wrap mode.345//346// 'hard' will never allow a string to take up more than columns characters.347//348// 'soft' allows long words to expand past the column length.349const exec = (string, columns, options = {}) => {350	if (options.trim !== false && string.trim() === '') {351		return '';352	}353 354	let returnValue = '';355	let escapeUrl;356	const activeStyles = [];357 358	const lengths = wordLengths(string);359	let rows = [''];360 361	for (const [index, word] of string.split(' ').entries()) {362		if (options.trim !== false) {363			rows[rows.length - 1] = rows.at(-1).trimStart();364		}365 366		let rowLength = stringWidth(rows.at(-1));367 368		if (index !== 0) {369			if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {370				// If we start with a new word but the current row length equals the length of the columns, add a new row371				rows.push('');372				rowLength = 0;373			}374 375			if (rowLength > 0 || options.trim === false) {376				rows[rows.length - 1] += ' ';377				rowLength++;378			}379		}380 381		// In 'hard' wrap mode, the length of a line is never allowed to extend past 'columns'382		if (options.hard && options.wordWrap !== false && lengths[index] > columns) {383			const remainingColumns = columns - rowLength;384			const breaksStartingThisLine = 1 + Math.floor((lengths[index] - remainingColumns - 1) / columns);385			const breaksStartingNextLine = Math.floor((lengths[index] - 1) / columns);386			if (breaksStartingNextLine < breaksStartingThisLine) {387				rows.push('');388			}389 390			wrapWord(rows, word, columns);391			continue;392		}393 394		if (rowLength + lengths[index] > columns && rowLength > 0 && lengths[index] > 0) {395			if (options.wordWrap === false && rowLength < columns) {396				wrapWord(rows, word, columns);397				continue;398			}399 400			rows.push('');401		}402 403		if (rowLength + lengths[index] > columns && options.wordWrap === false) {404			wrapWord(rows, word, columns);405			continue;406		}407 408		rows[rows.length - 1] += word;409	}410 411	if (options.trim !== false) {412		rows = rows.map(row => stringVisibleTrimSpacesRight(row));413	}414 415	const preString = rows.join('\n');416	const pre = getGraphemes(preString);417 418	// We need to keep a separate index as `String#slice()` works on Unicode code units, while `pre` is an array of grapheme clusters.419	let preStringIndex = 0;420 421	for (const [index, character] of pre.entries()) {422		returnValue += character;423 424		if (character === ANSI_ESCAPE && pre[index + 1] !== '\\') {425			const {groups} = ANSI_ESCAPE_REGEX.exec(preString.slice(preStringIndex)) || {groups: {}};426			if (groups.sgr !== undefined) {427				applySgrParameters(groups.sgr, activeStyles);428			} else if (groups.uri !== undefined) {429				escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;430			}431		} else if (character === ANSI_ESCAPE_CSI) {432			const {groups} = ANSI_ESCAPE_CSI_REGEX.exec(preString.slice(preStringIndex)) || {groups: {}};433			if (groups.sgr !== undefined) {434				applySgrParameters(groups.sgr, activeStyles);435			}436		}437 438		if (pre[index + 1] === '\n') {439			if (escapeUrl) {440				returnValue += wrapAnsiHyperlink('');441			}442 443			returnValue += getClosingSgrSequence(activeStyles);444		} else if (character === '\n') {445			const openingStyles = [...activeStyles];446			applyLeadingSgrResets(preString.slice(preStringIndex + 1), openingStyles);447			returnValue += getOpeningSgrSequence(openingStyles);448 449			if (escapeUrl) {450				returnValue += wrapAnsiHyperlink(escapeUrl);451			}452		}453 454		preStringIndex += character.length;455	}456 457	return returnValue;458};459 460// For each newline, invoke the method separately461export default function wrapAnsi(string, columns, options) {462	return String(string)463		.normalize()464		.replaceAll('\r\n', '\n')465		.split('\n')466		.map(line => exec(expandTabs(line), columns, options))467		.join('\n');468}469 
basant307/AI_Governance_Project · CoolFace