mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 11:54:44 -06:00
Add support for completion in string literals
This commit is contained in:
parent
0a277a1c60
commit
060f2a8563
@ -1032,10 +1032,10 @@ namespace ts {
|
||||
getCancellationToken?(): HostCancellationToken;
|
||||
getCurrentDirectory(): string;
|
||||
getDefaultLibFileName(options: CompilerOptions): string;
|
||||
log? (s: string): void;
|
||||
trace? (s: string): void;
|
||||
error? (s: string): void;
|
||||
useCaseSensitiveFileNames? (): boolean;
|
||||
log?(s: string): void;
|
||||
trace?(s: string): void;
|
||||
error?(s: string): void;
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
|
||||
/*
|
||||
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
|
||||
@ -2416,7 +2416,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// should be start of dependency list
|
||||
if (token !== SyntaxKind.OpenBracketToken) {
|
||||
if (token !== SyntaxKind.OpenBracketToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3912,10 +3912,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
|
||||
synchronizeHostData();
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
if (isInString(sourceFile, position)) {
|
||||
return getStringLiteralCompletionEntries(sourceFile, position);
|
||||
}
|
||||
|
||||
const completionData = getCompletionData(fileName, position);
|
||||
if (!completionData) {
|
||||
return undefined;
|
||||
@ -3928,12 +3933,10 @@ namespace ts {
|
||||
return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: getAllJsDocCompletionEntries() };
|
||||
}
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
const entries: CompletionEntry[] = [];
|
||||
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries);
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ false);
|
||||
addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames));
|
||||
}
|
||||
else {
|
||||
@ -3957,7 +3960,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, entries);
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true);
|
||||
}
|
||||
|
||||
// Add keywords if this is not a member completion list
|
||||
@ -4007,11 +4010,11 @@ namespace ts {
|
||||
}));
|
||||
}
|
||||
|
||||
function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry {
|
||||
function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean): CompletionEntry {
|
||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||
// not be accessed with a dot (a.1 <- invalid)
|
||||
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, /*performCharacterChecks*/ true, location);
|
||||
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, performCharacterChecks, location);
|
||||
if (!displayName) {
|
||||
return undefined;
|
||||
}
|
||||
@ -4032,12 +4035,12 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[]): Map<string> {
|
||||
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map<string> {
|
||||
const start = new Date().getTime();
|
||||
const uniqueNames: Map<string> = {};
|
||||
if (symbols) {
|
||||
for (const symbol of symbols) {
|
||||
const entry = createCompletionEntry(symbol, location);
|
||||
const entry = createCompletionEntry(symbol, location, performCharacterChecks);
|
||||
if (entry) {
|
||||
const id = escapeIdentifier(entry.name);
|
||||
if (!lookUp(uniqueNames, id)) {
|
||||
@ -4051,6 +4054,91 @@ namespace ts {
|
||||
log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start));
|
||||
return uniqueNames;
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number) {
|
||||
const node = findPrecedingToken(position, sourceFile);
|
||||
if (!node || node.kind !== SyntaxKind.StringLiteral) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
isNameOfExternalModuleImportOrDeclaration
|
||||
const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile);
|
||||
if (argumentInfo) {
|
||||
return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo);
|
||||
}
|
||||
else if (isElementAccessExpression(node.parent) && node.parent.argumentExpression === node) {
|
||||
return getStringLiteralCompletionEntriesFromElementAccess(node.parent);
|
||||
}
|
||||
else {
|
||||
return getStringLiteralCompletionEntriesFromContextualType(<StringLiteral>node);
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo) {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const candidates: Signature[] = [];
|
||||
const entries: CompletionEntry[] = [];
|
||||
|
||||
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (candidate.parameters.length > argumentInfo.argumentIndex) {
|
||||
const parameter = candidate.parameters[argumentInfo.argumentIndex];
|
||||
addStringLiteralCompletionsFromType(typeChecker.getTypeAtLocation(parameter.valueDeclaration), entries);
|
||||
}
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
return { isMemberCompletion: false, isNewIdentifierLocation: true, entries: entries };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression) {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const type = typeChecker.getTypeAtLocation(node.expression);
|
||||
const entries: CompletionEntry[] = [];
|
||||
if (type) {
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/false);
|
||||
if (entries.length) {
|
||||
return { isMemberCompletion: true, isNewIdentifierLocation: true, entries: entries };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral) {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const type = typeChecker.getContextualType(node);
|
||||
if (type) {
|
||||
const entries: CompletionEntry[] = [];
|
||||
addStringLiteralCompletionsFromType(type, entries);
|
||||
if (entries.length) {
|
||||
return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: entries };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addStringLiteralCompletionsFromType(type: Type, result: CompletionEntry[]): void {
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
forEach((<UnionType>type).types, t => addStringLiteralCompletionsFromType(t, result));
|
||||
}
|
||||
else {
|
||||
if (type.flags & TypeFlags.StringLiteral) {
|
||||
result.push({
|
||||
name: (<StringLiteralType>type).text,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
sortText: "0"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
@ -4215,7 +4303,7 @@ namespace ts {
|
||||
// try get the call/construct signature from the type if it matches
|
||||
let callExpression: CallExpression;
|
||||
if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) {
|
||||
callExpression = <CallExpression> location;
|
||||
callExpression = <CallExpression>location;
|
||||
}
|
||||
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
|
||||
callExpression = <CallExpression>location.parent;
|
||||
|
||||
15
tests/cases/fourslash/completionForStringLiteral.ts
Normal file
15
tests/cases/fourslash/completionForStringLiteral.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////type Options = "Option 1" | "Option 2" | "Option 3";
|
||||
////var x: Options = "/*1*/Option 3";
|
||||
////
|
||||
////function f(a: Options) { };
|
||||
////f("/*2*/
|
||||
|
||||
goTo.marker('1');
|
||||
verify.completionListContains("Option 1");
|
||||
verify.memberListCount(3);
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("Option 2");
|
||||
verify.memberListCount(3);
|
||||
20
tests/cases/fourslash/completionForStringLiteral2.ts
Normal file
20
tests/cases/fourslash/completionForStringLiteral2.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////var o = {
|
||||
//// foo() { },
|
||||
//// bar: 0,
|
||||
//// "some other name": 1
|
||||
////};
|
||||
////
|
||||
////o["/*1*/bar"];
|
||||
////o["/*2*/
|
||||
|
||||
goTo.marker('1');
|
||||
verify.completionListContains("foo");
|
||||
verify.completionListAllowsNewIdentifier();
|
||||
verify.memberListCount(3);
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("some other name");
|
||||
verify.completionListAllowsNewIdentifier();
|
||||
verify.memberListCount(3);
|
||||
20
tests/cases/fourslash/completionForStringLiteral3.ts
Normal file
20
tests/cases/fourslash/completionForStringLiteral3.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////declare function f(a: "A", b: number): void;
|
||||
////declare function f(a: "B", b: number): void;
|
||||
////declare function f(a: "C", b: number): void;
|
||||
////declare function f(a: string, b: number): void;
|
||||
////
|
||||
////f("/*1*/C", 2);
|
||||
////
|
||||
////f("/*2*/
|
||||
|
||||
goTo.marker('1');
|
||||
verify.completionListContains("A");
|
||||
verify.completionListAllowsNewIdentifier();
|
||||
verify.memberListCount(3);
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("A");
|
||||
verify.completionListAllowsNewIdentifier();
|
||||
verify.memberListCount(3);
|
||||
Loading…
x
Reference in New Issue
Block a user