MCPcopy Create free account
hub / github.com/MoonshotAI/kimi-code / wordWrapLine

Function wordWrapLine

packages/pi-tui/src/components/editor.ts:115–218  ·  view source on GitHub ↗
(line: string, maxWidth: number, preSegmented?: Intl.SegmentData[])

Source from the content-addressed store, hash-verified

113 * @returns Array of chunks with text and position information
114 */
115export function wordWrapLine(line: string, maxWidth: number, preSegmented?: Intl.SegmentData[]): TextChunk[] {
116 if (!line || maxWidth <= 0) {
117 return [{ text: "", startIndex: 0, endIndex: 0 }];
118 }
119
120 const lineWidth = visibleWidth(line);
121 if (lineWidth <= maxWidth) {
122 return [{ text: line, startIndex: 0, endIndex: line.length }];
123 }
124
125 const chunks: TextChunk[] = [];
126 const segments = preSegmented ?? [...graphemeSegmenter.segment(line)];
127
128 let currentWidth = 0;
129 let chunkStart = 0;
130
131 // Wrap opportunity: the position after the last whitespace before a non-whitespace
132 // grapheme, i.e. where a line break is allowed.
133 let wrapOppIndex = -1;
134 let wrapOppWidth = 0;
135
136 for (let i = 0; i < segments.length; i++) {
137 const seg = segments[i]!;
138 const grapheme = seg.segment;
139 const gWidth = visibleWidth(grapheme);
140 const charIndex = seg.index;
141 const isWs = !isPasteMarker(grapheme) && isWhitespaceChar(grapheme);
142
143 // Overflow check before advancing.
144 if (currentWidth + gWidth > maxWidth) {
145 if (wrapOppIndex >= 0 && currentWidth - wrapOppWidth + gWidth <= maxWidth) {
146 // Backtrack to last wrap opportunity (the remaining content
147 // plus the current grapheme still fits within maxWidth).
148 chunks.push({ text: line.slice(chunkStart, wrapOppIndex), startIndex: chunkStart, endIndex: wrapOppIndex });
149 chunkStart = wrapOppIndex;
150 currentWidth -= wrapOppWidth;
151 } else if (chunkStart < charIndex) {
152 // No viable wrap opportunity: force-break at current position.
153 // This also handles the case where backtracking to a word
154 // boundary wouldn't help because the remaining content plus
155 // the current grapheme (e.g. a wide character) still exceeds
156 // maxWidth.
157 chunks.push({ text: line.slice(chunkStart, charIndex), startIndex: chunkStart, endIndex: charIndex });
158 chunkStart = charIndex;
159 currentWidth = 0;
160 }
161 wrapOppIndex = -1;
162 }
163
164 if (gWidth > maxWidth) {
165 // Single atomic segment wider than maxWidth (e.g. paste marker
166 // in a narrow terminal). Re-wrap it at grapheme granularity.
167
168 // The segment remains logically atomic for cursor
169 // movement / editing — the split is purely visual for word-wrap layout.
170 const subSegments = [...graphemeSegmenter.segment(grapheme)];
171 if (subSegments.length <= 1) {
172 // An indivisible grapheme wider than maxWidth (e.g. a CJK

Callers 3

editor.test.tsFile · 0.90
layoutTextMethod · 0.85
buildVisualLineMapMethod · 0.85

Calls 5

visibleWidthFunction · 0.90
isWhitespaceCharFunction · 0.90
isPasteMarkerFunction · 0.85
segmentMethod · 0.45
pushMethod · 0.45

Tested by

no test coverage detected