• EnumHelper插件的安装和使用
  • 发布于 2个月前
  • 174 热度
    0 评论
背景
作为 Java 开发,经常需要根据数据库某个字段获取对应的枚举类,然后就要在枚举类添加如下的方法:
public static TestEnum getByCode(Integer code) {
    return Arrays.asList(values()).stream().filter(i -> i.getCode().equals(code)).findFirst().orElse(null);
}
这种方法在不同枚举类的实现都是很相似的,有什么办法可以避免这种重复劳动呢?于是,我就想到了写一个Idea插件去自动生成代码,EnumHelper应运而生。

插件展示
目前该插件已经发布,在Marketplace搜索EnumHelper下载安装即可体验,有其他建议大家也可以留言。

使用教程
1.复制你想要查询的枚举字段
2.生成对应的枚举方法

踩坑总结
该项目代码不是很复杂,就不在这里贴出来了,如果你感兴趣的话可以查看源代码(可以私我,发给大家),网上也有一些不错的插件开发教程,这里简单列一下核心代码。
核心代码:
public class GenerateEnumMethodServiceImpl implements GenerateEnumMethodService {
    @Override
    public void doGenerate(Project project, DataContext dataContext, PsiFile psiFile) {
        GenerateContext generateContext = getGenerateContext(project, dataContext, psiFile);
        GenerateMethodInfo generateMethodInfo = getMethodInfoFromClipboard(generateContext);
        this.generateCode(generateContext, generateMethodInfo);
    }

    private GenerateMethodInfo getMethodInfoFromClipboard(GenerateContext generateContext) {
        String clipboardText = MyUtil.getSystemClipboardText().trim();
        if (clipboardText == null || clipboardText == "") {
            throw new ShipException("please copy enum field first!");
        }
        PsiClass psiClass = generateContext.getPsiClass();
        JvmClassKind classKind = psiClass.getClassKind();
        if (!classKind.equals(JvmClassKind.ENUM)) {
            throw new ShipException("the class must be Enum type!");
        }

        PsiField psiField = getField(psiClass, clipboardText);
        if (psiField == null) {
            throw new ShipException("please check you copy field!");
        }
        GenerateMethodInfo generateMethodInfo = new GenerateMethodInfo();
        generateMethodInfo.setClassName(psiClass.getName());
        generateMethodInfo.setFieldName(psiField.getName());
        generateMethodInfo.setFieldType(psiField.getType().getPresentableText());
        return generateMethodInfo;
    }

    private PsiField getField(PsiClass psiClass, String name) {
        for (PsiField psiField : psiClass.getFields()) {
            if (psiField.getName().equals(name)) {
                return psiField;
            }
        }
        return null;
    }

    /**
     * 生成代码
     *
     * @param generateContext
     */
    private void generateCode(GenerateContext generateContext, GenerateMethodInfo generateMethodInfo) {
        Application application = ApplicationManager.getApplication();
        application.runWriteAction(() -> {
            if (MyUtil.isJava8OrHigher(generateContext)) {
                int currLineNumber = generateContext.getLineNumber();
                for (int i = 0; i < EnumHelperConstants.JDK_8_TOTAL_LINE_NUM; i++) {
                    // 每一行的起始位置offset
                    int lineStartOffset = generateContext.getDocument().getLineStartOffset(currLineNumber++);
                    String codeLine = CodeTemplate.buildCodeLine(i, generateMethodInfo);
                    WriteCommandAction.runWriteCommandAction(generateContext.getProject(), () -> {
                        generateContext.getDocument().insertString(lineStartOffset, codeLine);
                        generateContext.getEditor().getCaretModel().moveToOffset(lineStartOffset + 2);
                        generateContext.getEditor().getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
                    });
                }
            } else {
                int currLineNumber = generateContext.getLineNumber();
                for (int i = 0; i < EnumHelperConstants.JDK_7_TOTAL_LINE_NUM; i++) {
                    // 每一行的起始位置offset
                    int lineStartOffset = generateContext.getDocument().getLineStartOffset(currLineNumber++);
                    String codeLine = CodeTemplate.buildCodeLineForJdk7(i, generateMethodInfo);
                    WriteCommandAction.runWriteCommandAction(generateContext.getProject(), () -> {
                        generateContext.getDocument().insertString(lineStartOffset, codeLine);
                        generateContext.getEditor().getCaretModel().moveToOffset(lineStartOffset + 2);
                        generateContext.getEditor().getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
                    });
                }

            }
        });
    }

    /**
     * 获取上下文信息
     *
     * @param project
     * @param dataContext
     * @param psiFile
     * @return
     */
    private GenerateContext getGenerateContext(Project project, DataContext dataContext, PsiFile psiFile) {
        GenerateContext generateContext = new GenerateContext();
        generateContext.setProject(project);
        generateContext.setDataContext(dataContext);
        generateContext.setPsiFile(psiFile);

        Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
        PsiElement psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
        Document document = editor.getDocument();
        generateContext.setEditor(editor);
        generateContext.setDocument(document);
        generateContext.setPsiElement(psiElement);
        // 编辑器的位置
        generateContext.setOffset(editor.getCaretModel().getOffset());
        // 编辑器所在行
        generateContext.setLineNumber(document.getLineNumber(generateContext.getOffset()));
        generateContext.setStartOffset(document.getLineStartOffset(generateContext.getLineNumber()));
        generateContext.setEditorText(document.getCharsSequence());
         // 堆代码 duidaima.com
        String clazzName = psiFile.getName().replace(".java", "");
        // 获取类
        PsiClass[] psiClasses = PsiShortNamesCache.getInstance(generateContext.getProject()).getClassesByName(clazzName, GlobalSearchScope.projectScope(generateContext.getProject()));
        generateContext.setPsiClass(psiClasses[0]);
        return generateContext;
    }
}
相关教程:
官方文档:https://plugins.jetbrains.com/docs/intellij/plugins-quick-start.html

但是在开发过程中还是踩了不少坑,觉得有必要在这里记录下。
问题1: 插件开发创建项目有两种方式,第一种是选择Intelij Platform Plugin直接创建,第二种是选择Gradle的方式创建,开始选择了第一种方式,ProjectSDK选择时没有IntelliJ Platform Plugin SDK这个选项!

解决方案: 后面发现这个SDK是需要手动创建的,Project Structure-->SDKs-->点击+号添加。而且对于这两种方式,如果你不需要引入其他第三方依赖,那么推荐第一种方式创建项目更简单,毕竟Gradle配置太麻烦而且有很多人不熟悉Gradle。

问题2: 控制台显示Caused by: java.lang.ClassNotFoundException。
解决方案: 后来发现包依赖不全,需要在plugin.xml添加如下两个包。
<depends>com.intellij.modules.lang</depends>
<depends>com.intellij.modules.java</depends>
问题3: 通过New-->Plugin Devkit-->Action创建的action,快捷键触发无效!
解决方案: 看了官方文档才发现,idea创建的action配置是不全的,只有默认的情况,我的笔记本是Mac OS系统需要在plugin.xml添加配置如下:
 <keyboard-shortcut
        keymap="$default"
        first-keystroke="control shift J"/>

<!-- ...except the "Mac OS X" keymap and its children. -->
<keyboard-shortcut
        keymap="Mac OS X"
        first-keystroke="control shift J"
        remove="true"/>

<!-- The "Mac OS X 10.5+" keymap and its children will have only
this keyboard shortcut for this action. -->
<keyboard-shortcut
        keymap="Mac OS X 10.5+"
        first-keystroke="control shift J"
        replace-all="true"/>
问题4: 当时代码写完功能也测试完了,准备打包才发现找不到打包方式😅。于是就以为这种方式打不了包,换成Gradle重建项目,然后idea下载gradle很慢我就取消,手动下载了6.4.1的包,好不容易安装完gradle启动却报错至少要7.3+!

解决方案: build.gradle上org.jetbrains.intellij的版本和Gradle的版本有关联关系的,如果不想升Gradle版本也可以降低org.jetbrains.intellij版本,建议提前阅读https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html。

问题5: build.gradle报错==No signature of method: build_ah2nr93m937t291v887ebyou3.patchPluginXml() is applicable for argument types==。
解决方案: 这个问题google都没找到解决方案,后来感觉这个不重要就注释掉试试,问题解决。

问题6: Gradle版本的项目弄好了,启动沙箱环境调试时才发现插件入口调不出来,File菜单显示的还是之前的插件action名称。
解决方案: 后面想到可能是缓存导致的,就在项目SDKs配置那里找到沙箱环境路径,在命令行进入路径rm -rf * 删除所有文件,再次启动项目就行了。

问题原因就是本地不同的idea插件开发,共用一个沙箱环境。
最后提醒一下,这个插件暂时不支持低版本的 jdk,因为目前生成的代码,采取的是 Lambda 表达式的形式。

用户评论