Idea插件开发

2024-08-04 09:31:09 浏览数 (2)

前言

https://www.w3cschool.cn/intellij_idea_doc/

JDK要求必须11以上,我这里使用的是17。

下载JDK17

https://www.oracle.com/cn/java/technologies/downloads/#jdk17-windows

安装后确认下版本

代码语言:javascript复制
java --version

环境变量中的JAVA_HOME也要设置为JDK17的路径。

创建项目

创建项目

项目打开后点击plugin.xml配置插件的基本信息

如下

代码语言:javascript复制
<idea-plugin>
    <id>cn.psvmc.VueComp</id>
    <name>VueComp</name>
    <vendor email="183518918@qq.com" url="https://www.psvmc.cn">码客说</vendor>
    <description><![CDATA[
    方便生成Vue页面模板.<br>
    <em>支持TS Less 组合式API</em>
    <em>模板、脚本和样式分离</em>
  ]]></description>
    <depends>com.intellij.modules.platform</depends>
    <extensions defaultExtensionNs="com.intellij">
    </extensions>
    <actions>
        <action id="cn.psvmc.vuecomp.CreateVueCompAction"
                class="cn.psvmc.vuecomp.CreateVueCompAction"
                text="创建Vue组件/页面">
            <add-to-group group-id="NewGroup" anchor="first"/>
            <!-- 可通过 ctrl   H 快捷键触发 -->
            <keyboard-shortcut keymap="$default" first-keystroke="ctrl H"/>
        </action>
    </actions>
</idea-plugin>

报错

Could not resolve org.jetbrains.intellij.plugins:gradle-intellij-plugin:1.8.0

环境变量中的JAVA_HOME也要设置为JDK17的路径。

示例

简单提示

这里我们只是简单的在右下角弹出通知显示项目根目录

CreateVueCompAction.java

代码语言:javascript复制
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;

public class CreateVueCompAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        Notifications.Bus.notify(new Notification("Print", "", e.getProject().getBasePath(), NotificationType.INFORMATION), e.getProject());
    }
}

当然也可以弹窗显示

代码语言:javascript复制
Messages.showMessageDialog(e.getProject().getBasePath(), "Project BasePath", Messages.getInformationIcon());

在plugin.xml的根节点下添加

代码语言:javascript复制
<actions>
    <action id="cn.psvmc.vuecomp.CreateVueCompAction"
            class="cn.psvmc.vuecomp.CreateVueCompAction"
            text="创建Vue组件/页面">
        <add-to-group group-id="NewGroup" anchor="first"/>
        <!-- 可通过 ctrl   H 快捷键触发 -->
        <keyboard-shortcut keymap="$default" first-keystroke="ctrl H"/>
    </action>
</actions>

也可以在代码文件夹上点击鼠标右键,选择 New => Plugin DevKit => Action

如果没有的话,那么可能需要在先在IDEA中装个 Plugin DevKit插件。

获取选中的文件夹

代码语言:javascript复制
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;

import java.util.ArrayList;
import java.util.List;

public class CreateVueCompAction extends AnAction {
    @Override
    public void actionPerformed(AnActionEvent e) {
        Project project = e.getProject();
        if (project == null) {
            Messages.showMessageDialog("No project found", "Error", Messages.getErrorIcon());
            return;
        }

        VirtualFile[] files = e.getData(com.intellij.openapi.actionSystem.CommonDataKeys.VIRTUAL_FILE_ARRAY);
        if (files == null || files.length == 0) {
            Messages.showMessageDialog("No file selected", "Error", Messages.getErrorIcon());
            return;
        }

        List<String> folderPathList = new ArrayList<>();
        for (VirtualFile file : files) {
            if (file.isDirectory()) {
                folderPathList.add(file.getPath());
            }
        }
        String  folderPaths= String.join("n", folderPathList);
        Messages.showMessageDialog(folderPaths, "选择的文件夹", Messages.getInformationIcon());
    }
}

文件创建及写入

代码语言:javascript复制
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;

import java.io.IOException;

public class CreateVueCompAction extends AnAction {
    @Override
    public void actionPerformed(AnActionEvent e) {
        ApplicationManager.getApplication().runWriteAction(() -> {
            Project project = e.getProject();
            if (project == null) {
                return;
            }
            VirtualFile[] files = e.getData(com.intellij.openapi.actionSystem.CommonDataKeys.VIRTUAL_FILE_ARRAY);
            if (files == null || files.length == 0) {
                Messages.showMessageDialog("未选择文件夹!", "错误", Messages.getErrorIcon());
                return;
            }
            for (VirtualFile file : files) {
                if (file.isDirectory()) {
                    try {
                        String fileName = "main.txt";
                        VirtualFile newFile = file.findChild(fileName);
                        if (newFile == null) {
                            newFile = file.createChildData(this, fileName);
                            String content = "Hello, World!";
                            VirtualFile finalNewFile = newFile;
                            WriteAction.run(() -> {
                                finalNewFile.setBinaryContent(content.getBytes());
                            });
                        }
                    } catch (IOException ignored) {
                    }
                }
            }
            VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
        });
    }
}

生成Vue文件

代码语言:javascript复制
import cn.psvmc.vuecomp.utils.ZFreeMarkerUtils;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CreateVueCompAction extends AnAction {
    @Override
    public void actionPerformed(AnActionEvent e) {
        ApplicationManager.getApplication().runWriteAction(() -> {
            Project project = e.getProject();
            if (project == null) {
                return;
            }
            VirtualFile[] files = e.getData(com.intellij.openapi.actionSystem.CommonDataKeys.VIRTUAL_FILE_ARRAY);
            if (files == null || files.length == 0) {
                Messages.showMessageDialog("未选择文件夹!", "错误", Messages.getErrorIcon());
                return;
            }
            for (VirtualFile file : files) {
                if (file.isDirectory()) {
                    String fileName = file.getName();
                    Map<String, Object> dataModel = new HashMap<>();
                    dataModel.put("fileName", fileName);
                    List<Map<String, String>> templateList = new ArrayList<>();
                    Map<String, String> templateMap = new HashMap<>();
                    templateMap.put("templateName", "ts_vue.ftl");
                    templateMap.put("fileNameAll", fileName   ".vue");
                    templateList.add(templateMap);

                    Map<String, String> styleMap = new HashMap<>();
                    styleMap.put("templateName", "style.ftl");
                    styleMap.put("fileNameAll", fileName   ".less");
                    templateList.add(styleMap);

                    for (Map<String, String> item : templateList) {
                        try {
                            String fileNameAll = item.get("fileNameAll");
                            String templateName = item.get("templateName");
                            VirtualFile newFile = file.findChild(fileNameAll);
                            if (newFile == null) {
                                newFile = file.createChildData(this, fileNameAll);
                                String content = ZFreeMarkerUtils.getStrByFtl(templateName, dataModel);
                                VirtualFile finalNewFile = newFile;
                                WriteAction.run(() -> {
                                    finalNewFile.setBinaryContent(content.getBytes());
                                });
                            } else {
                                Notification notice = new Notification("Print", "", "文件已存在", NotificationType.INFORMATION);
                                Notifications.Bus.notify(notice, e.getProject());
                            }
                        } catch (IOException ignored) {
                        }
                    }
                }
            }
            VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
        });
    }
}

对应的模板文件

ts_vue.ftl

代码语言:javascript复制
<template>
    <div class="${fileName}">
        {{ loginName }}
    </div>
</template>

<script lang="ts" setup>
    import {ref} from "vue";
    const loginName = ref(null)
</script>

<style src="./${fileName}.less" lang="less" scoped>
</style>

style.ftl

代码语言:javascript复制
.${fileName} {
}

使用的时候,先创建我们的组件文件夹,比如MainView,右键点击创建Vue组件/页面,就会在这个文件夹中创建跟文件夹同名的两个文件。

打包

Gradle中通过Tasks/build/build来打包我们的插件。

构建好后我们可以在build/distributions目录下面找到我们的zip包,拿到后直接在idea上面进行离线安装即可。

注意

网上有说用intellij/buildPlugin打包,这是不对的,这样打的是Jar包,我们代码中引用的模板文件不会打进去,就不能正常使用。

注意事项

构建配置

build.gradle.kts

代码语言:javascript复制
plugins {
    id("java")
    id("org.jetbrains.intellij") version "1.8.0"
}

group = "cn.psvmc"
version = "1.0-SNAPSHOT"

repositories {
    maven("https://maven.aliyun.com/repository/central")
    maven("https://maven.aliyun.com/repository/public")
    maven ("https://maven.aliyun.com/repository/gradle-plugin")
    maven ("https://maven.aliyun.com/repository/apache-snapshots")
    mavenCentral()
    gradlePluginPortal()
    mavenCentral()
}

// Configure Gradle IntelliJ Plugin
// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
intellij {
    version.set("2021.3.3")
    type.set("IC") // Target IDE Platform

    plugins.set(listOf(/* Plugin Dependencies */))
}

tasks {
    // Set the JVM compatibility versions
    withType<JavaCompile> {
        options.encoding = "UTF-8"
        sourceCompatibility = "11"
        targetCompatibility = "11"
    }

    patchPluginXml {
        sinceBuild.set("213")
        untilBuild.set("223.*")
    }
}

注意相比于自动生成项目的配置,这里更改了如下几点:

镜像库

代码语言:javascript复制
repositories {
    maven("https://maven.aliyun.com/repository/central")
    maven("https://maven.aliyun.com/repository/public")
    maven ("https://maven.aliyun.com/repository/gradle-plugin")
    maven ("https://maven.aliyun.com/repository/apache-snapshots")
    mavenCentral()
    gradlePluginPortal()
    mavenCentral()
}

编码

要设置编码,否则中文会乱码。

代码语言:javascript复制
withType<JavaCompile> {
    options.encoding = "UTF-8"
    sourceCompatibility = "11"
    targetCompatibility = "11"
}

项目本身也设置为UTF-8

插件版本设置

intellij中的version.set("2021.3.3")和patchPluginXml中的sinceBuild.set("213")要匹配。

这是我们插件支持的最低IDEA版本

运行的时候会自动下载该版本的IDEA来运行我们的插件。

这个版本可以在IDEA的 Help => About 查看

Java版本设置

还有这个版本要和对应IDEA依赖的Java版本一致,可以和我们插件项目依赖的Java版本不一致,我就是用的JDK17,而这里配置的11,可以正常编译运行。

代码语言:javascript复制
withType<JavaCompile> {
    sourceCompatibility = "11"
    targetCompatibility = "11"
}

更换gradle版本

默认的7.5我在构建的时候报错,所以就更换为7.6版本了。

gradle-wrapper.properties

代码语言:javascript复制
#Wed Mar 20 15:45:42 HKT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

镜像地址

https://mirrors.cloud.tencent.com/gradle/

构建报错

Caused by: java.lang.NullPointerException: getHeaderField(“Location”) must not be null

这个错误不影响。

FreeMarker使用

build.gradle.kts中添加

代码语言:javascript复制
dependencies {
    implementation("org.freemarker:freemarker:2.3.31")
}

resources/templates/template.ftl

代码语言:javascript复制
# This is an example template
Hello, ${name}!

工具类

代码语言:javascript复制
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class ZFreeMarkerUtils {
    public static String getStrByFtl(String templateName,Map<String, Object> dataModel) {
        try {
            // 配置 Freemarker
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
            cfg.setClassForTemplateLoading(ZFreeMarkerUtils.class, "/templates");
            Template template = cfg.getTemplate(templateName);
            try (StringWriter out = new StringWriter()) {
                template.process(dataModel, out);
                String result = out.toString();
                System.out.println(result);
                return result;
            } catch (TemplateException e) {
               return "";
            }

        } catch (IOException ex) {
            return "";
        }
    }

    public static void test(){
        // 创建数据模型
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("name", "World");
        getStrByFtl("template.ftl",dataModel);
    }
}

0 人点赞