@@ -1032,10 +1032,10 @@ namespace ts {
10321032 getCancellationToken ?( ) : HostCancellationToken ;
10331033 getCurrentDirectory ( ) : string ;
10341034 getDefaultLibFileName ( options : CompilerOptions ) : string ;
1035- log ? ( s : string ) : void ;
1036- trace ? ( s : string ) : void ;
1037- error ? ( s : string ) : void ;
1038- useCaseSensitiveFileNames ? ( ) : boolean ;
1035+ log ?( s : string ) : void ;
1036+ trace ?( s : string ) : void ;
1037+ error ?( s : string ) : void ;
1038+ useCaseSensitiveFileNames ?( ) : boolean ;
10391039
10401040 /*
10411041 * LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
@@ -2416,7 +2416,7 @@ namespace ts {
24162416 }
24172417
24182418 // should be start of dependency list
2419- if ( token !== SyntaxKind . OpenBracketToken ) {
2419+ if ( token !== SyntaxKind . OpenBracketToken ) {
24202420 return true ;
24212421 }
24222422
@@ -3912,10 +3912,15 @@ namespace ts {
39123912 }
39133913 }
39143914
3915-
39163915 function getCompletionsAtPosition ( fileName : string , position : number ) : CompletionInfo {
39173916 synchronizeHostData ( ) ;
39183917
3918+ const sourceFile = getValidSourceFile ( fileName ) ;
3919+
3920+ if ( isInString ( sourceFile , position ) ) {
3921+ return getStringLiteralCompletionEntries ( sourceFile , position ) ;
3922+ }
3923+
39193924 const completionData = getCompletionData ( fileName , position ) ;
39203925 if ( ! completionData ) {
39213926 return undefined ;
@@ -3928,12 +3933,10 @@ namespace ts {
39283933 return { isMemberCompletion : false , isNewIdentifierLocation : false , entries : getAllJsDocCompletionEntries ( ) } ;
39293934 }
39303935
3931- const sourceFile = getValidSourceFile ( fileName ) ;
3932-
39333936 const entries : CompletionEntry [ ] = [ ] ;
39343937
39353938 if ( isSourceFileJavaScript ( sourceFile ) ) {
3936- const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries ) ;
3939+ const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ false ) ;
39373940 addRange ( entries , getJavaScriptCompletionEntries ( sourceFile , location . pos , uniqueNames ) ) ;
39383941 }
39393942 else {
@@ -3957,7 +3960,7 @@ namespace ts {
39573960 }
39583961 }
39593962
3960- getCompletionEntriesFromSymbols ( symbols , entries ) ;
3963+ getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true ) ;
39613964 }
39623965
39633966 // Add keywords if this is not a member completion list
@@ -4007,11 +4010,11 @@ namespace ts {
40074010 } ) ) ;
40084011 }
40094012
4010- function createCompletionEntry ( symbol : Symbol , location : Node ) : CompletionEntry {
4013+ function createCompletionEntry ( symbol : Symbol , location : Node , performCharacterChecks : boolean ) : CompletionEntry {
40114014 // Try to get a valid display name for this symbol, if we could not find one, then ignore it.
40124015 // We would like to only show things that can be added after a dot, so for instance numeric properties can
40134016 // not be accessed with a dot (a.1 <- invalid)
4014- const displayName = getCompletionEntryDisplayNameForSymbol ( symbol , program . getCompilerOptions ( ) . target , /* performCharacterChecks*/ true , location ) ;
4017+ const displayName = getCompletionEntryDisplayNameForSymbol ( symbol , program . getCompilerOptions ( ) . target , performCharacterChecks , location ) ;
40154018 if ( ! displayName ) {
40164019 return undefined ;
40174020 }
@@ -4032,12 +4035,12 @@ namespace ts {
40324035 } ;
40334036 }
40344037
4035- function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : CompletionEntry [ ] ) : Map < string > {
4038+ function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : CompletionEntry [ ] , location : Node , performCharacterChecks : boolean ) : Map < string > {
40364039 const start = new Date ( ) . getTime ( ) ;
40374040 const uniqueNames : Map < string > = { } ;
40384041 if ( symbols ) {
40394042 for ( const symbol of symbols ) {
4040- const entry = createCompletionEntry ( symbol , location ) ;
4043+ const entry = createCompletionEntry ( symbol , location , performCharacterChecks ) ;
40414044 if ( entry ) {
40424045 const id = escapeIdentifier ( entry . name ) ;
40434046 if ( ! lookUp ( uniqueNames , id ) ) {
@@ -4051,6 +4054,91 @@ namespace ts {
40514054 log ( "getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + ( new Date ( ) . getTime ( ) - start ) ) ;
40524055 return uniqueNames ;
40534056 }
4057+
4058+ function getStringLiteralCompletionEntries ( sourceFile : SourceFile , position : number ) {
4059+ const node = findPrecedingToken ( position , sourceFile ) ;
4060+ if ( ! node || node . kind !== SyntaxKind . StringLiteral ) {
4061+ return undefined ;
4062+ }
4063+
4064+ isNameOfExternalModuleImportOrDeclaration
4065+ const argumentInfo = SignatureHelp . getContainingArgumentInfo ( node , position , sourceFile ) ;
4066+ if ( argumentInfo ) {
4067+ return getStringLiteralCompletionEntriesFromCallExpression ( argumentInfo ) ;
4068+ }
4069+ else if ( isElementAccessExpression ( node . parent ) && node . parent . argumentExpression === node ) {
4070+ return getStringLiteralCompletionEntriesFromElementAccess ( node . parent ) ;
4071+ }
4072+ else {
4073+ return getStringLiteralCompletionEntriesFromContextualType ( < StringLiteral > node ) ;
4074+ }
4075+ }
4076+
4077+ function getStringLiteralCompletionEntriesFromCallExpression ( argumentInfo : SignatureHelp . ArgumentListInfo ) {
4078+ const typeChecker = program . getTypeChecker ( ) ;
4079+ const candidates : Signature [ ] = [ ] ;
4080+ const entries : CompletionEntry [ ] = [ ] ;
4081+
4082+ typeChecker . getResolvedSignature ( argumentInfo . invocation , candidates ) ;
4083+
4084+ for ( const candidate of candidates ) {
4085+ if ( candidate . parameters . length > argumentInfo . argumentIndex ) {
4086+ const parameter = candidate . parameters [ argumentInfo . argumentIndex ] ;
4087+ addStringLiteralCompletionsFromType ( typeChecker . getTypeAtLocation ( parameter . valueDeclaration ) , entries ) ;
4088+ }
4089+ }
4090+
4091+ if ( entries . length ) {
4092+ return { isMemberCompletion : false , isNewIdentifierLocation : true , entries : entries } ;
4093+ }
4094+
4095+ return undefined ;
4096+ }
4097+
4098+ function getStringLiteralCompletionEntriesFromElementAccess ( node : ElementAccessExpression ) {
4099+ const typeChecker = program . getTypeChecker ( ) ;
4100+ const type = typeChecker . getTypeAtLocation ( node . expression ) ;
4101+ const entries : CompletionEntry [ ] = [ ] ;
4102+ if ( type ) {
4103+ getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , node , /*performCharacterChecks*/ false ) ;
4104+ if ( entries . length ) {
4105+ return { isMemberCompletion : true , isNewIdentifierLocation : true , entries : entries } ;
4106+ }
4107+ }
4108+ return undefined ;
4109+ }
4110+
4111+ function getStringLiteralCompletionEntriesFromContextualType ( node : StringLiteral ) {
4112+ const typeChecker = program . getTypeChecker ( ) ;
4113+ const type = typeChecker . getContextualType ( node ) ;
4114+ if ( type ) {
4115+ const entries : CompletionEntry [ ] = [ ] ;
4116+ addStringLiteralCompletionsFromType ( type , entries ) ;
4117+ if ( entries . length ) {
4118+ return { isMemberCompletion : false , isNewIdentifierLocation : false , entries : entries } ;
4119+ }
4120+ }
4121+ return undefined ;
4122+ }
4123+
4124+ function addStringLiteralCompletionsFromType ( type : Type , result : CompletionEntry [ ] ) : void {
4125+ if ( ! type ) {
4126+ return ;
4127+ }
4128+ if ( type . flags & TypeFlags . Union ) {
4129+ forEach ( ( < UnionType > type ) . types , t => addStringLiteralCompletionsFromType ( t , result ) ) ;
4130+ }
4131+ else {
4132+ if ( type . flags & TypeFlags . StringLiteral ) {
4133+ result . push ( {
4134+ name : ( < StringLiteralType > type ) . text ,
4135+ kindModifiers : ScriptElementKindModifier . none ,
4136+ kind : ScriptElementKind . variableElement ,
4137+ sortText : "0"
4138+ } ) ;
4139+ }
4140+ }
4141+ }
40544142 }
40554143
40564144 function getCompletionEntryDetails ( fileName : string , position : number , entryName : string ) : CompletionEntryDetails {
@@ -4215,7 +4303,7 @@ namespace ts {
42154303 // try get the call/construct signature from the type if it matches
42164304 let callExpression : CallExpression ;
42174305 if ( location . kind === SyntaxKind . CallExpression || location . kind === SyntaxKind . NewExpression ) {
4218- callExpression = < CallExpression > location ;
4306+ callExpression = < CallExpression > location ;
42194307 }
42204308 else if ( isCallExpressionTarget ( location ) || isNewExpressionTarget ( location ) ) {
42214309 callExpression = < CallExpression > location . parent ;
0 commit comments