Skip to content

Commit 2cbad6a

Browse files
author
Andy
authored
Support completion details for string literal completions (microsoft#22664)
1 parent 4699c82 commit 2cbad6a

2 files changed

Lines changed: 66 additions & 18 deletions

File tree

src/services/completions.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ namespace ts.Completions {
8989
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
9090
}
9191
case StringLiteralCompletionKind.Types: {
92-
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.variableElement, sortText: "0" }));
92+
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.typeElement, sortText: "0" }));
9393
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
9494
}
9595
default:
@@ -531,6 +531,15 @@ namespace ts.Completions {
531531
): CompletionEntryDetails {
532532
const typeChecker = program.getTypeChecker();
533533
const { name } = entryId;
534+
535+
const contextToken = findPrecedingToken(position, sourceFile);
536+
if (isInString(sourceFile, position, contextToken)) {
537+
const stringLiteralCompletions = !contextToken || !isStringLiteralLike(contextToken)
538+
? undefined
539+
: getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host);
540+
return stringLiteralCompletions && stringLiteralCompletionDetails(name, contextToken, stringLiteralCompletions, sourceFile, typeChecker);
541+
}
542+
534543
// Compute all the completion symbols again.
535544
const symbolCompletion = getSymbolCompletionFromEntryId(typeChecker, log, compilerOptions, sourceFile, position, entryId, allSourceFiles);
536545
switch (symbolCompletion.type) {
@@ -550,29 +559,40 @@ namespace ts.Completions {
550559
case "symbol": {
551560
const { symbol, location, symbolToOriginInfoMap, previousToken } = symbolCompletion;
552561
const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles, preferences);
553-
const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol);
554-
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
555-
return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions, source: sourceDisplay };
562+
return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, codeActions, sourceDisplay);
556563
}
557-
case "none": {
564+
case "none":
558565
// Didn't find a symbol with this name. See if we can find a keyword instead.
559-
if (allKeywordsCompletions().some(c => c.name === name)) {
560-
return {
561-
name,
562-
kind: ScriptElementKind.keyword,
563-
kindModifiers: ScriptElementKindModifier.none,
564-
displayParts: [displayPart(name, SymbolDisplayPartKind.keyword)],
565-
documentation: undefined,
566-
tags: undefined,
567-
codeActions: undefined,
568-
source: undefined,
569-
};
570-
}
571-
return undefined;
566+
return allKeywordsCompletions().some(c => c.name === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.keyword, [displayPart(name, SymbolDisplayPartKind.keyword)]) : undefined;
567+
}
568+
}
569+
570+
function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
571+
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All);
572+
return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
573+
}
574+
575+
function stringLiteralCompletionDetails(name: string, location: Node, completion: StringLiteralCompletion, sourceFile: SourceFile, checker: TypeChecker): CompletionEntryDetails | undefined {
576+
switch (completion.kind) {
577+
case StringLiteralCompletionKind.Paths: {
578+
const match = find(completion.paths, p => p.name === name);
579+
return match && createCompletionDetails(name, ScriptElementKindModifier.none, match.kind, [textPart(name)]);
572580
}
581+
case StringLiteralCompletionKind.Properties: {
582+
const match = find(completion.symbols, s => s.name === name);
583+
return match && createCompletionDetailsForSymbol(match, checker, sourceFile, location);
584+
}
585+
case StringLiteralCompletionKind.Types:
586+
return find(completion.types, t => t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.typeElement, [textPart(name)]) : undefined;
587+
default:
588+
return Debug.assertNever(completion);
573589
}
574590
}
575591

592+
function createCompletionDetails(name: string, kindModifiers: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], documentation?: SymbolDisplayPart[], tags?: JSDocTagInfo[], codeActions?: CodeAction[], source?: SymbolDisplayPart[]): CompletionEntryDetails {
593+
return { name, kindModifiers, kind, displayParts, documentation, tags, codeActions, source };
594+
}
595+
576596
interface CodeActionsAndSourceDisplay {
577597
readonly codeActions: CodeAction[] | undefined;
578598
readonly sourceDisplay: SymbolDisplayPart[] | undefined;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @Filename: /other.ts
4+
////export const x = 0;
5+
6+
// @Filename: /a.ts
7+
////import {} from ".//*path*/";
8+
////
9+
////const x: "a" = "/*type*/";
10+
////
11+
////interface I {
12+
//// /** Prop doc */
13+
//// x: number;
14+
//// /** Method doc */
15+
//// m(): void;
16+
////}
17+
////declare const o: I;
18+
////o["/*prop*/"];
19+
20+
goTo.marker("path");
21+
verify.completionListContains("other", "other", "", "script");
22+
23+
goTo.marker("type");
24+
verify.completionListContains("a", "a", "", "type");
25+
26+
goTo.marker("prop");
27+
verify.completionListContains("x", "(property) I.x: number", "Prop doc ", "property");
28+
verify.completionListContains("m", "(method) I.m(): void", "Method doc ", "method");

0 commit comments

Comments
 (0)