diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index e915103f3ce..e95c96220b8 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -15,7 +15,7 @@ namespace ts { } /** Array that is only intended to be pushed to, never read. */ - interface Push { + export interface Push { push(value: T): void; } @@ -357,7 +357,7 @@ namespace ts { * Then it computes the set of parent folders for 'directory' that should have the same module resolution result * and for every parent folder in set it adds entry: parent -> module resolution. . * Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts. - * Set of parent folders that should have the same result will be: + * Set of parent folders that should have the same result will be: * [ * /a/b/c/d, /a/b/c, /a/b * ] @@ -391,7 +391,7 @@ namespace ts { } } } - + function getCommonPrefix(directory: Path, resolution: string) { if (resolution === undefined) { return undefined; @@ -1022,7 +1022,7 @@ namespace ts { /** * Represents result of search. Normally when searching among several alternatives we treat value `undefined` as indicator - * that search fails and we should try another option. + * that search fails and we should try another option. * 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). * SearchResult is used to deal with this issue, its values represents following outcomes: * - undefined - not found, continue searching @@ -1030,7 +1030,7 @@ namespace ts { * - { value: } - found - stop searching */ type SearchResult = { value: T | undefined } | undefined; - + /** * Wraps value to SearchResult. * @returns undefined if value is undefined or { value } otherwise diff --git a/src/services/completions.ts b/src/services/completions.ts index f0daa696474..d893e7212ec 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1,8 +1,6 @@ -/// - /* @internal */ namespace ts.Completions { - export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo { + export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo | undefined { if (isInReferenceComment(sourceFile, position)) { return getTripleSlashReferenceCompletion(sourceFile, position); } @@ -134,7 +132,7 @@ namespace ts.Completions { return uniqueNames; } - function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number) { + function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number): CompletionInfo | undefined { const node = findPrecedingToken(position, sourceFile); if (!node || node.kind !== SyntaxKind.StringLiteral) { return undefined; @@ -174,7 +172,7 @@ namespace ts.Completions { return getStringLiteralCompletionEntriesFromModuleNames(node); } else { - const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); + const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { // Get string literal completions from specialized signatures of the target // i.e. declare function f(a: 'A'); @@ -188,7 +186,7 @@ namespace ts.Completions { } } - function getStringLiteralCompletionEntriesFromPropertyAssignment(element: ObjectLiteralElement) { + function getStringLiteralCompletionEntriesFromPropertyAssignment(element: ObjectLiteralElement): CompletionInfo | undefined { const type = typeChecker.getContextualType((element.parent)); const entries: CompletionEntry[] = []; if (type) { @@ -199,7 +197,7 @@ namespace ts.Completions { } } - function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo) { + function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo): CompletionInfo | undefined { const candidates: Signature[] = []; const entries: CompletionEntry[] = []; @@ -219,7 +217,7 @@ namespace ts.Completions { return undefined; } - function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression) { + function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression): CompletionInfo | undefined { const type = typeChecker.getTypeAtLocation(node.expression); const entries: CompletionEntry[] = []; if (type) { @@ -231,7 +229,7 @@ namespace ts.Completions { return undefined; } - function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral) { + function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral): CompletionInfo | undefined { const type = typeChecker.getContextualType(node); if (type) { const entries: CompletionEntry[] = []; @@ -243,7 +241,7 @@ namespace ts.Completions { return undefined; } - function addStringLiteralCompletionsFromType(type: Type, result: CompletionEntry[]): void { + function addStringLiteralCompletionsFromType(type: Type, result: Push): void { if (type && type.flags & TypeFlags.TypeParameter) { type = typeChecker.getApparentType(type); } @@ -251,18 +249,18 @@ namespace ts.Completions { return; } if (type.flags & TypeFlags.Union) { - forEach((type).types, t => addStringLiteralCompletionsFromType(t, result)); - } - else { - if (type.flags & TypeFlags.StringLiteral) { - result.push({ - name: (type).text, - kindModifiers: ScriptElementKindModifier.none, - kind: ScriptElementKind.variableElement, - sortText: "0" - }); + for (const t of (type).types) { + addStringLiteralCompletionsFromType(t, result); } } + else if (type.flags & TypeFlags.StringLiteral) { + result.push({ + name: (type).text, + kindModifiers: ScriptElementKindModifier.none, + kind: ScriptElementKind.variableElement, + sortText: "0" + }); + } } function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral): CompletionInfo { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 44c2ede9fbb..9e4d906d300 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -260,7 +260,7 @@ namespace ts.SignatureHelp { * Returns relevant information for the argument list and the current argument if we are * in the argument of an invocation; returns undefined otherwise. */ - function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { + export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { const callExpression = node.parent; // There are 3 cases to handle: diff --git a/tests/cases/fourslash/completionForStringLiteral6.ts b/tests/cases/fourslash/completionForStringLiteral6.ts new file mode 100644 index 00000000000..ac4a378abfb --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral6.ts @@ -0,0 +1,12 @@ +/// + +////interface Foo { +//// x: "abc" | "def"; +////} +////function bar(f: Foo) { }; +////bar({x: "/**/"}); + +goTo.marker(); +verify.completionListContains("abc"); +verify.completionListContains("def"); +verify.completionListCount(2); diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index 9063d909b15..67bbce2a472 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -129,7 +129,7 @@ function dir(dirPath: string, spec?: string, options?: any) { function deleteFolderRecursive(dirPath: string) { if (fs.existsSync(dirPath)) { fs.readdirSync(dirPath).forEach((file) => { - const curPath = path.join(path, file); + const curPath = path.join(dirPath, file); if (fs.statSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); }