completions: isNewIdentifierLocation = false for string literal where all legal values are known (#22933)

This commit is contained in:
Andy 2018-03-27 19:36:54 -07:00 committed by GitHub
parent 6118f211d1
commit 659dc03f68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 42 additions and 52 deletions

View File

@ -48,10 +48,10 @@ namespace ts.codefix {
const classType = checker.getTypeAtLocation(classDeclaration);
if (!checker.getIndexTypeOfType(classType, IndexKind.Number)) {
if (!classType.getNumberIndexType()) {
createMissingIndexSignatureDeclaration(implementedType, IndexKind.Number);
}
if (!checker.getIndexTypeOfType(classType, IndexKind.String)) {
if (!classType.getStringIndexType()) {
createMissingIndexSignatureDeclaration(implementedType, IndexKind.String);
}

View File

@ -86,11 +86,11 @@ namespace ts.Completions {
case StringLiteralCompletionKind.Properties: {
const entries: CompletionEntry[] = [];
getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, preferences); // Target will not be used, so arbitrary
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries };
}
case StringLiteralCompletionKind.Types: {
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.typeElement, sortText: "0" }));
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
}
default:
return Debug.assertNever(completion);
@ -360,9 +360,14 @@ namespace ts.Completions {
}
const enum StringLiteralCompletionKind { Paths, Properties, Types }
interface StringLiteralCompletionsFromProperties {
readonly kind: StringLiteralCompletionKind.Properties;
readonly symbols: ReadonlyArray<Symbol>;
readonly hasIndexSignature: boolean;
}
type StringLiteralCompletion =
| { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: ReadonlyArray<PathCompletions.PathCompletion> }
| { readonly kind: StringLiteralCompletionKind.Properties, readonly symbols: ReadonlyArray<Symbol> }
| StringLiteralCompletionsFromProperties
| { readonly kind: StringLiteralCompletionKind.Types, readonly types: ReadonlyArray<StringLiteralType> };
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost): StringLiteralCompletion | undefined {
switch (node.parent.kind) {
@ -377,7 +382,7 @@ namespace ts.Completions {
// bar: string;
// }
// let x: Foo["/*completion position*/"]
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType).getApparentProperties() };
return stringLiteralCompletionsFromProperties(typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType));
default:
return undefined;
}
@ -396,8 +401,7 @@ namespace ts.Completions {
// foo({
// '/*completion position*/'
// });
const type = typeChecker.getContextualType(node.parent.parent);
return { kind: StringLiteralCompletionKind.Properties, symbols: type && type.getApparentProperties() };
return stringLiteralCompletionsFromProperties(typeChecker.getContextualType(node.parent.parent));
}
return fromContextualType();
@ -410,7 +414,7 @@ namespace ts.Completions {
// }
// let a: A;
// a['/*completion position*/']
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeAtLocation(expression).getApparentProperties() };
return stringLiteralCompletionsFromProperties(typeChecker.getTypeAtLocation(expression));
}
return undefined;
}
@ -454,6 +458,10 @@ namespace ts.Completions {
}
}
function stringLiteralCompletionsFromProperties(type: Type | undefined): StringLiteralCompletionsFromProperties | undefined {
return type && { kind: StringLiteralCompletionKind.Properties, symbols: type.getApparentProperties(), hasIndexSignature: hasIndexSignature(type) };
}
function getStringLiteralTypes(type: Type, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> {
if (type && type.flags & TypeFlags.TypeParameter) {
type = type.getConstraint();
@ -1051,7 +1059,7 @@ namespace ts.Completions {
}
function addTypeProperties(type: Type): void {
isNewIdentifierLocation = !!type.getStringIndexType() || !!type.getNumberIndexType();
isNewIdentifierLocation = hasIndexSignature(type);
if (isSourceFileJavaScript(sourceFile)) {
// In javascript files, for union types, we don't just get the members that
@ -1454,11 +1462,9 @@ namespace ts.Completions {
let existingMembers: ReadonlyArray<Declaration>;
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
// We are completing on contextual types, but may also include properties
// other than those within the declared type.
isNewIdentifierLocation = true;
const typeForObject = typeChecker.getContextualType(objectLikeContainer);
if (!typeForObject) return GlobalsSearch.Fail;
isNewIdentifierLocation = hasIndexSignature(typeForObject);
typeMembers = getPropertiesForCompletion(typeForObject, typeChecker, /*isForAccess*/ false);
existingMembers = objectLikeContainer.properties;
}
@ -2247,4 +2253,8 @@ namespace ts.Completions {
function isFromObjectTypeDeclaration(node: Node): boolean {
return node.parent && (isClassElement(node.parent) || isTypeElement(node.parent)) && isObjectTypeDeclaration(node.parent.parent);
}
function hasIndexSignature(type: Type): boolean {
return !!type.getStringIndexType() || !!type.getNumberIndexType();
}
}

View File

@ -13,5 +13,5 @@
//// '/*1*/': ''
//// }
verify.completionsAt("0", ["jspm", '"jspm:browser"', '"jspm:dev"', '"jspm:node"'], { isNewIdentifierLocation: true });
verify.completionsAt("1", ["jspm", "jspm:browser", "jspm:dev", "jspm:node"], { isNewIdentifierLocation: true });
verify.completionsAt("0", ["jspm", '"jspm:browser"', '"jspm:dev"', '"jspm:node"']);
verify.completionsAt("1", ["jspm", "jspm:browser", "jspm:dev", "jspm:node"]);

View File

@ -19,5 +19,5 @@
//// }
//// }
verify.completionsAt("0", ["jspm", '"jspm:browser"', '"jspm:dev"', '"jspm:node"'], { isNewIdentifierLocation: true });
verify.completionsAt("1", ["jspm", "jspm:browser", "jspm:dev", "jspm:node"], { isNewIdentifierLocation: true });
verify.completionsAt("0", ["jspm", '"jspm:browser"', '"jspm:dev"', '"jspm:node"']);
verify.completionsAt("1", ["jspm", "jspm:browser", "jspm:dev", "jspm:node"]);

View File

@ -15,5 +15,5 @@
//// '/*1*/': ""
//// }
verify.completionsAt("0", ["jspm", '"jspm:browser"'], { isNewIdentifierLocation: true });
verify.completionsAt("1", ["jspm", "jspm:browser"], { isNewIdentifierLocation: true });
verify.completionsAt("0", ["jspm", '"jspm:browser"']);
verify.completionsAt("1", ["jspm", "jspm:browser"]);

View File

@ -5,16 +5,11 @@
//// bar: 0,
//// "some other name": 1
////};
////declare const p: { [s: string]: any, a: number };
////
////o["/*1*/bar"];
////o["/*2*/
////o["/*2*/ ;
////p["/*3*/"];
goTo.marker('1');
verify.completionListContains("foo");
verify.completionListAllowsNewIdentifier();
verify.completionListCount(3);
goTo.marker('2');
verify.completionListContains("some other name");
verify.completionListAllowsNewIdentifier();
verify.completionListCount(3);
verify.completionsAt(["1", "2"], ["foo", "bar", "some other name"]);
verify.completionsAt("3", ["a"], { isNewIdentifierLocation: true });

View File

@ -9,12 +9,4 @@
////
////f("/*2*/
goTo.marker('1');
verify.completionListContains("A");
verify.completionListAllowsNewIdentifier();
verify.completionListCount(3);
goTo.marker('2');
verify.completionListContains("A");
verify.completionListAllowsNewIdentifier();
verify.completionListCount(3);
verify.completionsAt(["1", "2"], ["A", "B", "C"]);

View File

@ -5,8 +5,5 @@
////function f(x: T, ...args: U[]) { };
////f("/*1*/", "/*2*/", "/*3*/");
// TODO: GH#22907
const options = { isNewIdentifierLocation: true };
verify.completionsAt("1", ["foo", "bar"], options);
verify.completionsAt("2", ["oof", "rab"], options);
verify.completionsAt("3", ["oof", "rab"], options);
verify.completionsAt("1", ["foo", "bar"]);
verify.completionsAt(["2", "3"], ["oof", "rab"]);

View File

@ -14,8 +14,7 @@
////x[|./*a*/|];
////x["/*b*/"];
// TODO: GH#22907
verify.completionsAt("b", ["foo ", "bar", "break", "any", "#", "$", "b", "1b"], { isNewIdentifierLocation: true });
verify.completionsAt("b", ["foo ", "bar", "break", "any", "#", "$", "b", "1b"]);
const replacementSpan = test.ranges()[0];
verify.completionsAt("a", [

View File

@ -7,5 +7,4 @@
////const x = { p: "x" };
////x.p = "/**/";
// TODO: GH#22907
verify.completionsAt("", ["x", "y"], { isNewIdentifierLocation: true });
verify.completionsAt("", ["x", "y"]);

View File

@ -4,4 +4,4 @@
// @Filename: /a.js
////const x = /** @type {{ s: string }} */ ({ /**/ });
verify.completionsAt("", ["s", "x"], { isNewIdentifierLocation: true });
verify.completionsAt("", ["s", "x"]);

View File

@ -3,5 +3,4 @@
////interface Foo { foo: string; bar: string; }
////type T = Pick<Foo, "/**/">;
// TODO: GH#22907
verify.completionsAt("", ["foo", "bar"], { isNewIdentifierLocation: true });
verify.completionsAt("", ["foo", "bar"]);

View File

@ -7,5 +7,4 @@
// We specifically filter out any array-like types.
// Private members will be excluded by `createUnionOrIntersectionProperty`.
// TODO: GH#22907
verify.completionsAt("", ["x"], { isNewIdentifierLocation: true });
verify.completionsAt("", ["x"]);