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 (
200 code +
201 text +
202 mergeStyles(
203 (fg ? fgReset : "") +
204 (bg ? bgReset : "") +
205 (bold ? resetWeight : "") +
206 (dim ? resetWeight : ""),
207 )
208 );
209}
210
211/**
212 * Given a string of multiple style ansi codes, combines all of them.
213 * Must be used on a string of only ansi style codes.
214 */
215export function mergeStyles(code: string): string {
216 let styles = [];
217 while (code.length) {
218 if (!code.startsWith("\x1b[")) {
219 throw new Error("invalid style string: " + JSON.stringify(code));
220 }
221 const end = code.indexOf("m");
222 if (end === -1) {
223 throw new Error("invalid style string: " + JSON.stringify(code));
224 }
225 styles.push(code.slice(2, end));
226 code = code.slice(end + 1);
227 }
228 return `\x1b[${styles.join(";")}m`;
229}
230
231/**
232 * Convert a string with ansi text into a string where all the codes have been
233 * turned into their original names.
234 */
235export function debugAnsi(text: string): string {
236 // deno-fmt-ignore
237 const verbaitim: (keyof typeof self)[] = [
238 "fgCyan",
239 "fgBlue",
240 "bgReset",
241 "bgBlack",
242 "bgRed",
243 "bgGreen",
244 "bgYellow",
245 "bgBlue",
246 "bgMagenta",
247 "bgCyan",
248 "bgWhite",
249 "bgBrightBlack",
250 "bgBrightRed",
251 "bgBrightGreen",
252 "bgBrightYellow",
253 "bgBrightBlue",
254 "bgBrightMagenta",
255 "bgBrightCyan",
256 "bgBrightWhite",
257 "fgReset",
258 "fgBlack",
259 "fgRed",
260 "fgGreen",
261 "fgYellow",
262 "fgMagenta",
263 "fgWhite",
264 "fgBrightBlack",
265 "fgBrightRed",
266 "fgBrightGreen",
267 "fgBrightYellow",
268 "fgBrightBlue",
269 "fgBrightMagenta",
270 "fgBrightCyan",
271 "fgBrightWhite",
272 "clearToEndOfLine",
273 "clearToStartOfLine",
274 "clearFullLine",
275 "clearToEndOfScreen",
276 "syncStart",
277 "syncEnd",
278 "bold",
279 "dim",
280 "resetWeight",
281 "cursorSave",
282 "cursorRestore",
283 ];
284 for (const key of verbaitim) {
285 text = text.replaceAll(
286 String(self[key]),
287 style(key.includes("cursor") ? fgBlue : fgCyan, `\${${key}}`),
288 );
289 }
290 text = text.replace(/\x1b\[(\d+)A/g, style(fgYellow, "$${cursorUp($1)}"));
291 text = text.replace(/\x1b\[(\d+)B/g, style(fgYellow, "$${cursorDown($1)}"));
292 text = text.replace(/\x1b\[(\d+)C/g, style(fgYellow, "$${cursorRight($1)}"));
293 text = text.replace(/\x1b\[(\d+)D/g, style(fgYellow, "$${cursorLeft($1)}"));
294 text = text.replace(/\n/g, style(fgRed, "\\n"));
295 text = text.replace(/\r/g, style(fgMagenta, "\\r"));
296 return text;
297}
298
299/** Measure the visible length of a string, ignoring ansi codes. */
300export function widthInTerminal(str: string): number {
301 segmenter ??= new Intl.Segmenter();
302 let width = 0;
303 if (str.includes("\x1b")) str = str.replace(ansiEscapes, "");
304 for (let i = 0, { length } = str; i < length; i += 1) {
305 const code = str.charCodeAt(i);
306 if (code < 32) continue;
307 if (code < 127) {
308 width += 1;
309 continue;
310 }
311 // the remainder of the string is measured with the segmenter, which
312 // handles ascii correctly via segmentWidth (controls are zero-width).
313 for (const { segment } of segmenter.segment(str.slice(i))) {
314 width += segmentWidth(segment);
315 }
316 return width;
317 }
318 return width;
319}
320
321/**
322 * Like `str.slice(0, columns)`, but respecting width in a terminal.
323 * preserves ansi escape sequences.
324 */
325export function trimForTerminal(str: string, columns: number): string {
326 segmenter ??= new Intl.Segmenter();
327 let width = 0;
328 let ansi = false;
329 for (let i = 0, { length } = str; i < length; i += 1) {
330 const code = str.charCodeAt(i);
331 if (code < 32) {
332 if (code === 0x1b) {
333 const esc = str.slice(i).match(ansiEscapes);
334 if (esc) ((i += esc[0].length - 1), (ansi = true));
335 }
336 continue;
337 }
338 if (code < 127) {
339 width += 1;
340 if (width >= columns) {
341 const result = str.slice(0, i + 1);
342 return ansi ? result + "\x1b[0m" : result;
343 }
344 continue;
345 }
346 // non-ascii path
347 let out = str.slice(0, i);
348 str = str.slice(i);
349 const segments = segmenter.segment(str);
350 {
351 const { segment } = UNWRAP(segments.containing(0));
352 width += segmentWidth(segment);
353 if (width > columns) return ansi ? out + "\x1b[0m" : out;
354 out += segment;
355 i = segment.length;
356 }
357 for ({ length } = str; i < length; i += 1) {
358 const code = str.charCodeAt(i);
359 if (code === 0x1b) {
360 const esc = str.slice(i).match(ansiEscapes);
361 if (esc) {
362 ((i += esc[0].length - 1), (ansi = true));
363 out += esc[0];
364 continue;
365 }
366 }
367 const { segment } = UNWRAP(segments.containing(i));
368 width += segmentWidth(segment);
369 if (width > columns) return ansi ? out + "\x1b[0m" : out;
370 out += segment;
371 }
372 return out;
373 }
374 return str;
375}
376
377// derived from sindresorhus's wonderful `string-width`
378// https://github.com/sindresorhus/string-width/blob/42e7b697393a9b9ff732f9bd3ab1db87c3c208b2/index.js
379// https://github.com/sindresorhus/get-east-asian-width/blob/6aee3824b5e2ade910a38ed93cf9d89b7b5a8e5b/index.js
380// MIT license
381let segmenter: Intl.Segmenter | null = null;
382const ansiEscapes =
383 /[\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;
384const zeroWidthClusterRegex =
385 /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Mark}|\p{Surrogate})+$/v;
386const leadingNonPrintingRegex =
387 /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v;
388const rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
389
390/** remove all ansi escape sequences */
391export function strip(text: string): string {
392 return text.replace(ansiEscapes, "");
393}
394
395/**
396 * ansi text as html, for a browser. the sixteen colors are `var(--ansi0)` to
397 * `var(--ansi15)`, which {@linkcode htmlColors} defines. no element spans a
398 * line break, so the html splits into lines where the text does.
399 */
400export function toHtml(text: string): string {
401 const style = plainStyle();
402 let html = "";
403 let last = 0;
404 for (const match of text.matchAll(sequences)) {
405 html += formatRun(text.slice(last, match.index), style);
406 last = match.index + match[0].length;
407 if (match[2] === "m") applySgr(style, (match[1] || "0").split(/[;:]/).map(Number));
408 }
409 return html + formatRun(text.slice(last), style);
410}
411
412/** the colors that {@linkcode toHtml} refers to, as css declarations that follow `color-scheme` */
413export const htmlColors: string =
414 "--ansi0:light-dark(#000,#666);--ansi1:#cd3131;" +
415 "--ansi2:light-dark(#00a000,#0dbc79);--ansi3:light-dark(#949800,#e5e510);" +
416 "--ansi4:light-dark(#0451a5,#2472c8);--ansi5:#bc3fbc;--ansi6:light-dark(#0598bc,#11a8cd);" +
417 "--ansi7:light-dark(#555,#e5e5e5);--ansi8:#666;--ansi9:light-dark(#cd3131,#f14c4c);" +
418 "--ansi10:light-dark(#14ce14,#23d18b);--ansi11:light-dark(#b5ba00,#f5f543);" +
419 "--ansi12:light-dark(#0451a5,#3b8eea);--ansi13:light-dark(#bc05bc,#d670d6);" +
420 "--ansi14:light-dark(#0598bc,#29b8db);--ansi15:light-dark(#a5a5a5,#e5e5e5);";
421
422interface HtmlStyle {
423 fg: string;
424 bg: string;
425 weight: string;
426 italic: boolean;
427 underline: boolean;
428}
429const plainStyle = (): HtmlStyle => ({
430 fg: "",
431 bg: "",
432 weight: "",
433 italic: false,
434 underline: false,
435});
436
437const sequences =
438 /\x1b(?:\[([\x30-\x3f]*)[\x20-\x2f]*([\x40-\x7e])|\][^\x07\x1b]*(?:\x07|\x1b\\)?|.)/g;
439
440function formatRun(text: string, { fg, bg, weight, italic, underline }: HtmlStyle) {
441 if (!text) return "";
442 const css =
443 (fg && `color:${fg};`) +
444 (bg && `background:${bg};`) +
445 weight +
446 (italic ? "font-style:italic;" : "") +
447 (underline ? "text-decoration:underline;" : "");
448 if (!css) return escapeHtmlContent(text);
449 return text
450 .split("\n")
451 .map((line) => line && `<span style="${css}">${escapeHtmlContent(line)}</span>`)
452 .join("\n");
453}
454
455function applySgr(style: HtmlStyle, codes: number[]) {
456 for (let i = 0; i < codes.length; i += 1) {
457 const code = UNWRAP(codes[i]);
458 if (code === 0) Object.assign(style, plainStyle());
459 else if (code === 1) style.weight = "font-weight:bold;";
460 else if (code === 2) style.weight = "opacity:.65;";
461 else if (code === 22) style.weight = "";
462 else if (code === 3 || code === 23) style.italic = code === 3;
463 else if (code === 4 || code === 24) style.underline = code === 4;
464 else if (code === 39) style.fg = "";
465 else if (code === 49) style.bg = "";
466 else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {
467 style.fg = `var(--ansi${(code % 10) + (code >= 90 ? 8 : 0)})`;
468 } else if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {
469 style.bg = `var(--ansi${(code % 10) + (code >= 100 ? 8 : 0)})`;
470 } else if (code === 38 || code === 48) {
471 const indexed = codes[i + 1] === 5;
472 const [r = 0, g = 0, b = 0] = codes.slice(i + 2, i + 5);
473 const color = indexed ? color256(r) : `rgb(${r} ${g} ${b})`;
474 i += indexed ? 2 : 4;
475 if (code === 38) style.fg = color;
476 else style.bg = color;
477 }
478 }
479}
480
481function color256(n: number) {
482 if (n < 16) return `var(--ansi${n})`;
483 if (n >= 232)
484 return `rgb(${Array(3)
485 .fill(8 + (n - 232) * 10)
486 .join(" ")})`;
487 n -= 16;
488 const level = (c: number) => c && 55 + c * 40;
489 return `rgb(${level(Math.floor(n / 36))} ${level(Math.floor(n / 6) % 6)} ${level(n % 6)})`;
490}
491
492function segmentWidth(segment: string) {
493 if (zeroWidthClusterRegex.test(segment)) return 0;
494 if (rgiEmojiRegex.test(segment)) return 2;
495
496 // deno-fmt-ignore
497 const list = [
498 0x3000, 0x231a, 0x231b, 0x2329, 0x232a, 0x23f0, 0x23f3, 0x25fd, 0x25fe, 0x2614, 0x2615, 0x267f,
499 0x2693, 0x26a1, 0x26aa, 0x26ab, 0x26bd, 0x26be, 0x26c4, 0x26c5, 0x26ce, 0x26d4, 0x26ea, 0x26f2,
500 0x26f3, 0x26f5, 0x26fa, 0x26fd, 0x2705, 0x270a, 0x270b, 0x2728, 0x274c, 0x274e, 0x2757, 0x27b0,
501 0x27bf, 0x2b1b, 0x2b1c, 0x2b50, 0x2b55, 0x1affd, 0x1affe, 0x1b132, 0x1b155, 0x1f004, 0x1f0cf,
502 0x1f18e, 0x1f250, 0x1f251, 0x1f3f4, 0x1f440, 0x1f57a, 0x1f595, 0x1f596, 0x1f5a4, 0x1f6cc,
503 0x1f6eb, 0x1f6ec, 0x1f7f0, 0x1fac8,
504 ];
505 const x = segment.replace(leadingNonPrintingRegex, "").codePointAt(0) ?? 0;
506 return (
507 1 +
508 +(
509 // full
510 (x >= 0xff01 && x <= 0xff60) ||
511 (x >= 0xffe0 && x <= 0xffe6) ||
512 // wide
513 (x >= 0x1100 && x <= 0x115f) ||
514 (x >= 0x23e9 && x <= 0x23ec) ||
515 (x >= 0x2630 && x <= 0x2637) ||
516 (x >= 0x2648 && x <= 0x2653) ||
517 (x >= 0x268a && x <= 0x268f) ||
518 (x >= 0x2753 && x <= 0x2755) ||
519 (x >= 0x2795 && x <= 0x2797) ||
520 (x >= 0x2e80 && x <= 0x2e99) ||
521 (x >= 0x2e9b && x <= 0x2ef3) ||
522 (x >= 0x2f00 && x <= 0x2fd5) ||
523 (x >= 0x2ff0 && x <= 0x2fff) ||
524 (x >= 0x3001 && x <= 0x303e) ||
525 (x >= 0x3041 && x <= 0x3096) ||
526 (x >= 0x3099 && x <= 0x30ff) ||
527 (x >= 0x3105 && x <= 0x312f) ||
528 (x >= 0x3131 && x <= 0x318e) ||
529 (x >= 0x3190 && x <= 0x31e5) ||
530 (x >= 0x31ef && x <= 0x321e) ||
531 (x >= 0x3220 && x <= 0x3247) ||
532 (x >= 0x3250 && x <= 0xa48c) ||
533 (x >= 0xa490 && x <= 0xa4c6) ||
534 (x >= 0xa960 && x <= 0xa97c) ||
535 (x >= 0xac00 && x <= 0xd7a3) ||
536 (x >= 0xf900 && x <= 0xfaff) ||
537 (x >= 0xfe10 && x <= 0xfe19) ||
538 (x >= 0xfe30 && x <= 0xfe52) ||
539 (x >= 0xfe54 && x <= 0xfe66) ||
540 (x >= 0xfe68 && x <= 0xfe6b) ||
541 (x >= 0x16fe0 && x <= 0x16fe4) ||
542 (x >= 0x16ff0 && x <= 0x16ff6) ||
543 (x >= 0x17000 && x <= 0x18cd5) ||
544 (x >= 0x18cff && x <= 0x18d1e) ||
545 (x >= 0x18d80 && x <= 0x18df2) ||
546 (x >= 0x1aff0 && x <= 0x1aff3) ||
547 (x >= 0x1aff5 && x <= 0x1affb) ||
548 (x >= 0x1b150 && x <= 0x1b152) ||
549 (x >= 0x1b164 && x <= 0x1b167) ||
550 (x >= 0x1b170 && x <= 0x1b2fb) ||
551 (x >= 0x1d300 && x <= 0x1d356) ||
552 (x >= 0x1d360 && x <= 0x1d376) ||
553 (x >= 0x1f191 && x <= 0x1f19a) ||
554 (x >= 0x1f200 && x <= 0x1f202) ||
555 (x >= 0x1f210 && x <= 0x1f23b) ||
556 (x >= 0x1f240 && x <= 0x1f248) ||
557 (x >= 0x1f260 && x <= 0x1f265) ||
558 (x >= 0x1f300 && x <= 0x1f320) ||
559 (x >= 0x1f32d && x <= 0x1f335) ||
560 (x >= 0x1f337 && x <= 0x1f37c) ||
561 (x >= 0x1f37e && x <= 0x1f393) ||
562 (x >= 0x1f3a0 && x <= 0x1f3ca) ||
563 (x >= 0x1f3cf && x <= 0x1f3d3) ||
564 (x >= 0x1f3e0 && x <= 0x1f3f0) ||
565 (x >= 0x1f3f8 && x <= 0x1f43e) ||
566 (x >= 0x1f442 && x <= 0x1f4fc) ||
567 (x >= 0x1f4ff && x <= 0x1f53d) ||
568 (x >= 0x1f54b && x <= 0x1f54e) ||
569 (x >= 0x1f550 && x <= 0x1f567) ||
570 (x >= 0x1f5fb && x <= 0x1f64f) ||
571 (x >= 0x1f680 && x <= 0x1f6c5) ||
572 (x >= 0x1f6d0 && x <= 0x1f6d2) ||
573 (x >= 0x1f6d5 && x <= 0x1f6d8) ||
574 (x >= 0x1f6dc && x <= 0x1f6df) ||
575 (x >= 0x1f6f4 && x <= 0x1f6fc) ||
576 (x >= 0x1f7e0 && x <= 0x1f7eb) ||
577 (x >= 0x1f90c && x <= 0x1f93a) ||
578 (x >= 0x1f93c && x <= 0x1f945) ||
579 (x >= 0x1f947 && x <= 0x1f9ff) ||
580 (x >= 0x1fa70 && x <= 0x1fa7c) ||
581 (x >= 0x1fa80 && x <= 0x1fa8a) ||
582 (x >= 0x1fa8e && x <= 0x1fac6) ||
583 (x >= 0x1facd && x <= 0x1fadc) ||
584 (x >= 0x1fadf && x <= 0x1faea) ||
585 (x >= 0x1faef && x <= 0x1faf8) ||
586 (x >= 0x20000 && x <= 0x2fffd) ||
587 (x >= 0x30000 && x <= 0x3fffd) ||
588 (x >= 0x1b000 && x <= 0x1b122) ||
589 list.includes(x)
590 )
591 );
592}
593
594import { UNWRAP } from "../assert.ts";
595import { escapeHtmlContent } from "../string.ts";
596import * as self from "./ansi.ts";