From 8aa39d6cb85dc60e0229ebf3ca4cc8402006dee1 Mon Sep 17 00:00:00 2001 From: xiaofa Date: Thu, 18 Jul 2019 12:33:21 +0800 Subject: [PATCH] Set sort text while adding sysmbols --- src/harness/fourslash.ts | 11 +-- src/services/completions.ts | 77 ++++++++++--------- .../completionsPropertiesPriorities.ts | 6 +- .../completionsWithOptionalProperties.ts | 4 +- tests/cases/fourslash/fourslash.ts | 4 +- 5 files changed, 49 insertions(+), 53 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2984618cbf..c26b059ced 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -841,16 +841,7 @@ namespace FourSlash { assert.equal(actual.hasAction, hasAction); assert.equal(actual.isRecommended, isRecommended); assert.equal(actual.source, source); - - let isSortTextEqual: boolean; - if (!sortText) { - isSortTextEqual = [ts.Completions.SortText.LocationPriorityFulfilled, ts.Completions.SortText.LocationPriority].indexOf(actual.sortText as ts.Completions.SortText) !== -1 - || (actual.kindModifiers === "optional" && actual.sortText === ts.Completions.SortText.LocationPriorityOptional); - } - else { - isSortTextEqual = actual.sortText === sortText; - } - assert.equal(isSortTextEqual, true, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`)); + assert.equal(actual.sortText, sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`)); if (text !== undefined) { const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!; diff --git a/src/services/completions.ts b/src/services/completions.ts index 0cc9b774c7..3421b5465d 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -2,8 +2,8 @@ namespace ts.Completions { export enum SortText { LocationPriority = "0", - LocationPriorityOptional = "1", - LocationPriorityFulfilled = "2", + OptionalMember = "1", + MemberDeclaredBySpreadAssignment = "2", SuggestedClassMembers = "3", GlobalsOrKeywords = "4", AutoImportSuggestions = "5", @@ -105,7 +105,6 @@ namespace ts.Completions { isJsxInitializer, insideJsDocTagTypeExpression, symbolToSortTextMap, - fulfilledSymbols, } = completionData; if (location && location.parent && isJsxClosingElement(location.parent)) { @@ -167,7 +166,6 @@ namespace ts.Completions { recommendedCompletion, symbolToOriginInfoMap, symbolToSortTextMap, - fulfilledSymbols ); } @@ -321,7 +319,6 @@ namespace ts.Completions { recommendedCompletion?: Symbol, symbolToOriginInfoMap?: SymbolOriginInfoMap, symbolToSortTextMap?: SymbolSortTextMap, - fulfilledSymbols?: ReadonlyArray, ): Map { const start = timestamp(); // Tracks unique names. @@ -340,23 +337,9 @@ namespace ts.Completions { continue; } - let sortText = symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)]; - if (!sortText) { - if (fulfilledSymbols && fulfilledSymbols.length > 0) { - fulfilledSymbols.forEach(fulfilledSymbol => { - if (fulfilledSymbol.name === symbol.name) { - sortText = SortText.LocationPriorityFulfilled; - } - }); - } - } - if (!sortText) { - sortText = SymbolDisplay.getSymbolModifiers(symbol) === "optional" ? SortText.LocationPriorityOptional : SortText.LocationPriority; - } - const entry = createCompletionEntry( symbol, - sortText, + symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)] || SortText.LocationPriority, location, sourceFile, typeChecker, @@ -366,7 +349,7 @@ namespace ts.Completions { recommendedCompletion, propertyAccessToConvert, isJsxInitializer, - preferences, + preferences ); if (!entry) { continue; @@ -600,7 +583,6 @@ namespace ts.Completions { readonly isJsxInitializer: IsJsxInitializer; readonly insideJsDocTagTypeExpression: boolean; readonly symbolToSortTextMap: SymbolSortTextMap; - readonly fulfilledSymbols?: ReadonlyArray; } type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag }; @@ -892,7 +874,6 @@ namespace ts.Completions { let isNewIdentifierLocation = false; let keywordFilters = KeywordCompletionFilters.None; let symbols: Symbol[] = []; - let fulfilledSymbols: Symbol[] | undefined = []; const symbolToOriginInfoMap: SymbolOriginInfoMap = []; const symbolToSortTextMap: SymbolSortTextMap = []; @@ -946,7 +927,6 @@ namespace ts.Completions { isJsxInitializer, insideJsDocTagTypeExpression, symbolToSortTextMap, - fulfilledSymbols }; type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; @@ -1516,20 +1496,21 @@ namespace ts.Completions { if (typeMembers && typeMembers.length > 0) { // Add filtered items to the completion list symbols = filterObjectMembersList(typeMembers, Debug.assertDefined(existingMembers)); - if (existingMembers && existingMembers.length > 0) { - existingMembers.forEach(member => { - if (member.kind === SyntaxKind.SpreadAssignment) { - const expression = (member).expression; - const symbol = typeChecker.getSymbolAtLocation(expression); - const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression); - fulfilledSymbols = type && (type).properties; - } - }); - } } + setSortTextToOptionalMember(); + return GlobalsSearch.Success; } + // Set SortText to OptionalMember if it is an optinoal member + function setSortTextToOptionalMember() { + symbols.forEach(m => { + if (SymbolDisplay.getSymbolModifiers(m) === "optional") { + symbolToSortTextMap[getSymbolId(m)] = symbolToSortTextMap[getSymbolId(m)] || SortText.OptionalMember; + } + }); + } + /** * Aggregates relevant symbols for completion in import clauses and export clauses * whose declarations have a module specifier; for instance, symbols will be aggregated for @@ -1898,6 +1879,7 @@ namespace ts.Completions { return contextualMemberSymbols; } + const fulfilledSymbols: Symbol[] = []; const existingMemberNames = createUnderscoreEscapedMap(); for (const m of existingMembers) { // Ignore omitted expressions for missing members @@ -1906,7 +1888,8 @@ namespace ts.Completions { m.kind !== SyntaxKind.BindingElement && m.kind !== SyntaxKind.MethodDeclaration && m.kind !== SyntaxKind.GetAccessor && - m.kind !== SyntaxKind.SetAccessor) { + m.kind !== SyntaxKind.SetAccessor && + m.kind !== SyntaxKind.SpreadAssignment) { continue; } @@ -1917,7 +1900,16 @@ namespace ts.Completions { let existingName: __String | undefined; - if (isBindingElement(m) && m.propertyName) { + if (isSpreadAssignment(m)) { + const expression = m.expression; + const symbol = typeChecker.getSymbolAtLocation(expression); + const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression); + const properties = type && (type).properties; + if (properties) { + fulfilledSymbols.push(...properties); + } + } + else if (isBindingElement(m) && m.propertyName) { // include only identifiers in completion list if (m.propertyName.kind === SyntaxKind.Identifier) { existingName = m.propertyName.escapedText; @@ -1934,7 +1926,18 @@ namespace ts.Completions { existingMemberNames.set(existingName!, true); // TODO: GH#18217 } - return contextualMemberSymbols.filter(m => !existingMemberNames.get(m.escapedName)); + const filteredSymbols = contextualMemberSymbols.filter(m => !existingMemberNames.get(m.escapedName)); + + // Set SortText to MemberDeclaredBySpreadAssignment if it is fulfilled by spread assignment + for (const fulfilledSymbol of fulfilledSymbols) { + for (const contextualMemberSymbol of filteredSymbols) { + if (contextualMemberSymbol.name === fulfilledSymbol.name) { + symbolToSortTextMap[getSymbolId(contextualMemberSymbol)] = SortText.MemberDeclaredBySpreadAssignment; + } + } + } + + return filteredSymbols; } /** diff --git a/tests/cases/fourslash/completionsPropertiesPriorities.ts b/tests/cases/fourslash/completionsPropertiesPriorities.ts index beae7b87a0..9ef22a4903 100644 --- a/tests/cases/fourslash/completionsPropertiesPriorities.ts +++ b/tests/cases/fourslash/completionsPropertiesPriorities.ts @@ -21,9 +21,9 @@ verify.completions({ marker: ['a'], exact: [ - { name: 'B', kindModifiers: 'optional', sortText: completion.SortText.LocationPriorityFulfilled, kind: 'property' }, - { name: 'a', sortText: completion.SortText.LocationPriorityFulfilled, kind: 'property' }, - { name: 'c', kindModifiers: 'optional', sortText: completion.SortText.LocationPriorityOptional, kind: 'property' }, + { name: 'B', kindModifiers: 'optional', sortText: completion.SortText.MemberDeclaredBySpreadAssignment, kind: 'property' }, + { name: 'a', sortText: completion.SortText.MemberDeclaredBySpreadAssignment, kind: 'property' }, + { name: 'c', kindModifiers: 'optional', sortText: completion.SortText.OptionalMember, kind: 'property' }, { name: 'd', sortText: completion.SortText.LocationPriority, kind: 'property' } ] }); \ No newline at end of file diff --git a/tests/cases/fourslash/completionsWithOptionalProperties.ts b/tests/cases/fourslash/completionsWithOptionalProperties.ts index e40029ddef..78b0564647 100644 --- a/tests/cases/fourslash/completionsWithOptionalProperties.ts +++ b/tests/cases/fourslash/completionsWithOptionalProperties.ts @@ -13,6 +13,8 @@ verify.completions({ marker: "", - includes: ['world'] + exact: [ + { name: "world", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember } + ] }); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 8b448d36ad..d242e51d32 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -672,8 +672,8 @@ declare namespace completion { type Entry = FourSlashInterface.ExpectedCompletionEntryObject; export const enum SortText { LocationPriority = "0", - LocationPriorityOptional = "1", - LocationPriorityFulfilled = "2", + OptionalMember = "1", + MemberDeclaredBySpreadAssignment = "2", SuggestedClassMembers = "3", GlobalsOrKeywords = "4", AutoImportSuggestions = "5",