Skip to content

Commit df8cb9d

Browse files
author
Andy
authored
Merge pull request #13364 from Microsoft/string_literal_completions_fix
String literal completions: Use call signature only if we are *immediately* in a call expression
2 parents 1040247 + 23fa422 commit df8cb9d

File tree

5 files changed

+37
-27
lines changed

5 files changed

+37
-27
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace ts {
1515
}
1616

1717
/** Array that is only intended to be pushed to, never read. */
18-
interface Push<T> {
18+
export interface Push<T> {
1919
push(value: T): void;
2020
}
2121

@@ -357,7 +357,7 @@ namespace ts {
357357
* Then it computes the set of parent folders for 'directory' that should have the same module resolution result
358358
* and for every parent folder in set it adds entry: parent -> module resolution. .
359359
* Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts.
360-
* Set of parent folders that should have the same result will be:
360+
* Set of parent folders that should have the same result will be:
361361
* [
362362
* /a/b/c/d, /a/b/c, /a/b
363363
* ]
@@ -391,7 +391,7 @@ namespace ts {
391391
}
392392
}
393393
}
394-
394+
395395
function getCommonPrefix(directory: Path, resolution: string) {
396396
if (resolution === undefined) {
397397
return undefined;
@@ -1022,15 +1022,15 @@ namespace ts {
10221022

10231023
/**
10241024
* Represents result of search. Normally when searching among several alternatives we treat value `undefined` as indicator
1025-
* that search fails and we should try another option.
1025+
* that search fails and we should try another option.
10261026
* However this does not allow us to represent final result that should be used instead of further searching (i.e. a final result that was found in cache).
10271027
* SearchResult is used to deal with this issue, its values represents following outcomes:
10281028
* - undefined - not found, continue searching
10291029
* - { value: undefined } - not found - stop searching
10301030
* - { value: <some-value> } - found - stop searching
10311031
*/
10321032
type SearchResult<T> = { value: T | undefined } | undefined;
1033-
1033+
10341034
/**
10351035
* Wraps value to SearchResult.
10361036
* @returns undefined if value is undefined or { value } otherwise

src/services/completions.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
/// <reference path='../compiler/utilities.ts' />
2-
31
/* @internal */
42
namespace ts.Completions {
5-
export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo {
3+
export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo | undefined {
64
if (isInReferenceComment(sourceFile, position)) {
75
return getTripleSlashReferenceCompletion(sourceFile, position);
86
}
@@ -134,7 +132,7 @@ namespace ts.Completions {
134132
return uniqueNames;
135133
}
136134

137-
function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number) {
135+
function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number): CompletionInfo | undefined {
138136
const node = findPrecedingToken(position, sourceFile);
139137
if (!node || node.kind !== SyntaxKind.StringLiteral) {
140138
return undefined;
@@ -174,7 +172,7 @@ namespace ts.Completions {
174172
return getStringLiteralCompletionEntriesFromModuleNames(<StringLiteral>node);
175173
}
176174
else {
177-
const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile);
175+
const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile);
178176
if (argumentInfo) {
179177
// Get string literal completions from specialized signatures of the target
180178
// i.e. declare function f(a: 'A');
@@ -188,7 +186,7 @@ namespace ts.Completions {
188186
}
189187
}
190188

191-
function getStringLiteralCompletionEntriesFromPropertyAssignment(element: ObjectLiteralElement) {
189+
function getStringLiteralCompletionEntriesFromPropertyAssignment(element: ObjectLiteralElement): CompletionInfo | undefined {
192190
const type = typeChecker.getContextualType((<ObjectLiteralExpression>element.parent));
193191
const entries: CompletionEntry[] = [];
194192
if (type) {
@@ -199,7 +197,7 @@ namespace ts.Completions {
199197
}
200198
}
201199

202-
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo) {
200+
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo): CompletionInfo | undefined {
203201
const candidates: Signature[] = [];
204202
const entries: CompletionEntry[] = [];
205203

@@ -219,7 +217,7 @@ namespace ts.Completions {
219217
return undefined;
220218
}
221219

222-
function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression) {
220+
function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression): CompletionInfo | undefined {
223221
const type = typeChecker.getTypeAtLocation(node.expression);
224222
const entries: CompletionEntry[] = [];
225223
if (type) {
@@ -231,7 +229,7 @@ namespace ts.Completions {
231229
return undefined;
232230
}
233231

234-
function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral) {
232+
function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral): CompletionInfo | undefined {
235233
const type = typeChecker.getContextualType(node);
236234
if (type) {
237235
const entries: CompletionEntry[] = [];
@@ -243,26 +241,26 @@ namespace ts.Completions {
243241
return undefined;
244242
}
245243

246-
function addStringLiteralCompletionsFromType(type: Type, result: CompletionEntry[]): void {
244+
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>): void {
247245
if (type && type.flags & TypeFlags.TypeParameter) {
248246
type = typeChecker.getApparentType(type);
249247
}
250248
if (!type) {
251249
return;
252250
}
253251
if (type.flags & TypeFlags.Union) {
254-
forEach((<UnionType>type).types, t => addStringLiteralCompletionsFromType(t, result));
255-
}
256-
else {
257-
if (type.flags & TypeFlags.StringLiteral) {
258-
result.push({
259-
name: (<LiteralType>type).text,
260-
kindModifiers: ScriptElementKindModifier.none,
261-
kind: ScriptElementKind.variableElement,
262-
sortText: "0"
263-
});
252+
for (const t of (<UnionType>type).types) {
253+
addStringLiteralCompletionsFromType(t, result);
264254
}
265255
}
256+
else if (type.flags & TypeFlags.StringLiteral) {
257+
result.push({
258+
name: (<LiteralType>type).text,
259+
kindModifiers: ScriptElementKindModifier.none,
260+
kind: ScriptElementKind.variableElement,
261+
sortText: "0"
262+
});
263+
}
266264
}
267265

268266
function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral): CompletionInfo {

src/services/signatureHelp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ namespace ts.SignatureHelp {
260260
* Returns relevant information for the argument list and the current argument if we are
261261
* in the argument of an invocation; returns undefined otherwise.
262262
*/
263-
function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
263+
export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
264264
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
265265
const callExpression = <CallExpression>node.parent;
266266
// There are 3 cases to handle:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////interface Foo {
4+
//// x: "abc" | "def";
5+
////}
6+
////function bar(f: Foo) { };
7+
////bar({x: "/**/"});
8+
9+
goTo.marker();
10+
verify.completionListContains("abc");
11+
verify.completionListContains("def");
12+
verify.completionListCount(2);

tests/webTestServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function dir(dirPath: string, spec?: string, options?: any) {
129129
function deleteFolderRecursive(dirPath: string) {
130130
if (fs.existsSync(dirPath)) {
131131
fs.readdirSync(dirPath).forEach((file) => {
132-
const curPath = path.join(path, file);
132+
const curPath = path.join(dirPath, file);
133133
if (fs.statSync(curPath).isDirectory()) { // recurse
134134
deleteFolderRecursive(curPath);
135135
}

0 commit comments

Comments
 (0)