mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
fix(41259) : JS autocomplete doesn't work for object literal shorthands (#41539)
* fix: #41259 * fix: #41259 * fix: fourslash * fix: remove nested if * fix: change tc result for #41259 * fix: less restrictive shorthand completion rules * fix: use typeMembers to find out whether properties are empty * fix: typo * fix: lint * fix: exclude Object in completion * fix: test * fix: testcase tidy up * fix: apply completions for unclosed literal and missing comma * fix: ignore auto-imports * fix: use exact to ensure the order of completions * fix: use exact to ensure the order of completions * fix: add new lines so it can easy to be distinguished
This commit is contained in:
parent
22f452c5cd
commit
1e4a5c9b37
@ -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.
|
||||
@ -1892,13 +1895,29 @@ namespace ts.Completions {
|
||||
|
||||
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker);
|
||||
|
||||
// Check completions for Object property value shorthand
|
||||
if (instantiatedType === undefined) {
|
||||
return GlobalsSearch.Fail;
|
||||
if (objectLikeContainer.flags & NodeFlags.InWithStatement) {
|
||||
return GlobalsSearch.Fail;
|
||||
}
|
||||
isNonContextualObjectLiteral = true;
|
||||
return GlobalsSearch.Continue;
|
||||
}
|
||||
const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions);
|
||||
isNewIdentifierLocation = hasIndexSignature(completionsType || instantiatedType);
|
||||
const hasStringIndexType = (completionsType || instantiatedType).getStringIndexType();
|
||||
const hasNumberIndextype = (completionsType || instantiatedType).getNumberIndexType();
|
||||
isNewIdentifierLocation = !!hasStringIndexType || !!hasNumberIndextype;
|
||||
typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker);
|
||||
existingMembers = objectLikeContainer.properties;
|
||||
|
||||
if (typeMembers.length === 0) {
|
||||
// Edge case: If NumberIndexType exists
|
||||
if (!hasNumberIndextype) {
|
||||
isNonContextualObjectLiteral = true;
|
||||
return GlobalsSearch.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern);
|
||||
@ -2313,6 +2332,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/**/`.
|
||||
|
||||
@ -1,19 +1,27 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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: test.markers(), exact: undefined });
|
||||
verify.completions({ marker: test.markers(), exact: undefined });
|
||||
@ -0,0 +1,66 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//// declare const foo: number;
|
||||
//// interface Empty {}
|
||||
//// interface Typed { typed: number; }
|
||||
|
||||
//// 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<string, any>): void;
|
||||
//// declare function f6(obj: { [key: string]: number }): void;
|
||||
//// declare function f7<T>(obj: T): void;
|
||||
//// declare function f8<T extends object>(obj: T): void;
|
||||
//// declare function f9<T extends {}>(obj: T): void;
|
||||
//// declare function f10<T extends Empty>(obj: T): void;
|
||||
//// declare function f11<T extends (Empty | Record<string, any> | {})>(obj: T): void;
|
||||
|
||||
//// declare function f12(obj: Typed): void;
|
||||
//// declare function f13<T extends (Empty | Typed)>(obj: T): void;
|
||||
|
||||
//// declare function f14(obj: { [key: string]: number, prop: number }): void;
|
||||
|
||||
//// declare function f15(obj: Record<number, any>): void;
|
||||
//// declare function f16(obj: { [key: number]: number }): 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*/});
|
||||
|
||||
//// f14({f/*14*/});
|
||||
|
||||
//// f15({f/*15*/});
|
||||
//// f16({f/*16*/});
|
||||
|
||||
const locals = [
|
||||
...(() => {
|
||||
const symbols = [];
|
||||
for (let i = 1; i <= 16; i ++) {
|
||||
symbols.push(`f${i}`);
|
||||
}
|
||||
return symbols;
|
||||
})(),
|
||||
"foo"
|
||||
];
|
||||
verify.completions(
|
||||
// Non-contextual, any, unknown, object, Record<string, ..>, [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},
|
||||
);
|
||||
@ -0,0 +1,17 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//// const foo = 1;
|
||||
//// const bar = 2;
|
||||
|
||||
//// const obj1 = {
|
||||
//// foo b/*1*/
|
||||
//// };
|
||||
|
||||
//// const obj2: any = {
|
||||
//// foo b/*2*/
|
||||
//// };
|
||||
|
||||
verify.completions({
|
||||
marker: test.markers(),
|
||||
exact: completion.globalsPlus(["foo", "bar", "obj1", "obj2"]),
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//// const foo = 1;
|
||||
//// const bar = 2;
|
||||
//// const obj = {
|
||||
//// foo b/*1*/
|
||||
|
||||
verify.completions({
|
||||
marker: ["1"],
|
||||
exact: completion.globalsPlus(["foo", "bar", "obj"])
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//// const foo = 1;
|
||||
//// const bar = 2;
|
||||
//// const obj: any = {
|
||||
//// foo b/*1*/
|
||||
|
||||
verify.completions({
|
||||
marker: ["1"],
|
||||
exact: completion.globalsPlus(["foo", "bar", "obj"])
|
||||
});
|
||||
@ -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 }
|
||||
});
|
||||
@ -10,5 +10,8 @@
|
||||
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: []
|
||||
includes: [{
|
||||
name: "Object",
|
||||
sortText: completion.SortText.GlobalsOrKeywords
|
||||
}]
|
||||
});
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////function f1<T>(x: T) {}
|
||||
////f1({ abc/*1*/ });
|
||||
////
|
||||
////function f2<T extends { xyz: number }>(x: T) {}
|
||||
////f2({ x/*2*/ });
|
||||
//// function f1<T>(x: T) {}
|
||||
//// f1({ abc/*1*/ });
|
||||
|
||||
//// function f2<T extends { xyz: number }>(x: T) {}
|
||||
//// f2({ x/*2*/ });
|
||||
|
||||
|
||||
verify.completions({
|
||||
marker: "1",
|
||||
exact: []
|
||||
exact: completion.globalsPlus(["f1", "f2"])
|
||||
});
|
||||
|
||||
verify.completions({
|
||||
|
||||
@ -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 }
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user