Skip to content

Commit 99ea9c7

Browse files
committed
Add the members of interfaces that need to be implemented to class element completion
1 parent 588c4ec commit 99ea9c7

File tree

3 files changed

+533
-13
lines changed

3 files changed

+533
-13
lines changed

src/services/completions.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,8 @@ namespace ts.Completions {
937937
hasFilteredClassMemberKeywords = true;
938938

939939
const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration);
940-
if (baseTypeNode) {
940+
const implementsTypeNodes = getClassImplementsHeritageClauseElements(classLikeDeclaration);
941+
if (baseTypeNode || implementsTypeNodes) {
941942
const classElement = contextToken.parent;
942943
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
943944
// If this is context token is not something we are editing now, consider if this would lead to be modifier
@@ -954,13 +955,27 @@ namespace ts.Completions {
954955

955956
// No member list for private methods
956957
if (!(classElementModifierFlags & ModifierFlags.Private)) {
957-
const baseType = typeChecker.getTypeAtLocation(baseTypeNode);
958-
const typeToGetPropertiesFrom = (classElementModifierFlags & ModifierFlags.Static) ?
959-
typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, classLikeDeclaration) :
960-
baseType;
958+
let baseClassTypeToGetPropertiesFrom: Type;
959+
if (baseTypeNode) {
960+
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
961+
if (classElementModifierFlags & ModifierFlags.Static) {
962+
// Use static class to get property symbols from
963+
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(
964+
baseClassTypeToGetPropertiesFrom.symbol, classLikeDeclaration);
965+
}
966+
}
967+
const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ?
968+
undefined :
969+
flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
961970

962971
// List of property symbols of base type that are not private and already implemented
963-
symbols = filterClassMembersList(typeChecker.getPropertiesOfType(typeToGetPropertiesFrom), classLikeDeclaration.members, classElementModifierFlags);
972+
symbols = filterClassMembersList(
973+
baseClassTypeToGetPropertiesFrom ?
974+
typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) :
975+
undefined,
976+
implementedInterfaceTypePropertySymbols,
977+
classLikeDeclaration.members,
978+
classElementModifierFlags);
964979
}
965980
}
966981
}
@@ -1334,7 +1349,7 @@ namespace ts.Completions {
13341349
*
13351350
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
13361351
*/
1337-
function filterClassMembersList(baseSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
1352+
function filterClassMembersList(baseSymbols: Symbol[], implementingTypeSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
13381353
const existingMemberNames = createMap<boolean>();
13391354
for (const m of existingMembers) {
13401355
// Ignore omitted expressions for missing members
@@ -1358,7 +1373,7 @@ namespace ts.Completions {
13581373
// do not filter it out if the static presence doesnt match
13591374
const mIsStatic = hasModifier(m, ModifierFlags.Static);
13601375
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
1361-
if ((mIsStatic && currentElementIsStatic) ||
1376+
if ((mIsStatic && !currentElementIsStatic) ||
13621377
(!mIsStatic && currentElementIsStatic)) {
13631378
continue;
13641379
}
@@ -1369,10 +1384,16 @@ namespace ts.Completions {
13691384
}
13701385
}
13711386

1372-
return filter(baseSymbols, baseProperty =>
1373-
!existingMemberNames.get(baseProperty.name) &&
1374-
baseProperty.getDeclarations() &&
1375-
!(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private));
1387+
return concatenate(
1388+
filter(baseSymbols, baseProperty => isValidProperty(baseProperty, ModifierFlags.Private)),
1389+
filter(implementingTypeSymbols, implementingProperty => isValidProperty(implementingProperty, ModifierFlags.NonPublicAccessibilityModifier))
1390+
);
1391+
1392+
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
1393+
return !existingMemberNames.get(propertySymbol.name) &&
1394+
propertySymbol.getDeclarations() &&
1395+
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
1396+
}
13761397
}
13771398

13781399
/**

tests/cases/fourslash/completionEntryForClassMembers.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@
3030
//// }
3131
//// /*classThatHasDifferentMethodThanBaseAfterProtectedMethod*/
3232
////}
33+
////class D3 extends D1 {
34+
//// /*classThatExtendsClassExtendingAnotherClass*/
35+
////}
36+
////class D4 extends D1 {
37+
//// static /*classThatExtendsClassExtendingAnotherClassAndTypesStatic*/
38+
////}
39+
////class D5 extends D2 {
40+
//// /*classThatExtendsClassExtendingAnotherClassWithOverridingMember*/
41+
////}
42+
////class D6 extends D2 {
43+
//// static /*classThatExtendsClassExtendingAnotherClassWithOverridingMemberAndTypesStatic*/
44+
////}
3345
////class E {
3446
//// /*classThatDoesNotExtendAnotherClass*/
3547
////}
@@ -127,6 +139,12 @@ const allMembersOfBase: CompletionInfo[] = [
127139
["privateMethod", "(method) B.privateMethod(): void"],
128140
["staticMethod", "(method) B.staticMethod(): void"]
129141
];
142+
const publicCompletionInfoOfD1: CompletionInfo[] = [
143+
["getValue1", "(method) D1.getValue1(): number"]
144+
];
145+
const publicCompletionInfoOfD2: CompletionInfo[] = [
146+
["protectedMethod", "(method) D2.protectedMethod(): void"]
147+
];
130148
function filterCompletionInfo(fn: (a: CompletionInfo) => boolean): CompletionInfoVerifier {
131149
const validMembers: CompletionInfo[] = [];
132150
const invalidMembers: CompletionInfo[] = [];
@@ -147,6 +165,19 @@ const staticMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "st
147165
const instanceWithoutProtectedMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "getValue");
148166
const instanceWithoutPublicMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "protectedMethod");
149167

168+
const instanceMemberInfoD1: CompletionInfoVerifier = {
169+
validMembers: instanceMemberInfo.validMembers.concat(publicCompletionInfoOfD1),
170+
invalidMembers: instanceMemberInfo.invalidMembers
171+
};
172+
const instanceMemberInfoD2: CompletionInfoVerifier = {
173+
validMembers: instanceWithoutProtectedMemberInfo.validMembers.concat(publicCompletionInfoOfD2),
174+
invalidMembers: instanceWithoutProtectedMemberInfo.invalidMembers
175+
};
176+
const staticMemberInfoDn: CompletionInfoVerifier = {
177+
validMembers: staticMemberInfo.validMembers,
178+
invalidMembers: staticMemberInfo.invalidMembers.concat(publicCompletionInfoOfD1, publicCompletionInfoOfD2)
179+
};
180+
150181
// Not a class element declaration location
151182
const nonClassElementMarkers = [
152183
"InsideMethod"
@@ -210,4 +241,16 @@ const classInstanceElementWithoutProtectedMethodLocations = [
210241
"classThatHasAlreadyImplementedAnotherClassProtectedMethod",
211242
"classThatHasDifferentMethodThanBaseAfterProtectedMethod",
212243
];
213-
verifyClassElementLocations(instanceWithoutProtectedMemberInfo, classInstanceElementWithoutProtectedMethodLocations);
244+
verifyClassElementLocations(instanceWithoutProtectedMemberInfo, classInstanceElementWithoutProtectedMethodLocations);
245+
246+
// instance memebers in D1 and base class are shown
247+
verifyClassElementLocations(instanceMemberInfoD1, ["classThatExtendsClassExtendingAnotherClass"]);
248+
249+
// instance memebers in D2 and base class are shown
250+
verifyClassElementLocations(instanceMemberInfoD2, ["classThatExtendsClassExtendingAnotherClassWithOverridingMember"]);
251+
252+
// static base members and class member keywords allowed
253+
verifyClassElementLocations(staticMemberInfoDn, [
254+
"classThatExtendsClassExtendingAnotherClassAndTypesStatic",
255+
"classThatExtendsClassExtendingAnotherClassWithOverridingMemberAndTypesStatic"
256+
]);

0 commit comments

Comments
 (0)