namespace ts { export interface DocumentHighlights { fileName : string ; highlightSpans : HighlightSpan [ ] ; } export namespace DocumentHighlights { export function getDocumentHighlights ( program , cancellationToken , sourceFile , position , sourceFilesToSearch ) { const node = getTouchingPropertyName ( sourceFile , position ) ; if ( node . parent && ( isJsxOpeningElement ( node . parent ) && node . parent . tagName === node || isJsxClosingElement ( node . parent ) ) ) { const { openingElement , closingElement } = node . parent . parent ; const highlightSpans = [ openingElement , closingElement ] . map ( ( { tagName } ) => getHighlightSpanForNode ( tagName , sourceFile ) ) ; return [ { fileName : sourceFile . fileName , highlightSpans } ] ; } return getSemanticDocumentHighlights ( position , node , program , cancellationToken , sourceFilesToSearch ) || getSyntacticDocumentHighlights ( node , sourceFile ) ; } function getHighlightSpanForNode ( node , sourceFile ) { return { fileName : sourceFile . fileName , textSpan : createTextSpanFromNode ( node , sourceFile ) , kind : HighlightSpanKind . none } ; } function getSemanticDocumentHighlights ( position , node , program , cancellationToken , sourceFilesToSearch ) { const sourceFilesSet = new Set ( sourceFilesToSearch . map ( f => f . fileName ) ) ; const referenceEntries = FindAllReferences . getReferenceEntriesForNode ( position , node , program , sourceFilesToSearch , cancellationToken , undefined , sourceFilesSet ) ; if ( ! referenceEntries ) return undefined ; const map = arrayToMultiMap ( referenceEntries . map ( FindAllReferences . toHighlightSpan ) , e => e . fileName , e => e . span ) ; const getCanonicalFileName = createGetCanonicalFileName ( program . useCaseSensitiveFileNames ( ) ) ; return mapDefined ( arrayFrom ( map . entries ( ) ) , ( [ fileName , highlightSpans ] ) => { if ( ! sourceFilesSet . has ( fileName ) ) { if ( ! program . redirectTargetsMap . has ( toPath ( fileName , program . getCurrentDirectory ( ) , getCanonicalFileName ) ) ) { return undefined ; } const redirectTarget = program . getSourceFile ( fileName ) ; const redirect = find ( sourceFilesToSearch , f => ! ! f . redirectInfo && f . redirectInfo . redirectTarget === redirectTarget ) ! ; fileName = redirect . fileName ; Debug . assert ( sourceFilesSet . has ( fileName ) ) ; } return { fileName , highlightSpans } ; } ) ; } function getSyntacticDocumentHighlights ( node , sourceFile ) { const highlightSpans = getHighlightSpans ( node , sourceFile ) ; return highlightSpans && [ { fileName : sourceFile . fileName , highlightSpans } ] ; } function getHighlightSpans ( node , sourceFile ) { switch ( node . kind ) { case SyntaxKind . IfKeyword : case SyntaxKind . ElseKeyword : return isIfStatement ( node . parent ) ? getIfElseOccurrences ( node . parent , sourceFile ) : undefined ; case SyntaxKind . ReturnKeyword : return useParent ( node . parent , isReturnStatement , getReturnOccurrences ) ; case SyntaxKind . ThrowKeyword : return useParent ( node . parent , isThrowStatement , getThrowOccurrences ) ; case SyntaxKind . TryKeyword : case SyntaxKind . CatchKeyword : case SyntaxKind . FinallyKeyword : const tryStatement = node . kind === SyntaxKind . CatchKeyword ? node . parent . parent : node . parent ; return useParent ( tryStatement , isTryStatement , getTryCatchFinallyOccurrences ) ; case SyntaxKind . SwitchKeyword : return useParent ( node . parent , isSwitchStatement , getSwitchCaseDefaultOccurrences ) ; case SyntaxKind . CaseKeyword : case SyntaxKind . DefaultKeyword : { if ( isDefaultClause ( node . parent ) || isCaseClause ( node . parent ) ) { return useParent ( node . parent . parent . parent , isSwitchStatement , getSwitchCaseDefaultOccurrences ) ; } return undefined ; } case SyntaxKind . BreakKeyword : case SyntaxKind . ContinueKeyword : return useParent ( node . parent , isBreakOrContinueStatement , getBreakOrContinueStatementOccurrences ) ; case SyntaxKind . ForKeyword : case SyntaxKind . WhileKeyword : case SyntaxKind . DoKeyword : return useParent ( node . parent , ( n ) : n is IterationStatement => isIterationStatement ( n , true ) , getLoopBreakContinueOccurrences ) ; case SyntaxKind . ConstructorKeyword : return getFromAllDeclarations ( isConstructorDeclaration , [ SyntaxKind . ConstructorKeyword ] ) ; case SyntaxKind . GetKeyword : case SyntaxKind . SetKeyword : return getFromAllDeclarations ( isAccessor , [ SyntaxKind . GetKeyword , SyntaxKind . SetKeyword ] ) ; case SyntaxKind . AwaitKeyword : return useParent ( node . parent , isAwaitExpression , getAsyncAndAwaitOccurrences ) ; case SyntaxKind . AsyncKeyword : return highlightSpans ( getAsyncAndAwaitOccurrences ( node ) ) ; case SyntaxKind . YieldKeyword : return highlightSpans ( getYieldOccurrences ( node ) ) ; default : return isModifierKind ( node . kind ) && ( isDeclaration ( node . parent ) || isVariableStatement ( node . parent ) ) ? highlightSpans ( getModifierOccurrences ( node . kind , node . parent ) ) : undefined ; } function getFromAllDeclarations < T extends Node > ( nodeTest , keywords ) { return useParent ( node . parent , nodeTest , decl => mapDefined ( decl . symbol . declarations , d => nodeTest ( d ) ? find ( d . getChildren ( sourceFile ) , c => contains ( keywords , c . kind ) ) : undefined ) ) ; } function useParent < T extends Node > ( node , nodeTest , getNodes ) { return nodeTest ( node ) ? highlightSpans ( getNodes ( node , sourceFile ) ) : undefined ; } function highlightSpans ( nodes ) { return nodes && nodes . map ( node => getHighlightSpanForNode ( node , sourceFile ) ) ; } } /**
         * Aggregates all throw-statements within this node *without* crossing
         * into function boundaries and try-blocks with catch-clauses.
         */ function aggregateOwnedThrowStatements ( node ) { if ( isThrowStatement ( node ) ) { return [ node ] ; } else if ( isTryStatement ( node ) ) { return concatenate ( node . catchClause ? aggregateOwnedThrowStatements ( node . catchClause ) : node . tryBlock && aggregateOwnedThrowStatements ( node . tryBlock ) , node . finallyBlock && aggregateOwnedThrowStatements ( node . finallyBlock ) ) ; } return isFunctionLike ( node ) ? undefined : flatMapChildren ( node , aggregateOwnedThrowStatements ) ; } /**
         * For lack of a better name, this function takes a throw statement and returns the
         * nearest ancestor that is a try-block (whose try statement has a catch clause),
         * function-block, or source file.
         */ function getThrowStatementOwner ( throwStatement ) { let child = throwStatement ; while ( child . parent ) { const parent = child . parent ; if ( isFunctionBlock ( parent ) || parent . kind === SyntaxKind . SourceFile ) { return parent ; } if ( isTryStatement ( parent ) && parent . tryBlock === child && parent . catchClause ) { return child ; } child = parent ; } return undefined ; } function aggregateAllBreakAndContinueStatements ( node ) { return isBreakOrContinueStatement ( node ) ? [ node ] : isFunctionLike ( node ) ? undefined : flatMapChildren ( node , aggregateAllBreakAndContinueStatements ) ; } function flatMapChildren < T > ( node , cb ) { const result = [ ] ; node . forEachChild ( child => { const value = cb ( child ) ; if ( value !== undefined ) { result . push ( ... toArray ( value ) ) ; } } ) ; return result ; } function ownsBreakOrContinueStatement ( owner , statement ) { const actualOwner = getBreakOrContinueOwner ( statement ) ; return ! ! actualOwner && actualOwner === owner ; } function getBreakOrContinueOwner ( statement ) { return findAncestor ( statement , node => { switch ( node . kind ) { case SyntaxKind . SwitchStatement : if ( statement . kind === SyntaxKind . ContinueStatement ) { return false ; } case SyntaxKind . ForStatement : case SyntaxKind . ForInStatement : case SyntaxKind . ForOfStatement : case SyntaxKind . WhileStatement : case SyntaxKind . DoStatement : return ! statement . label || isLabeledBy ( node , statement . label . escapedText ) ; default : return isFunctionLike ( node ) && "quit" ; } } ) ; } function getModifierOccurrences ( modifier , declaration ) { return mapDefined ( getNodesToSearchForModifier ( declaration , modifierToFlag ( modifier ) ) , node => findModifier ( node , modifier ) ) ; } function getNodesToSearchForModifier ( declaration , modifierFlag ) { const container = declaration . parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ObjectTypeDeclaration | ObjectLiteralExpression ; switch ( container . kind ) { case SyntaxKind . ModuleBlock : case SyntaxKind . SourceFile : case SyntaxKind . Block : case SyntaxKind . CaseClause : case SyntaxKind . DefaultClause : if ( modifierFlag & ModifierFlags . Abstract && isClassDeclaration ( declaration ) ) { return [ ... declaration . members , declaration ] ; } else { return container . statements ; } case SyntaxKind . Constructor : case SyntaxKind . MethodDeclaration : case SyntaxKind . FunctionDeclaration : return [ ... container . parameters , ... ( isClassLike ( container . parent ) ? container . parent . members : [ ] ) ] ; case SyntaxKind . ClassDeclaration : case SyntaxKind . ClassExpression : case SyntaxKind . InterfaceDeclaration : case SyntaxKind . TypeLiteral : const nodes = container . members ; if ( modifierFlag & ( ModifierFlags . AccessibilityModifier | ModifierFlags . Readonly ) ) { const constructor = find ( container . members , isConstructorDeclaration ) ; if ( constructor ) { return [ ... nodes , ... constructor . parameters ] ; } } else if ( modifierFlag & ModifierFlags . Abstract ) { return [ ... nodes , container ] ; } return nodes ; case SyntaxKind . ObjectLiteralExpression : return undefined ; default : Debug . assertNever ( container , "Invalid container kind." ) ; } } function pushKeywordIf ( keywordList , token , ... expected ) { if ( token && contains ( expected , token . kind ) ) { keywordList . push ( token ) ; return true ; } return false ; } function getLoopBreakContinueOccurrences ( loopNode ) { const keywords = [ ] ; if ( pushKeywordIf ( keywords , loopNode . getFirstToken ( ) , SyntaxKind . ForKeyword , SyntaxKind . WhileKeyword , SyntaxKind . DoKeyword ) ) { if ( loopNode . kind === SyntaxKind . DoStatement ) { const loopTokens = loopNode . getChildren ( ) ; for ( let i = loopTokens . length - 1 ; i >= 0 ; i -- ) { if ( pushKeywordIf ( keywords , loopTokens [ i ] , SyntaxKind . WhileKeyword ) ) { break ; } } } } forEach ( aggregateAllBreakAndContinueStatements ( loopNode . statement ) , statement => { if ( ownsBreakOrContinueStatement ( loopNode , statement ) ) { pushKeywordIf ( keywords , statement . getFirstToken ( ) , SyntaxKind . BreakKeyword , SyntaxKind . ContinueKeyword ) ; } } ) ; return keywords ; } function getBreakOrContinueStatementOccurrences ( breakOrContinueStatement ) { const owner = getBreakOrContinueOwner ( breakOrContinueStatement ) ; if ( owner ) { switch ( owner . kind ) { case SyntaxKind . ForStatement : case SyntaxKind . ForInStatement : case SyntaxKind . ForOfStatement : case SyntaxKind . DoStatement : case SyntaxKind . WhileStatement : return getLoopBreakContinueOccurrences ( owner as IterationStatement ) ; case SyntaxKind . SwitchStatement : return getSwitchCaseDefaultOccurrences ( owner as SwitchStatement ) ; } } return undefined ; } function getSwitchCaseDefaultOccurrences ( switchStatement ) { const keywords = [ ] ; pushKeywordIf ( keywords , switchStatement . getFirstToken ( ) , SyntaxKind . SwitchKeyword ) ; forEach ( switchStatement . caseBlock . clauses , clause => { pushKeywordIf ( keywords , clause . getFirstToken ( ) , SyntaxKind . CaseKeyword , SyntaxKind . DefaultKeyword ) ; forEach ( aggregateAllBreakAndContinueStatements ( clause ) , statement => { if ( ownsBreakOrContinueStatement ( switchStatement , statement ) ) { pushKeywordIf ( keywords , statement . getFirstToken ( ) , SyntaxKind . BreakKeyword ) ; } } ) ; } ) ; return keywords ; } function getTryCatchFinallyOccurrences ( tryStatement , sourceFile ) { const keywords = [ ] ; pushKeywordIf ( keywords , tryStatement . getFirstToken ( ) , SyntaxKind . TryKeyword ) ; if ( tryStatement . catchClause ) { pushKeywordIf ( keywords , tryStatement . catchClause . getFirstToken ( ) , SyntaxKind . CatchKeyword ) ; } if ( tryStatement . finallyBlock ) { const finallyKeyword = findChildOfKind ( tryStatement , SyntaxKind . FinallyKeyword , sourceFile ) ! ; pushKeywordIf ( keywords , finallyKeyword , SyntaxKind . FinallyKeyword ) ; } return keywords ; } function getThrowOccurrences ( throwStatement , sourceFile ) { const owner = getThrowStatementOwner ( throwStatement ) ; if ( ! owner ) { return undefined ; } const keywords = [ ] ; forEach ( aggregateOwnedThrowStatements ( owner ) , throwStatement => { keywords . push ( findChildOfKind ( throwStatement , SyntaxKind . ThrowKeyword , sourceFile ) ! ) ; } ) ; if ( isFunctionBlock ( owner ) ) { forEachReturnStatement ( owner as Block , returnStatement => { keywords . push ( findChildOfKind ( returnStatement , SyntaxKind . ReturnKeyword , sourceFile ) ! ) ; } ) ; } return keywords ; } function getReturnOccurrences ( returnStatement , sourceFile ) { const func = getContainingFunction ( returnStatement ) as FunctionLikeDeclaration ; if ( ! func ) { return undefined ; } const keywords = [ ] ; forEachReturnStatement ( cast ( func . body , isBlock ) , returnStatement => { keywords . push ( findChildOfKind ( returnStatement , SyntaxKind . ReturnKeyword , sourceFile ) ! ) ; } ) ; forEach ( aggregateOwnedThrowStatements ( func . body ! ) , throwStatement => { keywords . push ( findChildOfKind ( throwStatement , SyntaxKind . ThrowKeyword , sourceFile ) ! ) ; } ) ; return keywords ; } function getAsyncAndAwaitOccurrences ( node ) { const func = getContainingFunction ( node ) as FunctionLikeDeclaration ; if ( ! func ) { return undefined ; } const keywords = [ ] ; if ( func . modifiers ) { func . modifiers . forEach ( modifier => { pushKeywordIf ( keywords , modifier , SyntaxKind . AsyncKeyword ) ; } ) ; } forEachChild ( func , child => { traverseWithoutCrossingFunction ( child , node => { if ( isAwaitExpression ( node ) ) { pushKeywordIf ( keywords , node . getFirstToken ( ) , SyntaxKind . AwaitKeyword ) ; } } ) ; } ) ; return keywords ; } function getYieldOccurrences ( node ) { const func = getContainingFunction ( node ) as FunctionDeclaration ; if ( ! func ) { return undefined ; } const keywords = [ ] ; forEachChild ( func , child => { traverseWithoutCrossingFunction ( child , node => { if ( isYieldExpression ( node ) ) { pushKeywordIf ( keywords , node . getFirstToken ( ) , SyntaxKind . YieldKeyword ) ; } } ) ; } ) ; return keywords ; } function traverseWithoutCrossingFunction ( node , cb ) { cb ( node ) ; if ( ! isFunctionLike ( node ) && ! isClassLike ( node ) && ! isInterfaceDeclaration ( node ) && ! isModuleDeclaration ( node ) && ! isTypeAliasDeclaration ( node ) && ! isTypeNode ( node ) ) { forEachChild ( node , child => traverseWithoutCrossingFunction ( child , cb ) ) ; } } function getIfElseOccurrences ( ifStatement , sourceFile ) { const keywords = getIfElseKeywords ( ifStatement , sourceFile ) ; const result = [ ] ; for ( let i = 0 ; i < keywords . length ; i ++ ) { if ( keywords [ i ] . kind === SyntaxKind . ElseKeyword && i < keywords . length - 1 ) { const elseKeyword = keywords [ i ] ; const ifKeyword = keywords [ i + 1 ] ; let shouldCombineElseAndIf = true ; for ( let j = ifKeyword . getStart ( sourceFile ) - 1 ; j >= elseKeyword . end ; j -- ) { if ( ! isWhiteSpaceSingleLine ( sourceFile . text . charCodeAt ( j ) ) ) { shouldCombineElseAndIf = false ; break ; } } if ( shouldCombineElseAndIf ) { result . push ( { fileName : sourceFile . fileName , textSpan : createTextSpanFromBounds ( elseKeyword . getStart ( ) , ifKeyword . end ) , kind : HighlightSpanKind . reference } ) ; i ++ ; continue ; } } result . push ( getHighlightSpanForNode ( keywords [ i ] , sourceFile ) ) ; } return result ; } function getIfElseKeywords ( ifStatement , sourceFile ) { const keywords = [ ] ; while ( isIfStatement ( ifStatement . parent ) && ifStatement . parent . elseStatement === ifStatement ) { ifStatement = ifStatement . parent ; } while ( true ) { const children = ifStatement . getChildren ( sourceFile ) ; pushKeywordIf ( keywords , children [ 0 ] , SyntaxKind . IfKeyword ) ; for ( let i = children . length - 1 ; i >= 0 ; i -- ) { if ( pushKeywordIf ( keywords , children [ i ] , SyntaxKind . ElseKeyword ) ) { break ; } } if ( ! ifStatement . elseStatement || ! isIfStatement ( ifStatement . elseStatement ) ) { break ; } ifStatement = ifStatement . elseStatement ; } return keywords ; } /**
         * Whether or not a 'node' is preceded by a label of the given string.
         * Note: 'node' cannot be a SourceFile.
         */ function isLabeledBy ( node , labelName ) { return ! ! findAncestor ( node . parent , owner => ! isLabeledStatement ( owner ) ? "quit" : owner . label . escapedText === labelName ) ; } } }