@@ -1064,10 +1064,10 @@ namespace ts {
10641064 getCancellationToken ?( ) : HostCancellationToken ;
10651065 getCurrentDirectory ( ) : string ;
10661066 getDefaultLibFileName ( options : CompilerOptions ) : string ;
1067- log ? ( s : string ) : void ;
1068- trace ? ( s : string ) : void ;
1069- error ? ( s : string ) : void ;
1070- useCaseSensitiveFileNames ? ( ) : boolean ;
1067+ log ?( s : string ) : void ;
1068+ trace ?( s : string ) : void ;
1069+ error ?( s : string ) : void ;
1070+ useCaseSensitiveFileNames ?( ) : boolean ;
10711071
10721072 /*
10731073 * LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
@@ -2499,7 +2499,7 @@ namespace ts {
24992499 }
25002500
25012501 // should be start of dependency list
2502- if ( token !== SyntaxKind . OpenBracketToken ) {
2502+ if ( token !== SyntaxKind . OpenBracketToken ) {
25032503 return true ;
25042504 }
25052505
@@ -4032,10 +4032,15 @@ namespace ts {
40324032 }
40334033 }
40344034
4035-
40364035 function getCompletionsAtPosition ( fileName : string , position : number ) : CompletionInfo {
40374036 synchronizeHostData ( ) ;
40384037
4038+ const sourceFile = getValidSourceFile ( fileName ) ;
4039+
4040+ if ( isInString ( sourceFile , position ) ) {
4041+ return getStringLiteralCompletionEntries ( sourceFile , position ) ;
4042+ }
4043+
40394044 const completionData = getCompletionData ( fileName , position ) ;
40404045 if ( ! completionData ) {
40414046 return undefined ;
@@ -4048,12 +4053,10 @@ namespace ts {
40484053 return { isMemberCompletion : false , isNewIdentifierLocation : false , entries : getAllJsDocCompletionEntries ( ) } ;
40494054 }
40504055
4051- const sourceFile = getValidSourceFile ( fileName ) ;
4052-
40534056 const entries : CompletionEntry [ ] = [ ] ;
40544057
40554058 if ( isSourceFileJavaScript ( sourceFile ) ) {
4056- const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries ) ;
4059+ const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ false ) ;
40574060 addRange ( entries , getJavaScriptCompletionEntries ( sourceFile , location . pos , uniqueNames ) ) ;
40584061 }
40594062 else {
@@ -4077,7 +4080,7 @@ namespace ts {
40774080 }
40784081 }
40794082
4080- getCompletionEntriesFromSymbols ( symbols , entries ) ;
4083+ getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true ) ;
40814084 }
40824085
40834086 // Add keywords if this is not a member completion list
@@ -4127,11 +4130,11 @@ namespace ts {
41274130 } ) ) ;
41284131 }
41294132
4130- function createCompletionEntry ( symbol : Symbol , location : Node ) : CompletionEntry {
4133+ function createCompletionEntry ( symbol : Symbol , location : Node , performCharacterChecks : boolean ) : CompletionEntry {
41314134 // Try to get a valid display name for this symbol, if we could not find one, then ignore it.
41324135 // We would like to only show things that can be added after a dot, so for instance numeric properties can
41334136 // not be accessed with a dot (a.1 <- invalid)
4134- const displayName = getCompletionEntryDisplayNameForSymbol ( symbol , program . getCompilerOptions ( ) . target , /* performCharacterChecks*/ true , location ) ;
4137+ const displayName = getCompletionEntryDisplayNameForSymbol ( symbol , program . getCompilerOptions ( ) . target , performCharacterChecks , location ) ;
41354138 if ( ! displayName ) {
41364139 return undefined ;
41374140 }
@@ -4152,12 +4155,12 @@ namespace ts {
41524155 } ;
41534156 }
41544157
4155- function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : CompletionEntry [ ] ) : Map < string > {
4158+ function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : CompletionEntry [ ] , location : Node , performCharacterChecks : boolean ) : Map < string > {
41564159 const start = new Date ( ) . getTime ( ) ;
41574160 const uniqueNames : Map < string > = { } ;
41584161 if ( symbols ) {
41594162 for ( const symbol of symbols ) {
4160- const entry = createCompletionEntry ( symbol , location ) ;
4163+ const entry = createCompletionEntry ( symbol , location , performCharacterChecks ) ;
41614164 if ( entry ) {
41624165 const id = escapeIdentifier ( entry . name ) ;
41634166 if ( ! lookUp ( uniqueNames , id ) ) {
@@ -4171,6 +4174,93 @@ namespace ts {
41714174 log ( "getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + ( new Date ( ) . getTime ( ) - start ) ) ;
41724175 return uniqueNames ;
41734176 }
4177+
4178+ function getStringLiteralCompletionEntries ( sourceFile : SourceFile , position : number ) {
4179+ const node = findPrecedingToken ( position , sourceFile ) ;
4180+ if ( ! node || node . kind !== SyntaxKind . StringLiteral ) {
4181+ return undefined ;
4182+ }
4183+
4184+ const argumentInfo = SignatureHelp . getContainingArgumentInfo ( node , position , sourceFile ) ;
4185+ if ( argumentInfo ) {
4186+ // Get string literal completions from specialized signatures of the target
4187+ return getStringLiteralCompletionEntriesFromCallExpression ( argumentInfo ) ;
4188+ }
4189+ else if ( isElementAccessExpression ( node . parent ) && node . parent . argumentExpression === node ) {
4190+ // Get all names of properties on the expression
4191+ return getStringLiteralCompletionEntriesFromElementAccess ( node . parent ) ;
4192+ }
4193+ else {
4194+ // Otherwise, get the completions from the contextual type if one exists
4195+ return getStringLiteralCompletionEntriesFromContextualType ( < StringLiteral > node ) ;
4196+ }
4197+ }
4198+
4199+ function getStringLiteralCompletionEntriesFromCallExpression ( argumentInfo : SignatureHelp . ArgumentListInfo ) {
4200+ const typeChecker = program . getTypeChecker ( ) ;
4201+ const candidates : Signature [ ] = [ ] ;
4202+ const entries : CompletionEntry [ ] = [ ] ;
4203+
4204+ typeChecker . getResolvedSignature ( argumentInfo . invocation , candidates ) ;
4205+
4206+ for ( const candidate of candidates ) {
4207+ if ( candidate . parameters . length > argumentInfo . argumentIndex ) {
4208+ const parameter = candidate . parameters [ argumentInfo . argumentIndex ] ;
4209+ addStringLiteralCompletionsFromType ( typeChecker . getTypeAtLocation ( parameter . valueDeclaration ) , entries ) ;
4210+ }
4211+ }
4212+
4213+ if ( entries . length ) {
4214+ return { isMemberCompletion : false , isNewIdentifierLocation : true , entries } ;
4215+ }
4216+
4217+ return undefined ;
4218+ }
4219+
4220+ function getStringLiteralCompletionEntriesFromElementAccess ( node : ElementAccessExpression ) {
4221+ const typeChecker = program . getTypeChecker ( ) ;
4222+ const type = typeChecker . getTypeAtLocation ( node . expression ) ;
4223+ const entries : CompletionEntry [ ] = [ ] ;
4224+ if ( type ) {
4225+ getCompletionEntriesFromSymbols ( type . getApparentProperties ( ) , entries , node , /*performCharacterChecks*/ false ) ;
4226+ if ( entries . length ) {
4227+ return { isMemberCompletion : true , isNewIdentifierLocation : true , entries } ;
4228+ }
4229+ }
4230+ return undefined ;
4231+ }
4232+
4233+ function getStringLiteralCompletionEntriesFromContextualType ( node : StringLiteral ) {
4234+ const typeChecker = program . getTypeChecker ( ) ;
4235+ const type = typeChecker . getContextualType ( node ) ;
4236+ if ( type ) {
4237+ const entries : CompletionEntry [ ] = [ ] ;
4238+ addStringLiteralCompletionsFromType ( type , entries ) ;
4239+ if ( entries . length ) {
4240+ return { isMemberCompletion : false , isNewIdentifierLocation : false , entries } ;
4241+ }
4242+ }
4243+ return undefined ;
4244+ }
4245+
4246+ function addStringLiteralCompletionsFromType ( type : Type , result : CompletionEntry [ ] ) : void {
4247+ if ( ! type ) {
4248+ return ;
4249+ }
4250+ if ( type . flags & TypeFlags . Union ) {
4251+ forEach ( ( < UnionType > type ) . types , t => addStringLiteralCompletionsFromType ( t , result ) ) ;
4252+ }
4253+ else {
4254+ if ( type . flags & TypeFlags . StringLiteral ) {
4255+ result . push ( {
4256+ name : ( < StringLiteralType > type ) . text ,
4257+ kindModifiers : ScriptElementKindModifier . none ,
4258+ kind : ScriptElementKind . variableElement ,
4259+ sortText : "0"
4260+ } ) ;
4261+ }
4262+ }
4263+ }
41744264 }
41754265
41764266 function getCompletionEntryDetails ( fileName : string , position : number , entryName : string ) : CompletionEntryDetails {
@@ -4335,7 +4425,7 @@ namespace ts {
43354425 // try get the call/construct signature from the type if it matches
43364426 let callExpression : CallExpression ;
43374427 if ( location . kind === SyntaxKind . CallExpression || location . kind === SyntaxKind . NewExpression ) {
4338- callExpression = < CallExpression > location ;
4428+ callExpression = < CallExpression > location ;
43394429 }
43404430 else if ( isCallExpressionTarget ( location ) || isNewExpressionTarget ( location ) ) {
43414431 callExpression = < CallExpression > location . parent ;
0 commit comments