Skip to content

Commit 6a50507

Browse files
committed
sort text for class member, JSX attribute, RHS of dot
1 parent 8aa39d6 commit 6a50507

File tree

6 files changed

+107
-27
lines changed

6 files changed

+107
-27
lines changed

src/services/completions.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ namespace ts.Completions {
165165
isJsxInitializer,
166166
recommendedCompletion,
167167
symbolToOriginInfoMap,
168-
symbolToSortTextMap,
168+
symbolToSortTextMap
169169
);
170170
}
171171

@@ -926,7 +926,7 @@ namespace ts.Completions {
926926
previousToken,
927927
isJsxInitializer,
928928
insideJsDocTagTypeExpression,
929-
symbolToSortTextMap,
929+
symbolToSortTextMap
930930
};
931931

932932
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
@@ -985,6 +985,7 @@ namespace ts.Completions {
985985
symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
986986
addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node));
987987
}
988+
setSortTextToOptionalMember();
988989

989990
return;
990991
}
@@ -994,11 +995,13 @@ namespace ts.Completions {
994995
if (isMetaProperty(node) && (node.keywordToken === SyntaxKind.NewKeyword || node.keywordToken === SyntaxKind.ImportKeyword)) {
995996
const completion = (node.keywordToken === SyntaxKind.NewKeyword) ? "target" : "meta";
996997
symbols.push(typeChecker.createSymbol(SymbolFlags.Property, escapeLeadingUnderscores(completion)));
998+
setSortTextToOptionalMember();
997999
return;
9981000
}
9991001

10001002
if (!isTypeLocation) {
10011003
addTypeProperties(typeChecker.getTypeAtLocation(node));
1004+
setSortTextToOptionalMember();
10021005
}
10031006
}
10041007

@@ -1079,6 +1082,7 @@ namespace ts.Completions {
10791082
const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
10801083
if (!attrsType) return GlobalsSearch.Continue;
10811084
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
1085+
setSortTextToOptionalMember();
10821086
completionKind = CompletionKind.MemberLike;
10831087
isNewIdentifierLocation = false;
10841088
return GlobalsSearch.Success;
@@ -1586,6 +1590,7 @@ namespace ts.Completions {
15861590
return type && typeChecker.getPropertiesOfType(classElementModifierFlags & ModifierFlags.Static ? typeChecker.getTypeOfSymbolAtLocation(type.symbol, decl) : type);
15871591
});
15881592
symbols = filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags);
1593+
setSortTextToOptionalMember();
15891594
}
15901595

15911596
return GlobalsSearch.Success;
@@ -1879,7 +1884,7 @@ namespace ts.Completions {
18791884
return contextualMemberSymbols;
18801885
}
18811886

1882-
const fulfilledSymbols: Symbol[] = [];
1887+
const membersDeclaredBySpreadAssignment: Symbol[] = [];
18831888
const existingMemberNames = createUnderscoreEscapedMap<boolean>();
18841889
for (const m of existingMembers) {
18851890
// Ignore omitted expressions for missing members
@@ -1906,7 +1911,7 @@ namespace ts.Completions {
19061911
const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression);
19071912
const properties = type && (<ObjectType>type).properties;
19081913
if (properties) {
1909-
fulfilledSymbols.push(...properties);
1914+
membersDeclaredBySpreadAssignment.push(...properties);
19101915
}
19111916
}
19121917
else if (isBindingElement(m) && m.propertyName) {
@@ -1927,17 +1932,20 @@ namespace ts.Completions {
19271932
}
19281933

19291934
const filteredSymbols = contextualMemberSymbols.filter(m => !existingMemberNames.get(m.escapedName));
1935+
setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment, contextualMemberSymbols);
1936+
1937+
return filteredSymbols;
1938+
}
19301939

1931-
// Set SortText to MemberDeclaredBySpreadAssignment if it is fulfilled by spread assignment
1932-
for (const fulfilledSymbol of fulfilledSymbols) {
1933-
for (const contextualMemberSymbol of filteredSymbols) {
1940+
// Set SortText to MemberDeclaredBySpreadAssignment if it is fulfilled by spread assignment
1941+
function setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment: Symbol[], contextualMemberSymbols: Symbol[]): void {
1942+
for (const fulfilledSymbol of membersDeclaredBySpreadAssignment) {
1943+
for (const contextualMemberSymbol of contextualMemberSymbols) {
19341944
if (contextualMemberSymbol.name === fulfilledSymbol.name) {
19351945
symbolToSortTextMap[getSymbolId(contextualMemberSymbol)] = SortText.MemberDeclaredBySpreadAssignment;
19361946
}
19371947
}
19381948
}
1939-
1940-
return filteredSymbols;
19411949
}
19421950

19431951
/**
@@ -1991,6 +1999,7 @@ namespace ts.Completions {
19911999
*/
19922000
function filterJsxAttributes(symbols: Symbol[], attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>): Symbol[] {
19932001
const seenNames = createUnderscoreEscapedMap<boolean>();
2002+
const membersDeclaredBySpreadAssignment: Symbol[] = [];
19942003
for (const attr of attributes) {
19952004
// If this is the current item we are editing right now, do not filter it out
19962005
if (isCurrentlyEditingNode(attr)) {
@@ -2000,9 +2009,21 @@ namespace ts.Completions {
20002009
if (attr.kind === SyntaxKind.JsxAttribute) {
20012010
seenNames.set(attr.name.escapedText, true);
20022011
}
2012+
else if (isJsxSpreadAttribute(attr)) {
2013+
const expression = attr.expression;
2014+
const symbol = typeChecker.getSymbolAtLocation(expression);
2015+
const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression);
2016+
const properties = type && (<ObjectType>type).properties;
2017+
if (properties) {
2018+
membersDeclaredBySpreadAssignment.push(...properties);
2019+
}
2020+
}
20032021
}
2022+
const filteredSymbols = symbols.filter(a => !seenNames.get(a.escapedName));
20042023

2005-
return symbols.filter(a => !seenNames.get(a.escapedName));
2024+
setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment, symbols);
2025+
2026+
return filteredSymbols;
20062027
}
20072028

20082029
function isCurrentlyEditingNode(node: Node): boolean {

tests/cases/fourslash/completionsOptionalKindModifier.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
verify.completions({
99
marker: "a",
1010
exact: [
11-
{ name: "a", kind: "property", kindModifiers: "optional" },
12-
{ name: "method", kind: "method", kindModifiers: "optional" },
11+
{ name: "a", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
12+
{ name: "method", kind: "method", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
1313
],
1414
});

tests/cases/fourslash/completionsPropertiesPriorities.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,38 @@
1818
//// /*a*/
1919
//// }
2020

21-
verify.completions({
22-
marker: ['a'],
23-
exact: [
24-
{ name: 'B', kindModifiers: 'optional', sortText: completion.SortText.MemberDeclaredBySpreadAssignment, kind: 'property' },
25-
{ name: 'a', sortText: completion.SortText.MemberDeclaredBySpreadAssignment, kind: 'property' },
26-
{ name: 'c', kindModifiers: 'optional', sortText: completion.SortText.OptionalMember, kind: 'property' },
27-
{ name: 'd', sortText: completion.SortText.LocationPriority, kind: 'property' }
28-
]
29-
});
21+
//// class A implements I {
22+
//// /*b*/
23+
//// }
24+
25+
const keywordEntries = ['private', 'protected', 'public', 'static', 'abstract', 'async', 'constructor', 'get', 'readonly', 'set'].map(keyword => {
26+
return {
27+
name: keyword,
28+
kind: 'keyword',
29+
kindModifiers: '',
30+
sortText: completion.SortText.GlobalsOrKeywords
31+
}
32+
});
33+
34+
verify.completions(
35+
{
36+
marker: ['a'],
37+
exact: [
38+
{ name: 'B', kindModifiers: 'optional', sortText: completion.SortText.MemberDeclaredBySpreadAssignment, kind: 'property' },
39+
{ name: 'a', sortText: completion.SortText.MemberDeclaredBySpreadAssignment, kind: 'property' },
40+
{ name: 'c', kindModifiers: 'optional', sortText: completion.SortText.OptionalMember, kind: 'property' },
41+
{ name: 'd', sortText: completion.SortText.LocationPriority, kind: 'property' }
42+
]
43+
},
44+
{
45+
marker: ['b'],
46+
isNewIdentifierLocation: true,
47+
exact:[
48+
{ name: 'B', kindModifiers: 'optional', sortText: completion.SortText.OptionalMember, kind: 'property' },
49+
{ name: 'a', sortText: completion.SortText.LocationPriority, kind: 'property' },
50+
{ name: 'c', kindModifiers: 'optional', sortText: completion.SortText.OptionalMember, kind: 'property' },
51+
{ name: 'd', sortText: completion.SortText.LocationPriority, kind: 'property' },
52+
...keywordEntries
53+
]
54+
}
55+
);

tests/cases/fourslash/tsxCompletion12.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@
2323
//// let opt4 = <Opt wrong /*5*/ />;
2424

2525
verify.completions(
26-
{ marker: ["1", "2", "5"], exact: ["propx", "propString", "optional"] },
27-
{ marker: "3", exact: ["propString", "optional"] },
26+
{
27+
marker: ["1", "2", "5"],
28+
exact: ["propx", "propString", { name: "optional", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember }]
29+
},
30+
{
31+
marker: "3",
32+
exact: ["propString", { name: "optional", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember }]
33+
},
2834
{ marker: "4", exact: "propString" },
2935
);

tests/cases/fourslash/tsxCompletion13.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,28 @@
3131
//// let opt = <MainButton wrong /*6*/ />;
3232

3333
verify.completions(
34-
{ marker: ["1", "6"], exact: ["onClick", "children", "className", "goTo"] },
35-
{ marker: "2", exact: ["onClick", "className", "goTo"] },
36-
{ marker: ["3", "4", "5"], exact: ["children", "className"] },
34+
{
35+
marker: ["1", "6"],
36+
exact: [
37+
"onClick",
38+
{ name: "children", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
39+
{ name: "className", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
40+
"goTo"
41+
]
42+
},
43+
{
44+
marker: "2",
45+
exact: [
46+
"onClick",
47+
{ name: "className", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
48+
"goTo"
49+
]
50+
},
51+
{
52+
marker: ["3", "4", "5"],
53+
exact: [
54+
{ name: "children", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
55+
{ name: "className", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember }
56+
]
57+
},
3758
);

tests/cases/fourslash/tsxCompletion7.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,10 @@
1010
//// let y = { ONE: '' };
1111
//// var x = <div {...y} /**/ />;
1212

13-
verify.completions({ marker: "", exact: ["ONE", "TWO"] });
13+
verify.completions({
14+
marker: "",
15+
exact: [
16+
{ name: "ONE", kind: "JSX attribute", kindModifiers: "declare", sortText: completion.SortText.MemberDeclaredBySpreadAssignment },
17+
{ name: "TWO", kind: "JSX attribute", kindModifiers: "declare", sortText: completion.SortText.LocationPriority }
18+
]
19+
});

0 commit comments

Comments
 (0)