22namespace ts . Completions {
33 export enum SortText {
44 LocationPriority = "0" ,
5- SuggestedClassMembers = "1" ,
6- GlobalsOrKeywords = "2" ,
7- AutoImportSuggestions = "3" ,
8- JavascriptIdentifiers = "4"
5+ OptionalMember = "1" ,
6+ MemberDeclaredBySpreadAssignment = "2" ,
7+ SuggestedClassMembers = "3" ,
8+ GlobalsOrKeywords = "4" ,
9+ AutoImportSuggestions = "5" ,
10+ JavascriptIdentifiers = "6"
911 }
1012 export type Log = ( message : string ) => void ;
1113
@@ -1109,6 +1111,7 @@ namespace ts.Completions {
11091111 const attrsType = jsxContainer && typeChecker . getContextualType ( jsxContainer . attributes ) ;
11101112 if ( ! attrsType ) return GlobalsSearch . Continue ;
11111113 symbols = filterJsxAttributes ( getPropertiesForObjectExpression ( attrsType , jsxContainer ! . attributes , typeChecker ) , jsxContainer ! . attributes . properties ) ;
1114+ setSortTextToOptionalMember ( ) ;
11121115 completionKind = CompletionKind . MemberLike ;
11131116 isNewIdentifierLocation = false ;
11141117 return GlobalsSearch . Success ;
@@ -1539,6 +1542,8 @@ namespace ts.Completions {
15391542 // Add filtered items to the completion list
15401543 symbols = filterObjectMembersList ( typeMembers , Debug . assertDefined ( existingMembers ) ) ;
15411544 }
1545+ setSortTextToOptionalMember ( ) ;
1546+
15421547 return GlobalsSearch . Success ;
15431548 }
15441549
@@ -1910,6 +1915,7 @@ namespace ts.Completions {
19101915 return contextualMemberSymbols ;
19111916 }
19121917
1918+ const membersDeclaredBySpreadAssignment = createMap < true > ( ) ;
19131919 const existingMemberNames = createUnderscoreEscapedMap < boolean > ( ) ;
19141920 for ( const m of existingMembers ) {
19151921 // Ignore omitted expressions for missing members
@@ -1918,7 +1924,8 @@ namespace ts.Completions {
19181924 m . kind !== SyntaxKind . BindingElement &&
19191925 m . kind !== SyntaxKind . MethodDeclaration &&
19201926 m . kind !== SyntaxKind . GetAccessor &&
1921- m . kind !== SyntaxKind . SetAccessor ) {
1927+ m . kind !== SyntaxKind . SetAccessor &&
1928+ m . kind !== SyntaxKind . SpreadAssignment ) {
19221929 continue ;
19231930 }
19241931
@@ -1929,7 +1936,10 @@ namespace ts.Completions {
19291936
19301937 let existingName : __String | undefined ;
19311938
1932- if ( isBindingElement ( m ) && m . propertyName ) {
1939+ if ( isSpreadAssignment ( m ) ) {
1940+ setMembersDeclaredBySpreadAssignment ( m , membersDeclaredBySpreadAssignment ) ;
1941+ }
1942+ else if ( isBindingElement ( m ) && m . propertyName ) {
19331943 // include only identifiers in completion list
19341944 if ( m . propertyName . kind === SyntaxKind . Identifier ) {
19351945 existingName = m . propertyName . escapedText ;
@@ -1946,7 +1956,43 @@ namespace ts.Completions {
19461956 existingMemberNames . set ( existingName ! , true ) ; // TODO: GH#18217
19471957 }
19481958
1949- return contextualMemberSymbols . filter ( m => ! existingMemberNames . get ( m . escapedName ) ) ;
1959+ const filteredSymbols = contextualMemberSymbols . filter ( m => ! existingMemberNames . get ( m . escapedName ) ) ;
1960+ setSortTextToMemberDeclaredBySpreadAssignment ( membersDeclaredBySpreadAssignment , filteredSymbols ) ;
1961+
1962+ return filteredSymbols ;
1963+ }
1964+
1965+ function setMembersDeclaredBySpreadAssignment ( declaration : SpreadAssignment | JsxSpreadAttribute , membersDeclaredBySpreadAssignment : Map < true > ) {
1966+ const expression = declaration . expression ;
1967+ const symbol = typeChecker . getSymbolAtLocation ( expression ) ;
1968+ const type = symbol && typeChecker . getTypeOfSymbolAtLocation ( symbol , expression ) ;
1969+ const properties = type && ( < ObjectType > type ) . properties ;
1970+ if ( properties ) {
1971+ properties . forEach ( property => {
1972+ membersDeclaredBySpreadAssignment . set ( property . name , true ) ;
1973+ } ) ;
1974+ }
1975+ }
1976+
1977+ // Set SortText to OptionalMember if it is an optinoal member
1978+ function setSortTextToOptionalMember ( ) {
1979+ symbols . forEach ( m => {
1980+ if ( m . flags & SymbolFlags . Optional ) {
1981+ symbolToSortTextMap [ getSymbolId ( m ) ] = symbolToSortTextMap [ getSymbolId ( m ) ] || SortText . OptionalMember ;
1982+ }
1983+ } ) ;
1984+ }
1985+
1986+ // Set SortText to MemberDeclaredBySpreadAssignment if it is fulfilled by spread assignment
1987+ function setSortTextToMemberDeclaredBySpreadAssignment ( membersDeclaredBySpreadAssignment : Map < true > , contextualMemberSymbols : Symbol [ ] ) : void {
1988+ if ( membersDeclaredBySpreadAssignment . size === 0 ) {
1989+ return ;
1990+ }
1991+ for ( const contextualMemberSymbol of contextualMemberSymbols ) {
1992+ if ( membersDeclaredBySpreadAssignment . has ( contextualMemberSymbol . name ) ) {
1993+ symbolToSortTextMap [ getSymbolId ( contextualMemberSymbol ) ] = SortText . MemberDeclaredBySpreadAssignment ;
1994+ }
1995+ }
19501996 }
19511997
19521998 /**
@@ -2000,6 +2046,7 @@ namespace ts.Completions {
20002046 */
20012047 function filterJsxAttributes ( symbols : Symbol [ ] , attributes : NodeArray < JsxAttribute | JsxSpreadAttribute > ) : Symbol [ ] {
20022048 const seenNames = createUnderscoreEscapedMap < boolean > ( ) ;
2049+ const membersDeclaredBySpreadAssignment = createMap < true > ( ) ;
20032050 for ( const attr of attributes ) {
20042051 // If this is the current item we are editing right now, do not filter it out
20052052 if ( isCurrentlyEditingNode ( attr ) ) {
@@ -2009,9 +2056,15 @@ namespace ts.Completions {
20092056 if ( attr . kind === SyntaxKind . JsxAttribute ) {
20102057 seenNames . set ( attr . name . escapedText , true ) ;
20112058 }
2059+ else if ( isJsxSpreadAttribute ( attr ) ) {
2060+ setMembersDeclaredBySpreadAssignment ( attr , membersDeclaredBySpreadAssignment ) ;
2061+ }
20122062 }
2063+ const filteredSymbols = symbols . filter ( a => ! seenNames . get ( a . escapedName ) ) ;
2064+
2065+ setSortTextToMemberDeclaredBySpreadAssignment ( membersDeclaredBySpreadAssignment , filteredSymbols ) ;
20132066
2014- return symbols . filter ( a => ! seenNames . get ( a . escapedName ) ) ;
2067+ return filteredSymbols ;
20152068 }
20162069
20172070 function isCurrentlyEditingNode ( node : Node ) : boolean {
0 commit comments