@@ -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.
@@ -295,7 +305,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
295
305
* @param {Token } token The token to set.
296
306
* @returns {void }
297
307
*/
298
- function setBaseline ( token , hardTabAdditional ) {
308
+ function setBaseline ( token ) {
299
309
const offsetInfo = offsets . get ( token )
300
310
if ( offsetInfo != null ) {
301
311
offsetInfo . baseline = true
@@ -353,17 +363,21 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
353
363
* The first node is offsetted from the given left token.
354
364
* Rest nodes are adjusted to the first node.
355
365
* @param {Node[] } nodeList The node to process.
356
- * @param {Node|null } leftToken The left parenthesis token.
357
- * @param {Node|null } rightToken The right parenthesis token.
366
+ * @param {Node|Token| null } left The left parenthesis token.
367
+ * @param {Node|Token| null } right The right parenthesis token.
358
368
* @param {number } offset The offset to set.
359
- * @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.
369
+ * @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.
360
370
* @returns {void }
361
371
*/
362
- function processNodeList ( nodeList , leftToken , rightToken , offset , alignVertically ) {
372
+ function processNodeList ( nodeList , left , right , offset , alignVertically ) {
363
373
let t
374
+ const leftToken = ( left && tokenStore . getFirstToken ( left ) ) || left
375
+ const rightToken = ( right && tokenStore . getFirstToken ( right ) ) || right
364
376
365
377
if ( nodeList . length >= 1 ) {
366
- let lastToken = leftToken
378
+ let baseToken = null
379
+ let lastToken = left
380
+ const alignTokensBeforeBaseToken = [ ]
367
381
const alignTokens = [ ]
368
382
369
383
for ( let i = 0 ; i < nodeList . length ; ++ i ) {
@@ -374,30 +388,50 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
374
388
}
375
389
const elementTokens = getFirstAndLastTokens ( node , lastToken != null ? lastToken . range [ 1 ] : 0 )
376
390
377
- // Collect related tokens.
378
- // Commas between this and the previous, and the first token of this node.
391
+ // Collect comma/comment tokens between the last token of the previous node and the first token of this node.
379
392
if ( lastToken != null ) {
380
393
t = lastToken
381
- while ( ( t = tokenStore . getTokenAfter ( t ) ) != null && t . range [ 1 ] <= elementTokens . firstToken . range [ 0 ] ) {
382
- alignTokens . push ( t )
394
+ while (
395
+ ( t = tokenStore . getTokenAfter ( t , ITERATION_OPTS ) ) != null &&
396
+ t . range [ 1 ] <= elementTokens . firstToken . range [ 0 ]
397
+ ) {
398
+ if ( baseToken == null ) {
399
+ alignTokensBeforeBaseToken . push ( t )
400
+ } else {
401
+ alignTokens . push ( t )
402
+ }
383
403
}
384
404
}
385
- alignTokens . push ( elementTokens . firstToken )
386
405
387
- // Save the last token to find tokens between the next token.
406
+ if ( baseToken == null ) {
407
+ baseToken = elementTokens . firstToken
408
+ } else {
409
+ alignTokens . push ( elementTokens . firstToken )
410
+ }
411
+
412
+ // Save the last token to find tokens between this node and the next node.
388
413
lastToken = elementTokens . lastToken
389
414
}
390
415
391
- // Check trailing commas.
416
+ // Check trailing commas and comments .
392
417
if ( rightToken != null && lastToken != null ) {
393
418
t = lastToken
394
- while ( ( t = tokenStore . getTokenAfter ( t ) ) != null && t . range [ 1 ] <= rightToken . range [ 0 ] ) {
395
- alignTokens . push ( t )
419
+ while (
420
+ ( t = tokenStore . getTokenAfter ( t , ITERATION_OPTS ) ) != null &&
421
+ t . range [ 1 ] <= rightToken . range [ 0 ]
422
+ ) {
423
+ if ( baseToken == null ) {
424
+ alignTokensBeforeBaseToken . push ( t )
425
+ } else {
426
+ alignTokens . push ( t )
427
+ }
396
428
}
397
429
}
398
430
399
431
// Set offsets.
400
- const baseToken = alignTokens . shift ( )
432
+ if ( leftToken != null ) {
433
+ setOffset ( alignTokensBeforeBaseToken , offset , leftToken )
434
+ }
401
435
if ( baseToken != null ) {
402
436
// Set offset to the first token.
403
437
if ( leftToken != null ) {
@@ -409,7 +443,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
409
443
setBaseline ( baseToken )
410
444
}
411
445
412
- if ( alignVertically === false ) {
446
+ if ( alignVertically === false && leftToken != null ) {
413
447
// Align tokens relatively to the left token.
414
448
setOffset ( alignTokens , offset , leftToken )
415
449
} else {
@@ -657,10 +691,10 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
657
691
* Validate the given token with the pre-calculated expected indentation.
658
692
* @param {Token } token The token to validate.
659
693
* @param {number } expectedIndent The expected indentation.
660
- * @param {number|undefined } optionalExpectedIndent The optional expected indentation.
694
+ * @param {number[] |undefined } optionalExpectedIndents The optional expected indentation.
661
695
* @returns {void }
662
696
*/
663
- function validateCore ( token , expectedIndent , optionalExpectedIndent ) {
697
+ function validateCore ( token , expectedIndent , optionalExpectedIndents ) {
664
698
const line = token . loc . start . line
665
699
const indentText = getIndentText ( token )
666
700
@@ -692,7 +726,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
692
726
}
693
727
}
694
728
695
- if ( actualIndent !== expectedIndent && ( optionalExpectedIndent === undefined || actualIndent !== optionalExpectedIndent ) ) {
729
+ if ( actualIndent !== expectedIndent && ( optionalExpectedIndents == null || ! optionalExpectedIndents . includes ( actualIndent ) ) ) {
696
730
context . report ( {
697
731
loc : {
698
732
start : { line, column : 0 } ,
@@ -716,7 +750,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
716
750
* @param {Token|null } nextToken The next token of comments.
717
751
* @param {number|undefined } nextExpectedIndent The expected indent of the next token.
718
752
* @param {number|undefined } lastExpectedIndent The expected indent of the last token.
719
- * @returns {{primary: number|undefined,secondary:number|undefined} }
753
+ * @returns {number[] }
720
754
*/
721
755
function getCommentExpectedIndents ( nextToken , nextExpectedIndent , lastExpectedIndent ) {
722
756
if ( typeof lastExpectedIndent === 'number' && isClosingToken ( nextToken ) ) {
@@ -725,26 +759,23 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
725
759
// <div>
726
760
// <!-- comment -->
727
761
// </div>
728
- return {
729
- primary : nextExpectedIndent + options . indentSize ,
730
- secondary : undefined
731
- }
762
+ return [ nextExpectedIndent + options . indentSize , nextExpectedIndent ]
732
763
}
733
764
734
765
// For last comment. E.g.,
735
766
// <div>
736
767
// <div></div>
737
768
// <!-- comment -->
738
769
// </div>
739
- return { primary : lastExpectedIndent , secondary : nextExpectedIndent }
770
+ return [ lastExpectedIndent , nextExpectedIndent ]
740
771
}
741
772
742
773
// Adjust to next normally. E.g.,
743
774
// <div>
744
775
// <!-- comment -->
745
776
// <div></div>
746
777
// </div>
747
- return { primary : nextExpectedIndent , secondary : undefined }
778
+ return [ nextExpectedIndent ]
748
779
}
749
780
750
781
/**
@@ -815,11 +846,17 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
815
846
// It allows the same indent level with the previous line.
816
847
const lastOffsetInfo = offsets . get ( lastToken )
817
848
const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo . expectedIndent
818
- const commentExpectedIndents = getCommentExpectedIndents ( firstToken , expectedIndent , lastExpectedIndent )
849
+ const commentOptionalExpectedIndents = getCommentExpectedIndents ( firstToken , expectedIndent , lastExpectedIndent )
819
850
820
851
// Validate.
821
852
for ( const comment of comments ) {
822
- validateCore ( comment , commentExpectedIndents . primary , commentExpectedIndents . secondary )
853
+ const commentExpectedIndents = getExpectedIndents ( [ comment ] )
854
+ const commentExpectedIndent =
855
+ commentExpectedIndents
856
+ ? commentExpectedIndents . expectedIndent
857
+ : commentOptionalExpectedIndents [ 0 ]
858
+
859
+ validateCore ( comment , commentExpectedIndent , commentOptionalExpectedIndents )
823
860
}
824
861
validateCore ( firstToken , expectedIndent )
825
862
}
@@ -844,16 +881,14 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
844
881
} ,
845
882
846
883
VElement ( node ) {
847
- const startTagToken = tokenStore . getFirstToken ( node )
848
- const endTagToken = node . endTag && tokenStore . getFirstToken ( node . endTag )
849
-
850
884
if ( node . name !== 'pre' ) {
851
- const childTokens = node . children . map ( n => tokenStore . getFirstToken ( n ) )
852
- setOffset ( childTokens , 1 , startTagToken )
885
+ processNodeList ( node . children . filter ( isNotEmptyTextNode ) , node . startTag , node . endTag , 1 )
853
886
} else {
887
+ const startTagToken = tokenStore . getFirstToken ( node )
888
+ const endTagToken = node . endTag && tokenStore . getFirstToken ( node . endTag )
889
+ setOffset ( endTagToken , 0 , startTagToken )
854
890
setPreformattedTokens ( node )
855
891
}
856
- setOffset ( endTagToken , 0 , startTagToken )
857
892
} ,
858
893
859
894
VEndTag ( node ) {
@@ -1116,7 +1151,6 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
1116
1151
1117
1152
setOffset ( leftParenToken , 1 , forToken )
1118
1153
processNodeList ( [ node . init , node . test , node . update ] , leftParenToken , rightParenToken , 1 )
1119
- setOffset ( rightParenToken , 0 , leftParenToken )
1120
1154
processMaybeBlock ( node . body , forToken )
1121
1155
} ,
1122
1156
@@ -1544,7 +1578,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
1544
1578
let lastValidatedToken = null
1545
1579
1546
1580
// Validate indentation of tokens.
1547
- for ( const token of tokenStore . getTokens ( node , { includeComments : true , filter : isNotWhitespace } ) ) {
1581
+ for ( const token of tokenStore . getTokens ( node , ITERATION_OPTS ) ) {
1548
1582
if ( tokensOnSameLine . length === 0 || tokensOnSameLine [ 0 ] . loc . start . line === token . loc . start . line ) {
1549
1583
// This is on the same line (or the first token).
1550
1584
tokensOnSameLine . push ( token )
0 commit comments