* Split text into words while keeping ANSI codes attached.
(text: string)
| 631 | * Split text into words while keeping ANSI codes attached. |
| 632 | */ |
| 633 | function splitIntoTokensWithAnsi(text: string): string[] { |
| 634 | const tokens: string[] = []; |
| 635 | let current = ""; |
| 636 | let pendingAnsi = ""; // ANSI codes waiting to be attached to next visible content |
| 637 | let currentKind: "space" | "word" | null = null; |
| 638 | let i = 0; |
| 639 | |
| 640 | const flushCurrent = (): void => { |
| 641 | if (!current) { |
| 642 | return; |
| 643 | } |
| 644 | tokens.push(current); |
| 645 | current = ""; |
| 646 | currentKind = null; |
| 647 | }; |
| 648 | |
| 649 | while (i < text.length) { |
| 650 | const ansiResult = extractAnsiCode(text, i); |
| 651 | if (ansiResult) { |
| 652 | // Hold ANSI codes separately - they'll be attached to the next visible char |
| 653 | pendingAnsi += ansiResult.code; |
| 654 | i += ansiResult.length; |
| 655 | continue; |
| 656 | } |
| 657 | |
| 658 | let end = i; |
| 659 | while (end < text.length && !extractAnsiCode(text, end)) { |
| 660 | end++; |
| 661 | } |
| 662 | |
| 663 | for (const { segment } of graphemeSegmenter.segment(text.slice(i, end))) { |
| 664 | const segmentIsSpace = segment === " "; |
| 665 | if (!segmentIsSpace && cjkBreakRegex.test(segment)) { |
| 666 | flushCurrent(); |
| 667 | const token = pendingAnsi + segment; |
| 668 | pendingAnsi = ""; |
| 669 | tokens.push(token); |
| 670 | continue; |
| 671 | } |
| 672 | |
| 673 | const segmentKind = segmentIsSpace ? "space" : "word"; |
| 674 | if (current && currentKind !== segmentKind) { |
| 675 | flushCurrent(); |
| 676 | } |
| 677 | |
| 678 | // Attach any pending ANSI codes to this visible character |
| 679 | if (pendingAnsi) { |
| 680 | current += pendingAnsi; |
| 681 | pendingAnsi = ""; |
| 682 | } |
| 683 | |
| 684 | currentKind = segmentKind; |
| 685 | current += segment; |
| 686 | } |
| 687 | |
| 688 | i = end; |
| 689 | } |
| 690 |
no test coverage detected