@@ -18,6 +18,7 @@ const KNOWN_NODES = new Set(['ArrayExpression', 'ArrayPattern', 'ArrowFunctionEx
18
18
const LT_CHAR = / [ \r \n \u2028 \u2029 ] /
19
19
const LINES = / [ ^ \r \n \u2028 \u2029 ] + (?: $ | \r \n | [ \r \n \u2028 \u2029 ] ) / g
20
20
const BLOCK_COMMENT_PREFIX = / ^ \s * \* /
21
+ const ITERATION_OPTS = Object . freeze ( { includeComments : true , filter : isNotWhitespace } )
21
22
22
23
/**
23
24
* Normalize options.
@@ -194,6 +195,15 @@ function isNotComment (token) {
194
195
return token != null && token . type !== 'Block' && token . type !== 'Line' && token . type !== 'Shebang' && ! token . type . endsWith ( 'Comment' )
195
196
}
196
197
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
+
197
207
/**
198
208
* Get the last element.
199
209
* @param {Array } xs The array to get the last element.
@@ -294,7 +304,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
294
304
* @param {Token } token The token to set.
295
305
* @returns {void }
296
306
*/
297
- function setBaseline ( token , hardTabAdditional ) {
307
+ function setBaseline ( token ) {
298
308
const offsetInfo = offsets . get ( token )
299
309
if ( offsetInfo != null ) {
300
310
offsetInfo . baseline = true
@@ -332,14 +342,18 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
332
342
* @param {Node|null } leftToken The left parenthesis token.
333
343
* @param {Node|null } rightToken The right parenthesis token.
334
344
* @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.
336
346
* @returns {void }
337
347
*/
338
- function processNodeList ( nodeList , leftToken , rightToken , offset , alignVertically ) {
348
+ function processNodeList ( nodeList , left , right , offset , alignVertically ) {
339
349
let t
350
+ const leftToken = ( left && tokenStore . getFirstToken ( left ) ) || left
351
+ const rightToken = ( right && tokenStore . getFirstToken ( right ) ) || right
340
352
341
353
if ( nodeList . length >= 1 ) {
342
- let lastToken = leftToken
354
+ let baseToken = null
355
+ let lastToken = left
356
+ const alignTokensBeforeBaseToken = [ ]
343
357
const alignTokens = [ ]
344
358
345
359
for ( let i = 0 ; i < nodeList . length ; ++ i ) {
@@ -350,30 +364,50 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
350
364
}
351
365
const elementTokens = getFirstAndLastTokens ( node , lastToken != null ? lastToken . range [ 1 ] : 0 )
352
366
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.
355
368
if ( lastToken != null ) {
356
369
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
+ }
359
379
}
360
380
}
361
- alignTokens . push ( elementTokens . firstToken )
362
381
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.
364
389
lastToken = elementTokens . lastToken
365
390
}
366
391
367
- // Check trailing commas.
392
+ // Check trailing commas and comments .
368
393
if ( rightToken != null && lastToken != null ) {
369
394
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
+ }
372
404
}
373
405
}
374
406
375
407
// Set offsets.
376
- const baseToken = alignTokens . shift ( )
408
+ if ( leftToken != null ) {
409
+ setOffset ( alignTokensBeforeBaseToken , offset , leftToken )
410
+ }
377
411
if ( baseToken != null ) {
378
412
// Set offset to the first token.
379
413
if ( leftToken != null ) {
@@ -385,7 +419,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
385
419
setBaseline ( baseToken )
386
420
}
387
421
388
- if ( alignVertically === false ) {
422
+ if ( alignVertically === false && leftToken != null ) {
389
423
// Align tokens relatively to the left token.
390
424
setOffset ( alignTokens , offset , leftToken )
391
425
} else {
@@ -633,10 +667,10 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
633
667
* Validate the given token with the pre-calculated expected indentation.
634
668
* @param {Token } token The token to validate.
635
669
* @param {number } expectedIndent The expected indentation.
636
- * @param {number|undefined } optionalExpectedIndent The optional expected indentation.
670
+ * @param {number[] |undefined } optionalExpectedIndents The optional expected indentation.
637
671
* @returns {void }
638
672
*/
639
- function validateCore ( token , expectedIndent , optionalExpectedIndent ) {
673
+ function validateCore ( token , expectedIndent , optionalExpectedIndents ) {
640
674
const line = token . loc . start . line
641
675
const indentText = getIndentText ( token )
642
676
@@ -668,7 +702,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
668
702
}
669
703
}
670
704
671
- if ( actualIndent !== expectedIndent && ( optionalExpectedIndent === undefined || actualIndent !== optionalExpectedIndent ) ) {
705
+ if ( actualIndent !== expectedIndent && ( optionalExpectedIndents == null || ! optionalExpectedIndents . includes ( actualIndent ) ) ) {
672
706
context . report ( {
673
707
loc : {
674
708
start : { line, column : 0 } ,
@@ -692,7 +726,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
692
726
* @param {Token|null } nextToken The next token of comments.
693
727
* @param {number|undefined } nextExpectedIndent The expected indent of the next token.
694
728
* @param {number|undefined } lastExpectedIndent The expected indent of the last token.
695
- * @returns {{primary: number|undefined,secondary:number|undefined} }
729
+ * @returns {number[] }
696
730
*/
697
731
function getCommentExpectedIndents ( nextToken , nextExpectedIndent , lastExpectedIndent ) {
698
732
if ( typeof lastExpectedIndent === 'number' && isClosingToken ( nextToken ) ) {
@@ -701,26 +735,23 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
701
735
// <div>
702
736
// <!-- comment -->
703
737
// </div>
704
- return {
705
- primary : nextExpectedIndent + options . indentSize ,
706
- secondary : undefined
707
- }
738
+ return [ nextExpectedIndent + options . indentSize , nextExpectedIndent ]
708
739
}
709
740
710
741
// For last comment. E.g.,
711
742
// <div>
712
743
// <div></div>
713
744
// <!-- comment -->
714
745
// </div>
715
- return { primary : lastExpectedIndent , secondary : nextExpectedIndent }
746
+ return [ lastExpectedIndent , nextExpectedIndent ]
716
747
}
717
748
718
749
// Adjust to next normally. E.g.,
719
750
// <div>
720
751
// <!-- comment -->
721
752
// <div></div>
722
753
// </div>
723
- return { primary : nextExpectedIndent , secondary : undefined }
754
+ return [ nextExpectedIndent ]
724
755
}
725
756
726
757
/**
@@ -786,11 +817,17 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
786
817
// It allows the same indent level with the previous line.
787
818
const lastOffsetInfo = offsets . get ( lastToken )
788
819
const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo . expectedIndent
789
- const commentExpectedIndents = getCommentExpectedIndents ( firstToken , expectedIndent , lastExpectedIndent )
820
+ const commentOptionalExpectedIndents = getCommentExpectedIndents ( firstToken , expectedIndent , lastExpectedIndent )
790
821
791
822
// Validate.
792
823
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 )
794
831
}
795
832
validateCore ( firstToken , expectedIndent )
796
833
}
@@ -815,14 +852,13 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
815
852
} ,
816
853
817
854
VElement ( node ) {
818
- const startTagToken = tokenStore . getFirstToken ( node )
819
- const endTagToken = node . endTag && tokenStore . getFirstToken ( node . endTag )
820
-
821
855
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 )
824
861
}
825
- setOffset ( endTagToken , 0 , startTagToken )
826
862
} ,
827
863
828
864
VEndTag ( node ) {
@@ -1081,7 +1117,6 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
1081
1117
1082
1118
setOffset ( leftParenToken , 1 , forToken )
1083
1119
processNodeList ( [ node . init , node . test , node . update ] , leftParenToken , rightParenToken , 1 )
1084
- setOffset ( rightParenToken , 0 , leftParenToken )
1085
1120
processMaybeBlock ( node . body , forToken )
1086
1121
} ,
1087
1122
@@ -1509,7 +1544,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
1509
1544
let lastValidatedToken = null
1510
1545
1511
1546
// 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 ) ) {
1513
1548
if ( tokensOnSameLine . length === 0 || tokensOnSameLine [ 0 ] . loc . start . line === token . loc . start . line ) {
1514
1549
// This is on the same line (or the first token).
1515
1550
tokensOnSameLine . push ( token )
0 commit comments