Skip to content

Commit 4c82690

Browse files
committed
Fixes to make rendering actually good.
1 parent 64776f5 commit 4c82690

File tree

8 files changed

+457
-97
lines changed

8 files changed

+457
-97
lines changed

examples/text-wrap/text-wrap.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function TextWrap() {
4646

4747
useEffect(() => {
4848
const timer = setInterval(() => {
49-
setCounter(prev => prev + 1);
49+
setCounter(previous => previous + 1);
5050
}, 100);
5151

5252
return () => {
@@ -75,6 +75,7 @@ function TextWrap() {
7575
</Box>
7676
<Box width={size.columns} flexDirection="column" borderStyle="single">
7777
{tsExample.split('\n').map((line, i) => (
78+
// eslint-disable-next-line react/no-array-index-key
7879
<Box key={i} flexDirection="row">
7980
<Box width={3} flexShrink={0} flexGrow={0}>
8081
<Text>{i + 1}</Text>

src/dom.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
widestLineFromStyledChars,
66
} from './measure-text.js';
77
import {type Styles} from './styles.js';
8-
import {wrapStyledChars, truncateStyledChars} from './wrap-text.js';
8+
import {wrapStyledChars, truncateStyledChars} from './text-wrap.js';
99
import squashTextNodes from './squash-text-nodes.js';
1010
import {type OutputTransformer} from './render-node-to-output.js';
1111
import type ResizeObserver from './resize-observer.js';

src/render-node-to-output.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Yoga from 'yoga-layout';
22
import {type StyledChar} from '@alcalzone/ansi-tokenize';
3-
import {truncateStyledChars, wrapStyledChars} from './wrap-text.js';
3+
import {truncateStyledChars, wrapStyledChars} from './text-wrap.js';
44
import getMaxWidth from './get-max-width.js';
55
import squashTextNodes from './squash-text-nodes.js';
66
import renderBorder from './render-border.js';

src/wrap-text.ts renamed to src/text-wrap.ts

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ const wrapWord = (
9696

9797
if (visible + characterLength > columns && visible > 0) {
9898
rows.push([]);
99+
99100
currentLine = rows.at(-1)!;
100-
visible = 0;
101+
visible = styledCharsWidth(currentLine);
101102
}
102103

103104
currentLine.push(character);
@@ -114,12 +115,20 @@ export const wrapStyledChars = (
114115
let currentWord: StyledChar[] = [];
115116

116117
for (const char of styledChars) {
117-
if (char.value === ' ') {
118+
if (char.value === '\n') {
118119
if (currentWord.length > 0) {
119120
words.push(currentWord);
120121
}
121122

122123
currentWord = [];
124+
words.push([char]);
125+
} else if (char.value === ' ') {
126+
if (currentWord.length > 0) {
127+
words.push(currentWord);
128+
}
129+
130+
currentWord = [];
131+
words.push([char]);
123132
} else {
124133
currentWord.push(char);
125134
}
@@ -129,44 +138,57 @@ export const wrapStyledChars = (
129138
words.push(currentWord);
130139
}
131140

132-
const space: StyledChar = {
133-
type: 'char',
134-
value: ' ',
135-
fullWidth: false,
136-
styles: [],
137-
};
141+
let isAtStartOfLogicalLine = true;
138142

139-
for (const [index, word] of words.entries()) {
140-
const wordWidth = styledCharsWidth(word);
141-
let rowWidth = styledCharsWidth(rows.at(-1)!);
143+
for (const word of words) {
144+
if (word.length === 0) {
145+
continue;
146+
}
142147

143-
if (index > 0) {
144-
rows.at(-1)!.push(space);
145-
rowWidth++;
148+
if (word[0]!.value === '\n') {
149+
rows.push([]);
150+
isAtStartOfLogicalLine = true;
151+
continue;
146152
}
147153

148-
if (wordWidth > columns) {
149-
if (index > 0) {
150-
rows[rows.length - 1] = rows.at(-1)!.slice(0, -1);
154+
const wordWidth = styledCharsWidth(word);
155+
const rowWidth = styledCharsWidth(rows.at(-1)!);
156+
157+
if (rowWidth + wordWidth > columns) {
158+
if (
159+
!isAtStartOfLogicalLine &&
160+
word[0]!.value === ' ' &&
161+
word.length === 1
162+
) {
163+
continue;
164+
}
151165

152-
if (rows.at(-1)!.length > 0) {
153-
rows.push([]);
166+
if (!isAtStartOfLogicalLine) {
167+
while (rows.at(-1)!.length > 0 && rows.at(-1)!.at(-1)!.value === ' ') {
168+
rows.at(-1)!.pop();
154169
}
155170
}
156171

157-
wrapWord(rows, word, columns);
158-
continue;
159-
}
172+
if (wordWidth > columns) {
173+
if (rowWidth > 0) {
174+
rows.push([]);
175+
}
160176

161-
if (rowWidth + wordWidth > columns && rowWidth > 0) {
162-
if (index > 0) {
163-
rows[rows.length - 1] = rows.at(-1)!.slice(0, -1);
177+
wrapWord(rows, word, columns);
178+
} else {
179+
rows.push([]);
180+
rows.at(-1)!.push(...word);
164181
}
165-
166-
rows.push(word);
167182
} else {
168183
rows.at(-1)!.push(...word);
169184
}
185+
186+
if (
187+
isAtStartOfLogicalLine &&
188+
!(word[0]!.value === ' ' && word.length === 1)
189+
) {
190+
isAtStartOfLogicalLine = false;
191+
}
170192
}
171193

172194
return rows;

test/indent-wrapping.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import test from 'ava';
3+
import {Box, Text} from '../src/index.js';
4+
import {renderToString} from './helpers/render-to-string.js';
5+
6+
test('preserves indentation when wrapping if there is room', t => {
7+
const text = 'if (\n foobarbaz\n)';
8+
const output = renderToString(
9+
<Box width={7}>
10+
<Text wrap="wrap">{text}</Text>
11+
</Box>,
12+
);
13+
14+
t.is(output, 'if (\n foo\n bar\n baz\n)');
15+
});
16+
17+
test('drops indentation when it does not fit', t => {
18+
const text = 'if (\n foobarbaz\n)';
19+
const output = renderToString(
20+
<Box width={4}>
21+
<Text wrap="wrap">{text}</Text>
22+
</Box>,
23+
);
24+
25+
t.is(output, 'if (\n\nfoob\narba\nz\n)');
26+
});
27+
28+
test('preserves just indentation', t => {
29+
const output = renderToString(
30+
<Box width={4}>
31+
<Text> </Text>
32+
</Box>,
33+
);
34+
t.is(output, '');
35+
});
36+
37+
test('preserves just indentation no box', t => {
38+
const output = renderToString(<Text> </Text>);
39+
t.is(output, '');
40+
});

0 commit comments

Comments
 (0)