Get one fix per interface

This commit is contained in:
Arthur Ozga 2016-11-09 16:04:56 -08:00
parent d842a6f665
commit b1e97b370f
3 changed files with 53 additions and 32 deletions

View File

@ -9,10 +9,13 @@ namespace ts.codefix {
const checker = context.program.getTypeChecker();
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
const classDeclaration = <ClassDeclaration>token.parent;
const startPos = classDeclaration.members.pos;
const classDecl = <ClassDeclaration>token.parent;
const startPos = classDecl.members.pos;
const insertion = getMissingAbstractMemberInsertion(classDeclaration, checker, context.newLineCharacter);
const InstantiatedExtendsType = <InterfaceType>checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl));
const resolvedExtendsType = checker.resolveStructuredTypeMembers(InstantiatedExtendsType);
const insertion = getMissingAbstractMembersInsertion(classDecl, resolvedExtendsType, checker, context.newLineCharacter);
if (insertion.length > 0) {
return [{

View File

@ -8,15 +8,40 @@ namespace ts.codefix {
const token = getTokenAtPosition(sourceFile, start);
const checker = context.program.getTypeChecker();
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
const classDeclaration = <ClassDeclaration>token.parent;
const startPos: number = classDeclaration.members.pos;
if (!(token.kind === SyntaxKind.Identifier && isClassLike(token.parent))) {
return undefined;
}
const classDecl = <ClassDeclaration>token.parent;
const startPos: number = classDecl.members.pos;
const insertion = getMissingInterfaceMembersInsertion(classDeclaration, checker, context.newLineCharacter);
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
const implementedTypes = implementedTypeNodes.map(checker.getTypeFromTypeReference);
const resolvedImplementedTypes = implementedTypes.map(checker.resolveStructuredTypeMembers);
let result: CodeAction[] | undefined = undefined;
for (const resolvedType of resolvedImplementedTypes) {
const insertion = getMissingMembersInsertion(classDecl, resolvedType, checker, context.newLineCharacter);
result = pushAction(insertion, getLocaleSpecificMessage(Diagnostics.Implement_interface_on_class), result);
}
// TODO: (arozga) Get this working and figure out how to test it reliably.
/*
// If there are multiple objects, we additionally try to generate a combined fix that simultaneously implements all types.
const intersectionType = checker.getIntersectionType(implementedTypes);
if(intersectionType.flags & TypeFlags.Intersection) {
const resolvedIntersectionType = checker.resolveStructuredTypeMembers(<IntersectionType>intersectionType)
const insertion = getMissingMembersInsertion(classDecl, resolvedIntersectionType, checker, context.newLineCharacter);
result = pushAction(insertion, "stubbed locale message", result)
}
*/
return result;
function pushAction(insertion: string, description: string, result?: CodeAction[]): CodeAction[] {
if (insertion && insertion.length) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Implement_interface_on_class),
const newAction: CodeAction = {
description: description,
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
@ -24,10 +49,16 @@ namespace ts.codefix {
newText: insertion
}]
}]
}];
};
if (result) {
result.push(newAction);
}
else {
result = [newAction];
}
}
return result;
}
return undefined;
}
});
}

View File

@ -1359,32 +1359,19 @@ namespace ts {
};
}
export function getMissingAbstractMemberInsertion(classDecl: ClassDeclaration, checker: TypeChecker, newlineChar: string): string {
export function getMissingAbstractMembersInsertion(classDecl: ClassDeclaration, resolvedType: ResolvedType, checker: TypeChecker, newlineChar: string): string {
const classSymbol = checker.getSymbolOfNode(classDecl);
const InstantiatedExtendsType = <InterfaceType>checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl));
const resolvedExtendsType = checker.resolveStructuredTypeMembers(InstantiatedExtendsType);
const missingMembers = filterMissingMembers(filterAbstract(filterNonPrivate(resolvedExtendsType.members)), classSymbol.members);
const missingMembers = filterMissingMembers(filterAbstract(filterNonPrivate(resolvedType.members)), classSymbol.members);
return getInsertionsForMembers(missingMembers, classDecl, checker, newlineChar);
}
export function getMissingInterfaceMembersInsertion(classDecl: ClassDeclaration, checker: TypeChecker, newlineChar: string): string {
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
/**
* Finds members of the resolved type that are missing in the class pointed to by class decl
* and generates source code for the missing members.
*/
export function getMissingMembersInsertion(classDecl: ClassDeclaration, resolvedType: ResolvedType, checker: TypeChecker, newlineChar: string): string {
const classSymbol = checker.getSymbolOfNode(classDecl);
let implementsIntersectionType: IntersectionType | InterfaceType;
if (implementedTypeNodes.length > 1) {
implementsIntersectionType = <IntersectionType>checker.getIntersectionType(implementedTypeNodes.map(checker.getTypeFromTypeReference));
}
else {
implementsIntersectionType = <InterfaceType>checker.getTypeFromTypeReference(implementedTypeNodes[0]);
}
const structuredType = checker.resolveStructuredTypeMembers(<IntersectionType | InterfaceType>implementsIntersectionType);
const missingMembers = filterMissingMembers(filterNonPrivate(structuredType.members), classSymbol.members);
const missingMembers = filterMissingMembers(filterNonPrivate(resolvedType.members), classSymbol.members);
return getInsertionsForMembers(missingMembers, classDecl, checker, newlineChar);
}