
简介本资源是一份面向Java开发者与IDE插件工程师的IntelliJ Platform插件开发实战指南聚焦于2023–2024版IntelliJ IDEA基于JetBrains Runtime 17.0.9的插件开发体系系统解决从零入门到UI工具类、语言级高级插件开发的核心问题。全册以PDF格式呈现共1个文件大小15.82MB内容结构清晰分为四大部分上册涵盖插件开发基础与图形化界面开发适用于框架集成、代码统计、效率工具等UI型插件下册深入语言服务插件开发支撑代码补全、依赖分析、静态检查等高阶功能附录则汇总开发工具链、API参考与权威资料链接。已有383人学习下载手册融合官方文档、一线实践与社区经验含详细工程搭建步骤、环境配置要点、插件测试方法及典型目录结构说明特别适合希望快速构建可商用插件的中高级开发者系统性掌握开发范式与避坑要点。1. 这不是写个“Hello World”就完事的 IDEA 插件开发它要真能跑在你每天打开的 IDE 里还要扛住 200 行代码改动、3 次 IDE 版本升级、5 个用户并发点击不卡死你手头这份《Intellij Platform Plugin 插件开发手册上.pdf》不是一本教你怎么点几下菜单生成空插件的速成指南。它讲的是——如何让一段 Java/Kotlin 代码真正嵌进 IntelliJ IDEA或 PyCharm、WebStorm、Android Studio 等所有基于 IntelliJ Platform 的 IDE的底层运行时里和编辑器的 PSI 树、编辑器事件循环、项目模型、调试器、甚至 JVM 启动参数打成一片。这意味着你写的不是独立应用而是 IDE 的“器官级组件”你调用的不是标准 JDK API而是com.intellij.openapi.*下近 3000 个包、1.2 万 类构成的私有契约你提交的不是 jar 包而是一个带plugin.xml声明、META-INF/MANIFEST.MF签名、resources/图标资源、lib/依赖隔离、且必须通过 JetBrains Plugin Repository 官方签名验证的.jar或.zip归档。新手常以为“写个 Action 就算插件”结果一上线就被用户反馈“点了没反应”“打开项目就报 NPE”“升级 IDEA 后直接消失”。这不是玄学——是没吃透 Platform 的类加载隔离机制、事件分发顺序、模块生命周期钩子。适合谁不是想快速做个代码生成器的脚手架党而是已用 IDEA 开发过 6 个月以上、能看懂PsiElement和AnActionEvent关系、愿意为一个按钮多写 200 行状态校验逻辑的实战派。本文就从你下载完手册 PDF 后真正要做的第一件事开始不是读而是搭出能编译、能安装、能断点调试的最小可运行环境。2. 用 IntelliJ IDEA 2024.1 Gradle 构建第一个可安装插件绕开模板陷阱直取最小可行骨架2.1 为什么不用官方 Plugin DevKit 模板——它默认绑死旧版 Gradle 和废弃 APIJetBrains 官网推荐的 Plugin DevKit 模板通过 New Project → Plugin看似省事但实际踩坑率极高默认使用 Gradle 7.6而 IDEA 2024.1 的 Platform SDK 要求 Gradle 8.4 才能正确解析intellij-platform-plugin-template的新 DSL模板生成的build.gradle.kts里硬编码intellij.version 2023.2导致编译时找不到com.intellij:openapi:241.14494.2222024.1 对应 build number 241.x自动生成的plugin.xml缺少depends显式声明com.intellij.modules.platform导致插件在 Community Edition 中无法激活尤其当你目标用户含大量开源版用户时。我一般会弃用模板手建骨架——只保留 4 个必要文件其余全靠 Gradle 插件动态注入my-first-plugin/ ├── build.gradle.kts # 核心构建脚本 ├── settings.gradle.kts # 空文件仅声明 root project ├── src/ │ └── main/ │ ├── kotlin/ # Kotlin 源码Java 同理 │ │ └── MyAction.kt │ ├── resources/ │ │ └── META-INF/ │ │ └── plugin.xml │ └── pluginDescription.html # 必须存在否则插件市场拒绝上传提示pluginDescription.html不需要复杂内容但必须存在且非空。最简版本只需一行pMy first plugin./p否则gradle buildPlugin会静默失败。2.2 build.gradle.kts用intellij-platform-plugin-template替代老旧 DevKit这是当前2024 年中最稳定、更新最快的构建方案。它由 JetBrains 官方维护自动适配最新 Platform SDK 和 Gradle 版本// build.gradle.kts plugins { id(org.jetbrains.intellij) version 1.17.2 apply false // 注意此版本号需与 IDEA 2024.1 匹配 id(org.jetbrains.kotlin.jvm) version 1.9.23 apply false } // 根 project 配置 allprojects { repositories { mavenCentral() maven(https://cache-redirector.jetbrains.com/repo.maven.apache.org/maven2/) // 加速国内访问 } } subprojects { apply(plugin org.jetbrains.intellij) apply(plugin org.jetbrains.kotlin.jvm) intellij { version.set(241.14494.222) // IDEA 2024.1.3 的 build number务必查官网确认 type.set(IC) // ICCommunity, IUUltimate, PYPyCharm... downloadSources.set(true) plugins.set(listOf(java, properties)) // 声明依赖的内置插件决定你的插件能访问哪些 API } dependencies { implementation(kotlin(stdlib)) // 注意不要添加 compileOnly com.intellij:openapi:xxx —— intellij {} 已自动引入 } tasks.withTypeorg.jetbrains.intellij.tasks.PackPluginTask { // 生成的插件包名强制小写避免 Windows 路径大小写问题 archiveBaseName.set(my-first-plugin) } }关键参数说明version.set(241.14494.222)不是 IDEA 版本号而是Build Number。必须去 IntelliJ Platform SDK Versions 查表匹配。填错会导致ClassNotFoundException: com.intellij.openapi.project.Project这类底层类找不到type.set(IC)明确指定目标 IDE。ICIntelliJ IDEA Community兼容性最广IUUltimate功能更多但用户基数小plugins.set(listOf(java, properties))声明你的插件依赖哪些内置插件提供的 API。比如你要操作 Java 文件就必须加java要读取.properties文件就得加properties。漏写会导致PsiJavaFile等类在编译期就报红archiveBaseName控制最终生成的my-first-plugin-1.0-SNAPSHOT.zip文件名。IDE 安装时以此识别插件 ID必须全小写、无空格、无特殊字符否则 Windows 下安装失败。2.3 plugin.xml声明即契约——3 行 XML 决定插件生死src/main/resources/META-INF/plugin.xml是插件的“宪法”IDE 启动时先读它再加载类。最简有效版如下!-- src/main/resources/META-INF/plugin.xml -- idea-plugin idmy.first.plugin/id nameMy First Plugin/name version1.0/version vendor emaildevmycompany.comMy Company/vendor dependscom.intellij.modules.platform/depends !-- 关键没有这行Community 版本根本不会加载你的插件 -- applicationListeners listener classMyStartupActivity topiccom.intellij.openapi.application.ApplicationActivationListener/ /applicationListeners actions action idMyFirstAction classMyAction textHello from Plugin descriptionMy first action add-to-group group-idToolsMenu anchorlast/ /action /actions /idea-plugin逐行解释idmy.first.plugin/id插件唯一标识符必须全局唯一。建议用反向域名格式如com.mycompany.myplugin避免与他人冲突dependscom.intellij.modules.platform/depends这是生死线。com.intellij.modules.platform是 Platform 的核心模块提供Application,Project,VirtualFile等基础类。不声明IDE 认为你的插件“不兼容本平台”直接跳过加载applicationListeners注册启动监听器。MyStartupActivity必须实现com.intellij.openapi.application.ApplicationActivationListener接口IDE 启动时自动调用其appActivated()方法——这是你做初始化如注册服务、预热缓存的唯一可靠时机actions定义菜单项。add-to-group group-idToolsMenu anchorlast/表示加到顶部菜单栏的 “Tools” 菜单末尾。group-id必须是 IDEA 内置的 Group ID查 Default Menu Groups 填错会导致菜单不显示。3. 在真实 IDEA 中调试插件不是 Run Configuration 一跑就完而是要复现用户现场的断点链3.1 创建正确的 Run Configuration用 “Plugin” 类型而非 “Application”很多人误用Application类型启动插件结果发现断点进不去MyAction.actionPerformed()Project对象始终为nullPsiManager.getInstance(project)抛NullPointerException。原因Application启动的是独立 JVM不加载 IntelliJ Platform 的类加载器、不初始化com.intellij.idea.IdeaApplication、不挂载 PSI 解析器。你调试的只是个空壳。✅ 正确做法用 IDEA 自带的Plugin Run ConfigurationRun → Edit Configurations → → PluginName:Debug My PluginPlugin path: 选择你项目根目录自动识别build/distributions/*.zipIDE path: 指向你本地安装的IntelliJ IDEA 2024.1 Community Edition不是 Ultimate确保与intellij.type一致VM options: 添加-Dsun.awt.noerasebackgroundtrue -XX:MaxMetaspaceSize512m防止 macOS 渲染异常和 Metaspace OOMBefore launch: 勾选Gradle task→ 选择buildPlugin确保每次 Debug 前自动打包最新版。注意首次运行前务必关闭所有已打开的 IDEA 实例。IDEA 的 Plugin Debugger 会启动一个沙箱实例Sandbox Instance其配置、插件、缓存全部隔离。你在主 IDEA 里装的插件对沙箱实例完全不可见——这是故意设计避免污染开发环境。3.2 断点策略从 UI 事件到 PSI 解析的完整链路一个典型 Action 的执行链是UI 点击 → Event Dispatch Thread → AnAction.actionPerformed() → 获取当前 Project → 获取 PsiFile → 解析 PSI Tree → 修改 AST → 提交 Document所以断点不能只打在actionPerformed()。必须覆盖三层断点位置触发时机为什么必打查什么MyAction.update(AnActionEvent e)每次菜单渲染前调用判断 Action 是否启用如当前是否在 Java 文件中e.getData(PlatformDataKeys.PROJECT)是否为 nulle.getData(LangDataKeys.PSI_FILE)是否为PsiJavaFileMyAction.actionPerformed(AnActionEvent e)用户点击后主逻辑入口e.getProject()是否有效e.getData(LangDataKeys.EDITOR)是否有光标位置MyStartupActivity.appActivated()IDE 启动完成时初始化单例服务、监听器ServiceManager.getService(MyService::class.java)是否返回非 null实操技巧在update()里加日志override fun update(e: AnActionEvent) { val project e.project val file e.getData(LangDataKeys.PSI_FILE) println([DEBUG] update: project$project, file$file, lang${file?.language?.displayName}) e.presentation.isEnabledAndVisible project ! null file is PsiJavaFile }这样启动沙箱 IDEA 后打开任意 Java 文件看 Console 输出就能确认 Action 是否被正确识别上下文。3.3 沙箱实例的调试技巧如何看到真实用户遇到的 NPE沙箱实例的idea.log位于Windows:%USERPROFILE%\.IntelliJIdea2024.1\system\log\idea.logmacOS:~/Library/Caches/JetBrains/IdeaIC2024.1/log/idea.logLinux:~/.cache/JetBrains/IdeaIC2024.1/log/idea.log当用户报告 “点击就崩溃”你不能只看自己 IDE 的 Console。必须在沙箱 IDEA 中复现问题立即打开对应idea.log搜索ERROR或java.lang.NullPointerException日志里会包含完整堆栈精确到哪一行PsiElement.getParent()返回了 null对照源码发现是PsiElement已被 GC常见于异步线程持有 PSI 引用从而定位到必须用ApplicationManager.getApplication().invokeLater{}切回 EDT 线程。血泪经验90% 的插件崩溃源于在非 EDT 线程访问 PSI。PsiElement不是线程安全的它的getParent()、getChildren()等方法必须在 Event Dispatch Thread 中调用。别信文档说 “某些方法线程安全”——实际场景中只要涉及 PSI 树遍历一律切回 EDT。4. 插件开发避坑指南5 条真实翻车记录每一条都来自用户投诉工单4.1 现象插件安装后菜单不显示plugin.xml里明明写了add-to-group原因group-id值错误或拼写错误。例如写成ToolsMenu正确 vsToolMenu少个 s vstoolsMenu大小写敏感。IDE 内部用GroupDescriptor查找 Group不存在则静默丢弃 Action。解决打开沙箱 IDEA →Help → Find Action→ 输入Internal Actions→ 启用Internal插件 → 搜索Show Group Structure→ 查看真实 Group ID 列表。或直接查看源码com.intellij.ide.actions包下的DefaultActionGroup子类。4.2 现象MyAction.actionPerformed()被调用但e.project为 null原因用户在未打开任何项目的 Welcome Screen 界面点击了你的 Action。AnActionEvent的project数据只在 Project Open 状态下提供。解决永远用e.getData(PlatformDataKeys.PROJECT)替代e.project并做空判断val project e.getData(PlatformDataKeys.PROJECT) ?: return // 退出不执行后续逻辑4.3 现象插件在 IDEA 2024.1 能运行升级到 2024.2 后抛NoClassDefFoundError: com/intellij/openapi/vfs/VirtualFile原因VirtualFile类在 2024.2 中被移至com.intellij.vfs包但你的代码仍引用旧路径com.intellij.openapi.vfs.VirtualFile。Platform SDK 的二进制兼容性只保证同一主版本内如 241.x跨主版本241→242需重新编译并适配 API 变更。解决升级intellij.version到242.xxxxx运行./gradlew buildPlugin --stacktrace看编译错误定位具体类变更查 IntelliJ Platform Changelog 确认迁移路径如VirtualFile新路径、PsiTreeUtil方法废弃等。4.4 现象插件修改代码后用户重启 IDEA 才生效无法热重载原因IntelliJ Platform不支持插件热重载。Reload plugin按钮只重新加载类但不重建 PSI 缓存、不重置 Service 实例、不刷新 UI 组件。强行热重载会导致PsiManager持有旧Project引用后续所有 PSI 操作都指向已销毁对象。解决接受现实——开发阶段用沙箱实例快速重启平均 8 秒发布后告知用户“需重启生效”。若真要热更新必须用com.intellij.openapi.util.Disposer注册清理钩子在插件卸载时主动释放所有 PSI 引用、取消监听器、关闭线程池。4.5 现象插件在 Windows 上正常macOS 上图标不显示菜单文字乱码原因plugin.xml中icon路径用反斜杠\Windows 风格而 macOS 使用正斜杠/。且图标资源未按规范放在src/main/resources/icons/下IDE 无法定位。解决所有路径用正斜杠/图标必须放在src/main/resources/icons/目录下plugin.xml中声明icon/icons/my_icon.svg/iconSVG 图标需符合 JetBrains Icon Guidelines 单色、无渐变、尺寸 16x16 和 32x32 两套。5. 从 “能跑” 到 “能交付”验证插件健壮性的 3 个硬指标和 1 个后悔药5.1 指标一启动耗时 ≤ 200ms —— 用户不会为你的插件多等 1 秒IDE 启动时会同步加载所有启用插件的plugin.xml并初始化ApplicationActivationListener。如果你的MyStartupActivity.appActivated()里做了耗时操作如扫描整个~/.m2仓库、解析大 JSON 配置用户会感知到 IDEA 启动变慢。验证方法在沙箱 IDEA 中禁用所有其他插件启动 IDEA打开Help → Diagnostic Tools → Debug Log Settings添加日志规则#com.intellij.openapi.application.impl.ApplicationImpl重启查看idea.log中ApplicationImpl: App init took行记录时间单独启用你的插件对比时间差。优化手段所有 IO 操作文件读取、网络请求必须放在线程池ApplicationManager.getApplication().executeOnPooledThread { val config loadConfigFromDisk() // 耗时操作 ApplicationManager.getApplication().invokeLater { myService.init(config) // 回到 EDT 更新 UI 或状态 } }配置加载加AtomicBoolean缓存避免重复解析appActivated()只做轻量注册重活交给ProjectOpenedListener项目打开时再触发。5.2 指标二内存泄漏检测 —— 用 YourKit 看 PSI 引用是否被正确释放插件最大的内存杀手是意外持有 PSI 元素引用。比如在MyService单例里缓存PsiClass用WeakReferencePsiElement但没清空MapDocument.addDocumentListener()后没调用removeDocumentListener()。验证工具YourKit免费社区版足够启动沙箱 IDEA打开一个大型 Java 项目安装 YourKit AgentHelp → Find Action →Attach Profiler执行你的插件功能 5 次手动触发 GCHelp → Find Action →Trigger GC拍摄 Heap Snapshot → 搜索Psi*类 → 查看retained size是否随操作次数增长。修复原则绝不缓存 PSI 元素只缓存VirtualFile或String文件路径所有DocumentListener、PsiTreeChangeListener必须在projectDisposed事件中注销用com.intellij.openapi.util.KeyT存储项目级数据而非静态 Map。5.3 指标三跨版本兼容性 —— 至少覆盖 3 个连续 IDEA 主版本JetBrains 要求插件在 Plugin Repository 上声明支持的 IDEA 版本范围如2023.3–2024.2。但实际开发中你只能编译一个intellij.version。如何保证兼容落地策略编译基线选中间版本如目标覆盖2023.3–2024.2则intellij.version 241.14494.2222024.1API 使用守则只用ApiStatus.Internal以下的 API查 Javadoc避免PsiTreeUtil.findChildOfType()这类易被重构的方法改用PsiElement.getChildren()instanceof调用新 API 前加运行时检查if (PsiTreeUtil.class.isMethodAvailable(findChildrenOfType)) { PsiTreeUtil.findChildrenOfType(element, PsiMethod::class.java) } else { element.children.filterIsInstancePsiMethod() }5.4 后悔药用PluginVerifier做上线前最后一道闸JetBrains 提供的plugin-verifier工具能在你上传插件前模拟所有目标版本的加载过程提前暴露兼容性问题。执行步骤下载 plugin-verifier 最新版运行命令java -jar plugin-verifier.jar \ --plugin-path ./build/distributions/my-first-plugin-1.0-SNAPSHOT.zip \ --ides IC-2023.3,IC-2024.1,IC-2024.2 \ --output-dir ./verifier-report查看verifier-report/summary.mdINCOMPATIBLE_CLASSES哪些类在旧版 IDEA 中不存在MISSING_DEPENDENCIESdepends声明缺失UNSUPPORTED_API_USAGES用了ApiStatus.ExperimentalAPI。我的习惯把plugin-verifier命令写进gradle check任务CI 流水线里自动执行。一次不通过PR 直接拒绝合并。这比用户投诉后再修快 10 倍。最后说句实在的插件开发没有银弹。你花 3 天搭好环境可能要用 3 周调通一个PsiElement的生命周期。但当你看到用户在 GitHub Issue 里写 “这个插件救了我的命”那种实打实的价值感是写业务 CRUD 永远给不了的。希望帮到你。本文还有配套的精品资源点击获取