| 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 */ |
| 13 | export 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 | */ |
| 19 | export 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 | */ |
| 25 | export const dim: string = `\x1b[2m`; |
| 26 | /** resets `bold` and `dim` */ |
| 27 | export const resetWeight: string = `\x1b[22m`; |
| 28 | |
| 29 | /** resets foreground color */ |
| 30 | export const fgReset: string = `\x1b[39m`; |
| 31 | /** sets the foreground color to black. on light mode terminals, this color may |
| 32 | * be visible. */ |
| 33 | export const fgBlack: string = `\x1b[30m`; |
| 34 | /** sets the foreground color to red */ |
| 35 | export const fgRed: string = `\x1b[31m`; |
| 36 | /** sets the foreground color to green */ |
| 37 | export const fgGreen: string = `\x1b[32m`; |
| 38 | /** sets the foreground color to yellow */ |
| 39 | export const fgYellow: string = `\x1b[33m`; |
| 40 | /** sets the foreground color to blue */ |
| 41 | export const fgBlue: string = `\x1b[34m`; |
| 42 | /** sets the foreground color to magenta */ |
| 43 | export const fgMagenta: string = `\x1b[35m`; |
| 44 | /** sets the foreground color to cyan */ |
| 45 | export const fgCyan: string = `\x1b[36m`; |
| 46 | /** |
| 47 | * sets the foreground color to (dark) white / grey / gray. this color is |
| 48 | * brighter than `fgBrightBlack`. consider `dim` for greyed out text and |
| 49 | * `fgReset` to set the default foreground color. |
| 50 | */ |
| 51 | export const fgWhite: string = `\x1b[37m`; |
| 52 | /** |
| 53 | * sets the foreground color to bright black / grey / gray. consider `dim` for |
| 54 | * greyed out text. |
| 55 | */ |
| 56 | export const fgBrightBlack: string = `\x1b[90m`; |
| 57 | /** sets the foreground color to bright red */ |
| 58 | export const fgBrightRed: string = `\x1b[91m`; |
| 59 | /** sets the foreground color to bright green */ |
| 60 | export const fgBrightGreen: string = `\x1b[92m`; |
| 61 | /** sets the foreground color to bright yellow */ |
| 62 | export const fgBrightYellow: string = `\x1b[93m`; |
| 63 | /** sets the foreground color to bright blue */ |
| 64 | export const fgBrightBlue: string = `\x1b[94m`; |
| 65 | /** sets the foreground color to bright magenta */ |
| 66 | export const fgBrightMagenta: string = `\x1b[95m`; |
| 67 | /** sets the foreground color to bright cyan */ |
| 68 | export const fgBrightCyan: string = `\x1b[96m`; |
| 69 | /** |
| 70 | * sets the foreground color to bright white. consider `dim` for greyed out |
| 71 | * text and `fgReset` to set the default foreground color. |
| 72 | */ |
| 73 | export const fgBrightWhite: string = `\x1b[97m`; |
| 74 | /** sets the foreground color to a color in the 256 color palette. */ |
| 75 | export function fg256(n: number): string { |
| 76 | return `\x1b[38;5;${n}m`; |
| 77 | } |
| 78 | /** sets the foreground color to an rgb color. */ |
| 79 | export function fgRgb(r: number, g: number, b: number): string { |
| 80 | return `\x1b[38;2;${r};${g};${b}m`; |
| 81 | } |
| 82 | |
| 83 | /** resets the cell background color. */ |
| 84 | export const bgReset: string = `\x1b[49m`; |
| 85 | /** sets the cell background to black. consider `bgReset` to clear the background. */ |
| 86 | export const bgBlack: string = `\x1b[40m`; |
| 87 | /** sets the cell background to red. */ |
| 88 | export const bgRed: string = `\x1b[41m`; |
| 89 | /** sets the cell background to green. */ |
| 90 | export const bgGreen: string = `\x1b[42m`; |
| 91 | /** sets the cell background to yellow. */ |
| 92 | export const bgYellow: string = `\x1b[43m`; |
| 93 | /** sets the cell background to blue. */ |
| 94 | export const bgBlue: string = `\x1b[44m`; |
| 95 | /** sets the cell background to magenta. */ |
| 96 | export const bgMagenta: string = `\x1b[45m`; |
| 97 | /** sets the cell background to cyan. */ |
| 98 | export const bgCyan: string = `\x1b[46m`; |
| 99 | /** |
| 100 | * sets the cell background to (dark) white / grey / gray. this color is |
| 101 | * brighter than `bgBrightBlack`. |
| 102 | */ |
| 103 | export const bgWhite: string = `\x1b[47m`; |
| 104 | /** |
| 105 | * sets the cell background to bright black. consider `bgReset` to clear the |
| 106 | * background. |
| 107 | */ |
| 108 | export const bgBrightBlack: string = `\x1b[100m`; |
| 109 | /** sets the cell background to bright red. */ |
| 110 | export const bgBrightRed: string = `\x1b[101m`; |
| 111 | /** sets the cell background to bright green. */ |
| 112 | export const bgBrightGreen: string = `\x1b[102m`; |
| 113 | /** sets the cell background to bright yellow. */ |
| 114 | export const bgBrightYellow: string = `\x1b[103m`; |
| 115 | /** sets the cell background to bright blue. */ |
| 116 | export const bgBrightBlue: string = `\x1b[104m`; |
| 117 | /** sets the cell background to bright magenta. */ |
| 118 | export const bgBrightMagenta: string = `\x1b[105m`; |
| 119 | /** sets the cell background to bright cyan. */ |
| 120 | export const bgBrightCyan: string = `\x1b[106m`; |
| 121 | /** sets the cell background to bright white. */ |
| 122 | export const bgBrightWhite: string = `\x1b[107m`; |
| 123 | /** sets the background color to a color in the 256 color palette. */ |
| 124 | export function bg256(n: number): string { |
| 125 | return `\x1b[48;5;${n}m`; |
| 126 | } |
| 127 | /** sets the background color to an rgb color. */ |
| 128 | export function bgRgb(r: number, g: number, b: number): string { |
| 129 | return `\x1b[48;2;${r};${g};${b}m`; |
| 130 | } |
| 131 | |
| 132 | // cursor management |
| 133 | |
| 134 | /** save the position of the cursor, restored with {@linkcode cursorRestore}. */ |
| 135 | export const cursorSave: string = "\x1b7"; |
| 136 | /** restore the position of the cursor, saved with {@linkcode cursorSave}. */ |
| 137 | export const cursorRestore: string = "\x1b8"; |
| 138 | |
| 139 | /** move the cursor up `n` units. */ |
| 140 | export function cursorUp(n: number): string { |
| 141 | return n ? `\x1b[${n}A` : ""; |
| 142 | } |
| 143 | /** move the cursor down `n` units. */ |
| 144 | export function cursorDown(n: number): string { |
| 145 | return n ? `\x1b[${n}B` : ""; |
| 146 | } |
| 147 | /** move the cursor right `n` units. */ |
| 148 | export function cursorRight(n: number): string { |
| 149 | return n ? `\x1b[${n}C` : ""; |
| 150 | } |
| 151 | /** move the cursor right `n` units. */ |
| 152 | export function cursorLeft(n: number): string { |
| 153 | return n ? `\x1b[${n}D` : ""; |
| 154 | } |
| 155 | /** move the cursor to the start of it's current line. */ |
| 156 | export const startOfLine: string = "\r"; |
| 157 | /** move the cursor to the start of the next line. */ |
| 158 | export const startOfNextLine: string = "\n"; |
| 159 | |
| 160 | // erasure |
| 161 | /** clear from and including the current cell right to the end of the line. */ |
| 162 | export const clearToEndOfLine: string = "\x1b[K"; |
| 163 | /** clear from and including the current cell left to the start of the line. */ |
| 164 | export const clearToStartOfLine: string = "\x1b[1K"; |
| 165 | /** clear the entire line that the cursor is on without moving it. */ |
| 166 | export const clearFullLine: string = "\x1b[2K"; |
| 167 | /** clear from and including the current cell to the end of the screen. */ |
| 168 | export const clearToEndOfScreen: string = "\x1b[0J"; |
| 169 | |
| 170 | /** |
| 171 | * Begin Synchronized Output |
| 172 | * https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 |
| 173 | */ |
| 174 | export const syncStart: string = "\x1b[?2026h"; |
| 175 | /** End Synchronized Output */ |
| 176 | export const syncEnd: string = "\x1b[?2026l"; |
| 177 | |
| 178 | /** |
| 179 | * Apply ANSI styles to text. `code` is a concatenation of zero or more styles, |
| 180 | * which will be cleared as required. |
| 181 | */ |
| 182 | export function style(code: string, text: string): string { |
| 183 | code = mergeStyles(code); |
| 184 | if (!code) return text; |
| 185 | const fg = /[\[;][39]/.test(code); |
| 186 | const bg = /[\[;](?:4|10)/.test(code); |
| 187 | const bold = /[\[;](?:1)[;m]/.test(code); |
| 188 | const dim = /[\[;](?:2)[;m]/.test(code); |
| 189 | return code + text |
| 190 | + mergeStyles( |
| 191 | (fg ? fgReset : "") |
| 192 | + (bg ? bgReset : "") |
| 193 | + (bold ? resetWeight : "") |
| 194 | + (dim ? resetWeight : ""), |
| 195 | ); |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Given a string of multiple style ansi codes, combines all of them. |
| 200 | * Must be used on a string of only ansi style codes. |
| 201 | */ |
| 202 | export function mergeStyles(code: string): string { |
| 203 | let styles = []; |
| 204 | while (code.length) { |
| 205 | if (!code.startsWith("\x1b[")) { |
| 206 | throw new Error("invalid style string: " + JSON.stringify(code)); |
| 207 | } |
| 208 | const end = code.indexOf("m"); |
| 209 | if (end === -1) { |
| 210 | throw new Error("invalid style string: " + JSON.stringify(code)); |
| 211 | } |
| 212 | styles.push(code.slice(2, end)); |
| 213 | code = code.slice(end + 1); |
| 214 | } |
| 215 | return `\x1b[${styles.join(";")}m`; |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Convert a string with ansi text into a string where all the codes have been |
| 220 | * turned into their original names. |
| 221 | */ |
| 222 | export function debugAnsi(text: string): string { |
| 223 | // deno-fmt-ignore |
| 224 | const verbaitim: (keyof typeof self)[] = [ |
| 225 | "fgCyan", |
| 226 | "fgBlue", |
| 227 | "bgReset", |
| 228 | "bgBlack", |
| 229 | "bgRed", |
| 230 | "bgGreen", |
| 231 | "bgYellow", |
| 232 | "bgBlue", |
| 233 | "bgMagenta", |
| 234 | "bgCyan", |
| 235 | "bgWhite", |
| 236 | "bgBrightBlack", |
| 237 | "bgBrightRed", |
| 238 | "bgBrightGreen", |
| 239 | "bgBrightYellow", |
| 240 | "bgBrightBlue", |
| 241 | "bgBrightMagenta", |
| 242 | "bgBrightCyan", |
| 243 | "bgBrightWhite", |
| 244 | "fgReset", |
| 245 | "fgBlack", |
| 246 | "fgRed", |
| 247 | "fgGreen", |
| 248 | "fgYellow", |
| 249 | "fgMagenta", |
| 250 | "fgWhite", |
| 251 | "fgBrightBlack", |
| 252 | "fgBrightRed", |
| 253 | "fgBrightGreen", |
| 254 | "fgBrightYellow", |
| 255 | "fgBrightBlue", |
| 256 | "fgBrightMagenta", |
| 257 | "fgBrightCyan", |
| 258 | "fgBrightWhite", |
| 259 | "clearToEndOfLine", |
| 260 | "clearToStartOfLine", |
| 261 | "clearFullLine", |
| 262 | "clearToEndOfScreen", |
| 263 | "syncStart", |
| 264 | "syncEnd", |
| 265 | "bold", |
| 266 | "dim", |
| 267 | "resetWeight", |
| 268 | "cursorSave", |
| 269 | "cursorRestore", |
| 270 | ]; |
| 271 | for (const key of verbaitim) { |
| 272 | text = text.replaceAll( |
| 273 | String(self[key]), |
| 274 | style(key.includes("cursor") ? fgBlue : fgCyan, `\${${key}}`), |
| 275 | ); |
| 276 | } |
| 277 | text = text.replace(/\x1b\[(\d+)A/g, style(fgYellow, "$${cursorUp($1)}")); |
| 278 | text = text.replace(/\x1b\[(\d+)B/g, style(fgYellow, "$${cursorDown($1)}")); |
| 279 | text = text.replace(/\x1b\[(\d+)C/g, style(fgYellow, "$${cursorRight($1)}")); |
| 280 | text = text.replace(/\x1b\[(\d+)D/g, style(fgYellow, "$${cursorLeft($1)}")); |
| 281 | text = text.replace(/\n/g, style(fgRed, "\\n")); |
| 282 | text = text.replace(/\r/g, style(fgMagenta, "\\r")); |
| 283 | return text; |
| 284 | } |
| 285 | |
| 286 | /** Measure the visible length of a string, ignoring ansi codes. */ |
| 287 | export function widthInTerminal(str: string): number { |
| 288 | segmenter ??= new Intl.Segmenter(); |
| 289 | let width = 0; |
| 290 | if (str.includes("\x1b")) str = str.replace(ansiEscapes, ""); |
| 291 | for (let i = 0, { length } = str; i < length; i += 1) { |
| 292 | const code = str.charCodeAt(i); |
| 293 | if (code < 32) continue; |
| 294 | if (code < 127) { |
| 295 | width += 1; |
| 296 | continue; |
| 297 | } |
| 298 | // the remainder of the string is measured with the segmenter, which |
| 299 | // handles ascii correctly via segmentWidth (controls are zero-width). |
| 300 | for (const { segment } of segmenter.segment(str.slice(i))) { |
| 301 | width += segmentWidth(segment); |
| 302 | } |
| 303 | return width; |
| 304 | } |
| 305 | return width; |
| 306 | } |
| 307 | |
| 308 | /** |
| 309 | * Like `str.slice(0, columns)`, but respecting width in a terminal. |
| 310 | * preserves ansi escape sequences. |
| 311 | */ |
| 312 | export function trimForTerminal(str: string, columns: number): string { |
| 313 | segmenter ??= new Intl.Segmenter(); |
| 314 | let width = 0; |
| 315 | let ansi = false; |
| 316 | for (let i = 0, { length } = str; i < length; i += 1) { |
| 317 | const code = str.charCodeAt(i); |
| 318 | if (code < 32) { |
| 319 | if (code === 0x1b) { |
| 320 | const esc = str.slice(i).match(ansiEscapes); |
| 321 | if (esc) i += esc[0].length - 1, ansi = true; |
| 322 | } |
| 323 | continue; |
| 324 | } |
| 325 | if (code < 127) { |
| 326 | width += 1; |
| 327 | if (width >= columns) { |
| 328 | const result = str.slice(0, i + 1); |
| 329 | return ansi ? result + "\x1b[0m" : result; |
| 330 | } |
| 331 | continue; |
| 332 | } |
| 333 | // non-ascii path |
| 334 | let out = str.slice(0, i); |
| 335 | str = str.slice(i); |
| 336 | const segments = segmenter.segment(str); |
| 337 | { |
| 338 | const { segment } = UNWRAP(segments.containing(0)); |
| 339 | width += segmentWidth(segment); |
| 340 | if (width > columns) return ansi ? out + "\x1b[0m" : out; |
| 341 | out += segment; |
| 342 | i = segment.length; |
| 343 | } |
| 344 | for ({ length } = str; i < length; i += 1) { |
| 345 | const code = str.charCodeAt(i); |
| 346 | if (code === 0x1b) { |
| 347 | const esc = str.slice(i).match(ansiEscapes); |
| 348 | if (esc) { |
| 349 | i += esc[0].length - 1, ansi = true; |
| 350 | out += esc[0]; |
| 351 | continue; |
| 352 | } |
| 353 | } |
| 354 | const { segment } = UNWRAP(segments.containing(i)); |
| 355 | width += segmentWidth(segment); |
| 356 | if (width > columns) return ansi ? out + "\x1b[0m" : out; |
| 357 | out += segment; |
| 358 | } |
| 359 | return out; |
| 360 | } |
| 361 | return str; |
| 362 | } |
| 363 | |
| 364 | // derived from sindresorhus's wonderful `string-width` |
| 365 | // https://github.com/sindresorhus/string-width/blob/42e7b697393a9b9ff732f9bd3ab1db87c3c208b2/index.js |
| 366 | // https://github.com/sindresorhus/get-east-asian-width/blob/6aee3824b5e2ade910a38ed93cf9d89b7b5a8e5b/index.js |
| 367 | // MIT license |
| 368 | let segmenter: Intl.Segmenter | null = null; |
| 369 | const ansiEscapes = |
| 370 | /[\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; |
| 371 | const zeroWidthClusterRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Mark}|\p{Surrogate})+$/v; |
| 372 | const leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v; |
| 373 | const rgiEmojiRegex = /^\p{RGI_Emoji}$/v; |
| 374 | |
| 375 | /** remove all ansi escape sequences */ |
| 376 | export function strip(text: string): string { |
| 377 | return text.replace(ansiEscapes, ""); |
| 378 | } |
| 379 | |
| 380 | function segmentWidth(segment: string) { |
| 381 | if (zeroWidthClusterRegex.test(segment)) return 0; |
| 382 | if (rgiEmojiRegex.test(segment)) return 2; |
| 383 | |
| 384 | // deno-fmt-ignore |
| 385 | const list = [ |
| 386 | 0x3000, |
| 387 | 0x231A, |
| 388 | 0x231B, |
| 389 | 0x2329, |
| 390 | 0x232A, |
| 391 | 0x23F0, |
| 392 | 0x23F3, |
| 393 | 0x25FD, |
| 394 | 0x25FE, |
| 395 | 0x2614, |
| 396 | 0x2615, |
| 397 | 0x267F, |
| 398 | 0x2693, |
| 399 | 0x26A1, |
| 400 | 0x26AA, |
| 401 | 0x26AB, |
| 402 | 0x26BD, |
| 403 | 0x26BE, |
| 404 | 0x26C4, |
| 405 | 0x26C5, |
| 406 | 0x26CE, |
| 407 | 0x26D4, |
| 408 | 0x26EA, |
| 409 | 0x26F2, |
| 410 | 0x26F3, |
| 411 | 0x26F5, |
| 412 | 0x26FA, |
| 413 | 0x26FD, |
| 414 | 0x2705, |
| 415 | 0x270A, |
| 416 | 0x270B, |
| 417 | 0x2728, |
| 418 | 0x274C, |
| 419 | 0x274E, |
| 420 | 0x2757, |
| 421 | 0x27B0, |
| 422 | 0x27BF, |
| 423 | 0x2B1B, |
| 424 | 0x2B1C, |
| 425 | 0x2B50, |
| 426 | 0x2B55, |
| 427 | 0x1AFFD, |
| 428 | 0x1AFFE, |
| 429 | 0x1B132, |
| 430 | 0x1B155, |
| 431 | 0x1F004, |
| 432 | 0x1F0CF, |
| 433 | 0x1F18E, |
| 434 | 0x1F250, |
| 435 | 0x1F251, |
| 436 | 0x1F3F4, |
| 437 | 0x1F440, |
| 438 | 0x1F57A, |
| 439 | 0x1F595, |
| 440 | 0x1F596, |
| 441 | 0x1F5A4, |
| 442 | 0x1F6CC, |
| 443 | 0x1F6EB, |
| 444 | 0x1F6EC, |
| 445 | 0x1F7F0, |
| 446 | 0x1FAC8, |
| 447 | ]; |
| 448 | const x = segment.replace(leadingNonPrintingRegex, "").codePointAt(0) ?? 0; |
| 449 | return 1 + +( |
| 450 | // full |
| 451 | x >= 0xFF01 && x <= 0xFF60 || x >= 0xFFE0 && x <= 0xFFE6 |
| 452 | // wide |
| 453 | || x >= 0x1100 && x <= 0x115F || x >= 0x23E9 && x <= 0x23EC |
| 454 | || x >= 0x2630 && x <= 0x2637 || x >= 0x2648 && x <= 0x2653 |
| 455 | || x >= 0x268A && x <= 0x268F || x >= 0x2753 && x <= 0x2755 |
| 456 | || x >= 0x2795 && x <= 0x2797 || x >= 0x2E80 && x <= 0x2E99 |
| 457 | || x >= 0x2E9B && x <= 0x2EF3 || x >= 0x2F00 && x <= 0x2FD5 |
| 458 | || x >= 0x2FF0 && x <= 0x2FFF || x >= 0x3001 && x <= 0x303E |
| 459 | || x >= 0x3041 && x <= 0x3096 || x >= 0x3099 && x <= 0x30FF |
| 460 | || x >= 0x3105 && x <= 0x312F || x >= 0x3131 && x <= 0x318E |
| 461 | || x >= 0x3190 && x <= 0x31E5 || x >= 0x31EF && x <= 0x321E |
| 462 | || x >= 0x3220 && x <= 0x3247 || x >= 0x3250 && x <= 0xA48C |
| 463 | || x >= 0xA490 && x <= 0xA4C6 || x >= 0xA960 && x <= 0xA97C |
| 464 | || x >= 0xAC00 && x <= 0xD7A3 || x >= 0xF900 && x <= 0xFAFF |
| 465 | || x >= 0xFE10 && x <= 0xFE19 || x >= 0xFE30 && x <= 0xFE52 |
| 466 | || x >= 0xFE54 && x <= 0xFE66 || x >= 0xFE68 && x <= 0xFE6B |
| 467 | || x >= 0x16FE0 && x <= 0x16FE4 || x >= 0x16FF0 && x <= 0x16FF6 |
| 468 | || x >= 0x17000 && x <= 0x18CD5 || x >= 0x18CFF && x <= 0x18D1E |
| 469 | || x >= 0x18D80 && x <= 0x18DF2 || x >= 0x1AFF0 && x <= 0x1AFF3 |
| 470 | || x >= 0x1AFF5 && x <= 0x1AFFB || x >= 0x1B150 && x <= 0x1B152 |
| 471 | || x >= 0x1B164 && x <= 0x1B167 || x >= 0x1B170 && x <= 0x1B2FB |
| 472 | || x >= 0x1D300 && x <= 0x1D356 || x >= 0x1D360 && x <= 0x1D376 |
| 473 | || x >= 0x1F191 && x <= 0x1F19A || x >= 0x1F200 && x <= 0x1F202 |
| 474 | || x >= 0x1F210 && x <= 0x1F23B || x >= 0x1F240 && x <= 0x1F248 |
| 475 | || x >= 0x1F260 && x <= 0x1F265 || x >= 0x1F300 && x <= 0x1F320 |
| 476 | || x >= 0x1F32D && x <= 0x1F335 || x >= 0x1F337 && x <= 0x1F37C |
| 477 | || x >= 0x1F37E && x <= 0x1F393 || x >= 0x1F3A0 && x <= 0x1F3CA |
| 478 | || x >= 0x1F3CF && x <= 0x1F3D3 || x >= 0x1F3E0 && x <= 0x1F3F0 |
| 479 | || x >= 0x1F3F8 && x <= 0x1F43E || x >= 0x1F442 && x <= 0x1F4FC |
| 480 | || x >= 0x1F4FF && x <= 0x1F53D || x >= 0x1F54B && x <= 0x1F54E |
| 481 | || x >= 0x1F550 && x <= 0x1F567 || x >= 0x1F5FB && x <= 0x1F64F |
| 482 | || x >= 0x1F680 && x <= 0x1F6C5 || x >= 0x1F6D0 && x <= 0x1F6D2 |
| 483 | || x >= 0x1F6D5 && x <= 0x1F6D8 || x >= 0x1F6DC && x <= 0x1F6DF |
| 484 | || x >= 0x1F6F4 && x <= 0x1F6FC || x >= 0x1F7E0 && x <= 0x1F7EB |
| 485 | || x >= 0x1F90C && x <= 0x1F93A || x >= 0x1F93C && x <= 0x1F945 |
| 486 | || x >= 0x1F947 && x <= 0x1F9FF || x >= 0x1FA70 && x <= 0x1FA7C |
| 487 | || x >= 0x1FA80 && x <= 0x1FA8A || x >= 0x1FA8E && x <= 0x1FAC6 |
| 488 | || x >= 0x1FACD && x <= 0x1FADC || x >= 0x1FADF && x <= 0x1FAEA |
| 489 | || x >= 0x1FAEF && x <= 0x1FAF8 || x >= 0x20000 && x <= 0x2FFFD |
| 490 | || x >= 0x30000 && x <= 0x3FFFD || x >= 0x1B000 && x <= 0x1B122 |
| 491 | || list.includes(x) |
| 492 | ); |
| 493 | } |
| 494 | |
| 495 | import { UNWRAP } from "../assert.ts"; |
| 496 | import * as self from "./ansi.ts"; |