From 78599eed1088358fe2cf4133f83ba4ad1d174dea Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sat, 14 Nov 2020 02:24:04 +0900 Subject: [PATCH 01/17] fix: #41259 --- .../fourslash/completionObjectLiteral.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/cases/fourslash/completionObjectLiteral.ts diff --git a/tests/cases/fourslash/completionObjectLiteral.ts b/tests/cases/fourslash/completionObjectLiteral.ts new file mode 100644 index 0000000000000..f33e2b306e8ad --- /dev/null +++ b/tests/cases/fourslash/completionObjectLiteral.ts @@ -0,0 +1,22 @@ +/// + +const variableOne = 1; +const variableTwo = 2; +const objAny: any = { + v/*1*/ +} +const objNone = { + v/*2*/ +} + +interface Typed { + variableThree: number; +} +const typed: Typed = { + v/*3*/ +} + +verify.completions( + { marker: ["1", "2"], includes: ["variableOne", "variableTwo"]}, + { marker: "3", includes: ["variableThree"], excludes: ["variableOne", "variableTwo"]}, +); From ea29b1580511d56935bfaddad2679f15226c48de Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sat, 14 Nov 2020 02:28:35 +0900 Subject: [PATCH 02/17] fix: #41259 --- src/services/completions.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 40f3cac68c95c..ba31c95a00d91 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1893,9 +1893,17 @@ namespace ts.Completions { if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker); if (instantiatedType === undefined) { - return GlobalsSearch.Fail; + if (objectLikeContainer.flags & (NodeFlags.ThisNodeHasError | NodeFlags.InWithStatement)) { + return GlobalsSearch.Fail; + } + return GlobalsSearch.Continue; } const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions); + if (completionsType) { + if (completionsType.flags & TypeFlags.Any) { + return GlobalsSearch.Continue; + } + } isNewIdentifierLocation = hasIndexSignature(completionsType || instantiatedType); typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker); existingMembers = objectLikeContainer.properties; From 78bd6a6f609141f0677d9bae4d693f447f117f49 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sat, 14 Nov 2020 12:12:29 +0900 Subject: [PATCH 03/17] fix: fourslash --- .../fourslash/completionObjectLiteral.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/cases/fourslash/completionObjectLiteral.ts b/tests/cases/fourslash/completionObjectLiteral.ts index f33e2b306e8ad..5193c83b94eed 100644 --- a/tests/cases/fourslash/completionObjectLiteral.ts +++ b/tests/cases/fourslash/completionObjectLiteral.ts @@ -1,20 +1,20 @@ /// -const variableOne = 1; -const variableTwo = 2; -const objAny: any = { - v/*1*/ -} -const objNone = { - v/*2*/ -} +//// const variableOne = 1; +//// const variableTwo = 2; +//// const objAny: any = { +//// v/*1*/ +//// } +//// const objNone = { +//// v/*2*/ +//// } -interface Typed { - variableThree: number; -} -const typed: Typed = { - v/*3*/ -} +//// interface Typed { +//// variableThree: number; +//// } +//// const typed: Typed = { +//// v/*3*/ +//// } verify.completions( { marker: ["1", "2"], includes: ["variableOne", "variableTwo"]}, From 6d3db109f0969f930ce2c5d4fa16c1ffb4cab275 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sat, 14 Nov 2020 12:27:11 +0900 Subject: [PATCH 04/17] fix: remove nested if --- src/services/completions.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index ba31c95a00d91..53212dff74aed 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1899,10 +1899,8 @@ namespace ts.Completions { return GlobalsSearch.Continue; } const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions); - if (completionsType) { - if (completionsType.flags & TypeFlags.Any) { - return GlobalsSearch.Continue; - } + if (completionsType && (completionsType?.flags & TypeFlags.Any)) { + return GlobalsSearch.Continue; } isNewIdentifierLocation = hasIndexSignature(completionsType || instantiatedType); typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker); From 0e6b319bfda26f6ca668bbe637b95baefb54b584 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sun, 15 Nov 2020 09:59:05 +0900 Subject: [PATCH 05/17] fix: change tc result for #41259 --- ...stAtIdentifierDefinitionLocations_destructuring.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts index 27c7f9a4dae21..29a4588efba92 100644 --- a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts @@ -16,4 +16,13 @@ //// function func2({ a, b/*parameter2*/ -verify.completions({ marker: test.markers(), exact: undefined }); +verify.completions({ marker: [ + "variable1", "variable2", + "variable3", "variable4", + "variable5", "variable6", + "parameter1" +], exact: undefined }); + +verify.completions({ marker: "parameter2", includes: [{ + name: "Object", sortText: completion.SortText.GlobalsOrKeywords +}] }); From 20e2ad7ab93f7716dd47c464d1b86d36565e4a54 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Tue, 17 Nov 2020 17:29:36 +0900 Subject: [PATCH 06/17] fix: less restrictive shorthand completion rules --- lib/tsserverlibrary.d.ts | 3 ++ lib/typescript.d.ts | 3 ++ src/compiler/checker.ts | 6 ++- src/compiler/types.ts | 4 ++ src/services/completions.ts | 16 +++++++- .../reference/api/tsserverlibrary.d.ts | 2 + tests/baselines/reference/api/typescript.d.ts | 2 + ...tifierDefinitionLocations_destructuring.ts | 16 +++++--- .../fourslash/completionObjectLiteral.ts | 22 ----------- ...letionPropertyShorthandForObjectLiteral.ts | 39 +++++++++++++++++++ .../completionsGenericUnconstrained.ts | 5 ++- .../fourslash/completionsSelfDeclaring2.ts | 5 ++- 12 files changed, 90 insertions(+), 33 deletions(-) delete mode 100644 tests/cases/fourslash/completionObjectLiteral.ts create mode 100644 tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts diff --git a/lib/tsserverlibrary.d.ts b/lib/tsserverlibrary.d.ts index cf6a6f941eb59..7f126da73f103 100644 --- a/lib/tsserverlibrary.d.ts +++ b/lib/tsserverlibrary.d.ts @@ -2182,6 +2182,9 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; + + isTypeSubsetOf(source: Type, target: Type): boolean; + isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/lib/typescript.d.ts b/lib/typescript.d.ts index dcee9ad7b1038..2522c458c594d 100644 --- a/lib/typescript.d.ts +++ b/lib/typescript.d.ts @@ -2182,6 +2182,9 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; + + isTypeSubsetOf(source: Type, target: Type): boolean; + isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65cbfed68a6eb..e00d711594e87 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -627,6 +627,7 @@ namespace ts { getESSymbolType: () => esSymbolType, getNeverType: () => neverType, getOptionalType: () => optionalType, + getGlobalObjectType: () => globalObjectType, isSymbolAccessible, isArrayType, isTupleType, @@ -704,6 +705,9 @@ namespace ts { getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, isDeclarationVisible, + + isTypeSubsetOf, + isEmptyObjectType, }; function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined { @@ -21162,7 +21166,7 @@ namespace ts { } function isTypeSubsetOf(source: Type, target: Type) { - return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target); + return source === target || !!(target.flags & TypeFlags.Union) && isTypeSubsetOfUnion(source, target); } function isTypeSubsetOfUnion(source: Type, target: UnionType) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0ce9c2afa7539..3c8bcf2016ed6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4075,6 +4075,7 @@ namespace ts { /* @internal */ getESSymbolType(): Type; /* @internal */ getNeverType(): Type; /* @internal */ getOptionalType(): Type; + /* @internal */ getGlobalObjectType(): Type; /* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type; /* @internal */ createArrayType(elementType: Type): Type; /* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined; @@ -4168,6 +4169,9 @@ namespace ts { /* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): readonly TypeParameter[] | undefined; /* @internal */ isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; + + isTypeSubsetOf(source: Type, target: Type): boolean; + isEmptyObjectType(type: Type): boolean; } /* @internal */ diff --git a/src/services/completions.ts b/src/services/completions.ts index 53212dff74aed..1418718b993ef 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1892,6 +1892,8 @@ namespace ts.Completions { if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker); + + // Check completions for Object property value shorthand if (instantiatedType === undefined) { if (objectLikeContainer.flags & (NodeFlags.ThisNodeHasError | NodeFlags.InWithStatement)) { return GlobalsSearch.Fail; @@ -1899,10 +1901,20 @@ namespace ts.Completions { return GlobalsSearch.Continue; } const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions); - if (completionsType && (completionsType?.flags & TypeFlags.Any)) { + if (completionsType && (completionsType?.flags & (TypeFlags.Any | TypeFlags.Unknown))) { + return GlobalsSearch.Continue; + } + const validType = completionsType || instantiatedType; + if (typeChecker.isTypeSubsetOf(typeChecker.getGlobalObjectType(), validType) || typeChecker.isEmptyObjectType(validType)) { return GlobalsSearch.Continue; } - isNewIdentifierLocation = hasIndexSignature(completionsType || instantiatedType); + const hasStringIndexType = validType.getStringIndexType(); + const hasNumberIndextype = validType.getNumberIndexType(); + if (hasStringIndexType) { + return GlobalsSearch.Continue; + } + + isNewIdentifierLocation = (!!hasStringIndexType || !!hasNumberIndextype); typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker); existingMembers = objectLikeContainer.properties; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 15653ecacf258..09e5c4e8ee03b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2238,6 +2238,8 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; + isTypeSubsetOf(source: Type, target: Type): boolean; + isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 26cdf6ba46056..010231299e5b2 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2238,6 +2238,8 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; + isTypeSubsetOf(source: Type, target: Type): boolean; + isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts index 29a4588efba92..ee58351ad050d 100644 --- a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts @@ -1,28 +1,32 @@ /// +// @Filename: a.ts //// var [x/*variable1*/ +// @Filename: b.ts //// var [x, y/*variable2*/ +// @Filename: c.ts //// var [./*variable3*/ +// @Filename: d.ts //// var [x, ...z/*variable4*/ +// @Filename: e.ts //// var {x/*variable5*/ +// @Filename: f.ts //// var {x, y/*variable6*/ +// @Filename: g.ts //// function func1({ a/*parameter1*/ +// @Filename: h.ts //// function func2({ a, b/*parameter2*/ verify.completions({ marker: [ "variable1", "variable2", "variable3", "variable4", "variable5", "variable6", - "parameter1" -], exact: undefined }); - -verify.completions({ marker: "parameter2", includes: [{ - name: "Object", sortText: completion.SortText.GlobalsOrKeywords -}] }); + "parameter1", "parameter2" +], exact: undefined }); \ No newline at end of file diff --git a/tests/cases/fourslash/completionObjectLiteral.ts b/tests/cases/fourslash/completionObjectLiteral.ts deleted file mode 100644 index 5193c83b94eed..0000000000000 --- a/tests/cases/fourslash/completionObjectLiteral.ts +++ /dev/null @@ -1,22 +0,0 @@ -/// - -//// const variableOne = 1; -//// const variableTwo = 2; -//// const objAny: any = { -//// v/*1*/ -//// } -//// const objNone = { -//// v/*2*/ -//// } - -//// interface Typed { -//// variableThree: number; -//// } -//// const typed: Typed = { -//// v/*3*/ -//// } - -verify.completions( - { marker: ["1", "2"], includes: ["variableOne", "variableTwo"]}, - { marker: "3", includes: ["variableThree"], excludes: ["variableOne", "variableTwo"]}, -); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts new file mode 100644 index 0000000000000..5c0a70ea3f7e2 --- /dev/null +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts @@ -0,0 +1,39 @@ +/// + +//// declare const foo: number; +//// interface Empty {} +//// interface Typed { typed: number; } + +//// declare function f1(obj: unknown): void; +//// declare function f2(obj: object): void; +//// declare function f3(obj: Object): void; +//// declare function f4(obj: Record): void; +//// declare function f5(obj: Record): void; +//// declare function f6(obj: { [key: string]: number }): void; +//// declare function f7(obj: { [key: number]: number }): void; +//// declare function f8(obj: Typed): void; +//// declare function f9(obj: T): void; +//// declare function f10(obj: T): void; +//// declare function f11(obj: T): void; +//// declare function f12(obj: T): void; +//// declare function f13(obj: T): void; + +//// f1({f/*1*/}); +//// f2({f/*2*/}); +//// f3({f/*3*/}); +//// f4({f/*4*/}); +//// f5({f/*5*/}); +//// f6({f/*6*/}); +//// f7({f/*7*/}); +//// f8({f/*8*/}); +//// f9({f/*9*/}); +//// f10({f/*10*/}); +//// f11({f/*11*/}); +//// f12({f/*12*/}); +//// f13({f/*13*/}); + +verify.completions( + { marker: ["1", "2", "3", "4", "6", "9", "10", "11", "12", "13"], includes: ["foo"]}, + { marker: ["5", "7"], excludes: ["foo"], isNewIdentifierLocation: true}, + { marker: ["8"], excludes: ["foo"]}, +); diff --git a/tests/cases/fourslash/completionsGenericUnconstrained.ts b/tests/cases/fourslash/completionsGenericUnconstrained.ts index 3865dc515618f..d18991830e7dd 100644 --- a/tests/cases/fourslash/completionsGenericUnconstrained.ts +++ b/tests/cases/fourslash/completionsGenericUnconstrained.ts @@ -10,5 +10,8 @@ verify.completions({ marker: "", - exact: [] + includes: [{ + name: "Object", + sortText: completion.SortText.GlobalsOrKeywords + }] }); diff --git a/tests/cases/fourslash/completionsSelfDeclaring2.ts b/tests/cases/fourslash/completionsSelfDeclaring2.ts index ee313de90c0bf..8d2cd18437ee5 100644 --- a/tests/cases/fourslash/completionsSelfDeclaring2.ts +++ b/tests/cases/fourslash/completionsSelfDeclaring2.ts @@ -9,7 +9,10 @@ verify.completions({ marker: "1", - exact: [] + includes: [{ + name: "Object", + sortText: completion.SortText.GlobalsOrKeywords + }] }); verify.completions({ From 2f8e44121960f82896398631ffa5f3e59826ea4e Mon Sep 17 00:00:00 2001 From: orange4glace Date: Wed, 18 Nov 2020 22:33:48 +0900 Subject: [PATCH 07/17] fix: use typeMembers to find out whether properties are empty --- lib/tsserverlibrary.d.ts | 5 +-- lib/typescript.d.ts | 3 -- src/compiler/checker.ts | 3 -- src/compiler/types.ts | 5 +-- src/services/completions.ts | 37 ++++++++++--------- .../reference/api/tsserverlibrary.d.ts | 2 - tests/baselines/reference/api/typescript.d.ts | 2 - ...letionPropertyShorthandForObjectLiteral.ts | 26 ++++++++----- 8 files changed, 37 insertions(+), 46 deletions(-) diff --git a/lib/tsserverlibrary.d.ts b/lib/tsserverlibrary.d.ts index 7f126da73f103..f7cad75befe38 100644 --- a/lib/tsserverlibrary.d.ts +++ b/lib/tsserverlibrary.d.ts @@ -2181,10 +2181,7 @@ declare namespace ts { * if the cancellation token is triggered. Typically, if it is used for error checking * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ - runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; - - isTypeSubsetOf(source: Type, target: Type): boolean; - isEmptyObjectType(type: Type): boolean; + runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T;; } export enum NodeBuilderFlags { None = 0, diff --git a/lib/typescript.d.ts b/lib/typescript.d.ts index 2522c458c594d..dcee9ad7b1038 100644 --- a/lib/typescript.d.ts +++ b/lib/typescript.d.ts @@ -2182,9 +2182,6 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; - - isTypeSubsetOf(source: Type, target: Type): boolean; - isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e00d711594e87..02b0c1642c9c7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -705,9 +705,6 @@ namespace ts { getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, isDeclarationVisible, - - isTypeSubsetOf, - isEmptyObjectType, }; function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3c8bcf2016ed6..44c538de35c66 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4075,7 +4075,7 @@ namespace ts { /* @internal */ getESSymbolType(): Type; /* @internal */ getNeverType(): Type; /* @internal */ getOptionalType(): Type; - /* @internal */ getGlobalObjectType(): Type; + /* @internal */ getGlobalObjectType: () => Type; /* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type; /* @internal */ createArrayType(elementType: Type): Type; /* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined; @@ -4169,9 +4169,6 @@ namespace ts { /* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): readonly TypeParameter[] | undefined; /* @internal */ isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; - - isTypeSubsetOf(source: Type, target: Type): boolean; - isEmptyObjectType(type: Type): boolean; } /* @internal */ diff --git a/src/services/completions.ts b/src/services/completions.ts index 1418718b993ef..85a2fad4447e7 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1892,7 +1892,7 @@ namespace ts.Completions { if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker); - + // Check completions for Object property value shorthand if (instantiatedType === undefined) { if (objectLikeContainer.flags & (NodeFlags.ThisNodeHasError | NodeFlags.InWithStatement)) { @@ -1901,22 +1901,18 @@ namespace ts.Completions { return GlobalsSearch.Continue; } const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions); - if (completionsType && (completionsType?.flags & (TypeFlags.Any | TypeFlags.Unknown))) { - return GlobalsSearch.Continue; - } - const validType = completionsType || instantiatedType; - if (typeChecker.isTypeSubsetOf(typeChecker.getGlobalObjectType(), validType) || typeChecker.isEmptyObjectType(validType)) { - return GlobalsSearch.Continue; - } - const hasStringIndexType = validType.getStringIndexType(); - const hasNumberIndextype = validType.getNumberIndexType(); - if (hasStringIndexType) { - return GlobalsSearch.Continue; - } - - isNewIdentifierLocation = (!!hasStringIndexType || !!hasNumberIndextype); - typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker); + const hasStringIndexType = (completionsType || instantiatedType).getStringIndexType(); + const hasNumberIndextype = (completionsType || instantiatedType).getNumberIndexType(); + isNewIdentifierLocation = !!hasStringIndexType || !!hasNumberIndextype; + typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker, /*ignoreGlobalObject*/true); existingMembers = objectLikeContainer.properties; + + if (typeMembers.length === 0) { + // Edge case: If NumberIndexType exists + if (!hasNumberIndextype) { + return GlobalsSearch.Continue; + } + } } else { Debug.assert(objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern); @@ -2693,21 +2689,26 @@ namespace ts.Completions { return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined); } - function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] { + function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker, ignoreGlobalObject?: boolean): Symbol[] { + const globalObjectType = checker.getGlobalObjectType(); const hasCompletionsType = completionsType && completionsType !== contextualType; const type = hasCompletionsType && !(completionsType!.flags & TypeFlags.AnyOrUnknown) ? checker.getUnionType([contextualType, completionsType!]) : contextualType; - const properties = type.isUnion() + let properties = type.isUnion() ? checker.getAllPossiblePropertiesOfTypes(type.types.filter(memberType => // If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals. !(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType) || + (ignoreGlobalObject && memberType === globalObjectType) || typeHasCallOrConstructSignatures(memberType, checker) || checker.isTypeInvalidDueToUnionDiscriminant(memberType, obj)))) : type.getApparentProperties(); + if (ignoreGlobalObject && type === globalObjectType) { + return []; + } return hasCompletionsType ? properties.filter(hasDeclarationOtherThanSelf) : properties; // Filter out members whose only declaration is the object literal itself to avoid diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 09e5c4e8ee03b..15653ecacf258 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2238,8 +2238,6 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; - isTypeSubsetOf(source: Type, target: Type): boolean; - isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 010231299e5b2..26cdf6ba46056 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2238,8 +2238,6 @@ declare namespace ts { * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; - isTypeSubsetOf(source: Type, target: Type): boolean; - isEmptyObjectType(type: Type): boolean; } export enum NodeBuilderFlags { None = 0, diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts index 5c0a70ea3f7e2..247ef3f1dfd66 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts @@ -10,13 +10,16 @@ //// declare function f4(obj: Record): void; //// declare function f5(obj: Record): void; //// declare function f6(obj: { [key: string]: number }): void; -//// declare function f7(obj: { [key: number]: number }): void; -//// declare function f8(obj: Typed): void; -//// declare function f9(obj: T): void; -//// declare function f10(obj: T): void; -//// declare function f11(obj: T): void; -//// declare function f12(obj: T): void; -//// declare function f13(obj: T): void; +//// declare function f7(obj: { [key: string]: number, prop: number }): void; +//// declare function f8(obj: { [key: number]: number }): void; +//// declare function f9(obj: Typed): void; +//// declare function f10(obj: T): void; +//// declare function f11(obj: T): void; +//// declare function f12(obj: T): void; +//// declare function f13(obj: T): void; +//// declare function f14(obj: T): void; +//// declare function f15 | Object)>(obj: T): void; +//// declare function f16(obj: T): void; //// f1({f/*1*/}); //// f2({f/*2*/}); @@ -31,9 +34,12 @@ //// f11({f/*11*/}); //// f12({f/*12*/}); //// f13({f/*13*/}); +//// f14({f/*14*/}); +//// f15({f/*15*/}); +//// f16({f/*16*/}); verify.completions( - { marker: ["1", "2", "3", "4", "6", "9", "10", "11", "12", "13"], includes: ["foo"]}, - { marker: ["5", "7"], excludes: ["foo"], isNewIdentifierLocation: true}, - { marker: ["8"], excludes: ["foo"]}, + { marker: ["1", "2", "3", "4", "6", "10", "11", "12", "13", "14", "15"], includes: ["foo"]}, + { marker: ["5", "7", "8"], excludes: ["foo"], isNewIdentifierLocation: true}, + { marker: ["9", "16"], excludes: ["foo"]}, ); From 9938e53919d9c854599906b9c1c79f7bb344557f Mon Sep 17 00:00:00 2001 From: orange4glace Date: Wed, 18 Nov 2020 22:34:42 +0900 Subject: [PATCH 08/17] fix: typo --- lib/tsserverlibrary.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tsserverlibrary.d.ts b/lib/tsserverlibrary.d.ts index f7cad75befe38..cf6a6f941eb59 100644 --- a/lib/tsserverlibrary.d.ts +++ b/lib/tsserverlibrary.d.ts @@ -2181,7 +2181,7 @@ declare namespace ts { * if the cancellation token is triggered. Typically, if it is used for error checking * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. */ - runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T;; + runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; } export enum NodeBuilderFlags { None = 0, From 524b90e4de8fcd6dc310838012d202106d6bec9f Mon Sep 17 00:00:00 2001 From: orange4glace Date: Wed, 18 Nov 2020 23:02:45 +0900 Subject: [PATCH 09/17] fix: lint --- src/services/completions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 85a2fad4447e7..380d6b408c143 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1892,7 +1892,7 @@ namespace ts.Completions { if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker); - + // Check completions for Object property value shorthand if (instantiatedType === undefined) { if (objectLikeContainer.flags & (NodeFlags.ThisNodeHasError | NodeFlags.InWithStatement)) { @@ -1904,7 +1904,7 @@ namespace ts.Completions { const hasStringIndexType = (completionsType || instantiatedType).getStringIndexType(); const hasNumberIndextype = (completionsType || instantiatedType).getNumberIndexType(); isNewIdentifierLocation = !!hasStringIndexType || !!hasNumberIndextype; - typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker, /*ignoreGlobalObject*/true); + typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker, /*ignoreGlobalObject*/ true); existingMembers = objectLikeContainer.properties; if (typeMembers.length === 0) { @@ -2696,7 +2696,7 @@ namespace ts.Completions { ? checker.getUnionType([contextualType, completionsType!]) : contextualType; - let properties = type.isUnion() + const properties = type.isUnion() ? checker.getAllPossiblePropertiesOfTypes(type.types.filter(memberType => // If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals. !(memberType.flags & TypeFlags.Primitive || From 8358a40adeb8cb0dd13e580e53264d668241a6df Mon Sep 17 00:00:00 2001 From: orange4glace Date: Fri, 20 Nov 2020 01:03:33 +0900 Subject: [PATCH 10/17] fix: exclude Object in completion --- src/compiler/checker.ts | 3 +-- src/compiler/types.ts | 1 - src/services/completions.ts | 9 ++------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 02b0c1642c9c7..65cbfed68a6eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -627,7 +627,6 @@ namespace ts { getESSymbolType: () => esSymbolType, getNeverType: () => neverType, getOptionalType: () => optionalType, - getGlobalObjectType: () => globalObjectType, isSymbolAccessible, isArrayType, isTupleType, @@ -21163,7 +21162,7 @@ namespace ts { } function isTypeSubsetOf(source: Type, target: Type) { - return source === target || !!(target.flags & TypeFlags.Union) && isTypeSubsetOfUnion(source, target); + return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target); } function isTypeSubsetOfUnion(source: Type, target: UnionType) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 44c538de35c66..0ce9c2afa7539 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4075,7 +4075,6 @@ namespace ts { /* @internal */ getESSymbolType(): Type; /* @internal */ getNeverType(): Type; /* @internal */ getOptionalType(): Type; - /* @internal */ getGlobalObjectType: () => Type; /* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type; /* @internal */ createArrayType(elementType: Type): Type; /* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined; diff --git a/src/services/completions.ts b/src/services/completions.ts index 380d6b408c143..0695649893e17 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1904,7 +1904,7 @@ namespace ts.Completions { const hasStringIndexType = (completionsType || instantiatedType).getStringIndexType(); const hasNumberIndextype = (completionsType || instantiatedType).getNumberIndexType(); isNewIdentifierLocation = !!hasStringIndexType || !!hasNumberIndextype; - typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker, /*ignoreGlobalObject*/ true); + typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker); existingMembers = objectLikeContainer.properties; if (typeMembers.length === 0) { @@ -2689,8 +2689,7 @@ namespace ts.Completions { return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined); } - function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker, ignoreGlobalObject?: boolean): Symbol[] { - const globalObjectType = checker.getGlobalObjectType(); + function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] { const hasCompletionsType = completionsType && completionsType !== contextualType; const type = hasCompletionsType && !(completionsType!.flags & TypeFlags.AnyOrUnknown) ? checker.getUnionType([contextualType, completionsType!]) @@ -2701,14 +2700,10 @@ namespace ts.Completions { // If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals. !(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType) || - (ignoreGlobalObject && memberType === globalObjectType) || typeHasCallOrConstructSignatures(memberType, checker) || checker.isTypeInvalidDueToUnionDiscriminant(memberType, obj)))) : type.getApparentProperties(); - if (ignoreGlobalObject && type === globalObjectType) { - return []; - } return hasCompletionsType ? properties.filter(hasDeclarationOtherThanSelf) : properties; // Filter out members whose only declaration is the object literal itself to avoid From a0f2476fac4e1f4382e8eb1b161020e4f04705ea Mon Sep 17 00:00:00 2001 From: orange4glace Date: Fri, 20 Nov 2020 01:06:36 +0900 Subject: [PATCH 11/17] fix: test --- .../completionPropertyShorthandForObjectLiteral.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts index 247ef3f1dfd66..7b06412c8df46 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts @@ -18,7 +18,7 @@ //// declare function f12(obj: T): void; //// declare function f13(obj: T): void; //// declare function f14(obj: T): void; -//// declare function f15 | Object)>(obj: T): void; +//// declare function f15 | {})>(obj: T): void; //// declare function f16(obj: T): void; //// f1({f/*1*/}); @@ -39,7 +39,7 @@ //// f16({f/*16*/}); verify.completions( - { marker: ["1", "2", "3", "4", "6", "10", "11", "12", "13", "14", "15"], includes: ["foo"]}, + { marker: ["1", "2", "4", "6", "10", "11", "13", "14", "15"], includes: ["foo"]}, { marker: ["5", "7", "8"], excludes: ["foo"], isNewIdentifierLocation: true}, - { marker: ["9", "16"], excludes: ["foo"]}, + { marker: ["3", "9", "12", "16"], excludes: ["foo"]}, ); From 00dcdd6a65b304f32d00667c55e659efdba445b7 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Fri, 20 Nov 2020 13:27:25 +0900 Subject: [PATCH 12/17] fix: testcase tidy up --- ...tifierDefinitionLocations_destructuring.ts | 7 +--- ...letionPropertyShorthandForObjectLiteral.ts | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts index ee58351ad050d..6cc77a3f61cf8 100644 --- a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_destructuring.ts @@ -24,9 +24,4 @@ // @Filename: h.ts //// function func2({ a, b/*parameter2*/ -verify.completions({ marker: [ - "variable1", "variable2", - "variable3", "variable4", - "variable5", "variable6", - "parameter1", "parameter2" -], exact: undefined }); \ No newline at end of file +verify.completions({ marker: test.markers(), exact: undefined }); \ No newline at end of file diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts index 7b06412c8df46..d7473f01ac328 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts @@ -4,22 +4,24 @@ //// interface Empty {} //// interface Typed { typed: number; } -//// declare function f1(obj: unknown): void; -//// declare function f2(obj: object): void; -//// declare function f3(obj: Object): void; -//// declare function f4(obj: Record): void; -//// declare function f5(obj: Record): void; +//// declare function f1(obj): void; +//// declare function f2(obj: any): void; +//// declare function f3(obj: unknown): void; +//// declare function f4(obj: object): void; +//// declare function f5(obj: Record): void; //// declare function f6(obj: { [key: string]: number }): void; -//// declare function f7(obj: { [key: string]: number, prop: number }): void; -//// declare function f8(obj: { [key: number]: number }): void; -//// declare function f9(obj: Typed): void; -//// declare function f10(obj: T): void; -//// declare function f11(obj: T): void; -//// declare function f12(obj: T): void; -//// declare function f13(obj: T): void; -//// declare function f14(obj: T): void; -//// declare function f15 | {})>(obj: T): void; -//// declare function f16(obj: T): void; +//// declare function f7(obj: T): void; +//// declare function f8(obj: T): void; +//// declare function f9(obj: T): void; +//// declare function f10(obj: T): void; +//// declare function f11 | {})>(obj: T): void; +//// declare function f12(obj: Object): void; +//// declare function f13(obj: T): void; +//// declare function f14(obj: Typed): void; +//// declare function f15(obj: T): void; +//// declare function f16(obj: Record): void; +//// declare function f17(obj: { [key: string]: number, prop: number }): void; +//// declare function f18(obj: { [key: number]: number }): void; //// f1({f/*1*/}); //// f2({f/*2*/}); @@ -37,9 +39,11 @@ //// f14({f/*14*/}); //// f15({f/*15*/}); //// f16({f/*16*/}); +//// f17({f/*17*/}); +//// f18({f/*18*/}); verify.completions( - { marker: ["1", "2", "4", "6", "10", "11", "13", "14", "15"], includes: ["foo"]}, - { marker: ["5", "7", "8"], excludes: ["foo"], isNewIdentifierLocation: true}, - { marker: ["3", "9", "12", "16"], excludes: ["foo"]}, + { marker: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], includes: ["foo"]}, + { marker: ["12", "13", "14", "15"], excludes: ["foo"]}, + { marker: ["16", "17", "18"], excludes: ["foo"], isNewIdentifierLocation: true}, ); From b70118f4fb21191bccbf5b3e0096faf7d3018acf Mon Sep 17 00:00:00 2001 From: orange4glace Date: Wed, 25 Nov 2020 01:30:36 +0900 Subject: [PATCH 13/17] fix: apply completions for unclosed literal and missing comma --- src/services/completions.ts | 3 ++- ...pletionPropertyShorthandForObjectLiteral2.ts | 17 +++++++++++++++++ ...pletionPropertyShorthandForObjectLiteral3.ts | 11 +++++++++++ ...pletionPropertyShorthandForObjectLiteral4.ts | 11 +++++++++++ .../globalCompletionListInsideObjectLiterals.ts | 5 ++--- 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts create mode 100644 tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts create mode 100644 tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 0695649893e17..387910daf255e 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1895,7 +1895,7 @@ namespace ts.Completions { // Check completions for Object property value shorthand if (instantiatedType === undefined) { - if (objectLikeContainer.flags & (NodeFlags.ThisNodeHasError | NodeFlags.InWithStatement)) { + if (objectLikeContainer.flags & NodeFlags.InWithStatement) { return GlobalsSearch.Fail; } return GlobalsSearch.Continue; @@ -2326,6 +2326,7 @@ namespace ts.Completions { } return isDeclarationName(contextToken) + && !isShorthandPropertyAssignment(contextToken.parent) && !isJsxAttribute(contextToken.parent) // Don't block completions if we're in `class C /**/`, because we're *past* the end of the identifier and might want to complete `extends`. // If `contextToken !== previousToken`, this is `class C ex/**/`. diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts new file mode 100644 index 0000000000000..fcd448178e1dd --- /dev/null +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts @@ -0,0 +1,17 @@ +/// + +//// const foo = 1; +//// const bar = 2; + +//// const obj1 = { +//// foo b/*1*/ +//// }; + +//// const obj2: any = { +//// foo b/*2*/ +//// }; + +verify.completions({ + marker: test.markers(), + includes: ["bar"] +}); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts new file mode 100644 index 0000000000000..07b4f55d7ade3 --- /dev/null +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts @@ -0,0 +1,11 @@ +/// + +//// const foo = 1; +//// const bar = 2; +//// const obj = { +//// foo b/*1*/ + +verify.completions({ + marker: ["1"], + includes: ["bar"] +}); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts new file mode 100644 index 0000000000000..c50ffb2140858 --- /dev/null +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts @@ -0,0 +1,11 @@ +/// + +//// const foo = 1; +//// const bar = 2; +//// const obj: any = { +//// foo b/*1*/ + +verify.completions({ + marker: ["1"], + includes: ["bar"] +}); diff --git a/tests/cases/fourslash/globalCompletionListInsideObjectLiterals.ts b/tests/cases/fourslash/globalCompletionListInsideObjectLiterals.ts index e41ec0a99f44d..503758461489b 100644 --- a/tests/cases/fourslash/globalCompletionListInsideObjectLiterals.ts +++ b/tests/cases/fourslash/globalCompletionListInsideObjectLiterals.ts @@ -28,7 +28,6 @@ // 5, 6: Literal member completion after member name with empty member expression. const exact = ["p1", "p2", "p3", "p4", ...completion.globalsPlus(["ObjectLiterals"])]; verify.completions( - { marker: ["1"], exact, isNewIdentifierLocation: true }, - { marker: ["2", "3", "5", "6"], exact }, - { marker: "4", exact: undefined }, + { marker: ["1",], exact, isNewIdentifierLocation: true }, + { marker: ["2", "3", "4", "5", "6"], exact } ); From a91b5e223fdac7ab4e962b34682c5b6ab467a214 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Fri, 27 Nov 2020 00:31:20 +0900 Subject: [PATCH 14/17] fix: ignore auto-imports --- src/services/completions.ts | 5 +++++ .../completionPropertyShorthandForObjectLiteral5.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 387910daf255e..51a296aeafb80 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1086,6 +1086,7 @@ namespace ts.Completions { const semanticStart = timestamp(); let completionKind = CompletionKind.None; let isNewIdentifierLocation = false; + let isNonContextualObjectLiteral = false; let keywordFilters = KeywordCompletionFilters.None; // This also gets mutated in nested-functions after the return let symbols: Symbol[] = []; @@ -1471,6 +1472,8 @@ namespace ts.Completions { } function shouldOfferImportCompletions(): boolean { + // If current completion is for non-contextual Object literal shortahands, ignore auto-import symbols + if (isNonContextualObjectLiteral) return false; // If not already a module, must have modules enabled. if (!preferences.includeCompletionsForModuleExports) return false; // If already using ES6 modules, OK to continue using them. @@ -1898,6 +1901,7 @@ namespace ts.Completions { if (objectLikeContainer.flags & NodeFlags.InWithStatement) { return GlobalsSearch.Fail; } + isNonContextualObjectLiteral = true; return GlobalsSearch.Continue; } const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions); @@ -1910,6 +1914,7 @@ namespace ts.Completions { if (typeMembers.length === 0) { // Edge case: If NumberIndexType exists if (!hasNumberIndextype) { + isNonContextualObjectLiteral = true; return GlobalsSearch.Continue; } } diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts new file mode 100644 index 0000000000000..c2765c962e4fb --- /dev/null +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts @@ -0,0 +1,13 @@ +// @module: esnext + +// @Filename: /a.ts +//// export const exportedConstant = 0; + +// @Filename: /b.ts +//// const obj = { exp/**/ + +verify.completions({ + marker: "", + exact: completion.globalsPlus(['obj']), + preferences: { includeCompletionsForModuleExports: true } +}); \ No newline at end of file From 4b84a5945ce8e98fa98ba8c511396294a40da5d0 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sun, 6 Dec 2020 23:28:22 +0900 Subject: [PATCH 15/17] fix: use exact to ensure the order of completions --- ...letionPropertyShorthandForObjectLiteral.ts | 35 ++++++++++++------- ...etionPropertyShorthandForObjectLiteral2.ts | 2 +- ...etionPropertyShorthandForObjectLiteral3.ts | 2 +- ...etionPropertyShorthandForObjectLiteral4.ts | 2 +- ...etionPropertyShorthandForObjectLiteral5.ts | 2 +- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts index d7473f01ac328..c4695d8dc8662 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts @@ -15,13 +15,11 @@ //// declare function f9(obj: T): void; //// declare function f10(obj: T): void; //// declare function f11 | {})>(obj: T): void; -//// declare function f12(obj: Object): void; -//// declare function f13(obj: T): void; -//// declare function f14(obj: Typed): void; -//// declare function f15(obj: T): void; -//// declare function f16(obj: Record): void; -//// declare function f17(obj: { [key: string]: number, prop: number }): void; -//// declare function f18(obj: { [key: number]: number }): void; +//// declare function f12(obj: Typed): void; +//// declare function f13(obj: T): void; +//// declare function f14(obj: { [key: string]: number, prop: number }): void; +//// declare function f15(obj: Record): void; +//// declare function f16(obj: { [key: number]: number }): void; //// f1({f/*1*/}); //// f2({f/*2*/}); @@ -39,11 +37,24 @@ //// f14({f/*14*/}); //// f15({f/*15*/}); //// f16({f/*16*/}); -//// f17({f/*17*/}); -//// f18({f/*18*/}); +const locals = [ + ...(() => { + const symbols = []; + for (let i = 1; i <= 16; i ++) { + symbols.push(`f${i}`); + } + return symbols; + })(), + "foo" +]; verify.completions( - { marker: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], includes: ["foo"]}, - { marker: ["12", "13", "14", "15"], excludes: ["foo"]}, - { marker: ["16", "17", "18"], excludes: ["foo"], isNewIdentifierLocation: true}, + // Non-contextual, any, unknown, object, Record, [key: string]: .., Type parameter, etc.. + { marker: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], exact: completion.globalsPlus(locals)}, + // Has named property + { marker: ["12", "13"], exact: "typed"}, + // Has both StringIndexType and named property + { marker: ["14"], exact: "prop", isNewIdentifierLocation: true}, + // NumberIndexType + { marker: ["15", "16"], exact: [], isNewIdentifierLocation: true}, ); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts index fcd448178e1dd..ec54418c77935 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral2.ts @@ -13,5 +13,5 @@ verify.completions({ marker: test.markers(), - includes: ["bar"] + exact: completion.globalsPlus(["foo", "bar", "obj1", "obj2"]), }); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts index 07b4f55d7ade3..0a6fad60b293e 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral3.ts @@ -7,5 +7,5 @@ verify.completions({ marker: ["1"], - includes: ["bar"] + exact: completion.globalsPlus(["foo", "bar", "obj"]) }); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts index c50ffb2140858..35daa6185a3f1 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral4.ts @@ -7,5 +7,5 @@ verify.completions({ marker: ["1"], - includes: ["bar"] + exact: completion.globalsPlus(["foo", "bar", "obj"]) }); diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts index c2765c962e4fb..74ded23477463 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral5.ts @@ -8,6 +8,6 @@ verify.completions({ marker: "", - exact: completion.globalsPlus(['obj']), + exact: completion.globalsPlus(["obj"]), preferences: { includeCompletionsForModuleExports: true } }); \ No newline at end of file From 707e0267f27b6e6330cc8b72aa081dc64f3884ce Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sun, 6 Dec 2020 23:30:54 +0900 Subject: [PATCH 16/17] fix: use exact to ensure the order of completions --- .../cases/fourslash/completionsSelfDeclaring2.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/cases/fourslash/completionsSelfDeclaring2.ts b/tests/cases/fourslash/completionsSelfDeclaring2.ts index 8d2cd18437ee5..7a46020a2c210 100644 --- a/tests/cases/fourslash/completionsSelfDeclaring2.ts +++ b/tests/cases/fourslash/completionsSelfDeclaring2.ts @@ -1,18 +1,15 @@ /// -////function f1(x: T) {} -////f1({ abc/*1*/ }); -//// -////function f2(x: T) {} -////f2({ x/*2*/ }); +//// function f1(x: T) {} +//// f1({ abc/*1*/ }); + +//// function f2(x: T) {} +//// f2({ x/*2*/ }); verify.completions({ marker: "1", - includes: [{ - name: "Object", - sortText: completion.SortText.GlobalsOrKeywords - }] + exact: completion.globalsPlus(["f1", "f2"]) }); verify.completions({ From 77856061612972ee35fd5174deeb69c672e0c5f9 Mon Sep 17 00:00:00 2001 From: orange4glace Date: Sun, 6 Dec 2020 23:37:29 +0900 Subject: [PATCH 17/17] fix: add new lines so it can easy to be distinguished --- .../completionPropertyShorthandForObjectLiteral.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts index c4695d8dc8662..0f8f65e70ebd1 100644 --- a/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts +++ b/tests/cases/fourslash/completionPropertyShorthandForObjectLiteral.ts @@ -15,9 +15,12 @@ //// declare function f9(obj: T): void; //// declare function f10(obj: T): void; //// declare function f11 | {})>(obj: T): void; + //// declare function f12(obj: Typed): void; //// declare function f13(obj: T): void; + //// declare function f14(obj: { [key: string]: number, prop: number }): void; + //// declare function f15(obj: Record): void; //// declare function f16(obj: { [key: number]: number }): void; @@ -32,9 +35,12 @@ //// f9({f/*9*/}); //// f10({f/*10*/}); //// f11({f/*11*/}); + //// f12({f/*12*/}); //// f13({f/*13*/}); + //// f14({f/*14*/}); + //// f15({f/*15*/}); //// f16({f/*16*/});