diff --git a/src/services/completions.ts b/src/services/completions.ts index 2bec3132df955..9f1798d5a11ba 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1,13 +1,14 @@ /* @internal */ namespace ts.Completions { export enum SortText { - LocationPriority = "0", - OptionalMember = "1", - MemberDeclaredBySpreadAssignment = "2", - SuggestedClassMembers = "3", - GlobalsOrKeywords = "4", - AutoImportSuggestions = "5", - JavascriptIdentifiers = "6" + SameName = "0", + LocationPriority = "1", + OptionalMember = "2", + MemberDeclaredBySpreadAssignment = "3", + SuggestedClassMembers = "4", + GlobalsOrKeywords = "5", + AutoImportSuggestions = "6", + JavascriptIdentifiers = "7" } export type Log = (message: string) => void; @@ -945,6 +946,7 @@ namespace ts.Completions { let isStartingCloseTag = false; let isJsxInitializer: IsJsxInitializer = false; let isJsxIdentifierExpected = false; + let contextPropertyName: string | undefined; let location = getTouchingPropertyName(sourceFile, position); if (contextToken) { @@ -958,6 +960,8 @@ namespace ts.Completions { if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) { isRightOfDot = contextToken.kind === SyntaxKind.DotToken; isRightOfQuestionDot = contextToken.kind === SyntaxKind.QuestionDotToken; + const propertyAssignment = tryCast(findAncestor(contextToken, n => rangeContainsRange(location, n) && isPropertyAssignment(n)), isPropertyAssignment); + contextPropertyName = tryCast(propertyAssignment?.name, isIdentifier)?.escapedText as string | undefined; switch (parent.kind) { case SyntaxKind.PropertyAccessExpression: propertyAccessToConvert = parent as PropertyAccessExpression; @@ -1208,7 +1212,7 @@ namespace ts.Completions { } } } - addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot); + addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot, contextPropertyName); } return; @@ -1238,11 +1242,11 @@ namespace ts.Completions { } } } - addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot); + addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot, contextPropertyName); } } - function addTypeProperties(type: Type, insertAwait: boolean, insertQuestionDot: boolean): void { + function addTypeProperties(type: Type, insertAwait: boolean, insertQuestionDot: boolean, contextPropertyName: string | undefined): void { isNewIdentifierLocation = !!type.getStringIndexType(); if (isRightOfQuestionDot && some(type.getCallSignatures())) { isNewIdentifierLocation = true; @@ -1260,6 +1264,9 @@ namespace ts.Completions { else { for (const symbol of type.getApparentProperties()) { if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) { + if (contextPropertyName && symbol.name === contextPropertyName) { + symbolToSortTextMap[getSymbolId(symbol)] = SortText.SameName; + } addPropertySymbol(symbol, /*insertAwait*/ false, insertQuestionDot); } } diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 3115109572a0d..ea6a6fd6823a5 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -163,7 +163,7 @@ namespace ts.JsDoc { name: tagName, kind: ScriptElementKind.keyword, kindModifiers: "", - sortText: "0", + sortText: Completions.SortText.LocationPriority, }; })); } @@ -176,7 +176,7 @@ namespace ts.JsDoc { name: `@${tagName}`, kind: ScriptElementKind.keyword, kindModifiers: "", - sortText: "0" + sortText: Completions.SortText.LocationPriority }; })); } @@ -211,7 +211,7 @@ namespace ts.JsDoc { return undefined; } - return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText: "0" }; + return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority }; }); } diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index c3473b6251eae..61285f0a890dd 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -40,7 +40,7 @@ namespace ts.Completions.StringCompletions { name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.string, - sortText: "0", + sortText: SortText.LocationPriority, replacementSpan: getReplacementSpanForContextToken(contextToken) })); return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: completion.isNewIdentifier, entries }; diff --git a/src/testRunner/unittests/tsserver/metadataInResponse.ts b/src/testRunner/unittests/tsserver/metadataInResponse.ts index 10b1c58d737a9..b798b6d67e0c0 100644 --- a/src/testRunner/unittests/tsserver/metadataInResponse.ts +++ b/src/testRunner/unittests/tsserver/metadataInResponse.ts @@ -55,8 +55,8 @@ namespace ts.projectSystem { offset: aTs.content.indexOf("this.") + 1 + "this.".length }; const expectedCompletionEntries: readonly protocol.CompletionEntry[] = [ - { name: "foo", kind: ScriptElementKind.memberFunctionElement, kindModifiers: "", sortText: "0" }, - { name: "prop", kind: ScriptElementKind.memberVariableElement, kindModifiers: "", sortText: "0" } + { name: "foo", kind: ScriptElementKind.memberFunctionElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority }, + { name: "prop", kind: ScriptElementKind.memberVariableElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority } ]; it("can pass through metadata when the command returns array", () => { diff --git a/tests/cases/fourslash/completionForAssignmentByAccess.ts b/tests/cases/fourslash/completionForAssignmentByAccess.ts new file mode 100644 index 0000000000000..96bfb05dc1737 --- /dev/null +++ b/tests/cases/fourslash/completionForAssignmentByAccess.ts @@ -0,0 +1,21 @@ +/// + +//// interface T { +//// a: string +//// b: number +//// } +//// interface U { +//// b: number +//// } +//// declare const a: T +//// const b: U = { +//// b: a./*1*/ +//// } + +verify.completions({ + marker: "1", + exact: [ + { name: "a", sortText: completion.SortText.LocationPriority }, + { name: "b", sortText: completion.SortText.SameName }, + ] +}); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index f50cb18774415..c1002f3c075b5 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -759,13 +759,14 @@ declare var classification: typeof FourSlashInterface.classification; declare namespace completion { type Entry = FourSlashInterface.ExpectedCompletionEntryObject; export const enum SortText { - LocationPriority = "0", - OptionalMember = "1", - MemberDeclaredBySpreadAssignment = "2", - SuggestedClassMembers = "3", - GlobalsOrKeywords = "4", - AutoImportSuggestions = "5", - JavascriptIdentifiers = "6" + SameName = "0", + LocationPriority = "1", + OptionalMember = "2", + MemberDeclaredBySpreadAssignment = "3", + SuggestedClassMembers = "4", + GlobalsOrKeywords = "5", + AutoImportSuggestions = "6", + JavascriptIdentifiers = "7" } export const enum CompletionSource { ThisProperty = "ThisProperty/"