Skip to content

Commit 8e14ac7

Browse files
author
Andy
authored
Improve string literal completions from a signature (#23062)
1 parent 65323e0 commit 8e14ac7

File tree

4 files changed

+41
-15
lines changed

4 files changed

+41
-15
lines changed

src/services/completions.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ namespace ts.Completions {
8383
}
8484
case StringLiteralCompletionKind.Types: {
8585
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 };
8787
}
8888
default:
8989
return Debug.assertNever(completion);
@@ -358,16 +358,18 @@ namespace ts.Completions {
358358
readonly symbols: ReadonlyArray<Symbol>;
359359
readonly hasIndexSignature: boolean;
360360
}
361-
type StringLiteralCompletion =
362-
| { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: ReadonlyArray<PathCompletions.PathCompletion> }
363-
| StringLiteralCompletionsFromProperties
364-
| { readonly kind: StringLiteralCompletionKind.Types, readonly types: ReadonlyArray<StringLiteralType> };
361+
interface StringLiteralCompletionsFromTypes {
362+
readonly kind: StringLiteralCompletionKind.Types;
363+
readonly types: ReadonlyArray<StringLiteralType>;
364+
readonly isNewIdentifier: boolean;
365+
}
366+
type StringLiteralCompletion = { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: ReadonlyArray<PathCompletions.PathCompletion> } | StringLiteralCompletionsFromProperties | StringLiteralCompletionsFromTypes;
365367
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost): StringLiteralCompletion | undefined {
366368
switch (node.parent.kind) {
367369
case SyntaxKind.LiteralType:
368370
switch (node.parent.parent.kind) {
369371
case SyntaxKind.TypeReference:
370-
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(node.parent as LiteralTypeNode), typeChecker) };
372+
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(node.parent as LiteralTypeNode), typeChecker), isNewIdentifier: false };
371373
case SyntaxKind.IndexedAccessType:
372374
// Get all apparent property names
373375
// i.e. interface Foo {
@@ -419,13 +421,7 @@ namespace ts.Completions {
419421
// Get string literal completions from specialized signatures of the target
420422
// i.e. declare function f(a: 'A');
421423
// 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();
424+
return argumentInfo ? getStringLiteralCompletionsFromSignature(argumentInfo, typeChecker) : fromContextualType();
429425
}
430426
// falls through (is `require("")` or `import("")`)
431427

@@ -447,10 +443,26 @@ namespace ts.Completions {
447443
function fromContextualType(): StringLiteralCompletion {
448444
// Get completion for string literal from string literal type
449445
// i.e. var x: "hi" | "hello" = "/*completion position*/"
450-
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker), typeChecker) };
446+
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker), typeChecker), isNewIdentifier: false };
451447
}
452448
}
453449

450+
function getStringLiteralCompletionsFromSignature(argumentInfo: SignatureHelp.ArgumentListInfo, checker: TypeChecker): StringLiteralCompletionsFromTypes {
451+
let isNewIdentifier = false;
452+
453+
const uniques = createMap<true>();
454+
const candidates: Signature[] = [];
455+
checker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
456+
const types = flatMap(candidates, candidate => {
457+
if (!candidate.hasRestParameter && argumentInfo.argumentCount > candidate.parameters.length) return;
458+
const type = checker.getParameterType(candidate, argumentInfo.argumentIndex);
459+
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
460+
return getStringLiteralTypes(type, checker, uniques);
461+
});
462+
463+
return { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier };
464+
}
465+
454466
function stringLiteralCompletionsFromProperties(type: Type | undefined): StringLiteralCompletionsFromProperties | undefined {
455467
return type && { kind: StringLiteralCompletionKind.Properties, symbols: type.getApparentProperties(), hasIndexSignature: hasIndexSignature(type) };
456468
}

tests/cases/fourslash/completionForStringLiteral3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
////
1010
////f("/*2*/
1111

12-
verify.completionsAt(["1", "2"], ["A", "B", "C"]);
12+
verify.completionsAt(["1", "2"], ["A", "B", "C"], { isNewIdentifierLocation: true });
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////declare function f(a: "x"): void;
4+
////declare function f(a: string): void;
5+
////f("/**/");
6+
7+
verify.completionsAt("", ["x"], { isNewIdentifierLocation: true });
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////declare function f(a: "x"): void;
4+
////declare function f(a: string, b: number): void;
5+
////f("/**/", 0);
6+
7+
verify.completionsAt("", [], { isNewIdentifierLocation: true });

0 commit comments

Comments
 (0)