配合视频享用效果更佳: IDEA插件开发

开发一块自己Idea插件

前言

自己平时用idea开发,就琢磨着idea插件的开发。在这里介绍一下idea大致开发的流程。和自己没事开发的两个简单的idea插件。一个是毒鸡汤插件,一个是代码阅读笔记插件。idea插件开发的资料网上确实不多,学习途径呢,主要是看官方文档和找一些其它的开源插件项目阅读源码。

使用DevKit插件开发流程

  1. 搭建开发环境
  2. 创建一个插件项目
  3. 创建动作(插件具体内容的开发)
  4. 运行和调试插件
  5. 部署插件
  6. 发布插件

环境准备

  1. 安装IDEA
  2. 在IDEA中Plugin DevKit,为开发IDEA插件提供支持
  3. 配置IntelliJ Platform Plugin SDK

创建一个插件项目

  1. file->new Project

  2. 填写工程名称

  3. 工程创建成功,工程目录如下,plugin.xml是核心配置文件

  4. 核心配置文件说明

    <idea-plugin><!-- 插件唯一id,不能和其他插件项目重复,所以推荐使用com.xxx.xxx的格式插件不同版本之间不能更改,若没有指定,则与插件名称相同 --><id>com.your.company.unique.plugin.id</id><!-- 插件名称,别人在官方插件库搜索你的插件时使用的名称 --><name>Plugin display name here</name><!-- 插件版本号 --><version>1.0</version><!-- 供应商主页和email(不能使用默认值,必须修改成自己的)--><vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor><!-- 插件的描述 (不能使用默认值,必须修改成自己的。并且需要大于40个字符)--><description><![CDATA[Enter short description for your plugin here.<br><em>most HTML tags may be used</em>]]></description><!-- 插件版本变更信息,支持HTML标签;将展示在 settings | Plugins 对话框和插件仓库的Web页面 --><change-notes><![CDATA[Add change notes here.<br><em>most HTML tags may be used</em>]]></change-notes><!-- 插件兼容IDEAbuild 号--><idea-version since-build="173.0"/><!-- 插件所依赖的其他插件的id --><depends>com.intellij.modules.platform</depends><extensions defaultExtensionNs="com.intellij"><!-- 声明该插件对IDEA core或其他插件的扩展 --></extensions><!-- 编写插件动作 --><actions></actions></idea-plugin>
    

创建一个动作action

  1. 创建action

  2. 填写相关参数

  • ① action的基本信息,其中Name属性的值作为将来菜单的文本内容
  • ② 作为Tools菜单下的子菜单
  • ③ 子菜单位置放在第一个
  • ④ 为子菜单添加快捷键

  1. 编写点击菜单的通知内容

    /*** 通过Pulgins Devkit创建的action继承了Ananction**/
    public class TestAction extends AnAction {/*** 需要实现点击事件发生之后的抽象方法*/@Overridepublic void actionPerformed(AnActionEvent e) {NotificationGroup notificationGroup = new NotificationGroup("testid", NotificationDisplayType.BALLOON, false);/*** content :  通知内容* type  :通知的类型,warning,info,error*/Notification notification = notificationGroup.createNotification("测试通知", MessageType.INFO);Notifications.Bus.notify(notification);}
    }
    

运行和调试插件

  1. 和我正常调试java代码一样,也可以在需要的位置打上断点。

  1. 运行结果

部署插件

  1. 打包

  1. 部署(从硬盘选择安装文件的方式)

发布插件

注册idea账号访问plugins.jetbrains.com/author/me/

  1. 登录插件库

  1. 选择打包好的插件,进行上传,等待审核结果。一般需要2-3个工作日出结果。如果成功了,别人就可以在线搜索咱们开发的插件了。

小试牛刀之毒鸡汤插件

需求

需求描述
  • 在idea启动的时候,弹出对话框,展示一碗毒鸡汤。当点击再干一碗的时候,我们切换内容。

需求分析
  1. 怎么抓住idea启动的这个时间点?
  2. 如何显示一个对话框?
  3. 怎么添加按钮的点击事件?
  4. 毒鸡汤内容的来源?

代码编写

Components组件
组件类型 描述 接口 plugins.xml加载配置元素
ApplicationComponent 在IDEA启动的时候初始化,整个IDEA中只有一个实例。 ApplicationComponent
ProjectComponent IDEA会为每一个Project实例创建对应级别的Component ProjectComponent
ModuleComponent IDEA会为每一个已经加载的Project中的每一个模块(Module)创建Module级别的Component ModuleComponent

Application 级别的 components 在 IDEA 启动时加载 初始化:调用 initComponent() 方法。所以我们覆写initComponent()方法,找到idea启动的时点。

弹出对话框

翻阅官方文档

package icu.jogeen.dialog;import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;/*** 对话框类,继承IDEA的DialogWrapper*/
public class TuantDialog extends DialogWrapper {public TuantDialog() {super(true);init();//初始化dialogsetTitle("每天一碗毒鸡汤");//设置对话框标题标题}/*** 创建对话框中间的内容面板* @return*/@Nullable@Overrideprotected JComponent createCenterPanel() {//创建一个面板,设置其布局为边界布局JPanel centerPanel = new JPanel(new BorderLayout());//创建一个文字标签,来承载内容JLabel label = new JLabel("毒鸡汤的内容");//设置首先大小label.setPreferredSize(new Dimension(100,100));//将文字标签添加的面板的正中间centerPanel.add(label,BorderLayout.CENTER);return centerPanel;}
}

自定义按钮,并添加点击事件

覆写createSouthPanel()方法。

@Override
protected JComponent createSouthPanel() {JPanel southPanel = new JPanel(new FlowLayout());JButton button=new JButton("再干一碗");button.addActionListener(e -> {label.setText("再干一碗");});southPanel.add(button);return southPanel;
}
毒鸡汤内容来源
  • 获取网上的毒鸡汤API地址,api.nextrt.com/V1/Dutang。
  • 为了使用RestTemplate发送Http请求,添加3个依赖包。

使用RestTemplate发送http请求毒鸡汤API

public class ContentUtil {public static String getContent() {RestTemplate restTemplate = new RestTemplate();try {ResponseEntity<Map> forEntity = new RestTemplate().getForEntity("https://api.nextrt.com/V1/Dutang", Map.class);HttpStatus statusCode = forEntity.getStatusCode();String content = "";if (statusCode.is2xxSuccessful()) {List data = (List) forEntity.getBody().get("data");Map<String, String> contontMap = (Map<String, String>) data.get(0);return contontMap.get("content");}} catch (Exception e) {return  "汤碗都碎了";}return "今天没有鸡汤";}
}
  • 布局

进入正题之笔记插件

需求

需求描述
  • 在idea中选择任意文本,添加笔记的标题和内容。最后可以将笔记按照指定特定模板,生成markdown文章。
  • 选中任意文本右键弹出包含自定义的子菜单JogeenNoteAction

  • 点击子菜单JogeenNoteAction弹出对话框,在对话框中,编辑这条笔记的标题和内容,点击添加到笔记列表

  • 填写文档的标题,点击生成文档。选择生成文档保存的目录

  • 打开生成的文档,展示生成的文档

需求分析
  1. 如何添加一个右键点击之后的子菜单
  2. 如何获取编辑器中已经选中的文本
  3. 如何弹出对话,获取用户编辑的笔记内容
  4. 如何使用ToolWindow展示笔记列表
  5. 如何在ToolWindow中添加表格
  6. 如何让用户选择文档生成的目录
  7. 如何将笔记列表静态化生成文档

代码编写

创建工程

新创建一个工程叫做MarkBook,作为我们的工程名,也作为这个插件产品的名称

<idea-plugin><id>com.itheima.cd.markbook.id</id><name>MarkBook</name><version>1.0</version><vendor email="chenjiagen@itcast.cn" url="http://www.itheima.com">itheima</vendor><description><![CDATA[这是一款可以帮助程序在阅读代码是添加笔记,并将笔记生成文档的插件。<br><em>MarkDown文档</em>]]></description><change-notes><![CDATA[第一版,包含笔记添加和文档生成的主体功能<br><em>仅支持生成Markdown形式笔记。</em>]]></change-notes><!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description --><idea-version since-build="173.0"/><!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.htmlon how to target different products --><depends>com.intellij.modules.platform</depends><extensions defaultExtensionNs="com.intellij"><!-- Add your extensions here --></extensions><actions><!-- Add your actions here --></actions></idea-plugin>
添加一个右键点击之后的子菜单
  • 创建action,注意选择EditorPopupMenu,顺便设置了快捷键方式ctrl+p

  • 创建字后自动生成的配置文件和PopupAction类
    <action id="MB_PopupAction" class="com.itheima.markbook.action.PopupAction" text="添加MB笔记" description="添加MB笔记的子菜单"><add-to-group group-id="EditorPopupMenu" anchor="first"/><keyboard-shortcut keymap="$default" first-keystroke="ctrl P"/></action>
public class PopupAction extends AnAction {@Overridepublic void actionPerformed(AnActionEvent e) {// TODO: insert action logic hereSystem.out.println("添加笔记的操作");}
}
  • 测试结果

获取编辑器中已经选中的文本
  • 修改PopupAction对象
public class PopupAction extends AnAction {@Overridepublic void actionPerformed(AnActionEvent e) {//获取当前编辑器对象Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);//获取选择的数据模型SelectionModel selectionModel = editor.getSelectionModel();//获取当前选择的文本String selectedText = selectionModel.getSelectedText();System.out.println(selectedText);}
}
弹出对话框,获取用户编辑的笔记内容
  • 创建AddNoteDialog

    package com.itheima.markbook.dialog;import com.intellij.openapi.ui.DialogWrapper;
    import com.intellij.ui.EditorTextField;
    import org.jetbrains.annotations.Nullable;import javax.swing.*;
    import java.awt.*;public class AddNoteDialog extends DialogWrapper {/*** 标题输入框*/private EditorTextField etfTitle;/*** 内容输入框*/private EditorTextField etfMark;public AddNoteDialog() {super(true);init();setTitle("添加笔记注释");}@Nullable@Overrideprotected JComponent createCenterPanel() {JPanel panel = new JPanel(new BorderLayout());etfTitle = new EditorTextField("笔记标题");etfMark = new EditorTextField("笔记内容");etfMark.setPreferredSize(new Dimension(200,100));panel.add(etfTitle, BorderLayout.NORTH);panel.add(etfMark, BorderLayout.CENTER);return panel;}@Overrideprotected JComponent createSouthPanel() {JPanel panel = new JPanel(new FlowLayout());JButton btnAdd = new JButton("添加到笔记列表");//按钮点击事件处理btnAdd.addActionListener(e -> {//获取标题String title = etfTitle.getText();//获取内容String content = etfMark.getText();System.out.println(title + ":" + content);});panel.add(btnAdd);return panel;}
    }
    
完善笔记内容
  • 确定一条笔记需要的字段创建NoteData类
  package com.itheima.markbook.data;public class NoteData {/*** 笔记标题*/private String title;/*** 笔记内容*/private String mark;/*** 标记的源码*/private String content;/*** 源码所在的文件名*/private String fileName;/*** 源码所在的文件类型*/private String fileType;//省略get set方法}
  • 找一个存储位置
  package com.itheima.markbook.data;import java.util.LinkedList;import java.util.List;public class DataCenter {/*** 选择的文本*/public static String SELECTED_TEXT = null;/*** 当前的文件名称*/public static String CURRENT_FILE_NAME = null;/***  当前的文件类型*/public static String CURRENT_FILE_TYPE = null;/*** 笔记列表集合*/public static List<NoteData> NOTE_LIST = new LinkedList<>();}
  • 获取文件名称和类型,存储在全局变量
  //文件名称DataCenter.CURRENT_FILE_NAME = e.getRequiredData(CommonDataKeys.PSI_FILE).getViewProvider().getVirtualFile().getName();DataCenter.CURRENT_FILE_TYPE =DataCenter.CURRENT_FILE_NAME.substring(DataCenter.CURRENT_FILE_NAME.lastIndexOf(".")+1);
  • 添加笔记到笔记列表集合
  //选择的内容DataCenter.SELECTED_TEXT = selectedText;//文件名称DataCenter.CURRENT_FILE_NAME = e.getRequiredData(CommonDataKeys.PSI_FILE).getViewProvider().getVirtualFile().getName();DataCenter.CURRENT_FILE_TYPE =DataCenter.CURRENT_FILE_NAME.substring(DataCenter.CURRENT_FILE_NAME.lastIndexOf(".")+1);
如何创建一个ToolWindow
  • 创建一个GUI Form

  • 创建之后自动生成的NoteListWindow
  package com.itheima.markbook.window;import com.intellij.openapi.project.Project;import com.intellij.openapi.wm.ToolWindow;import javax.swing.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class NoteListWindow {private JPanel jcontent;private JTextField topicEtf;private JTable contentTable;private JButton createBtn;private JButton clearBtn;private JButton closeBtn;public NoteListWindow(Project project, ToolWindow toolWindow) {createBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {}});clearBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {}});closeBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {}});}public JPanel getJcontent() {return jcontent;}}
  • 创建NoteListWindowFactory
package com.itheima.markbook.window;import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import org.jetbrains.annotations.NotNull;public class NoteListWindowFactory implements ToolWindowFactory {@Overridepublic void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {//创建出NoteListWindow对象NoteListWindow noteListWindow = new NoteListWindow(project, toolWindow);//获取内容工厂的实例ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();//获取用于toolWindow显示的内容Content content = contentFactory.createContent(noteListWindow.getJcontent(), "", false);//给toolWindow设置内容toolWindow.getContentManager().addContent(content);}
}
  • 配置加载toolWindow扩展内容
  <extensions defaultExtensionNs="com.intellij"><!-- Add your extensions here --><toolWindow id="MarkBookWindown" secondary="true" anchor="right" factoryClass="com.itheima.markbook.window.NoteListWindowFactory" icon="/markbook/pluginIcon.svg"></toolWindow></extensions>
在ToolWindow中添加表格
  • 在数据中心添加内容
  private static String[] COLUMN_NAME={"标题","备注","文件名","代码段"};public static DefaultTableModel TABLE_MODEL = new DefaultTableModel(null,COLUMN_NAME);
  • 定义表格初始化设置,并在NoteListWindow构造方法中调用init
  public void init(){contentTable.setModel(DataCenter.TABLE_MODEL);contentTable.setEnabled(true);}
  • btnAdd按钮的点击事件中添加
  //添加DataCenter.TABLE_MODEL.addRow(DataConvert.toStringArray(noteData));
  • 设置关闭
  toolWindow.hide(null);
  • 设置清空列表
DataCenter.reset();
让用户选择文档生成的目录
  • 添加文件选择,获取用户选择的目录
  createBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {VirtualFile virtualFile = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, project.getBaseDir());if(virtualFile!=null){String path = virtualFile.getPath();System.out.println(path);}}});

将笔记列表静态化生成文档
  • 定义处理的接口
public interface Processor {public void process(SourceNoteData sourceNoteData) throws Exception;
}
  • 编写Freemarker的抽象类
  public abstract class AbstractFreeMarkerProcessor implements Processor {protected abstract Template getTemplate() throws IOException, Exception;protected abstract Object getModel(SourceNoteData sourceNoteData);protected abstract Writer getWriter(SourceNoteData sourceNoteData) throws FileNotFoundException, Exception;@Overridepublic final void process(SourceNoteData sourceNoteData) throws Exception {Template template = getTemplate();Object model = getModel(sourceNoteData);Writer writer = getWriter(sourceNoteData);template.process(model, writer);}}
  • 编写MDFreeMarkProcessor继承AbstractFreeMarkerProcessor。实现抽象方法
  public class MDFreeMarkProcessor extends AbstractFreeMarkerProcessor {@Overrideprotected Template getTemplate() throws Exception {//加载模板字符串String templateString = UrlUtil.loadText(MDFreeMarkProcessor.class.getResource("/template/md.ftl"));//创建模板配置Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);//创建字符串模板的导入器StringTemplateLoader stringTemplateLoader=new StringTemplateLoader();//导入字符串模板stringTemplateLoader.putTemplate("MDTemplate",templateString);configuration.setTemplateLoader(stringTemplateLoader);//获取模板return configuration.getTemplate("MDTemplate");}@Overrideprotected Object getModel(SourceNoteData sourceNoteData) {HashMap model = new HashMap();model.put("topic",sourceNoteData.getNoteTopic());model.put("noteList",sourceNoteData.getNoteDataList());return model;}@Overrideprotected Writer getWriter(SourceNoteData sourceNoteData) throws Exception {String filePath = sourceNoteData.getFilePath();File file = new File(filePath);return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"utf-8"));}}
  • 添加处理操作
  createBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {VirtualFile virtualFile = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, project.getBaseDir());if (virtualFile != null) {String path = virtualFile.getPath();String topic = topicEtf.getText();String filePath = path + "/" + topic + ".md";Processor processor = new MDFreeMarkProcessor();try {processor.process(new DefaultSourceNoteData(topic, filePath, DataCenter.NOTE_LIST));} catch (Exception ex) {ex.printStackTrace();}}}});
  • 完善提示
  • 对话框提示
MessageDialogBuilder.yesNo("操作结果","添加成功!").show();
  • 通知提示
NotificationGroup notificationGroup = new NotificationGroup("testid", NotificationDisplayType.BALLOON, false);
/*** content :  通知内容* type  :通知的类型,warning,info,error*/
Notification notification = notificationGroup.createNotification("测试通知", MessageType.INFO);
Notifications.Bus.notify(notification);

作者:迈克尔嘿嘿
链接:https://juejin.cn/post/6844904127990857742
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Idea插件开发-开发自己的第一款idea插件相关推荐

  1. AI开发学习: 第一款AI药物诞生:英国公司“光速”制药碾压传统研发

    使用人工智技术开发出第一款药物.这款药物可用来治疗强迫症,不久将开始进入临床测试阶段.需要强调的是,这款药物从提出概念至今不到一年时间.据媒体报道,近期英国初创公司 ExScientia 称,他们已经 ...

  2. 我决定开发自己的第一款iPhone应用

    为更好地掌握编程,我决定开发自己的第一款iPhone应用. 我是一名设计师和前端开发者,曾用服务器端编程语言(PHP)写过一些程序,不但会使用Ruby.PHP等语言,还从更高层次了解了这些语言.但遗憾 ...

  3. Spring Boot(1)——开发你的第一款Spring Boot应用(Edition1)

    Spring Boot(1)--开发你的第一款Spring Boot应用(Edition1) 准备工作: java:java 8 或者 java 9: Spring框架:5.0.8.RELEASE或以 ...

  4. 在idea中新建的text文件_开发属于自己的第一款 IDEA 插件!

    往期热门文章: 1.<往期精选优秀博文都在这里了!>2.又一个程序员跑路删库跑路被抓了,导致服务器瘫痪 36 个小时!3.恕我直言,有了这款 IDEA 插件,你可能只需要写 30% 的代码 ...

  5. idea 2020.2 如何设置classpath_开发属于自己的第一款IDEA插件!

    作者:木杉 http://imushan.com/ 写Java代码的时候,经常会涉及到重复性的操作,这个时候就会想要是有这样一个插件就好了,如果是大家都会遇到的场景,IDE或许已经提供了,再不然也有可 ...

  6. 我的第一款 Drone 插件

    女主宣言 本文的作者为奇舞团前端工程师怡红公子,他是高人气开源博客平台 Firekylin (over 1.2k Stars) 的作者,今天他分享的是基于 Nodejs 编写一个 Telegram 消 ...

  7. 我开发的第一款HTML5游戏《驴子跳》

    转自   http://www.iteye.com/topic/1122395 经过两个多月断断续续的开发,我的第一款游戏<驴子跳>终于完成了,此时,我已经迫不及待地想跟大家分享这个过程, ...

  8. 浅谈Unity与VR产业,用Unity开发第一款VR应用

    部分内容为整合与装载 浅谈VR VR(Virtual Reality的缩写,中文翻译-虚拟现实)概念早在80年代初就被提出来的,其具体是指借助计算机及最新传感器技术创造的一种崭新的人机交互手段. 中国 ...

  9. 普本在校生是如何零基础在一个月内开发出第一款微信小程序的

    先自我介绍一下,我是一个普通本科的大三在读生,在2020年春节前后,自己一个人开发并上线了第一款微信小程序,后续陆续开发了N款小程序,并且自己通过毕设.课设的开发在2020年上半年的疫情期间挣了一万多 ...

  10. 第一款能运行Android11 的RK3399开发板 tinkerboard2

    对于许多需要HMI(人机交互)的项目来说,首选的方案还是Android.目前市面上Android的方案有很多,包括高通.MTK.展锐.海思这类手机SOC厂商,还有RK.Amlogic.全志这类平板.机 ...

最新文章

  1. vue - check-versions.js for child_process
  2. jquery 临时存值
  3. 《研发企业管理——思想、方法、流程和工具》——1.11 论成本
  4. PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
  5. 【机器人】一转眼,波士顿动力 Atlas 机器人又会过独木桥了!|湾区人工智能...
  6. matlab拟合高次相,matlab 多元高次非线性函数拟合,回归,求教高手!
  7. Android开发笔记(五十三)远程接口调用AIDL
  8. Date与String之间的转换
  9. java开发之路——个人开发模板之技巧
  10. 安卓网络连接全解:包括网络连接状态的监听、网络数据使用状态的监听、获取当前网络连接情况、启动wifi、获取当前连接wifi的网络情况、扫描wifi热点
  11. 细节至上——Splus微博设计之界面篇(转)
  12. tomcat+mysql+腾讯云_腾讯云服务器配置tomcat和mysql
  13. 服务产品(商品)评论中的产品特征挖掘方法
  14. 女程序员开发软件挂专家号,转手获利被刑拘
  15. 聊聊几个阿里 P8、P9 程序员的故事
  16. 深入理解Java虚拟机-垃圾收集算法
  17. 蚂蚁金服区块链朱永春:蚂蚁金服业务新思路,用以往通用场景结合出新的解决方案...
  18. 硅基压力传感器—MEMS
  19. 亚洲研究院微软笔试题
  20. macd金叉kdj死叉的准确率_MACD金叉不涨又死叉

热门文章

  1. MDUI 输入确认框
  2. cad里面f命令用不了_南方CASS软件里CAD命令不能使用怎么办
  3. 解决myeclipse html页面乱码问题
  4. opencv学习笔记三十二:Haar特征与积分图像
  5. 计算机更新过后cad,升级windows10系统后cad无法打开的两种解决方法
  6. 明解C语言第四章习题
  7. 如何使用keil 5 编写 51单片机 工程
  8. [状压dp] 玉米田(状压dp)
  9. C# NOPI 项目实战(经典)(可下载项目源码)
  10. 企业BI项目蓝图规划建设方案