namespace ts . formatting { const standardScanner = createScanner ( ScriptTarget . Latest , false , LanguageVariant . Standard ) ; const jsxScanner = createScanner ( ScriptTarget . Latest , false , LanguageVariant . JSX ) ; export interface FormattingScanner { advance ( ) : void ; getStartPos ( ) : number ; isOnToken ( ) : boolean ; isOnEOF ( ) : boolean ; readTokenInfo ( n ) : TokenInfo ; readEOFTokenRange ( ) : TextRangeWithKind ; getCurrentLeadingTrivia ( ) : TextRangeWithKind [ ] | undefined ; lastTrailingTriviaWasNewLine ( ) : boolean ; skipToEndOf ( node ) : void ; skipToStartOf ( node ) : void ; } const enum ScanAction { Scan , RescanGreaterThanToken , RescanSlashToken , RescanTemplateToken , RescanJsxIdentifier , RescanJsxText , RescanJsxAttributeValue } export function getFormattingScanner < T > ( text , languageVariant , startPos , endPos , cb ) { const scanner = languageVariant === LanguageVariant . JSX ? jsxScanner : standardScanner ; scanner . setText ( text ) ; scanner . setTextPos ( startPos ) ; let wasNewLine = true ; let leadingTrivia ; let trailingTrivia ; let savedPos ; let lastScanAction ; let lastTokenInfo ; const res = cb ( { advance , readTokenInfo , readEOFTokenRange , isOnToken , isOnEOF , getCurrentLeadingTrivia : ( ) => leadingTrivia , lastTrailingTriviaWasNewLine : ( ) => wasNewLine , skipToEndOf , skipToStartOf , getStartPos : ( ) => lastTokenInfo ?. token . pos ?? scanner . getTokenPos ( ) , } ) ; lastTokenInfo = undefined ; scanner . setText ( undefined ) ; return res ; function advance ( ) { lastTokenInfo = undefined ; const isStarted = scanner . getStartPos ( ) !== startPos ; if ( isStarted ) { wasNewLine = ! ! trailingTrivia && last ( trailingTrivia ) . kind === SyntaxKind . NewLineTrivia ; } else { scanner . scan ( ) ; } leadingTrivia = undefined ; trailingTrivia = undefined ; let pos = scanner . getStartPos ( ) ; while ( pos < endPos ) { const t = scanner . getToken ( ) ; if ( ! isTrivia ( t ) ) { break ; } scanner . scan ( ) ; const item = { pos , end : scanner . getStartPos ( ) , kind : t } ; pos = scanner . getStartPos ( ) ; leadingTrivia = append ( leadingTrivia , item ) ; } savedPos = scanner . getStartPos ( ) ; } function shouldRescanGreaterThanToken ( node ) { switch ( node . kind ) { case SyntaxKind . GreaterThanEqualsToken : case SyntaxKind . GreaterThanGreaterThanEqualsToken : case SyntaxKind . GreaterThanGreaterThanGreaterThanEqualsToken : case SyntaxKind . GreaterThanGreaterThanGreaterThanToken : case SyntaxKind . GreaterThanGreaterThanToken : return true ; } return false ; } function shouldRescanJsxIdentifier ( node ) { if ( node . parent ) { switch ( node . parent . kind ) { case SyntaxKind . JsxAttribute : case SyntaxKind . JsxOpeningElement : case SyntaxKind . JsxClosingElement : case SyntaxKind . JsxSelfClosingElement : return isKeyword ( node . kind ) || node . kind === SyntaxKind . Identifier ; } } return false ; } function shouldRescanJsxText ( node ) { return isJsxText ( node ) ; } function shouldRescanSlashToken ( container ) { return container . kind === SyntaxKind . RegularExpressionLiteral ; } function shouldRescanTemplateToken ( container ) { return container . kind === SyntaxKind . TemplateMiddle || container . kind === SyntaxKind . TemplateTail ; } function shouldRescanJsxAttributeValue ( node ) { return node . parent && isJsxAttribute ( node . parent ) && node . parent . initializer === node ; } function startsWithSlashToken ( t ) { return t === SyntaxKind . SlashToken || t === SyntaxKind . SlashEqualsToken ; } function readTokenInfo ( n ) { Debug . assert ( isOnToken ( ) ) ; const expectedScanAction = shouldRescanGreaterThanToken ( n ) ? ScanAction . RescanGreaterThanToken : shouldRescanSlashToken ( n ) ? ScanAction . RescanSlashToken : shouldRescanTemplateToken ( n ) ? ScanAction . RescanTemplateToken : shouldRescanJsxIdentifier ( n ) ? ScanAction . RescanJsxIdentifier : shouldRescanJsxText ( n ) ? ScanAction . RescanJsxText : shouldRescanJsxAttributeValue ( n ) ? ScanAction . RescanJsxAttributeValue : ScanAction . Scan ; if ( lastTokenInfo && expectedScanAction === lastScanAction ) { return fixTokenKind ( lastTokenInfo , n ) ; } if ( scanner . getStartPos ( ) !== savedPos ) { Debug . assert ( lastTokenInfo !== undefined ) ; scanner . setTextPos ( savedPos ) ; scanner . scan ( ) ; } let currentToken = getNextToken ( n , expectedScanAction ) ; const token = createTextRangeWithKind ( scanner . getStartPos ( ) , scanner . getTextPos ( ) , currentToken ) ; if ( trailingTrivia ) { trailingTrivia = undefined ; } while ( scanner . getStartPos ( ) < endPos ) { currentToken = scanner . scan ( ) ; if ( ! isTrivia ( currentToken ) ) { break ; } const trivia = createTextRangeWithKind ( scanner . getStartPos ( ) , scanner . getTextPos ( ) , currentToken ) ; if ( ! trailingTrivia ) { trailingTrivia = [ ] ; } trailingTrivia . push ( trivia ) ; if ( currentToken === SyntaxKind . NewLineTrivia ) { scanner . scan ( ) ; break ; } } lastTokenInfo = { leadingTrivia , trailingTrivia , token } ; return fixTokenKind ( lastTokenInfo , n ) ; } function getNextToken ( n , expectedScanAction ) { const token = scanner . getToken ( ) ; lastScanAction = ScanAction . Scan ; switch ( expectedScanAction ) { case ScanAction . RescanGreaterThanToken : if ( token === SyntaxKind . GreaterThanToken ) { lastScanAction = ScanAction . RescanGreaterThanToken ; const newToken = scanner . reScanGreaterToken ( ) ; Debug . assert ( n . kind === newToken ) ; return newToken ; } break ; case ScanAction . RescanSlashToken : if ( startsWithSlashToken ( token ) ) { lastScanAction = ScanAction . RescanSlashToken ; const newToken = scanner . reScanSlashToken ( ) ; Debug . assert ( n . kind === newToken ) ; return newToken ; } break ; case ScanAction . RescanTemplateToken : if ( token === SyntaxKind . CloseBraceToken ) { lastScanAction = ScanAction . RescanTemplateToken ; return scanner . reScanTemplateToken ( false ) ; } break ; case ScanAction . RescanJsxIdentifier : lastScanAction = ScanAction . RescanJsxIdentifier ; return scanner . scanJsxIdentifier ( ) ; case ScanAction . RescanJsxText : lastScanAction = ScanAction . RescanJsxText ; return scanner . reScanJsxToken ( false ) ; case ScanAction . RescanJsxAttributeValue : lastScanAction = ScanAction . RescanJsxAttributeValue ; return scanner . reScanJsxAttributeValue ( ) ; case ScanAction . Scan : break ; default : Debug . assertNever ( expectedScanAction ) ; } return token ; } function readEOFTokenRange ( ) { Debug . assert ( isOnEOF ( ) ) ; return createTextRangeWithKind ( scanner . getStartPos ( ) , scanner . getTextPos ( ) , SyntaxKind . EndOfFileToken ) ; } function isOnToken ( ) { const current = lastTokenInfo ? lastTokenInfo . token . kind : scanner . getToken ( ) ; return current !== SyntaxKind . EndOfFileToken && ! isTrivia ( current ) ; } function isOnEOF ( ) { const current = lastTokenInfo ? lastTokenInfo . token . kind : scanner . getToken ( ) ; return current === SyntaxKind . EndOfFileToken ; } function fixTokenKind ( tokenInfo , container ) { if ( isToken ( container ) && tokenInfo . token . kind !== container . kind ) { tokenInfo . token . kind = container . kind ; } return tokenInfo ; } function skipToEndOf ( node ) { scanner . setTextPos ( node . end ) ; savedPos = scanner . getStartPos ( ) ; lastScanAction = undefined ; lastTokenInfo = undefined ; wasNewLine = false ; leadingTrivia = undefined ; trailingTrivia = undefined ; } function skipToStartOf ( node ) { scanner . setTextPos ( node . pos ) ; savedPos = scanner . getStartPos ( ) ; lastScanAction = undefined ; lastTokenInfo = undefined ; wasNewLine = false ; leadingTrivia = undefined ; trailingTrivia = undefined ; } } }