Skip to content

Commit b76498c

Browse files
authored
Fix/#95 text indentation (#96)
Fix #95
1 parent d5537be commit b76498c

File tree

10 files changed

+161
-84
lines changed

10 files changed

+161
-84
lines changed

packages/expression-parser/package-lock.json

Lines changed: 22 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/prettyhtml-formatter/index.js

Lines changed: 69 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -114,25 +114,7 @@ function format(options) {
114114
}
115115

116116
if (is('comment', node)) {
117-
/**
118-
* indent last line of comment
119-
* e.g
120-
* <!--
121-
* foo
122-
* -->
123-
* to
124-
* <!--
125-
* foo
126-
* -->
127-
*/
128-
129-
let commentLines = node.value.split(single)
130-
if (commentLines.length > 1) {
131-
commentLines[commentLines.length - 1] =
132-
repeat(indent, level - 1) +
133-
commentLines[commentLines.length - 1].trim()
134-
node.value = commentLines.join(single)
135-
}
117+
indentComment(node, indent, level)
136118
}
137119

138120
/**
@@ -144,7 +126,7 @@ function format(options) {
144126

145127
// clear empty script, textarea, pre, style tags
146128
if (length) {
147-
const empty = containsOnlyEmptyTextNodes(node)
129+
const empty = hasOnlyEmptyTextChildren(node)
148130
const isEmbeddedContent =
149131
isElement(node, 'style') || isElement(node, 'script')
150132
if (empty) {
@@ -158,6 +140,9 @@ function format(options) {
158140
return visit.SKIP
159141
}
160142

143+
let newline = false
144+
const willBreak = collapsed(node, children)
145+
161146
/**
162147
* Indent children
163148
*/
@@ -166,12 +151,16 @@ function format(options) {
166151
let child = children[index]
167152

168153
// only indent text in nodes
169-
// root text nodes should't influence other root nodes
154+
// root text nodes should't influence other root nodes^^
170155
if (node.type === 'root') {
171156
break
172157
}
173158

174159
if (is('text', child)) {
160+
if (containsNewline(child) || willBreak) {
161+
newline = true
162+
}
163+
175164
child.value = child.value
176165
// reduce newlines to one newline
177166
// $& contains the lastMatch
@@ -182,12 +171,9 @@ function format(options) {
182171
// reset
183172
result = []
184173
index = -1
185-
186174
node.children = result
187175

188-
let prevChild
189-
let hasLeadingNewline = false
190-
176+
let prevChild = null
191177
if (length) {
192178
// walk through children
193179
// hint: a child has no children informations we already walking through
@@ -215,25 +201,14 @@ function format(options) {
215201
value: double + repeat(indent, indentLevel)
216202
})
217203
} else if (
218-
!endsWithNewline(prevChild) &&
219-
beforeChildNodeAddedHook(node, children, child, index, prevChild)
204+
beforeChildNodeAddedHook(node, children, child, index, prevChild) ||
205+
(newline && index === 0)
220206
) {
221-
// all template expression are indented on a ewline thats why need to check
222-
// so that we don't add another one
223-
if (index === 0 && checkForTemplateExpression(child.value)) {
224-
hasLeadingNewline = true
225-
}
226207
// only necessary because we are trying to indent tags on newlines
227208
// even when in inline context when possible
228209
if (is('text', prevChild)) {
229210
// remove trailing whitespaces and tabs because a newline is inserted before
230211
prevChild.value = prevChild.value.replace(/[ \t]+$/, '')
231-
232-
// adds a leading newline because the sibling node is indented on a newline
233-
if (index === 1 && hasLeadingNewline === false) {
234-
prevChild.value =
235-
single + repeat(indent, indentLevel) + prevChild.value
236-
}
237212
}
238213
// remove leading whitespaces and tabs because a newline is inserted before
239214
if (is('text', child)) {
@@ -245,23 +220,14 @@ function format(options) {
245220
value: single + repeat(indent, indentLevel)
246221
})
247222
}
248-
// adds a leading newline when he sibling element was inteded on a newline and when no newlines was added
249-
else if (
250-
is('text', child) &&
251-
hasLeadingNewline === false &&
252-
endsWithNewline(child) &&
253-
!startsWithNewline(child)
254-
) {
255-
child.value = single + repeat(indent, indentLevel) + child.value
256-
}
257223

258224
prevChild = child
259225

260226
result.push(child)
261227
}
262228
}
263229

264-
if (afterChildNodesAddedHook(node, prevChild)) {
230+
if (afterChildNodesAddedHook(node, prevChild) || newline) {
265231
result.push({
266232
type: 'text',
267233
value: single + repeat(indent, level - 1)
@@ -279,12 +245,36 @@ function startsWithNewline(node) {
279245
return is('text', node) && node.value && /^\s*\n/.test(node.value)
280246
}
281247

248+
function containsNewline(node) {
249+
return node.value.indexOf(single) !== -1
250+
}
251+
252+
/**
253+
* indent last line of comment
254+
* e.g
255+
* <!--
256+
* foo
257+
* -->
258+
* to
259+
* <!--
260+
* foo
261+
* -->
262+
*/
263+
function indentComment(node, indent, level) {
264+
let commentLines = node.value.split(single)
265+
if (commentLines.length > 1) {
266+
commentLines[commentLines.length - 1] =
267+
repeat(indent, level - 1) + commentLines[commentLines.length - 1].trim()
268+
node.value = commentLines.join(single)
269+
}
270+
}
271+
282272
function handleTemplateExpression(child, children) {
283273
const brackets = checkForTemplateExpression(child.value)
284274
if (brackets) {
285275
// dont touch nodes with single text element
286276
if (
287-
containsOnlyTextNodes({
277+
hasOnlyTextChildren({
288278
children
289279
})
290280
) {
@@ -300,7 +290,30 @@ function handleTemplateExpression(child, children) {
300290
}
301291
}
302292

293+
/**
294+
* Check if any children will be wrapped on a newline
295+
* @param {*} node
296+
* @param {*} children
297+
*/
298+
function collapsed(node, children) {
299+
let index = -1
300+
let prevChild = false
301+
while (++index < children.length) {
302+
let child = children[index]
303+
if (beforeChildNodeAddedHook(node, children, child, index, prevChild)) {
304+
return true
305+
}
306+
prevChild = child
307+
}
308+
}
309+
303310
function beforeChildNodeAddedHook(node, children, child, index, prev) {
311+
// don't add newline when prev child already has one
312+
if (endsWithNewline(prev)) {
313+
return false
314+
}
315+
316+
// every template expression is indented on a newline
304317
if (is('text', child) && handleTemplateExpression(child, children)) {
305318
return true
306319
}
@@ -310,11 +323,12 @@ function beforeChildNodeAddedHook(node, children, child, index, prev) {
310323
return true
311324
}
312325

326+
// embedded content is indented on newlines
313327
if (isElement(child, ['script', 'style']) && index !== 0) {
314328
return true
315329
}
316330

317-
// don't add newline on the first element
331+
// don't add newline on the first element of the page
318332
const isRootElement = node.type === 'root' && index === 0
319333
if (isRootElement) {
320334
return false
@@ -336,15 +350,15 @@ function afterChildNodesAddedHook(node, prev) {
336350
/**
337351
* e.g <label><input/>foo</label>
338352
*/
339-
if (hasChilds && !containsOnlyTextNodes(node) && !isVoid(node)) {
353+
if (hasChilds && !hasOnlyTextChildren(node) && !isVoid(node)) {
340354
return true
341355
}
342356

343357
/**
344358
* e.g <label>foo</label>
345359
*/
346-
const isPrevRawText = is('text', prev)
347-
return hasChilds && !isVoid(node) && !isPrevRawText
360+
const isPrevTextNode = is('text', prev)
361+
return hasChilds && !isVoid(node) && !isPrevTextNode
348362
}
349363

350364
function checkForTemplateExpression(value) {
@@ -363,7 +377,7 @@ function checkForTemplateExpression(value) {
363377
return null
364378
}
365379

366-
function containsOnlyTextNodes(node) {
380+
function hasOnlyTextChildren(node) {
367381
const children = node.children || []
368382

369383
if (children.length === 0) {
@@ -373,7 +387,7 @@ function containsOnlyTextNodes(node) {
373387
return children.every(n => is('text', n))
374388
}
375389

376-
function containsOnlyEmptyTextNodes(node) {
390+
function hasOnlyEmptyTextChildren(node) {
377391
const children = node.children || []
378392

379393
if (children.length === 0) {

packages/prettyhtml-formatter/test/fixtures/indent-multiline-text/output.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
<label>my text
1+
<label>
2+
my text
23
on multiple
3-
lines</label>
4+
lines
5+
</label>
46
<label>
57
my text
68
on multiple
@@ -17,6 +19,8 @@
1719
my text
1820
on multiple
1921
lines
20-
<div>dedwedwed
21-
dwedwedwe</div>
22+
<div>
23+
dedwedwed
24+
dwedwedwe
25+
</div>
2226
</div>

0 commit comments

Comments
 (0)