(str: string)
| 69 | } |
| 70 | |
| 71 | function* segmentGraphemes(str: string): Generator<Grapheme> { |
| 72 | // Terminal C0 line controls must remain individual actions even when the |
| 73 | // surrounding text takes the Unicode path. Intl.Segmenter groups CRLF into |
| 74 | // one grapheme cluster, which is correct for text editing but wrong for a |
| 75 | // terminal replay/parser: CR returns to column 0, LF advances the row. |
| 76 | let start = 0 |
| 77 | for (let index = 0; index < str.length; index += 1) { |
| 78 | const code = str.charCodeAt(index) |
| 79 | if (code !== 0x0d && code !== 0x0a) { |
| 80 | continue |
| 81 | } |
| 82 | if (start < index) { |
| 83 | yield* segmentPrintableGraphemes(str.slice(start, index)) |
| 84 | } |
| 85 | yield { value: str[index]!, width: 1 } |
| 86 | start = index + 1 |
| 87 | } |
| 88 | if (start > 0) { |
| 89 | if (start < str.length) { |
| 90 | yield* segmentPrintableGraphemes(str.slice(start)) |
| 91 | } |
| 92 | return |
| 93 | } |
| 94 | |
| 95 | yield* segmentPrintableGraphemes(str) |
| 96 | } |
| 97 | |
| 98 | function* segmentPrintableGraphemes(str: string): Generator<Grapheme> { |
| 99 | // Long-history transcript output is overwhelmingly ASCII source text. |
no test coverage detected