1/**
2 * ANSI escape sequences can be used to style text in a tty or perform other
3 * interactions. this file contains many constants for escape codes, as well as
4 * functions for ones that take an argument, such as `cursorUp`.
5 *
6 * note that this file only produces escape sequences, and does not yet feature
7 * detection or fallback code.
8 *
9 * @module ansi
10 */
11
12/** resets all ansi styles */
13export const reset: string = `\x1b[0m`;
14
15/**
16 * puts the text in bold. can be combined with `dim` and colors.
17 * reset with `resetWeight`.
18 */
19export const bold: string = `\x1b[1m`;
20/**
21 * puts the text a more faded color, dimming it. can be combined with `bold` to
22 * produce bold-dim text. dim can be combined with any color to make a dimmed
23 * version of that color. reset with `resetWeight`.
24 */
25export const dim: string = `\x1b[2m`;
26/** resets `bold` and `dim` */
27export const resetWeight: string = `\x1b[22m`;
28
29/**
30 * swaps the foreground and background colors, which reads as a highlight in
31 * any color scheme. reset with `resetInverse`.
32 */
33export const inverse: string = `\x1b[7m`;
34/** resets `inverse` */
35export const resetInverse: string = `\x1b[27m`;
36
37/** resets foreground color */
38export const fgReset: string = `\x1b[39m`;
39/** sets the foreground color to black. on light mode terminals, this color may
40 * be visible. */
41export const fgBlack: string = `\x1b[30m`;
42/** sets the foreground color to red */
43export const fgRed: string = `\x1b[31m`;
44/** sets the foreground color to green */
45export const fgGreen: string = `\x1b[32m`;
46/** sets the foreground color to yellow */
47export const fgYellow: string = `\x1b[33m`;
48/** sets the foreground color to blue */
49export const fgBlue: string = `\x1b[34m`;
50/** sets the foreground color to magenta */
51export const fgMagenta: string = `\x1b[35m`;
52/** sets the foreground color to cyan */
53export const fgCyan: string = `\x1b[36m`;
54/**
55 * sets the foreground color to (dark) white / grey / gray. this color is
56 * brighter than `fgBrightBlack`. consider `dim` for greyed out text and
57 * `fgReset` to set the default foreground color.
58 */
59export const fgWhite: string = `\x1b[37m`;
60/**
61 * sets the foreground color to bright black / grey / gray. consider `dim` for
62 * greyed out text.
63 */
64export const fgBrightBlack: string = `\x1b[90m`;
65/** sets the foreground color to bright red */
66export const fgBrightRed: string = `\x1b[91m`;
67/** sets the foreground color to bright green */
68export const fgBrightGreen: string = `\x1b[92m`;
69/** sets the foreground color to bright yellow */
70export const fgBrightYellow: string = `\x1b[93m`;
71/** sets the foreground color to bright blue */
72export const fgBrightBlue: string = `\x1b[94m`;
73/** sets the foreground color to bright magenta */
74export const fgBrightMagenta: string = `\x1b[95m`;
75/** sets the foreground color to bright cyan */
76export const fgBrightCyan: string = `\x1b[96m`;
77/**
78 * sets the foreground color to bright white. consider `dim` for greyed out
79 * text and `fgReset` to set the default foreground color.
80 */
81export const fgBrightWhite: string = `\x1b[97m`;
82/** sets the foreground color to a color in the 256 color palette. */
83export function fg256(n: number): string {
84 return `\x1b[38;5;${n}m`;
85}
86/** sets the foreground color to an rgb color. */
87export function fgRgb(r: number, g: number, b: number): string {
88 return `\x1b[38;2;${r};${g};${b}m`;
89}
90
91/** resets the cell background color. */
92export const bgReset: string = `\x1b[49m`;
93/** sets the cell background to black. consider `bgReset` to clear the background. */
94export const bgBlack: string = `\x1b[40m`;
95/** sets the cell background to red. */
96export const bgRed: string = `\x1b[41m`;
97/** sets the cell background to green. */
98export const bgGreen: string = `\x1b[42m`;
99/** sets the cell background to yellow. */
100export const bgYellow: string = `\x1b[43m`;
101/** sets the cell background to blue. */
102export const bgBlue: string = `\x1b[44m`;
103/** sets the cell background to magenta. */
104export const bgMagenta: string = `\x1b[45m`;
105/** sets the cell background to cyan. */
106export const bgCyan: string = `\x1b[46m`;
107/**
108 * sets the cell background to (dark) white / grey / gray. this color is
109 * brighter than `bgBrightBlack`.
110 */
111export const bgWhite: string = `\x1b[47m`;
112/**
113 * sets the cell background to bright black. consider `bgReset` to clear the
114 * background.
115 */
116export const bgBrightBlack: string = `\x1b[100m`;
117/** sets the cell background to bright red. */
118export const bgBrightRed: string = `\x1b[101m`;
119/** sets the cell background to bright green. */
120export const bgBrightGreen: string = `\x1b[102m`;
121/** sets the cell background to bright yellow. */
122export const bgBrightYellow: string = `\x1b[103m`;
123/** sets the cell background to bright blue. */
124export const bgBrightBlue: string = `\x1b[104m`;
125/** sets the cell background to bright magenta. */
126export const bgBrightMagenta: string = `\x1b[105m`;
127/** sets the cell background to bright cyan. */
128export const bgBrightCyan: string = `\x1b[106m`;
129/** sets the cell background to bright white. */
130export const bgBrightWhite: string = `\x1b[107m`;
131/** sets the background color to a color in the 256 color palette. */
132export function bg256(n: number): string {
133 return `\x1b[48;5;${n}m`;
134}
135/** sets the background color to an rgb color. */
136export function bgRgb(r: number, g: number, b: number): string {
137 return `\x1b[48;2;${r};${g};${b}m`;
138}
139
140// cursor management
141
142/** save the position of the cursor, restored with {@linkcode cursorRestore}. */
143export const cursorSave: string = "\x1b7";
144/** restore the position of the cursor, saved with {@linkcode cursorSave}. */
145export const cursorRestore: string = "\x1b8";
146
147/** move the cursor up `n` units. */
148export function cursorUp(n: number): string {
149 return n ? `\x1b[${n}A` : "";
150}
151/** move the cursor down `n` units. */
152export function cursorDown(n: number): string {
153 return n ? `\x1b[${n}B` : "";
154}
155/** move the cursor right `n` units. */
156export function cursorRight(n: number): string {
157 return n ? `\x1b[${n}C` : "";
158}
159/** move the cursor right `n` units. */
160export function cursorLeft(n: number): string {
161 return n ? `\x1b[${n}D` : "";
162}
163/** move the cursor to the top left of the screen. */
164export const cursorHome: string = "\x1b[H";
165/** move the cursor to the start of it's current line. */
166export const startOfLine: string = "\r";
167/** move the cursor to the start of the next line. */
168export const startOfNextLine: string = "\n";
169
170// erasure
171/** clear from and including the current cell right to the end of the line. */
172export const clearToEndOfLine: string = "\x1b[K";
173/** clear from and including the current cell left to the start of the line. */
174export const clearToStartOfLine: string = "\x1b[1K";
175/** clear the entire line that the cursor is on without moving it. */
176export const clearFullLine: string = "\x1b[2K";
177/** clear from and including the current cell to the end of the screen. */
178export const clearToEndOfScreen: string = "\x1b[0J";
179
180/**
181 * Begin Synchronized Output
182 * https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
183 */
184export const syncStart: string = "\x1b[?2026h";
185/** End Synchronized Output */
186export const syncEnd: string = "\x1b[?2026l";
187
188/**
189 * Apply ANSI styles to text. `code` is a concatenation of zero or more styles,
190 * which will be cleared as required.
191 */
192export function style(code: string, text: string): string {
193 code = mergeStyles(code);
194 if (!code) return text;
195 const fg = /[\[;][39]/.test(code);
196 const bg = /[\[;](?:4|10)/.test(code);
197 const bold = /[\[;](?:1)[;m]/.test(code);
198 const dim = /[\[;](?:2)[;m]/.test(code);
199 return code + text
200 + mergeStyles(
201 (fg ? fgReset : "")
202 + (bg ? bgReset : "")
203 + (bold ? resetWeight : "")
204 + (dim ? resetWeight : ""),
205 );
206}
207
208/**
209 * Given a string of multiple style ansi codes, combines all of them.
210 * Must be used on a string of only ansi style codes.
211 */
212export function mergeStyles(code: string): string {
213 let styles = [];
214 while (code.length) {
215 if (!code.startsWith("\x1b[")) {
216 throw new Error("invalid style string: " + JSON.stringify(code));
217 }
218 const end = code.indexOf("m");
219 if (end === -1) {
220 throw new Error("invalid style string: " + JSON.stringify(code));
221 }
222 styles.push(code.slice(2, end));
223 code = code.slice(end + 1);
224 }
225 return `\x1b[${styles.join(";")}m`;
226}
227
228/**
229 * Convert a string with ansi text into a string where all the codes have been
230 * turned into their original names.
231 */
232export function debugAnsi(text: string): string {
233 // deno-fmt-ignore
234 const verbaitim: (keyof typeof self)[] = [
235 "fgCyan",
236 "fgBlue",
237 "bgReset",
238 "bgBlack",
239 "bgRed",
240 "bgGreen",
241 "bgYellow",
242 "bgBlue",
243 "bgMagenta",
244 "bgCyan",
245 "bgWhite",
246 "bgBrightBlack",
247 "bgBrightRed",
248 "bgBrightGreen",
249 "bgBrightYellow",
250 "bgBrightBlue",
251 "bgBrightMagenta",
252 "bgBrightCyan",
253 "bgBrightWhite",
254 "fgReset",
255 "fgBlack",
256 "fgRed",
257 "fgGreen",
258 "fgYellow",
259 "fgMagenta",
260 "fgWhite",
261 "fgBrightBlack",
262 "fgBrightRed",
263 "fgBrightGreen",
264 "fgBrightYellow",
265 "fgBrightBlue",
266 "fgBrightMagenta",
267 "fgBrightCyan",
268 "fgBrightWhite",
269 "clearToEndOfLine",
270 "clearToStartOfLine",
271 "clearFullLine",
272 "clearToEndOfScreen",
273 "syncStart",
274 "syncEnd",
275 "bold",
276 "dim",
277 "resetWeight",
278 "cursorSave",
279 "cursorRestore",
280 ];
281 for (const key of verbaitim) {
282 text = text.replaceAll(
283 String(self[key]),
284 style(key.includes("cursor") ? fgBlue : fgCyan, `\${${key}}`),
285 );
286 }
287 text = text.replace(/\x1b\[(\d+)A/g, style(fgYellow, "$${cursorUp($1)}"));
288 text = text.replace(/\x1b\[(\d+)B/g, style(fgYellow, "$${cursorDown($1)}"));
289 text = text.replace(/\x1b\[(\d+)C/g, style(fgYellow, "$${cursorRight($1)}"));
290 text = text.replace(/\x1b\[(\d+)D/g, style(fgYellow, "$${cursorLeft($1)}"));
291 text = text.replace(/\n/g, style(fgRed, "\\n"));
292 text = text.replace(/\r/g, style(fgMagenta, "\\r"));
293 return text;
294}
295
296/** Measure the visible length of a string, ignoring ansi codes. */
297export function widthInTerminal(str: string): number {
298 segmenter ??= new Intl.Segmenter();
299 let width = 0;
300 if (str.includes("\x1b")) str = str.replace(ansiEscapes, "");
301 for (let i = 0, { length } = str; i < length; i += 1) {
302 const code = str.charCodeAt(i);
303 if (code < 32) continue;
304 if (code < 127) {
305 width += 1;
306 continue;
307 }
308 // the remainder of the string is measured with the segmenter, which
309 // handles ascii correctly via segmentWidth (controls are zero-width).
310 for (const { segment } of segmenter.segment(str.slice(i))) {
311 width += segmentWidth(segment);
312 }
313 return width;
314 }
315 return width;
316}
317
318/**
319 * Like `str.slice(0, columns)`, but respecting width in a terminal.
320 * preserves ansi escape sequences.
321 */
322export function trimForTerminal(str: string, columns: number): string {
323 segmenter ??= new Intl.Segmenter();
324 let width = 0;
325 let ansi = false;
326 for (let i = 0, { length } = str; i < length; i += 1) {
327 const code = str.charCodeAt(i);
328 if (code < 32) {
329 if (code === 0x1b) {
330 const esc = str.slice(i).match(ansiEscapes);
331 if (esc) i += esc[0].length - 1, ansi = true;
332 }
333 continue;
334 }
335 if (code < 127) {
336 width += 1;
337 if (width >= columns) {
338 const result = str.slice(0, i + 1);
339 return ansi ? result + "\x1b[0m" : result;
340 }
341 continue;
342 }
343 // non-ascii path
344 let out = str.slice(0, i);
345 str = str.slice(i);
346 const segments = segmenter.segment(str);
347 {
348 const { segment } = UNWRAP(segments.containing(0));
349 width += segmentWidth(segment);
350 if (width > columns) return ansi ? out + "\x1b[0m" : out;
351 out += segment;
352 i = segment.length;
353 }
354 for ({ length } = str; i < length; i += 1) {
355 const code = str.charCodeAt(i);
356 if (code === 0x1b) {
357 const esc = str.slice(i).match(ansiEscapes);
358 if (esc) {
359 i += esc[0].length - 1, ansi = true;
360 out += esc[0];
361 continue;
362 }
363 }
364 const { segment } = UNWRAP(segments.containing(i));
365 width += segmentWidth(segment);
366 if (width > columns) return ansi ? out + "\x1b[0m" : out;
367 out += segment;
368 }
369 return out;
370 }
371 return str;
372}
373
374// derived from sindresorhus's wonderful `string-width`
375// https://github.com/sindresorhus/string-width/blob/42e7b697393a9b9ff732f9bd3ab1db87c3c208b2/index.js
376// https://github.com/sindresorhus/get-east-asian-width/blob/6aee3824b5e2ade910a38ed93cf9d89b7b5a8e5b/index.js
377// MIT license
378let segmenter: Intl.Segmenter | null = null;
379const ansiEscapes =
380 /[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/\#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/\#&.:=?%@~_]*)*)?(?:\u0007|\u001B\u005C|\u009C))|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
381const zeroWidthClusterRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Mark}|\p{Surrogate})+$/v;
382const leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v;
383const rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
384
385/** remove all ansi escape sequences */
386export function strip(text: string): string {
387 return text.replace(ansiEscapes, "");
388}
389
390/**
391 * ansi text as html, for a browser. the sixteen colors are `var(--ansi0)` to
392 * `var(--ansi15)`, which {@linkcode htmlColors} defines. no element spans a
393 * line break, so the html splits into lines where the text does.
394 */
395export function toHtml(text: string): string {
396 const style = plainStyle();
397 let html = "";
398 let last = 0;
399 for (const match of text.matchAll(sequences)) {
400 html += formatRun(text.slice(last, match.index), style);
401 last = match.index + match[0].length;
402 if (match[2] === "m") applySgr(style, (match[1] || "0").split(/[;:]/).map(Number));
403 }
404 return html + formatRun(text.slice(last), style);
405}
406
407/** the colors that {@linkcode toHtml} refers to, as css declarations that follow `color-scheme` */
408export const htmlColors: string = "--ansi0:light-dark(#000,#666);--ansi1:#cd3131;"
409 + "--ansi2:light-dark(#00a000,#0dbc79);--ansi3:light-dark(#949800,#e5e510);"
410 + "--ansi4:light-dark(#0451a5,#2472c8);--ansi5:#bc3fbc;--ansi6:light-dark(#0598bc,#11a8cd);"
411 + "--ansi7:light-dark(#555,#e5e5e5);--ansi8:#666;--ansi9:light-dark(#cd3131,#f14c4c);"
412 + "--ansi10:light-dark(#14ce14,#23d18b);--ansi11:light-dark(#b5ba00,#f5f543);"
413 + "--ansi12:light-dark(#0451a5,#3b8eea);--ansi13:light-dark(#bc05bc,#d670d6);"
414 + "--ansi14:light-dark(#0598bc,#29b8db);--ansi15:light-dark(#a5a5a5,#e5e5e5);";
415
416interface HtmlStyle {
417 fg: string;
418 bg: string;
419 bold: boolean;
420 dim: boolean;
421 italic: boolean;
422 underline: boolean;
423 inverse: boolean;
424}
425const plainStyle = (): HtmlStyle => ({
426 fg: "",
427 bg: "",
428 bold: false,
429 dim: false,
430 italic: false,
431 underline: false,
432 inverse: false,
433});
434
435const sequences = /\x1b(?:\[([\x30-\x3f]*)[\x20-\x2f]*([\x40-\x7e])|\][^\x07\x1b]*(?:\x07|\x1b\\)?|.)/g;
436
437function formatRun(text: string, { fg, bg, bold, dim, italic, underline, inverse }: HtmlStyle) {
438 if (!text) return "";
439 // inverse swaps the colors, the default ones included
440 const color = inverse ? bg || "Canvas" : fg;
441 const background = inverse ? fg || "CanvasText" : bg;
442 const css = (color && `color:${color};`) + (background && `background:${background};`)
443 + (bold ? "font-weight:bold;" : "") + (dim ? "opacity:.65;" : "")
444 + (italic ? "font-style:italic;" : "") + (underline ? "text-decoration:underline;" : "");
445 if (!css) return escapeHtmlContent(text);
446 return text.split("\n")
447 .map((line) => line && `<span style="${css}">${escapeHtmlContent(line)}</span>`)
448 .join("\n");
449}
450
451function applySgr(style: HtmlStyle, codes: number[]) {
452 for (let i = 0; i < codes.length; i += 1) {
453 const code = UNWRAP(codes[i]);
454 if (code === 0) Object.assign(style, plainStyle());
455 else if (code === 1) style.bold = true;
456 else if (code === 2) style.dim = true;
457 else if (code === 22) style.bold = style.dim = false;
458 else if (code === 3 || code === 23) style.italic = code === 3;
459 else if (code === 4 || code === 24) style.underline = code === 4;
460 else if (code === 7 || code === 27) style.inverse = code === 7;
461 else if (code === 39) style.fg = "";
462 else if (code === 49) style.bg = "";
463 else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {
464 style.fg = `var(--ansi${code % 10 + (code >= 90 ? 8 : 0)})`;
465 } else if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {
466 style.bg = `var(--ansi${code % 10 + (code >= 100 ? 8 : 0)})`;
467 } else if (code === 38 || code === 48) {
468 const indexed = codes[i + 1] === 5;
469 const [r = 0, g = 0, b = 0] = codes.slice(i + 2, i + 5);
470 const color = indexed ? color256(r) : `rgb(${r} ${g} ${b})`;
471 i += indexed ? 2 : 4;
472 if (code === 38) style.fg = color;
473 else style.bg = color;
474 }
475 }
476}
477
478function color256(n: number) {
479 if (n < 16) return `var(--ansi${n})`;
480 if (n >= 232) return `rgb(${Array(3).fill(8 + (n - 232) * 10).join(" ")})`;
481 n -= 16;
482 const level = (c: number) => c && 55 + c * 40;
483 return `rgb(${level(Math.floor(n / 36))} ${level(Math.floor(n / 6) % 6)} ${level(n % 6)})`;
484}
485
486function segmentWidth(segment: string) {
487 if (zeroWidthClusterRegex.test(segment)) return 0;
488 if (rgiEmojiRegex.test(segment)) return 2;
489
490 // deno-fmt-ignore
491 const list = [
492 0x3000,
493 0x231A,
494 0x231B,
495 0x2329,
496 0x232A,
497 0x23F0,
498 0x23F3,
499 0x25FD,
500 0x25FE,
501 0x2614,
502 0x2615,
503 0x267F,
504 0x2693,
505 0x26A1,
506 0x26AA,
507 0x26AB,
508 0x26BD,
509 0x26BE,
510 0x26C4,
511 0x26C5,
512 0x26CE,
513 0x26D4,
514 0x26EA,
515 0x26F2,
516 0x26F3,
517 0x26F5,
518 0x26FA,
519 0x26FD,
520 0x2705,
521 0x270A,
522 0x270B,
523 0x2728,
524 0x274C,
525 0x274E,
526 0x2757,
527 0x27B0,
528 0x27BF,
529 0x2B1B,
530 0x2B1C,
531 0x2B50,
532 0x2B55,
533 0x1AFFD,
534 0x1AFFE,
535 0x1B132,
536 0x1B155,
537 0x1F004,
538 0x1F0CF,
539 0x1F18E,
540 0x1F250,
541 0x1F251,
542 0x1F3F4,
543 0x1F440,
544 0x1F57A,
545 0x1F595,
546 0x1F596,
547 0x1F5A4,
548 0x1F6CC,
549 0x1F6EB,
550 0x1F6EC,
551 0x1F7F0,
552 0x1FAC8,
553 ];
554 const x = segment.replace(leadingNonPrintingRegex, "").codePointAt(0) ?? 0;
555 return 1 + +(
556 // full
557 x >= 0xFF01 && x <= 0xFF60 || x >= 0xFFE0 && x <= 0xFFE6
558 // wide
559 || x >= 0x1100 && x <= 0x115F || x >= 0x23E9 && x <= 0x23EC
560 || x >= 0x2630 && x <= 0x2637 || x >= 0x2648 && x <= 0x2653
561 || x >= 0x268A && x <= 0x268F || x >= 0x2753 && x <= 0x2755
562 || x >= 0x2795 && x <= 0x2797 || x >= 0x2E80 && x <= 0x2E99
563 || x >= 0x2E9B && x <= 0x2EF3 || x >= 0x2F00 && x <= 0x2FD5
564 || x >= 0x2FF0 && x <= 0x2FFF || x >= 0x3001 && x <= 0x303E
565 || x >= 0x3041 && x <= 0x3096 || x >= 0x3099 && x <= 0x30FF
566 || x >= 0x3105 && x <= 0x312F || x >= 0x3131 && x <= 0x318E
567 || x >= 0x3190 && x <= 0x31E5 || x >= 0x31EF && x <= 0x321E
568 || x >= 0x3220 && x <= 0x3247 || x >= 0x3250 && x <= 0xA48C
569 || x >= 0xA490 && x <= 0xA4C6 || x >= 0xA960 && x <= 0xA97C
570 || x >= 0xAC00 && x <= 0xD7A3 || x >= 0xF900 && x <= 0xFAFF
571 || x >= 0xFE10 && x <= 0xFE19 || x >= 0xFE30 && x <= 0xFE52
572 || x >= 0xFE54 && x <= 0xFE66 || x >= 0xFE68 && x <= 0xFE6B
573 || x >= 0x16FE0 && x <= 0x16FE4 || x >= 0x16FF0 && x <= 0x16FF6
574 || x >= 0x17000 && x <= 0x18CD5 || x >= 0x18CFF && x <= 0x18D1E
575 || x >= 0x18D80 && x <= 0x18DF2 || x >= 0x1AFF0 && x <= 0x1AFF3
576 || x >= 0x1AFF5 && x <= 0x1AFFB || x >= 0x1B150 && x <= 0x1B152
577 || x >= 0x1B164 && x <= 0x1B167 || x >= 0x1B170 && x <= 0x1B2FB
578 || x >= 0x1D300 && x <= 0x1D356 || x >= 0x1D360 && x <= 0x1D376
579 || x >= 0x1F191 && x <= 0x1F19A || x >= 0x1F200 && x <= 0x1F202
580 || x >= 0x1F210 && x <= 0x1F23B || x >= 0x1F240 && x <= 0x1F248
581 || x >= 0x1F260 && x <= 0x1F265 || x >= 0x1F300 && x <= 0x1F320
582 || x >= 0x1F32D && x <= 0x1F335 || x >= 0x1F337 && x <= 0x1F37C
583 || x >= 0x1F37E && x <= 0x1F393 || x >= 0x1F3A0 && x <= 0x1F3CA
584 || x >= 0x1F3CF && x <= 0x1F3D3 || x >= 0x1F3E0 && x <= 0x1F3F0
585 || x >= 0x1F3F8 && x <= 0x1F43E || x >= 0x1F442 && x <= 0x1F4FC
586 || x >= 0x1F4FF && x <= 0x1F53D || x >= 0x1F54B && x <= 0x1F54E
587 || x >= 0x1F550 && x <= 0x1F567 || x >= 0x1F5FB && x <= 0x1F64F
588 || x >= 0x1F680 && x <= 0x1F6C5 || x >= 0x1F6D0 && x <= 0x1F6D2
589 || x >= 0x1F6D5 && x <= 0x1F6D8 || x >= 0x1F6DC && x <= 0x1F6DF
590 || x >= 0x1F6F4 && x <= 0x1F6FC || x >= 0x1F7E0 && x <= 0x1F7EB
591 || x >= 0x1F90C && x <= 0x1F93A || x >= 0x1F93C && x <= 0x1F945
592 || x >= 0x1F947 && x <= 0x1F9FF || x >= 0x1FA70 && x <= 0x1FA7C
593 || x >= 0x1FA80 && x <= 0x1FA8A || x >= 0x1FA8E && x <= 0x1FAC6
594 || x >= 0x1FACD && x <= 0x1FADC || x >= 0x1FADF && x <= 0x1FAEA
595 || x >= 0x1FAEF && x <= 0x1FAF8 || x >= 0x20000 && x <= 0x2FFFD
596 || x >= 0x30000 && x <= 0x3FFFD || x >= 0x1B000 && x <= 0x1B122
597 || list.includes(x)
598 );
599}
600
601import { UNWRAP } from "../assert.ts";
602import { escapeHtmlContent } from "../string.ts";
603import * as self from "./ansi.ts";