(contentWidth: number)
| 987 | } |
| 988 | |
| 989 | private layoutText(contentWidth: number): LayoutLine[] { |
| 990 | const layoutLines: LayoutLine[] = []; |
| 991 | |
| 992 | if (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === "")) { |
| 993 | // Empty editor |
| 994 | layoutLines.push({ |
| 995 | text: "", |
| 996 | hasCursor: true, |
| 997 | cursorPos: 0, |
| 998 | }); |
| 999 | return layoutLines; |
| 1000 | } |
| 1001 | |
| 1002 | // Process each logical line |
| 1003 | for (let i = 0; i < this.state.lines.length; i++) { |
| 1004 | const line = this.state.lines[i] || ""; |
| 1005 | const isCurrentLine = i === this.state.cursorLine; |
| 1006 | const lineVisibleWidth = visibleWidth(line); |
| 1007 | |
| 1008 | if (lineVisibleWidth <= contentWidth) { |
| 1009 | // Line fits in one layout line |
| 1010 | if (isCurrentLine) { |
| 1011 | layoutLines.push({ |
| 1012 | text: line, |
| 1013 | hasCursor: true, |
| 1014 | cursorPos: this.state.cursorCol, |
| 1015 | }); |
| 1016 | } else { |
| 1017 | layoutLines.push({ |
| 1018 | text: line, |
| 1019 | hasCursor: false, |
| 1020 | }); |
| 1021 | } |
| 1022 | } else { |
| 1023 | // Line needs wrapping - use word-aware wrapping |
| 1024 | const chunks = wordWrapLine(line, contentWidth, [...this.segment(line, "grapheme")]); |
| 1025 | |
| 1026 | for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) { |
| 1027 | const chunk = chunks[chunkIndex]; |
| 1028 | if (!chunk) continue; |
| 1029 | |
| 1030 | const cursorPos = this.state.cursorCol; |
| 1031 | const isLastChunk = chunkIndex === chunks.length - 1; |
| 1032 | |
| 1033 | // Determine if cursor is in this chunk |
| 1034 | // For word-wrapped chunks, we need to handle the case where |
| 1035 | // cursor might be in trimmed whitespace at end of chunk |
| 1036 | let hasCursorInChunk = false; |
| 1037 | let adjustedCursorPos = 0; |
| 1038 | |
| 1039 | if (isCurrentLine) { |
| 1040 | if (isLastChunk) { |
| 1041 | // Last chunk: cursor belongs here if >= startIndex |
| 1042 | hasCursorInChunk = cursorPos >= chunk.startIndex; |
| 1043 | adjustedCursorPos = cursorPos - chunk.startIndex; |
| 1044 | } else { |
| 1045 | // Non-last chunk: cursor belongs here if in range [startIndex, endIndex) |
| 1046 | // But we need to handle the visual position in the trimmed text |
no test coverage detected