前言
以下代码为开发idea quick fix功能插件演示代码,该代码实现的功能是:对于标注有SpingMVC中的 GetMapping 、PostMapping 、PatchMapping 、PutMapping 、DeleteMapping 和RequestMapping 等注解等方法,若没有同时标注LevenApi 注解,则在方法名上会出现警告,提示开发者加上LevenApi 注解,此时使用quick fix的功能即可在方法上生成LevenApi 注解。
一、Quick Fix是什么?
quick fix就是快速修复的意思,在idea编辑代码的时候,当我们输入一些违法或者是不安全(或是不推荐)的代码时,idea会自动帮我们检测出来,并使用红色或黄色波浪线提示我们修改代码。对于有些非法或者是不推荐的代码,idea能够推测出正确或者是被推荐的代码时,就会给我们提供一个快速修复的功能,这个功能就是quick fix。 而前面的检测行为则是由Annotator功能完成的,因此quick fix通常是跟着Annotator出现的,而有Annotator却是不一定有quick fix(就是没有推荐修改的情况)。
以上图片演示就是常见的quick fix场景——导入类。红色方块就是Annotator,提示“无法解析BigDecimal”;橙色框则是quick fix,通过点击Import class 或者使用快捷键(Windows是alt + enter ,Mac是option + enter )快速导入BigDecimal类。
二、开发quick fix 通常包含两部分
1. Annotator
筛选出需要quick fix的代码段:校验标注有SpringMVC mapping相关注解的方法是否同时标注有@LevenApi 注解,如果没有则在这个方法名上添加Annotator ,提示需要加上@LevenApi 注解
package run.leven.fix;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class LevenApiAnnotator implements Annotator {
private static final Set<String> MAPPING_NAME_SET;
static {
MAPPING_NAME_SET = new HashSet<>(
Arrays.asList(
FixAnnotationEnum.PatchMapping.getQualifiedName(),
FixAnnotationEnum.PutMapping.getQualifiedName(),
FixAnnotationEnum.PostMapping.getQualifiedName(),
FixAnnotationEnum.GetMapping.getQualifiedName(),
FixAnnotationEnum.RequestMapping.getQualifiedName(),
FixAnnotationEnum.DeleteMapping.getQualifiedName()
)
);
}
@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
final Project project = element.getProject();
if (JavaPsiFacade.getInstance(project).findClass(FixAnnotationEnum.LevenApi.getQualifiedName(), GlobalSearchScope.allScope(project)) == null){
return;
}
if (!(element instanceof PsiMethod)){
return;
}
if (!(element.getContainingFile() instanceof PsiJavaFile)){
return;
}
final PsiMethod psiMethod = (PsiMethod) element;
final PsiAnnotation[] methodAnnotations = psiMethod.getAnnotations();
PsiAnnotation mappingAnnotation = null;
PsiAnnotation levenApiAnnotation = null;
for (PsiAnnotation methodAnnotation : methodAnnotations) {
if (MAPPING_NAME_SET.contains(methodAnnotation.getQualifiedName())){
mappingAnnotation = methodAnnotation;
}else if (FixAnnotationEnum.LevenApi.getQualifiedName().equals(methodAnnotation.getQualifiedName())
&& psiMethod.getNameIdentifier() != null){
levenApiAnnotation = methodAnnotation;
}
}
if (mappingAnnotation != null && levenApiAnnotation == null && psiMethod.getNameIdentifier() != null){
holder.newAnnotation(HighlightSeverity.WARNING, "添加@LevenApi注解")
.range(psiMethod.getNameIdentifier())
.highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
.withFix(new LevenApiQuickFix(psiMethod))
.create();
}
}
}
2.quick fix
quick fix处理:在方法上创建@LevenApi 注解
package run.leven.fix;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
public class LevenApiQuickFix extends BaseIntentionAction {
private final PsiMethod psiMethod;
public LevenApiQuickFix(PsiMethod psiMethod) {
this.psiMethod = psiMethod;
}
@Override
public @IntentionName @NotNull String getText() {
return "Create @LevenApi";
}
@Override
public @NotNull @IntentionFamilyName String getFamilyName() {
return "Create @LevenApi";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile psiFile) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile psiFile) throws IncorrectOperationException {
final PsiClass levenApiClass = JavaPsiFacade.getInstance(project).findClass(FixAnnotationEnum.LevenApi.getQualifiedName(), GlobalSearchScope.projectScope(project));
if (levenApiClass != null){
ApplicationManager.getApplication().invokeLater(() -> WriteCommandAction.runWriteCommandAction(project, () ->{
final PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null){
return;
}
final PsiImportList importList = ((PsiJavaFile) containingClass.getContainingFile()).getImportList();
if (importList == null){
return;
}
importLevenApiClass(project, levenApiClass, importList);
psiMethod.getModifierList().addAnnotation("LevenApi(name = \"" + psiMethod.getName() + "\")");
}));
}
}
private void importLevenApiClass(@NotNull Project project, PsiClass levenApiClass, PsiImportList importList) {
boolean notExistLevenApiImportStatement = true;
for (PsiImportStatement importStatement : importList.getImportStatements()) {
if (FixAnnotationEnum.LevenApi.getQualifiedName().equals(importStatement.getQualifiedName())){
notExistLevenApiImportStatement = false;
}
}
if (notExistLevenApiImportStatement){
final PsiImportStatement levenApiImportStatement = JavaPsiFacade.getElementFactory(project).createImportStatement(levenApiClass);
importList.add(levenApiImportStatement);
}
}
}
3. FixAnnotationEnum
package run.leven.fix;
public enum FixAnnotationEnum {
GetMapping("GetMapping", "org.springframework.web.bind.annotation.GetMapping"),
PostMapping("PostMapping", "org.springframework.web.bind.annotation.PostMapping"),
PutMapping("PutMapping", "org.springframework.web.bind.annotation.PutMapping"),
PatchMapping("PatchMapping", "org.springframework.web.bind.annotation.PatchMapping"),
RequestMapping("RequestMapping", "org.springframework.web.bind.annotation.RequestMapping"),
DeleteMapping("DeleteMapping", "org.springframework.web.bind.annotation.DeleteMapping"),
LevenApi("LevenApi", "run.leven.fix.LevenApi");
FixAnnotationEnum(String shortName, String qualifiedName) {
this.shortName = shortName;
this.qualifiedName = qualifiedName;
}
private String shortName;
private String qualifiedName;
public String getShortName() {
return shortName;
}
public String getQualifiedName() {
return qualifiedName;
}
}
总结
提示:这里对文章进行总结:
|