mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-23 07:07:09 -05:00
add quick fix for add missing enum member
This commit is contained in:
@@ -11,11 +11,15 @@ namespace ts.codefix {
|
||||
getCodeActions(context) {
|
||||
const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker());
|
||||
if (!info) return undefined;
|
||||
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.preferences);
|
||||
|
||||
if (isEnumInfo(info)) {
|
||||
return singleElementArray(getActionForEnumMemberDeclaration(context, info.enumDeclarationSourceFile, info.declaration, info.token));
|
||||
}
|
||||
const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, context.preferences);
|
||||
const addMember = inJs ?
|
||||
singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, classDeclaration, token.text, makeStatic)) :
|
||||
getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, classDeclaration, token, makeStatic);
|
||||
singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, declaration, token.text, makeStatic)) :
|
||||
getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, declaration, token, makeStatic);
|
||||
return concatenate(singleElementArray(methodCodeAction), addMember);
|
||||
},
|
||||
fixIds: [fixId],
|
||||
@@ -23,31 +27,44 @@ namespace ts.codefix {
|
||||
const seenNames = createMap<true>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const { program, preferences } = context;
|
||||
const info = getInfo(diag.file, diag.start, program.getTypeChecker());
|
||||
if (!info) return;
|
||||
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
if (!addToSeen(seenNames, token.text)) {
|
||||
const checker = program.getTypeChecker();
|
||||
const info = getInfo(diag.file, diag.start, checker);
|
||||
if (!info || !addToSeen(seenNames, info.token.text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always prefer to add a method declaration if possible.
|
||||
if (call) {
|
||||
addMethodDeclaration(context, changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, preferences);
|
||||
if (isEnumInfo(info)) {
|
||||
const { token, declaration, enumDeclarationSourceFile } = info;
|
||||
addEnumMemberDeclaration(changes, checker, token, declaration, enumDeclarationSourceFile);
|
||||
}
|
||||
else {
|
||||
if (inJs) {
|
||||
addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic);
|
||||
const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
// Always prefer to add a method declaration if possible.
|
||||
if (call) {
|
||||
addMethodDeclaration(context, changes, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, preferences);
|
||||
}
|
||||
else {
|
||||
const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token);
|
||||
addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic);
|
||||
if (inJs) {
|
||||
addMissingMemberInJs(changes, classDeclarationSourceFile, declaration, token.text, makeStatic);
|
||||
}
|
||||
else {
|
||||
const typeNode = getTypeNode(program.getTypeChecker(), declaration, token);
|
||||
addPropertyDeclaration(changes, classDeclarationSourceFile, declaration, token.text, typeNode, makeStatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
|
||||
interface EnumInfo { token: Identifier; declaration: EnumDeclaration; enumDeclarationSourceFile: SourceFile; }
|
||||
interface ClassInfo { token: Identifier; declaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
|
||||
type Info = EnumInfo | ClassInfo;
|
||||
|
||||
function isEnumInfo (info: Info): info is EnumInfo {
|
||||
return isEnumDeclaration(info.declaration);
|
||||
}
|
||||
|
||||
function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined {
|
||||
// The identifier of the missing property. eg:
|
||||
// this.missing = 1;
|
||||
@@ -62,15 +79,21 @@ namespace ts.codefix {
|
||||
|
||||
const leftExpressionType = skipConstraint(checker.getTypeAtLocation(parent.expression)!);
|
||||
const { symbol } = leftExpressionType;
|
||||
const classDeclaration = symbol && symbol.declarations && find(symbol.declarations, isClassLike);
|
||||
if (!classDeclaration) return undefined;
|
||||
if (!symbol || !symbol.declarations) return undefined;
|
||||
|
||||
const makeStatic = (leftExpressionType as TypeReference).target !== checker.getDeclaredTypeOfSymbol(symbol);
|
||||
const classDeclarationSourceFile = classDeclaration.getSourceFile();
|
||||
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
|
||||
const call = tryCast(parent.parent, isCallExpression);
|
||||
|
||||
return { token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
|
||||
const classDeclaration = find(symbol.declarations, isClassLike);
|
||||
if (classDeclaration) {
|
||||
const makeStatic = (leftExpressionType as TypeReference).target !== checker.getDeclaredTypeOfSymbol(symbol);
|
||||
const classDeclarationSourceFile = classDeclaration.getSourceFile();
|
||||
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
|
||||
const call = tryCast(parent.parent, isCallExpression);
|
||||
return { token, declaration: classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
|
||||
}
|
||||
const enumDeclaration = find(symbol.declarations, isEnumDeclaration);
|
||||
if (enumDeclaration) {
|
||||
return { token, declaration: enumDeclaration, enumDeclarationSourceFile: enumDeclaration.getSourceFile() };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined {
|
||||
@@ -188,6 +211,16 @@ namespace ts.codefix {
|
||||
return createCodeFixAction(fixName, changes, [makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, token.text], fixId, Diagnostics.Add_all_missing_members);
|
||||
}
|
||||
|
||||
function getActionForEnumMemberDeclaration(
|
||||
context: CodeFixContext,
|
||||
enumDeclarationSourceFile: SourceFile,
|
||||
enumDeclaration: EnumDeclaration,
|
||||
token: Identifier
|
||||
): CodeFixAction | undefined {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, enumDeclaration, enumDeclarationSourceFile));
|
||||
return createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_enum_members);
|
||||
}
|
||||
|
||||
function addMethodDeclaration(
|
||||
context: CodeFixContextBase,
|
||||
changeTracker: textChanges.ChangeTracker,
|
||||
@@ -209,4 +242,32 @@ namespace ts.codefix {
|
||||
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
function createEnumMemberFromEnumDeclaration(checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) {
|
||||
/**
|
||||
* create initializer only string enum.
|
||||
* value of initializer is a string literal that equal to name of enum member.
|
||||
* literal enum or empty enum will not create initializer.
|
||||
*/
|
||||
const firstMember = firstOrUndefined(enumDeclaration.members);
|
||||
let enumMemberInitializer: Expression | undefined;
|
||||
if (firstMember && firstMember.initializer) {
|
||||
const memberType = checker.getTypeAtLocation(firstMember.initializer);
|
||||
if (memberType && memberType.flags & TypeFlags.StringLike) {
|
||||
enumMemberInitializer = createStringLiteral(token.text);
|
||||
}
|
||||
}
|
||||
return createEnumMember(token, enumMemberInitializer);
|
||||
}
|
||||
|
||||
function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration, file: SourceFile) {
|
||||
const enumMember = createEnumMemberFromEnumDeclaration(checker, token, enumDeclaration);
|
||||
changes.replaceNode(file, enumDeclaration, updateEnumDeclaration(
|
||||
enumDeclaration,
|
||||
enumDeclaration.decorators,
|
||||
enumDeclaration.modifiers,
|
||||
enumDeclaration.name,
|
||||
concatenate(enumDeclaration.members, singleElementArray(enumMember))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user