* Write a single line's characters into the screen buffer. * Extracted from Output.get() so JSC can optimize this tight, * monomorphic loop independently — better register allocation, * setCellAt inlining, and type feedback than when buried inside * a 300-line dispatch function. * * Returns th
( screen: Screen, line: string, x: number, y: number, screenWidth: number, stylePool: StylePool, charCache: Map<string, ClusteredChar[]>, )
| 753 | * line via stringWidth(). Caller computes the debug cell-count as end-x. |
| 754 | */ |
| 755 | function writeLineToScreen( |
| 756 | screen: Screen, |
| 757 | line: string, |
| 758 | x: number, |
| 759 | y: number, |
| 760 | screenWidth: number, |
| 761 | stylePool: StylePool, |
| 762 | charCache: Map<string, ClusteredChar[]>, |
| 763 | ): number { |
| 764 | const writeLineStart = performance.now() |
| 765 | if (line.length >= 16 && isPlainAsciiLine(line)) { |
| 766 | const writeLoopStart = performance.now() |
| 767 | const endX = writePlainAsciiLineAt(screen, x, y, line, stylePool.none) |
| 768 | recordWriteLineToScreenStats({ |
| 769 | lineLength: line.length, |
| 770 | clusteredChars: Math.max(0, endX - x), |
| 771 | cacheHit: false, |
| 772 | usedPlainAsciiFastPath: true, |
| 773 | materializeDurationMs: 0, |
| 774 | writeLineDurationMs: performance.now() - writeLineStart, |
| 775 | writeLoopDurationMs: performance.now() - writeLoopStart, |
| 776 | }) |
| 777 | return endX |
| 778 | } |
| 779 | |
| 780 | let characters = charCache.get(line) |
| 781 | let materializeDurationMs = 0 |
| 782 | let usedPlainAsciiFastPath = false |
| 783 | if (!characters) { |
| 784 | const materializeStart = performance.now() |
| 785 | const plainAsciiCharacters = tryBuildPlainAsciiClusters(line, stylePool.none) |
| 786 | if (plainAsciiCharacters) { |
| 787 | characters = plainAsciiCharacters |
| 788 | usedPlainAsciiFastPath = true |
| 789 | } else { |
| 790 | characters = reorderBidi( |
| 791 | styledCharsWithGraphemeClustering( |
| 792 | styledCharsFromTokens(tokenize(line)), |
| 793 | stylePool, |
| 794 | ), |
| 795 | ) |
| 796 | } |
| 797 | materializeDurationMs = performance.now() - materializeStart |
| 798 | charCache.set(line, characters) |
| 799 | } |
| 800 | |
| 801 | let offsetX = x |
| 802 | const writeLoopStart = performance.now() |
| 803 | |
| 804 | for (let charIdx = 0; charIdx < characters.length; charIdx++) { |
| 805 | const character = characters[charIdx]! |
| 806 | const codePoint = character.value.codePointAt(0) |
| 807 | |
| 808 | // Handle C0 control characters (0x00-0x1F) that cause cursor movement |
| 809 | // mismatches. stringWidth treats these as width 0, but terminals may |
| 810 | // move the cursor differently. |
| 811 | if (codePoint !== undefined && codePoint <= 0x1f) { |
| 812 | // Tab (0x09): expand to spaces to reach next tab stop |
no test coverage detected