@@ -162,37 +162,58 @@ namespace ts {
162
162
* on the same line as the closing tag. See examples in
163
163
* tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
164
164
* See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model
165
+ *
166
+ * An equivalent algorithm would be:
167
+ * - If there is only one line, return it.
168
+ * - If there is only whitespace (but multiple lines), return `undefined`.
169
+ * - Split the text into lines.
170
+ * - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines.
171
+ * - Decode entities on each line (individually).
172
+ * - Remove empty lines and join the rest with " ".
165
173
*/
166
174
function fixupWhitespaceAndDecodeEntities ( text : string ) : string | undefined {
167
175
let acc : string | undefined ;
176
+ // First non-whitespace character on this line.
168
177
let firstNonWhitespace = 0 ;
178
+ // Last non-whitespace character on this line.
169
179
let lastNonWhitespace = - 1 ;
180
+ // These initial values are special because the first line is:
181
+ // firstNonWhitespace = 0 to indicate that we want leading whitsepace,
182
+ // but lastNonWhitespace = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
170
183
171
184
for ( let i = 0 ; i < text . length ; i ++ ) {
172
185
const c = text . charCodeAt ( i ) ;
173
186
if ( isLineBreak ( c ) ) {
174
- if ( firstNonWhitespace !== - 1 && ( lastNonWhitespace - firstNonWhitespace + 1 > 0 ) ) {
175
- const part = decodeEntities ( text . substr ( firstNonWhitespace , lastNonWhitespace - firstNonWhitespace + 1 ) ) ;
176
- acc = acc === undefined ? part : acc + " " + part ;
187
+ // If we've seen any non-whitespace characters on this line, add the 'trim' of the line.
188
+ // (lastNonWhitespace === -1 is a special flag to detect whether the first line is all whitespace.)
189
+ if ( firstNonWhitespace !== - 1 && lastNonWhitespace !== - 1 ) {
190
+ acc = addLineOfJsxText ( acc , text . substr ( firstNonWhitespace , lastNonWhitespace - firstNonWhitespace + 1 ) ) ;
177
191
}
178
192
193
+ // Reset firstNonWhitespace for the next line.
194
+ // Don't bother to reset lastNonWhitespace because we ignore it if firstNonWhitespace = -1.
179
195
firstNonWhitespace = - 1 ;
180
196
}
181
- else if ( ! isWhiteSpace ( c ) ) {
197
+ else if ( ! isWhiteSpaceSingleLine ( c ) ) {
182
198
lastNonWhitespace = i ;
183
199
if ( firstNonWhitespace === - 1 ) {
184
200
firstNonWhitespace = i ;
185
201
}
186
202
}
187
203
}
188
204
189
- if ( firstNonWhitespace !== - 1 ) {
190
- const lastPart = decodeEntities ( text . substr ( firstNonWhitespace ) ) ;
191
- return acc ? acc + lastPart : lastPart ;
192
- }
193
- else {
194
- return acc ;
195
- }
205
+ return firstNonWhitespace !== - 1
206
+ // Last line had a non-whitespace character. Emit the 'trimLeft', meaning keep trailing whitespace.
207
+ ? addLineOfJsxText ( acc , text . substr ( firstNonWhitespace ) )
208
+ // Last line was all whitespace, so ignore it
209
+ : acc ;
210
+ }
211
+
212
+ function addLineOfJsxText ( acc : string | undefined , trimmedLine : string ) : string {
213
+ // We do not escape the string here as that is handled by the printer
214
+ // when it emits the literal. We do, however, need to decode JSX entities.
215
+ const decoded = decodeEntities ( trimmedLine ) ;
216
+ return acc === undefined ? decoded : acc + " " + decoded ;
196
217
}
197
218
198
219
/**
0 commit comments