@@ -2565,61 +2565,59 @@ module ts {
25652565 }
25662566
25672567 /// Completion
2568- function getValidCompletionEntryDisplayNameForSymbol ( symbol : Symbol , target : ScriptTarget ) : string {
2568+ function getCompletionEntryDisplayNameForSymbol ( symbol : Symbol , target : ScriptTarget , performCharacterChecks : boolean ) : string {
25692569 let displayName = symbol . getName ( ) ;
2570- if ( displayName && displayName . length > 0 ) {
2570+ if ( displayName ) {
25712571 let firstCharCode = displayName . charCodeAt ( 0 ) ;
25722572 // First check of the displayName is not external module; if it is an external module, it is not valid entry
25732573 if ( ( symbol . flags & SymbolFlags . Namespace ) && ( firstCharCode === CharacterCodes . singleQuote || firstCharCode === CharacterCodes . doubleQuote ) ) {
25742574 // If the symbol is external module, don't show it in the completion list
25752575 // (i.e declare module "http" { let x; } | // <= request completion here, "http" should not be there)
25762576 return undefined ;
25772577 }
2578-
2579- if ( displayName && displayName . length >= 2 &&
2580- firstCharCode === displayName . charCodeAt ( displayName . length - 1 ) &&
2581- ( firstCharCode === CharacterCodes . singleQuote || firstCharCode === CharacterCodes . doubleQuote ) ) {
2582- // If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
2583- // invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
2584- displayName = displayName . substring ( 1 , displayName . length - 1 ) ;
2585- }
2586-
2587- let isValid = isIdentifierStart ( displayName . charCodeAt ( 0 ) , target ) ;
2588- for ( let i = 1 , n = displayName . length ; isValid && i < n ; i ++ ) {
2589- isValid = isIdentifierPart ( displayName . charCodeAt ( i ) , target ) ;
2590- }
2591-
2592- if ( isValid ) {
2593- return unescapeIdentifier ( displayName ) ;
2594- }
25952578 }
25962579
2597- return undefined ;
2580+ return getCompletionEntryDisplayName ( displayName , target , performCharacterChecks ) ;
25982581 }
25992582
2600- function getValidCompletionEntryDisplayName ( displayName : string , target : ScriptTarget ) : string {
2583+ function getCompletionEntryDisplayName ( displayName : string , target : ScriptTarget , performCharacterChecks : boolean ) : string {
2584+ if ( ! displayName ) {
2585+ return undefined ;
2586+ }
2587+
26012588 let firstCharCode = displayName . charCodeAt ( 0 ) ;
2602- if ( displayName && displayName . length >= 2 &&
2589+ if ( displayName . length >= 2 &&
26032590 firstCharCode === displayName . charCodeAt ( displayName . length - 1 ) &&
26042591 ( firstCharCode === CharacterCodes . singleQuote || firstCharCode === CharacterCodes . doubleQuote ) ) {
26052592 // If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
26062593 // invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
26072594 displayName = displayName . substring ( 1 , displayName . length - 1 ) ;
26082595 }
26092596
2610- let isValid = isIdentifierStart ( displayName . charCodeAt ( 0 ) , target ) ;
2611- for ( let i = 1 , n = displayName . length ; isValid && i < n ; i ++ ) {
2612- isValid = isIdentifierPart ( displayName . charCodeAt ( i ) , target ) ;
2597+ if ( ! displayName ) {
2598+ return undefined ;
2599+ }
2600+
2601+ if ( performCharacterChecks ) {
2602+ if ( ! isIdentifierStart ( displayName . charCodeAt ( 0 ) , target ) ) {
2603+ return undefined ;
2604+ }
2605+
2606+ for ( let i = 1 , n = displayName . length ; i < n ; i ++ ) {
2607+ if ( ! isIdentifierPart ( displayName . charCodeAt ( i ) , target ) ) {
2608+ return undefined ;
2609+ }
2610+ }
26132611 }
26142612
2615- return isValid ? unescapeIdentifier ( displayName ) : undefined ;
2613+ return unescapeIdentifier ( displayName ) ;
26162614 }
26172615
26182616 function createCompletionEntry ( symbol : Symbol , typeChecker : TypeChecker , location : Node ) : CompletionEntry {
26192617 // Try to get a valid display name for this symbol, if we could not find one, then ignore it.
26202618 // We would like to only show things that can be added after a dot, so for instance numeric properties can
26212619 // not be accessed with a dot (a.1 <- invalid)
2622- let displayName = getValidCompletionEntryDisplayNameForSymbol ( symbol , program . getCompilerOptions ( ) . target ) ;
2620+ let displayName = getCompletionEntryDisplayNameForSymbol ( symbol , program . getCompilerOptions ( ) . target , /*performCharacterChecks:*/ true ) ;
26232621 if ( ! displayName ) {
26242622 return undefined ;
26252623 }
@@ -2640,37 +2638,18 @@ module ts {
26402638 } ;
26412639 }
26422640
2643- // If symbolName is undefined, all symbols at the specified are returned. If symbolName
2644- // is not undefined, then the first symbol with that name at the specified position
2645- // will be returned. Calling without symbolName is useful when you want the entire
2646- // list of symbols (like for getCompletionsAtPosition). Calling with a symbolName is
2647- // useful when you want information about a single symbol (like for getCompletionEntryDetails).
2648- function getCompletionData ( fileName : string , position : number , symbolName ?: string ) {
2649- let result = getCompletionDataWorker ( fileName , position , symbolName ) ;
2650- if ( ! result ) {
2651- return undefined ;
2652- }
2653-
2654- if ( result . symbols && symbolName ) {
2655- var target = program . getCompilerOptions ( ) . target ;
2656- result . symbols = filter ( result . symbols , s => getValidCompletionEntryDisplayNameForSymbol ( s , target ) === symbolName ) ;
2657- }
2658-
2659- return result ;
2660- }
2661-
2662- function getCompletionDataWorker ( fileName : string , position : number , symbolName : string ) {
2641+ function getCompletionData ( fileName : string , position : number ) {
26632642 let syntacticStart = new Date ( ) . getTime ( ) ;
26642643 let sourceFile = getValidSourceFile ( fileName ) ;
26652644
26662645 let start = new Date ( ) . getTime ( ) ;
26672646 let currentToken = getTokenAtPosition ( sourceFile , position ) ;
2668- log ( "getCompletionsAtPosition : Get current token: " + ( new Date ( ) . getTime ( ) - start ) ) ;
2647+ log ( "getCompletionData : Get current token: " + ( new Date ( ) . getTime ( ) - start ) ) ;
26692648
26702649 start = new Date ( ) . getTime ( ) ;
26712650 // Completion not allowed inside comments, bail out if this is the case
26722651 let insideComment = isInsideComment ( sourceFile , currentToken , position ) ;
2673- log ( "getCompletionsAtPosition : Is inside comment: " + ( new Date ( ) . getTime ( ) - start ) ) ;
2652+ log ( "getCompletionData : Is inside comment: " + ( new Date ( ) . getTime ( ) - start ) ) ;
26742653
26752654 if ( insideComment ) {
26762655 log ( "Returning an empty list because completion was inside a comment." ) ;
@@ -2681,14 +2660,14 @@ module ts {
26812660 // Note: previousToken can be undefined if we are the beginning of the file
26822661 start = new Date ( ) . getTime ( ) ;
26832662 let previousToken = findPrecedingToken ( position , sourceFile ) ;
2684- log ( "getCompletionsAtPosition : Get previous token 1: " + ( new Date ( ) . getTime ( ) - start ) ) ;
2663+ log ( "getCompletionData : Get previous token 1: " + ( new Date ( ) . getTime ( ) - start ) ) ;
26852664
26862665 // The caret is at the end of an identifier; this is a partial identifier that we want to complete: e.g. a.toS|
26872666 // Skip this partial identifier to the previous token
26882667 if ( previousToken && position <= previousToken . end && previousToken . kind === SyntaxKind . Identifier ) {
26892668 let start = new Date ( ) . getTime ( ) ;
26902669 previousToken = findPrecedingToken ( previousToken . pos , sourceFile ) ;
2691- log ( "getCompletionsAtPosition : Get previous token 2: " + ( new Date ( ) . getTime ( ) - start ) ) ;
2670+ log ( "getCompletionData : Get previous token 2: " + ( new Date ( ) . getTime ( ) - start ) ) ;
26922671 }
26932672
26942673 // Check if this is a valid completion location
@@ -2731,8 +2710,7 @@ module ts {
27312710 }
27322711 }
27332712
2734- // Add keywords if this is not a member completion list
2735- log ( "getCompletionsAtPosition: Semantic work: " + ( new Date ( ) . getTime ( ) - semanticStart ) ) ;
2713+ log ( "getCompletionData: Semantic work: " + ( new Date ( ) . getTime ( ) - semanticStart ) ) ;
27362714
27372715 return { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot } ;
27382716
@@ -2808,12 +2786,7 @@ module ts {
28082786 /// TODO filter meaning based on the current context
28092787 let scopeNode = getScopeNode ( previousToken , position , sourceFile ) ;
28102788 let symbolMeanings = SymbolFlags . Type | SymbolFlags . Value | SymbolFlags . Namespace | SymbolFlags . Alias ;
2811-
2812- // Filter down to the symbol that matches the symbolName if we were given one.
2813- let predicate = symbolName !== undefined
2814- ? ( s : Symbol ) => getValidCompletionEntryDisplayNameForSymbol ( s , target ) === symbolName
2815- : undefined ;
2816- symbols = typeInfoResolver . getSymbolsInScope ( scopeNode , symbolMeanings , predicate ) ;
2789+ symbols = typeInfoResolver . getSymbolsInScope ( scopeNode , symbolMeanings ) ;
28172790 }
28182791
28192792 return true ;
@@ -3160,7 +3133,7 @@ module ts {
31603133
31613134 var target = program . getCompilerOptions ( ) . target ;
31623135 for ( let name in allIdentifiers ) {
3163- let displayName = getValidCompletionEntryDisplayName ( name , target ) ;
3136+ let displayName = getCompletionEntryDisplayName ( name , target , /*performCharacterChecks:*/ true ) ;
31643137 if ( displayName ) {
31653138 // Use '1' so that all javascript identifier entries sort after Symbol entries.
31663139 entries . push ( {
@@ -3201,12 +3174,19 @@ module ts {
32013174 function getCompletionEntryDetails ( fileName : string , position : number , entryName : string ) : CompletionEntryDetails {
32023175 synchronizeHostData ( ) ;
32033176
3204- // Look up a completion symbol with this name .
3205- let completionData = getCompletionData ( fileName , position , entryName ) ;
3177+ // Compute all the completion symbols again .
3178+ let completionData = getCompletionData ( fileName , position ) ;
32063179 if ( completionData ) {
32073180 let { symbols, location } = completionData ;
3208- if ( symbols && symbols . length > 0 ) {
3209- let symbol = symbols [ 0 ] ;
3181+
3182+ // Find the symbol with the matching entry name.
3183+ let target = program . getCompilerOptions ( ) . target ;
3184+ // We don't need to perform character checks here because we're only comparing the
3185+ // name against 'entryName' (which is known to be good), not building a new
3186+ // completion entry.
3187+ let symbol = forEach ( symbols , s => getCompletionEntryDisplayNameForSymbol ( s , target , /*performCharacterChecks:*/ false ) === entryName ? s : undefined ) ;
3188+
3189+ if ( symbol ) {
32103190 let displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind ( symbol , getValidSourceFile ( fileName ) , location , typeInfoResolver , location , SemanticMeaning . All ) ;
32113191 return {
32123192 name : entryName ,
0 commit comments