4
4
namespace ts . Completions {
5
5
export type Log = ( message : string ) => void ;
6
6
7
- type SymbolOriginInfo = { type : "this-type" } | SymbolOriginInfoExport ;
7
+ type SymbolOriginInfo = { type : "this-type" } | { type : "symbol-member" } | SymbolOriginInfoExport ;
8
8
interface SymbolOriginInfoExport {
9
9
type : "export" ;
10
10
moduleSymbol : Symbol ;
@@ -83,7 +83,7 @@ namespace ts.Completions {
83
83
}
84
84
case StringLiteralCompletionKind . Types : {
85
85
const entries = completion . types . map ( type => ( { name : type . value , kindModifiers : ScriptElementKindModifier . none , kind : ScriptElementKind . typeElement , sortText : "0" } ) ) ;
86
- return { isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , entries } ;
86
+ return { isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : completion . isNewIdentifier , entries } ;
87
87
}
88
88
default :
89
89
return Debug . assertNever ( completion ) ;
@@ -206,11 +206,13 @@ namespace ts.Completions {
206
206
if ( origin && origin . type === "this-type" ) {
207
207
insertText = needsConvertPropertyAccess ? `this[${ quote ( name , preferences ) } ]` : `this.${ name } ` ;
208
208
}
209
- else if ( needsConvertPropertyAccess ) {
210
- insertText = `[${ quote ( name , preferences ) } ]` ;
209
+ // We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
210
+ // Somehow there was a global with a non-identifier name. Hopefully someone will complain about getting a "foo bar" global completion and provide a repro.
211
+ else if ( ( origin && origin . type === "symbol-member" || needsConvertPropertyAccess ) && propertyAccessToConvert ) {
212
+ insertText = needsConvertPropertyAccess ? `[${ quote ( name , preferences ) } ]` : `[${ name } ]` ;
211
213
const dot = findChildOfKind ( propertyAccessToConvert ! , SyntaxKind . DotToken , sourceFile ) ! ;
212
214
// If the text after the '.' starts with this name, write over it. Else, add new text.
213
- const end = startsWith ( name , propertyAccessToConvert ! . name . text ) ? propertyAccessToConvert ! . name . end : dot . end ;
215
+ const end = startsWith ( name , propertyAccessToConvert . name . text ) ? propertyAccessToConvert . name . end : dot . end ;
214
216
replacementSpan = createTextSpanFromBounds ( dot . getStart ( sourceFile ) , end ) ;
215
217
}
216
218
@@ -358,16 +360,18 @@ namespace ts.Completions {
358
360
readonly symbols : ReadonlyArray < Symbol > ;
359
361
readonly hasIndexSignature : boolean ;
360
362
}
361
- type StringLiteralCompletion =
362
- | { readonly kind : StringLiteralCompletionKind . Paths , readonly paths : ReadonlyArray < PathCompletions . PathCompletion > }
363
- | StringLiteralCompletionsFromProperties
364
- | { readonly kind : StringLiteralCompletionKind . Types , readonly types : ReadonlyArray < StringLiteralType > } ;
363
+ interface StringLiteralCompletionsFromTypes {
364
+ readonly kind : StringLiteralCompletionKind . Types ;
365
+ readonly types : ReadonlyArray < StringLiteralType > ;
366
+ readonly isNewIdentifier : boolean ;
367
+ }
368
+ type StringLiteralCompletion = { readonly kind : StringLiteralCompletionKind . Paths , readonly paths : ReadonlyArray < PathCompletions . PathCompletion > } | StringLiteralCompletionsFromProperties | StringLiteralCompletionsFromTypes ;
365
369
function getStringLiteralCompletionEntries ( sourceFile : SourceFile , node : StringLiteralLike , position : number , typeChecker : TypeChecker , compilerOptions : CompilerOptions , host : LanguageServiceHost ) : StringLiteralCompletion | undefined {
366
370
switch ( node . parent . kind ) {
367
371
case SyntaxKind . LiteralType :
368
372
switch ( node . parent . parent . kind ) {
369
373
case SyntaxKind . TypeReference :
370
- return { kind : StringLiteralCompletionKind . Types , types : getStringLiteralTypes ( typeChecker . getTypeArgumentConstraint ( node . parent as LiteralTypeNode ) , typeChecker ) } ;
374
+ return { kind : StringLiteralCompletionKind . Types , types : getStringLiteralTypes ( typeChecker . getTypeArgumentConstraint ( node . parent as LiteralTypeNode ) , typeChecker ) , isNewIdentifier : false } ;
371
375
case SyntaxKind . IndexedAccessType :
372
376
// Get all apparent property names
373
377
// i.e. interface Foo {
@@ -376,6 +380,8 @@ namespace ts.Completions {
376
380
// }
377
381
// let x: Foo["/*completion position*/"]
378
382
return stringLiteralCompletionsFromProperties ( typeChecker . getTypeFromTypeNode ( ( node . parent . parent as IndexedAccessTypeNode ) . objectType ) ) ;
383
+ case SyntaxKind . ImportTypeNode :
384
+ return { kind : StringLiteralCompletionKind . Paths , paths : PathCompletions . getStringLiteralCompletionsFromModuleNames ( sourceFile , node , compilerOptions , host , typeChecker ) } ;
379
385
default :
380
386
return undefined ;
381
387
}
@@ -419,13 +425,7 @@ namespace ts.Completions {
419
425
// Get string literal completions from specialized signatures of the target
420
426
// i.e. declare function f(a: 'A');
421
427
// f("/*completion position*/")
422
- if ( argumentInfo ) {
423
- const candidates : Signature [ ] = [ ] ;
424
- typeChecker . getResolvedSignature ( argumentInfo . invocation , candidates , argumentInfo . argumentCount ) ;
425
- const uniques = createMap < true > ( ) ;
426
- return { kind : StringLiteralCompletionKind . Types , types : flatMap ( candidates , candidate => getStringLiteralTypes ( typeChecker . getParameterType ( candidate , argumentInfo . argumentIndex ) , typeChecker , uniques ) ) } ;
427
- }
428
- return fromContextualType ( ) ;
428
+ return argumentInfo ? getStringLiteralCompletionsFromSignature ( argumentInfo , typeChecker ) : fromContextualType ( ) ;
429
429
}
430
430
// falls through (is `require("")` or `import("")`)
431
431
@@ -447,10 +447,26 @@ namespace ts.Completions {
447
447
function fromContextualType ( ) : StringLiteralCompletion {
448
448
// Get completion for string literal from string literal type
449
449
// i.e. var x: "hi" | "hello" = "/*completion position*/"
450
- return { kind : StringLiteralCompletionKind . Types , types : getStringLiteralTypes ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) } ;
450
+ return { kind : StringLiteralCompletionKind . Types , types : getStringLiteralTypes ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) , isNewIdentifier : false } ;
451
451
}
452
452
}
453
453
454
+ function getStringLiteralCompletionsFromSignature ( argumentInfo : SignatureHelp . ArgumentListInfo , checker : TypeChecker ) : StringLiteralCompletionsFromTypes {
455
+ let isNewIdentifier = false ;
456
+
457
+ const uniques = createMap < true > ( ) ;
458
+ const candidates : Signature [ ] = [ ] ;
459
+ checker . getResolvedSignature ( argumentInfo . invocation , candidates , argumentInfo . argumentCount ) ;
460
+ const types = flatMap ( candidates , candidate => {
461
+ if ( ! candidate . hasRestParameter && argumentInfo . argumentCount > candidate . parameters . length ) return ;
462
+ const type = checker . getParameterType ( candidate , argumentInfo . argumentIndex ) ;
463
+ isNewIdentifier = isNewIdentifier || ! ! ( type . flags & TypeFlags . String ) ;
464
+ return getStringLiteralTypes ( type , checker , uniques ) ;
465
+ } ) ;
466
+
467
+ return { kind : StringLiteralCompletionKind . Types , types, isNewIdentifier } ;
468
+ }
469
+
454
470
function stringLiteralCompletionsFromProperties ( type : Type | undefined ) : StringLiteralCompletionsFromProperties | undefined {
455
471
return type && { kind : StringLiteralCompletionKind . Properties , symbols : type . getApparentProperties ( ) , hasIndexSignature : hasIndexSignature ( type ) } ;
456
472
}
@@ -1051,13 +1067,34 @@ namespace ts.Completions {
1051
1067
}
1052
1068
else {
1053
1069
for ( const symbol of type . getApparentProperties ( ) ) {
1054
- if ( typeChecker . isValidPropertyAccessForCompletions ( node . kind === SyntaxKind . ImportTypeNode ? < ImportTypeNode > node : < PropertyAccessExpression > ( node . parent ) , type , symbol ) ) {
1055
- symbols . push ( symbol ) ;
1070
+ if ( typeChecker . isValidPropertyAccessForCompletions ( node . kind === SyntaxKind . ImportTypeNode ? < ImportTypeNode > node : < PropertyAccessExpression > node . parent , type , symbol ) ) {
1071
+ addPropertySymbol ( symbol ) ;
1056
1072
}
1057
1073
}
1058
1074
}
1059
1075
}
1060
1076
1077
+ function addPropertySymbol ( symbol : Symbol ) {
1078
+ // If this is e.g. [Symbol.iterator], add a completion for `Symbol`.
1079
+ const symbolSymbol = firstDefined ( symbol . declarations , decl => {
1080
+ const name = getNameOfDeclaration ( decl ) ;
1081
+ const leftName = name . kind === SyntaxKind . ComputedPropertyName ? getLeftMostName ( name . expression ) : undefined ;
1082
+ return leftName && typeChecker . getSymbolAtLocation ( leftName ) ;
1083
+ } ) ;
1084
+ if ( symbolSymbol ) {
1085
+ symbols . push ( symbolSymbol ) ;
1086
+ symbolToOriginInfoMap [ getSymbolId ( symbolSymbol ) ] = { type : "symbol-member" } ;
1087
+ }
1088
+ else {
1089
+ symbols . push ( symbol ) ;
1090
+ }
1091
+ }
1092
+
1093
+ /** Given 'a.b.c', returns 'a'. */
1094
+ function getLeftMostName ( e : Expression ) : Identifier | undefined {
1095
+ return isIdentifier ( e ) ? e : isPropertyAccessExpression ( e ) ? getLeftMostName ( e . expression ) : undefined ;
1096
+ }
1097
+
1061
1098
function tryGetGlobalSymbols ( ) : boolean {
1062
1099
const result : GlobalsSearch = tryGetObjectLikeCompletionSymbols ( )
1063
1100
|| tryGetImportOrExportClauseCompletionSymbols ( )
@@ -2049,7 +2086,7 @@ namespace ts.Completions {
2049
2086
// TODO: GH#18169
2050
2087
return { name : JSON . stringify ( name ) , needsConvertPropertyAccess : false } ;
2051
2088
case CompletionKind . PropertyAccess :
2052
- case CompletionKind . Global :
2089
+ case CompletionKind . Global : // For a 'this.' completion it will be in a global context, but may have a non-identifier name.
2053
2090
// Don't add a completion for a name starting with a space. See https://github.com/Microsoft/TypeScript/pull/20547
2054
2091
return name . charCodeAt ( 0 ) === CharacterCodes . space ? undefined : { name, needsConvertPropertyAccess : true } ;
2055
2092
case CompletionKind . None :
0 commit comments