8000 Fix: improve comment indentation (fixes #514) · vuejs/eslint-plugin-vue@375dcc9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 375dcc9

Browse files
committed
Fix: improve comment indentation (fixes #514)
1 parent b18d351 commit 375dcc9

File tree

2 files changed

+78
-35
lines changed

2 files changed

+78
-35
lines changed

lib/utils/indent-common.js

Lines changed: 70 additions & 35 deletions
/**
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const KNOWN_NODES = new Set(['ArrayExpression', 'ArrayPattern', 'ArrowFunctionEx
1818
const LT_CHAR = /[\r\n\u2028\u2029]/
1919
const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g
2020
const BLOCK_COMMENT_PREFIX = /^\s*\*/
21+
const ITERATION_OPTS = Object.freeze({ includeComments: true, filter: isNotWhitespace })
2122

2223
/**
2324
* Normalize options.
@@ -194,6 +195,15 @@ function isNotComment (token) {
194195
return token != null && token.type !== 'Block' && token.type !== 'Line' && token.type !== 'Shebang' && !token.type.endsWith('Comment')
195196
}
196197

198+
/**
199+
* Check whether the given node is not an empty text node.
200+
* @param {Node} node The node to check.
201+
* @returns {boolean} `false` if the token is empty text node.
202+
*/
203+
function isNotEmptyTextNode (node) {
204+
return !(node.type === 'VText' && node.value.trim() === '')
205+
}
206+
197207
/**
198208
* Get the last element.
199209
* @param {Array} xs The array to get the last element.
@@ -294,7 +304,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
294304
* @param {Token} token The token to set.
295305
* @returns {void}
296306
*/
297-
function setBaseline (token, hardTabAdditional) {
307+
function setBaseline (token) {
298308
const offsetInfo = offsets.get(token)
299309
if (offsetInfo != null) {
300310
offsetInfo.baseline = true
@@ -332,14 +342,18 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
332342
* @param {Node|null} leftToken The left parenthesis token.
333343
* @param {Node|null} rightToken The right parenthesis token.
334344
* @param {number} offset The offset to set.
335-
* @param {Node} [alignVertically=true] The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line.
345+
* @param {boolean} [alignVertically=true] The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line.
336346
* @returns {void}
337347
*/
338-
function processNodeList (nodeList, leftToken, rightToken, offset, alignVertically) {
348+
function processNodeList (nodeList, left, right, offset, alignVertically) {
339349
let t
350+
const leftToken = (left && tokenStore.getFirstToken(left)) || left
351+
const rightToken = (right && tokenStore.getFirstToken(right)) || right
340352

341353
if (nodeList.length >= 1) {
342-
let lastToken = leftToken
354+
let baseToken = null
355+
let lastToken = left
356+
const alignTokensBeforeBaseToken = []
343357
const alignTokens = []
344358

345359
for (let i = 0; i < nodeList.length; ++i) {
@@ -350,30 +364,50 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
350364
}
351365
const elementTokens = getFirstAndLastTokens(node, lastToken != null ? lastToken.range[1] : 0)
352366

353-
// Collect related tokens.
354-
// Commas between this and the previous, and the first token of this node.
367+
// Collect comma/comment tokens between the last token of the previous node and the first token of this node.
355368
if (lastToken != null) {
356369
t = lastToken
357-
while ((t = tokenStore.getTokenAfter(t)) != null && t.range[1] <= elementTokens.firstToken.range[0]) {
358-
alignTokens.push(t)
370+
while (
371+
(t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null &&
372+
t.range[1] <= elementTokens.firstToken.range[0]
373+
) {
374+
if (baseToken == null) {
375+
alignTokensBeforeBaseToken.push(t)
376+
} else {
377+
alignTokens.push(t)
378+
}
359379
}
360380
}
361-
alignTokens.push(elementTokens.firstToken)
362381

363-
// Save the last token to find tokens between the next token.
382+
if (baseToken == null) {
383+
baseToken = elementTokens.firstToken
384+
} else {
385+
alignTokens.push(elementTokens.firstToken)
386+
}
387+
388+
// Save the last token to find tokens between this node and the next node.
364389
lastToken = elementTokens.lastToken
365390
}
366391

367-
// Check trailing commas.
392+
// Check trailing commas and comments.
368393
if (rightToken != null && lastToken != null) {
369394
t = lastToken
370-
while ((t = tokenStore.getTokenAfter(t)) != null && t.range[1] <= rightToken.range[0]) {
371-
alignTokens.push(t)
395+
while (
396+
(t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null &&
397+
t.range[1] <= rightToken.range[0]
398+
) {
399+
if (baseToken == null) {
400+
alignTokensBeforeBaseToken.push(t)
401+
} else {
402+
alignTokens.push(t)
403+
}
372404
}
373405
}
374406

375407
// Set offsets.
376-
const baseToken = alignTokens.shift()
408+
if (leftToken != null) {
409+
setOffset(alignTokensBeforeBaseToken, offset, leftToken)
410+
}
377411
if (baseToken != null) {
378412
// Set offset to the first token.
379413
if (leftToken != null) {
@@ -385,7 +419,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
385419
setBaseline(baseToken)
386420
}
387421

388-
if (alignVertically === false) {
422+
if (alignVertically === false && leftToken != null) {
389423
// Align tokens relatively to the left token.
390424
setOffset(alignTokens, offset, leftToken)
391425
} else {
@@ -633,10 +667,10 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
633667
* Validate the given token with the pre-calculated expected indentation.
634668
* @param {Token} token The token to validate.
635669
* @param {number} expectedIndent The expected indentation.
636-
* @param {number|undefined} optionalExpectedIndent The optional expected indentation.
670+
* @param {number[]|undefined} optionalExpectedIndents The optional expected indentation.
637671
* @returns {void}
638672
*/
639-
function validateCore (token, expectedIndent, optionalExpectedIndent) {
673+
function validateCore (token, expectedIndent, optionalExpectedIndents) {
640674
const line = token.loc.start.line
641675
const indentText = getIndentText(token)
642676

@@ -668,7 +702,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
668702
}
669703
}
670704

671-
if (actualIndent !== expectedIndent && (optionalExpectedIndent === undefined || actualIndent !== optionalExpectedIndent)) {
705+
if (actualIndent !== expectedIndent && (optionalExpectedIndents == null || !optionalExpectedIndents.includes(actualIndent))) {
672706
context.report({
673707
loc: {
674708
start: { line, column: 0 },
@@ -692,7 +726,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
692726
* @param {Token|null} nextToken The next token of comments.
693727
* @param {number|undefined} nextExpectedIndent The expected indent of the next token.
694728
* @param {number|undefined} lastExpectedIndent The expected indent of the last token.
695-
* @returns {{primary:number|undefined,secondary:number|undefined}}
729+
* @returns {number[]}
696730
*/
697731
function getCommentExpectedIndents (nextToken, nextExpectedIndent, lastExpectedIndent) {
698732
if (typeof lastExpectedIndent === 'number' && isClosingToken(nextToken)) {
@@ -701,26 +735,23 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
701735
// <div>
702736
// <!-- comment -->
703737
// </div>
704-
return {
705-
primary: nextExpectedIndent + options.indentSize,
706-
secondary: undefined
707-
}
738+
return [nextExpectedIndent + options.indentSize, nextExpectedIndent]
708739
}
709740

710741
// For last comment. E.g.,
711742
// <div>
712743
// <div></div>
713744
// <!-- comment -->
714745
// </div>
715-
return { primary: lastExpectedIndent, secondary: nextExpectedIndent }
746+
return [lastExpectedIndent, nextExpectedIndent]
716747
}
717748

718749
// Adjust to next normally. E.g.,
719750
// <div>
720751
// <!-- comment -->
721752
// <div></div>
722753
// </div>
723-
return { primary: nextExpectedIndent, secondary: undefined }
754+
return [nextExpectedIndent]
724755
}
725756

726757
@@ -786,11 +817,17 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
786817
// It allows the same indent level with the previous line.
787818
const lastOffsetInfo = offsets.get(lastToken)
788819
const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo.expectedIndent
789-
const commentExpectedIndents = getCommentExpectedIndents(firstToken, expectedIndent, lastExpectedIndent)
820+
const commentOptionalExpectedIndents = getCommentExpectedIndents(firstToken, expectedIndent, lastExpectedIndent)
790821

791822
// Validate.
792823
for (const comment of comments) {
793-
validateCore(comment, commentExpectedIndents.primary, commentExpectedIndents.secondary)
824+
const commentExpectedIndents = getExpectedIndents([comment])
825+
const commentExpectedIndent =
826+
commentExpectedIndents
827+
? commentExpectedIndents.expectedIndent
828+
: commentOptionalExpectedIndents[0]
829+
830+
validateCore(comment, commentExpectedIndent, commentOptionalExpectedIndents)
794831
}
795832
validateCore(firstToken, expectedIndent)
796833
}
@@ -815,14 +852,13 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
815852
},
816853

817854
VElement (node) {
818-
const startTagToken = tokenStore.getFirstToken(node)
819-
const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag)
820-
821855
if (node.name !== 'pre') {
822-
const childTokens = node.children.map(n => tokenStore.getFirstToken(n))
823-
setOffset(childTokens, 1, startTagToken)
856+
processNodeList(node.children.filter(isNotEmptyTextNode), node.startTag, node.endTag, 1)
857+
} else {
858+
const startTagToken = tokenStore.getFirstToken(node)
859+
const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag)
860+
setOffset(endTagToken, 0, startTagToken)
824861
}
825-
setOffset(endTagToken, 0, startTagToken)
826862
},
827863

828864
VEndTag (node) {
@@ -1081,7 +1117,6 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
10811117

10821118
setOffset(leftParenToken, 1, forToken)
10831119
processNodeList([node.init, node.test, node.update], leftParenToken, rightParenToken, 1)
1084-
setOffset(rightParenToken, 0, leftParenToken)
10851120
processMaybeBlock(node.body, forToken)
10861121
},
10871122

@@ -1509,7 +1544,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
15091544
let lastValidatedToken = null
15101545

15111546
// Validate indentation of tokens.
1512-
for (const token of tokenStore.getTokens(node, { includeComments: true, filter: isNotWhitespace })) {
1547+
for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) {
15131548
if (tokensOnSameLine.length === 0 || tokensOnSameLine[0].loc.start.line === token.loc.start.line) {
15141549
// This is on the same line (or the first token).
15151550
tokensOnSameLine.push(token)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!--{}-->
2+
<template>
3+
<div>
4+
<input type="text"
5+
value="foo">
6+
<!-- comment -->
7+
</div>
8+
</template>

0 commit comments

Comments
 (0)
0