@@ -1064,10 +1064,10 @@ namespace ts {
1064
1064
getCancellationToken ?( ) : HostCancellationToken ;
1065
1065
getCurrentDirectory ( ) : string ;
1066
1066
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 ;
1071
1071
1072
1072
/*
1073
1073
* 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 {
2499
2499
}
2500
2500
2501
2501
// should be start of dependency list
2502
- if ( token !== SyntaxKind . OpenBracketToken ) {
2502
+ if ( token !== SyntaxKind . OpenBracketToken ) {
2503
2503
return true ;
2504
2504
}
2505
2505
@@ -4032,10 +4032,15 @@ namespace ts {
4032
4032
}
4033
4033
}
4034
4034
4035
-
4036
4035
function getCompletionsAtPosition ( fileName : string , position : number ) : CompletionInfo {
4037
4036
synchronizeHostData ( ) ;
4038
4037
4038
+ const sourceFile = getValidSourceFile ( fileName ) ;
4039
+
4040
+ if ( isInString ( sourceFile , position ) ) {
4041
+ return getStringLiteralCompletionEntries ( sourceFile , position ) ;
4042
+ }
4043
+
4039
4044
const completionData = getCompletionData ( fileName , position ) ;
4040
4045
if ( ! completionData ) {
4041
4046
return undefined ;
@@ -4048,12 +4053,10 @@ namespace ts {
4048
4053
return { isMemberCompletion : false , isNewIdentifierLocation : false , entries : getAllJsDocCompletionEntries ( ) } ;
4049
4054
}
4050
4055
4051
- const sourceFile = getValidSourceFile ( fileName ) ;
4052
-
4053
4056
const entries : CompletionEntry [ ] = [ ] ;
4054
4057
4055
4058
if ( isSourceFileJavaScript ( sourceFile ) ) {
4056
- const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries ) ;
4059
+ const uniqueNames = getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ false ) ;
4057
4060
addRange ( entries , getJavaScriptCompletionEntries ( sourceFile , location . pos , uniqueNames ) ) ;
4058
4061
}
4059
4062
else {
@@ -4077,7 +4080,7 @@ namespace ts {
4077
4080
}
4078
4081
}
4079
4082
4080
- getCompletionEntriesFromSymbols ( symbols , entries ) ;
4083
+ getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true ) ;
4081
4084
}
4082
4085
4083
4086
// Add keywords if this is not a member completion list
@@ -4127,11 +4130,11 @@ namespace ts {
4127
4130
} ) ) ;
4128
4131
}
4129
4132
4130
- function createCompletionEntry ( symbol : Symbol , location : Node ) : CompletionEntry {
4133
+ function createCompletionEntry ( symbol : Symbol , location : Node , performCharacterChecks : boolean ) : CompletionEntry {
4131
4134
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
4132
4135
// We would like to only show things that can be added after a dot, so for instance numeric properties can
4133
4136
// 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 ) ;
4135
4138
if ( ! displayName ) {
4136
4139
return undefined ;
4137
4140
}
@@ -4152,12 +4155,12 @@ namespace ts {
4152
4155
} ;
4153
4156
}
4154
4157
4155
- function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : CompletionEntry [ ] ) : Map < string > {
4158
+ function getCompletionEntriesFromSymbols ( symbols : Symbol [ ] , entries : CompletionEntry [ ] , location : Node , performCharacterChecks : boolean ) : Map < string > {
4156
4159
const start = new Date ( ) . getTime ( ) ;
4157
4160
const uniqueNames : Map < string > = { } ;
4158
4161
if ( symbols ) {
4159
4162
for ( const symbol of symbols ) {
4160
- const entry = createCompletionEntry ( symbol , location ) ;
4163
+ const entry = createCompletionEntry ( symbol , location , performCharacterChecks ) ;
4161
4164
if ( entry ) {
4162
4165
const id = escapeIdentifier ( entry . name ) ;
4163
4166
if ( ! lookUp ( uniqueNames , id ) ) {
@@ -4171,6 +4174,93 @@ namespace ts {
4171
4174
log ( "getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + ( new Date ( ) . getTime ( ) - start ) ) ;
4172
4175
return uniqueNames ;
4173
4176
}
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
+ }
4174
4264
}
4175
4265
4176
4266
function getCompletionEntryDetails ( fileName : string , position : number , entryName : string ) : CompletionEntryDetails {
@@ -4335,7 +4425,7 @@ namespace ts {
4335
4425
// try get the call/construct signature from the type if it matches
4336
4426
let callExpression : CallExpression ;
4337
4427
if ( location . kind === SyntaxKind . CallExpression || location . kind === SyntaxKind . NewExpression ) {
4338
- callExpression = < CallExpression > location ;
4428
+ callExpression = < CallExpression > location ;
4339
4429
}
4340
4430
else if ( isCallExpressionTarget ( location ) || isNewExpressionTarget ( location ) ) {
4341
4431
callExpression = < CallExpression > location . parent ;
0 commit comments