Skip to content

Commit fcb0f46

Browse files
committed
Tune the completion list for static and private modifiers
Do not show inherited members in completion for when writing private member Show only static inherited members when writing static member
1 parent 37a2cdd commit fcb0f46

File tree

2 files changed

+106
-38
lines changed

2 files changed

+106
-38
lines changed

src/services/completions.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -939,10 +939,31 @@ namespace ts.Completions {
939939

940940
const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration);
941941
if (baseTypeNode) {
942-
const baseType = typeChecker.getTypeAtLocation(baseTypeNode);
943-
// List of property symbols of base type that are not private
944-
symbols = filter(typeChecker.getPropertiesOfType(baseType),
945-
baseProperty => !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private));
942+
const classElement = contextToken.parent;
943+
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
944+
// If this is context token is not something we are editing now, consider if this would lead to be modifier
945+
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
946+
switch (contextToken.getText()) {
947+
case "private":
948+
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
949+
break;
950+
case "static":
951+
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
952+
break;
953+
}
954+
}
955+
956+
// No member list for private methods
957+
if (!(classElementModifierFlags & ModifierFlags.Private)) {
958+
const baseType = typeChecker.getTypeAtLocation(baseTypeNode);
959+
const typeToGetPropertiesFrom = (classElementModifierFlags & ModifierFlags.Static) ?
960+
typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, classLikeDeclaration) :
961+
baseType;
962+
963+
// List of property symbols of base type that are not private
964+
symbols = filter(typeChecker.getPropertiesOfType(typeToGetPropertiesFrom),
965+
baseProperty => baseProperty.getDeclarations() && !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private));
966+
}
946967
}
947968

948969
return true;
@@ -1248,7 +1269,7 @@ namespace ts.Completions {
12481269

12491270
for (const element of namedImportsOrExports) {
12501271
// If this is the current item we are editing right now, do not filter it out
1251-
if (element.getStart() <= position && position <= element.getEnd()) {
1272+
if (isCurrentlyEditingNode(element)) {
12521273
continue;
12531274
}
12541275

@@ -1287,7 +1308,7 @@ namespace ts.Completions {
12871308
}
12881309

12891310
// If this is the current item we are editing right now, do not filter it out
1290-
if (m.getStart() <= position && position <= m.getEnd()) {
1311+
if (isCurrentlyEditingNode(m)) {
12911312
continue;
12921313
}
12931314

@@ -1322,7 +1343,7 @@ namespace ts.Completions {
13221343
const seenNames = createMap<boolean>();
13231344
for (const attr of attributes) {
13241345
// If this is the current item we are editing right now, do not filter it out
1325-
if (attr.getStart() <= position && position <= attr.getEnd()) {
1346+
if (isCurrentlyEditingNode(attr)) {
13261347
continue;
13271348
}
13281349

@@ -1333,6 +1354,10 @@ namespace ts.Completions {
13331354

13341355
return filter(symbols, a => !seenNames.get(a.name));
13351356
}
1357+
1358+
function isCurrentlyEditingNode(node: Node): boolean {
1359+
return node.getStart() <= position && position <= node.getEnd();
1360+
}
13361361
}
13371362

13381363
/**

tests/cases/fourslash/completionEntryForClassMembers.ts

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,19 @@
2323
////class F extends B {
2424
//// public /*classThatHasWrittenPublicKeyword*/
2525
////}
26+
////class F2 extends B {
27+
//// private /*classThatHasWrittenPrivateKeyword*/
28+
////}
2629
////class G extends B {
2730
//// static /*classElementContainingStatic*/
2831
////}
32+
////class G2 extends B {
33+
//// private static /*classElementContainingPrivateStatic*/
34+
////}
2935
////class H extends B {
3036
//// prop/*classThatStartedWritingIdentifier*/
3137
////}
38+
//////Class for location verification
3239
////class I extends B {
3340
//// prop0: number
3441
//// /*propDeclarationWithoutSemicolon*/
@@ -68,38 +75,87 @@
6875
////class L extends B {
6976
//// public identi/*classThatStartedWritingIdentifierAfterModifier*/
7077
////}
71-
////class L extends B {
78+
////class L2 extends B {
79+
//// private identi/*classThatStartedWritingIdentifierAfterPrivateModifier*/
80+
////}
81+
////class M extends B {
7282
//// static identi/*classThatStartedWritingIdentifierAfterStaticModifier*/
7383
////}
84+
////class M extends B {
85+
//// private static identi/*classThatStartedWritingIdentifierAfterPrivateStaticModifier*/
86+
////}
7487

7588
const allowedKeywordCount = verify.allowedClassElementKeywords.length;
89+
type CompletionInfo = [string, string];
90+
type CompletionInfoVerifier = { validMembers: CompletionInfo[], invalidMembers: CompletionInfo[] };
91+
92+
function verifyClassElementLocations({ validMembers, invalidMembers }: CompletionInfoVerifier, classElementCompletionLocations: string[]) {
93+
for (const marker of classElementCompletionLocations) {
94+
goTo.marker(marker);
95+
verifyCompletionInfo(validMembers, verify);
96+
verifyCompletionInfo(invalidMembers, verify.not);
97+
verify.completionListContainsClassElementKeywords();
98+
verify.completionListCount(allowedKeywordCount + validMembers.length);
99+
}
100+
}
101+
102+
function verifyCompletionInfo(memberInfo: CompletionInfo[], verify: FourSlashInterface.verifyNegatable) {
103+
for (const [symbol, text] of memberInfo) {
104+
verify.completionListContains(symbol, text, /*documentation*/ undefined, "method");
105+
}
106+
}
107+
108+
const allMembersOfBase: CompletionInfo[] = [
109+
["getValue", "(method) B.getValue(): number"],
110+
["protectedMethod", "(method) B.protectedMethod(): void"],
111+
["privateMethod", "(method) B.privateMethod(): void"],
112+
["staticMethod", "(method) B.staticMethod(): void"]
113+
];
114+
function filterCompletionInfo(fn: (a: CompletionInfo) => boolean): CompletionInfoVerifier {
115+
const validMembers: CompletionInfo[] = [];
116+
const invalidMembers: CompletionInfo[] = [];
117+
for (const member of allMembersOfBase) {
118+
if (fn(member)) {
119+
validMembers.push(member);
120+
}
121+
else {
122+
invalidMembers.push(member);
123+
}
124+
}
125+
return { validMembers, invalidMembers };
126+
}
127+
128+
129+
const instanceMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "getValue" || a === "protectedMethod");
130+
const staticMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "staticMethod");
131+
132+
// Not a class element declaration location
76133
const nonClassElementMarkers = [
77134
"InsideMethod"
78135
];
79136
for (const marker of nonClassElementMarkers) {
80137
goTo.marker(marker);
81-
verify.not.completionListContains("getValue");
138+
verifyCompletionInfo(allMembersOfBase, verify.not);
82139
verify.not.completionListIsEmpty();
83140
}
84141

85-
// Only keywords allowed at this position since they dont extend the class
142+
// Only keywords allowed at this position since they dont extend the class or are private
86143
const onlyClassElementKeywordLocations = [
87144
"abstractClass",
88-
"classThatDoesNotExtendAnotherClass"
145+
"classThatDoesNotExtendAnotherClass",
146+
"classThatHasWrittenPrivateKeyword",
147+
"classElementContainingPrivateStatic",
148+
"classThatStartedWritingIdentifierAfterPrivateModifier",
149+
"classThatStartedWritingIdentifierAfterPrivateStaticModifier"
89150
];
90-
for (const marker of onlyClassElementKeywordLocations) {
91-
goTo.marker(marker);
92-
verify.completionListContainsClassElementKeywords();
93-
verify.completionListCount(allowedKeywordCount);
94-
}
151+
verifyClassElementLocations({ validMembers: [], invalidMembers: allMembersOfBase }, onlyClassElementKeywordLocations);
95152

96-
// Base members and class member keywords allowed
97-
const classElementCompletionLocations = [
153+
// Instance base members and class member keywords allowed
154+
const classInstanceElementLocations = [
98155
"classThatIsEmptyAndExtendingAnotherClass",
99156
"classThatHasAlreadyImplementedAnotherClassMethod",
100157
"classThatHasAlreadyImplementedAnotherClassMethodAfterMethod",
101158
"classThatHasWrittenPublicKeyword",
102-
"classElementContainingStatic",
103159
"classThatStartedWritingIdentifier",
104160
"propDeclarationWithoutSemicolon",
105161
"propDeclarationWithSemicolon",
@@ -115,25 +171,12 @@ const classElementCompletionLocations = [
115171
"classThatStartedWritingIdentifierOfGetAccessor",
116172
"classThatStartedWritingIdentifierOfSetAccessor",
117173
"classThatStartedWritingIdentifierAfterModifier",
118-
"classThatStartedWritingIdentifierAfterStaticModifier"
119174
];
175+
verifyClassElementLocations(instanceMemberInfo, classInstanceElementLocations);
120176

121-
const validMembersOfBase = [
122-
["getValue", "(method) B.getValue(): number"],
123-
["protectedMethod", "(method) B.protectedMethod(): void"]
124-
];
125-
const invalidMembersOfBase = [
126-
["privateMethod", "(method) B.privateMethod(): void"],
127-
["staticMethod", "(method) B.staticMethod(): void"]
177+
// Static Base members and class member keywords allowed
178+
const staticClassLocations = [
179+
"classElementContainingStatic",
180+
"classThatStartedWritingIdentifierAfterStaticModifier"
128181
];
129-
for (const marker of classElementCompletionLocations) {
130-
goTo.marker(marker);
131-
for (const [validMemberOfBaseSymbol, validMemberOfBaseText] of validMembersOfBase) {
132-
verify.completionListContains(validMemberOfBaseSymbol, validMemberOfBaseText, /*documentation*/ undefined, "method");
133-
}
134-
for (const [invalidMemberOfBaseSymbol, invalidMemberOfBaseText] of invalidMembersOfBase) {
135-
verify.not.completionListContains(invalidMemberOfBaseSymbol, invalidMemberOfBaseText, /*documentation*/ undefined, "method");
136-
}
137-
verify.completionListContainsClassElementKeywords();
138-
verify.completionListCount(allowedKeywordCount + validMembersOfBase.length);
139-
}
182+
verifyClassElementLocations(staticMemberInfo, staticClassLocations);

0 commit comments

Comments
 (0)