如有疏漏欢迎指正,如想深入了解欢迎探讨。
IntelliJIDEA与IntelliJPlatform
IntelliJIDEA简称IDEA,是Jetbrains公司旗下的一款JAVA开发工具,支持Java、Scala、Groovy等语言的开发,同时具备支持目前主流的技术和框架,擅长于企业应用、移动应用和Web应用的开发,提供了丰富的功能,智能代码助手、代码自动提示、重构、J2EE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、创新的GUI设计等。
IntelliJPlatform是一个构建IDE的开源平台,基于它构建的IDE有IntelliJIDEA、WebStorm、DataGrip、以及AndroidStudio等等。IDEA插件也是基于IntelliJPlatform开发的。
注意各软件版本要对应
PluginDevKit是IntelliJ的一个插件,它使用IntelliJIDEA自己的构建系统来为开发IDEA插件提供支持。开发IDEA插件之前需要安装并启用PluginDevKit。\打开IDEA,导航到Settings|Plugins,若插件列表中没有PluginDevKit,点击InstallJetBrainsplugin,搜索并安装。
IntelliJPlatformPluginSDK就是开发IntelliJ平台插件的SDK,是基于JDK之上运行的,类似于开发Android应用需要AndroidSDK。\3.1导航到File|ProjectStructure,选择对话框左侧栏PlatformSettings下的SDKs
3.2点击+按钮,先选择JDK,指定JDK的路径;再创建IntelliJPlatformPluginSDK,指定homepath为IDEA的安装路径,如图
创建好IntelliJPlatformPluginSDK后,选择左侧栏ProjectSettings下的Projects,在ProjectSDK下选择刚创建的IntelliJPlatformPluginSDK。
4.1查看build号:打开IDEA,Help|About,查看版本号及build号
4.3选择工程结构设置后选择SDKs->选中之前在第3步添加的sdk点击SourcePath后按如下1点击添加一个sourcePath,选择上面下载额源码后点击OK、点击Applay
4.4未安装源码时点击某一个action(NewModuleAction)会看到如下所示阅读起来会比较晦涩难懂。
IntelliJIDEA插件以Debug/Run模式运行时是在SandBox中进行的,不会影响当前的IntelliJIDEA;但是同一台机器同时开发多个插件时默认使用的同一个sandbox,即在创建IntelliJPlatformSDK时默认指定的SandboxHome
如果需要每个插件的开发环境是相互独立的,可以创建多个IntelliJPlatformSDK,为SandboxHome指定不同的目录。
插件的创建、配置、运行、打包流程,以及action
选择File|New|Project,左侧栏中选择IntelliJPlatformPlugin工程类型
点击Next,设置工程名称及位置,点击Finish完成创建。可以到File|ProjectStructure来自定义工程设置。
插件工程内容:
PluginDemo/resources/META-INF/plugin.xmlsrc/com/foo/.........
下面示例描述了可在plugin.xml文件配置的主要元素:
Action是实现插件功能的类,一个Action类需要继承AnAction并且实现actionPerformed方法。当用户点击菜单或者工具栏按钮,按快捷键,或者通过Help|FindAction点击时,IntelliJPlatform系统会回调对应Action的actionPerformed方法。\一个Action表示IDEA菜单里的一个menuitem或工具栏上的一个按钮,通过继承AnActionclass实现,当选择一个menuitem或点击工具栏上的按钮时,就会调用AnAction类的actionPerformed方法。\实现自定义Action分两步:
定义一个Javaclass,继承AnAction类,并重写actionPerformed方法,如
publicclassActionDemoextendsAnAction{publicvoidactionPerformed(AnActionEventevent){Projectproject=event.getData(PlatformDataKeys.PROJECT);Messages.showInputDialog(project,"Whatisyourname","Inputyourname",Messages.getQuestionIcon());}}
在plugin.xml文件的
上面示例会定义一个被添加到IDEA主菜单的最后面的“SampleMenu”的菜单,点击该菜单将弹出一个“TextBoxes”item,如图
IntelliJPlatform提供了NewAction向导,它会帮助我们创建actionclass并配置plugin.xml文件:
在目标package上右键,选择New|PluginDevKit|Action:
注意:该向导只能向主菜单中已存在的actiongroup或工具栏上添加action,若要创建新的actiongroup,请参考前面的内容。
运行/调试插件可直接在IntelliJIDEA进行,选择Run|EditConfigurations...,若左侧栏没有Plugin类型的Configuration,点击右上角+按钮,选择Plugin类型,如图
Useclasspathofmodule选择要调试的module,其余配置一般默认即可;切换到Logs选项卡,如果勾选了idea.log,运行插件时idea.log文件的内容将输出到idea.logconsole。
运行插件点击工具栏上运行按钮Run
\
选择Build|PreparePluginModule‘modulename’forDeployment来打包插件:
jar类型的插件包:
PluginDemo.jar/com/xxx/.........META-INF/plugin.xml
zip类型的插件包:
PluginDemo.zip/lib/libxxx.jarlibbar.jarPluginDemo.jar/com/xxx/.........META-INF/plugin.xml
导航到File|Settings|Plugins页面,点击Installpluginfromdisk...
这个时候我们了解的都比较浅显还停留在demo层面,如何进行深入的了解呢?
eg:我们怎么知道都有哪些action或actiongroup可以被我们添加呢?
1、我们可以点击配置group-id="MainMenu"下的MainMenu
2、进入PlatformActions.xml如下图,这个时候不难看出这里就是主菜单的第一列子菜单
3.这个时候如果我们想新建个类似与File-->New和Open的菜单该怎么做呢?
3.1我们应该先实现布局,添加主菜单MainMenu
3.2实现自定义的打开文件
其实是通过下面的action配置的OpenFileAction找到源码
在将源码拷贝出来粘贴到自己的action内。这样就可以实现自己的主菜单File下的Open子菜单
3.3这个时候有人会有疑问我不知道去哪找New对应的action呀?
这个时候我们通过界面可以看到ProjectfromExistingSources...,这里我们就可以去搜这个文本呀。既然显示在页面上。必然有地方定义了它。ActionBundle.properties
这个时候我们在根据对应的action定义的文本在去搜索对应的action,com.intellij.ide.actions.ImportProjectAction
3.4这个时候我们将对应的action拷贝到自己的插件定义的配置上也就形成了3.1的一级和二级菜单
1.筛选后查找要添加的group\2.选择对应的action\3.选择要添加到这个action的某个位置
IntelliJIDEA的组件模型是基于PicoContainer的,组件都包含在这些容器中,但容器有三种级别:applicationcontainer,projectcontainer以及modulecontainer。applicationcontainer可以包含多个projectcontainer,而projectcontainer可以包含多个modulecontainer。
Components是插件开发的基础,Components有三种类型:
components需要配置在plugin.xml中,并指定interface和implementation,interface类用于从其他组件中检索组件,implementation类用于实例化组件。示例:
//创建一个applicationlevelcomponentpublicinterfaceComponent1extendsApplicationComponent{}publicclassComponent1ImplimplementsComponent1{@OverridepublicStringgetComponentName(){return"PluginDemo.Component1";}}
plugin.xml
注意:一个interface-class不能有多个implementation-class,如下图:
ApplicationComponent的生命周期方法:
//构造方法publicconstructor(){}//初始化publicvoidinitComponent(){}publicvoiddisposeComponent(){}
ProjectComponent的生命周期方法:
//构造方法publicconstructor(){}//通知一个project已经完成加载publicvoidprojectOpened(){}publicvoidprojectClosed(){}//执行初始化操作以及与其他components的通信publicvoidinitComponent(){}//释放系统资源或执行其他清理publicvoiddisposeComponent(){}
ModuleComponent的生命周期方法:
ModuleComponent的生命周期方法中比ProjectComponent多一个moduleAdded(),用于通知module已经被添加到project中。
Application级别的components在IDEA启动时加载,Project和Module级别的components在项目启动时共同加载。
一个组件加载过程:
示例:
publicclassMyComponentimplementsApplicationComponent{privatefinalMyOtherComponentotherComponent;publicMyComponent(MyOtherComponentotherComponent){this.otherComponent=otherComponent;}...}
一个组件卸载过程:
前面我们提到有三种不同的容器,applicationcontainer实现Application接口;projectcontainer实现Project接口;
modulecontainer实现Module接口。每一个容器都有自己的方法去获取容器内的component。
获取application容器及其内部的组件:
/获取application容器Applicationapplication=ApplicationManager.getApplication();//获取application容器中的组件MyComponentmyComponent=application.getComponent(MyComponent.class);
获取project/module容器及其内部的组件:
publicclassMyComponentimplementsProjectComponent{Projectproject;publicMyComponent(Projectproject){this.project=project;}publicvoidinitComponent(){OtherComponentotherComponent=project.getComponent(OtherComponent.class);}}
在这个例子中,组件在构造方法中获取了容器对象,将其保存,然后在component其他地方进行引用。
7.1创建一个ApplicationComponent
packagecom.plugin.demo.component;importcom.intellij.openapi.components.ApplicationComponent;//创建一个applicationlevelcomponentpublicinterfaceApplicationComponentDemoextendsApplicationComponent{}packagecom.plugin.demo.component;importcom.intellij.openapi.application.Application;importcom.intellij.openapi.application.ApplicationManager;publicclassApplicationComponentDemoImplimplementsApplicationComponentDemo{@OverridepublicStringgetComponentName(){System.out.println("ApplicationComponentDemoImpl="+this.getClass().getName());returnthis.getClass().getName();}//初始化publicvoidinitComponent(){System.out.println("ApplicationComponentDemoImplinitComponent");}publicvoiddisposeComponent(){//获取application容器Applicationapplication=ApplicationManager.getApplication();//获取application容器中的组件ApplicationComponentDemoImplmyComponent=application.getComponent(ApplicationComponentDemoImpl.class);System.out.println("disposeComponent="+myComponent.getComponentName());}}
7.2创建一个ProjectComponent
7.3创建一个ModuleComponent
7.4注册配置Component
7.5运行后的预期是先执行应用层组件,在执行工程级组件,在执行模块级组件
如果插件需要扩展IDEAPlatform或其他插件的功能,或为其他插件提供可以扩展自己的接口,那么就要用到extensions和extensionpoints,用于与IDEA和其他插件交互。
示例上述代码中的MyExtensionPoint1的beanClass:
publicclassMyBeanClassextendsAbstractExtensionPointBean{@Attribute("key")publicStringkey;@Attribute("implementationClass")publicStringimplementationClass;...}
插件的service的实现就是扩展IDEAPlatform的applicationService或projectService两个extensionpoints
IntelliJPlatform的部分extensionpoints
Service也是一种按需加载的component,在调用ServiceManager.getService(Class)时才会加载,且程序中只有一个实例。
Service是插件的一个组件,是为了把公共的逻辑放到一起,Service的实例是单例的。
Serivce在IntelliJIDEA中是以extensionpoint形式提供的,实现自己的service需要扩展相应extensionpoint。
在需要放置service的package上右键,New|PluginDevKit|xxxxService,如图
选择相应service,弹出如下对话框,填写interface类和implementation类,若不勾选Separateinterfacefromimplementation,只需填写implementation类。
\IntelliJIDEA会自动创建相应类并配置plugin.xml文件。\
示例:plugin.xml:
生成的service类:
publicinterfaceApplicationServiceDemo{staticApplicationServiceDemogetInstance(){returnServiceManager.getService(ApplicationServiceDemo.class);}}publicinterfaceProjectServiceDemo{staticProjectServiceDemogetInstance(@NotNullProjectproject){returnServiceManager.getService(project,ProjectServiceDemo.class);}}publicinterfaceModuleServiceDemo{staticModuleServiceDemogetInstance(@NotNullModulemodule){returnmodule.getService(ModuleServiceDemo.class);}}
publicclassApplicationServiceDemoImplimplementsApplicationServiceDemo{publicApplicationServiceDemoImpl(){System.out.println("ApplicationServiceDemoImpl=");}}publicclassProjectServiceDemoImplimplementsProjectServiceDemo{publicProjectServiceDemoImpl(Projectproject){System.out.println("ProjectServiceDemoImpl="+project);}}publicclassModuleServiceDemoImplimplementsModuleServiceDemo{publicModuleServiceDemoImpl(Moduleproject){System.out.println("ModuleServiceDemoImpl="+project);}}
MyApplicationServiceapplicationService=ServiceManager.getService(MyApplicationService.class);//获取project级别的service,需要提供project对象MyProjectServiceprojectService=ServiceManager.getService(project,MyProjectService.class);//获取module级别的service,需要提供module对象MyModuleServicemoduleService=ModuleServiceManager.getService(module,MyModuleService.class);
我们在使用IDE开始开发工作之前,总是要先在settings页面进行一些设置,且每次重新打开IDE后这些设置仍然保留着,那么这些设置是如何保存下来的呢?
IntelliJPlatform提供了一些API,可以使components或services在每次打开IDE时仍然使用之前的数据,即持久化其状态。
对于一些简单少量的值,我们可以使用PropertiesComponent,它可以保存application级别和project级别的值。
下面方法用于获取PropertiesComponent对象:
//获取application级别的PropertiesComponentPropertiesComponent.getInstance()//获取project级别的PropertiesComponent,指定相应的projectPropertiesComponent.getInstance(Project)propertiesComponent.setValue(name,value)propertiesComponent.getValue(name)
PropertiesComponent保存的是键值对,由于所有插件使用的是同一个namespace,强烈建议使用前缀来命名name,比如使用pluginid。
PersistentStateComponent用于持久化比较复杂的components或services,可以指定需要持久化的值、值的格式以及存储位置。
要使用PersistentStateComponent持久化状态:
下面通过两个例子进行说明:
classMyServiceimplementsPersistentStateComponent
//这里的state就是实现类本身classMyServiceimplementsPersistentStateComponent
a、字段要求
state类中可能有多个字段,但不是所有字段都可以被持久化,可以被持久化的字段:
这些字段也有类型要求:
如果不希望某个字段被持久化,可以使用@com.intellij.util.xmlb.annotations.Transient注解。
b、构造器要求
state类必须有一个默认构造器,这个构造器返回的state对象被认为是默认状态,只有当当前状态与默认状态不同时,状态才会被持久化。
我们可以使用@State注解来定义存储位置
@State(name="PersistentDemo",storages={@Storage(value="PluginDemo.xml")})publicclassPersistentDemoimplementsPersistentStateComponent
name:定义xml文件根标签的名称
storages:一个或多个@Storage,定义存储的位置
代码中获取状态与获取service的方式一样:
PersistentDemopersistDemo=ServiceManager.getService(PersistentDemo.class);PersistentDemo2persistDemo2=ServiceManager.getService(project,PersistentDemo.class);
获取状态与获取component的方式一样:
publicstaticPersistentComponentgetInstance(){returnApplicationManager.getApplication().getComponent(PersistentComponent.class);}publicstaticPersistentComponentgetInstance(Projectproject){returnproject.getComponent(PersistentComponent.class);}
开发插件时可能会用到其他插件,可能是IDEA绑定的,也可能是第三方的插件。
配置插件依赖需要将插件包添加到SDK的classpath中,并在plugin.xml配置。
配置plugin.xml在plugin.xml的部分添加所依赖插件的id。
org.jetbrains.kotlin
pluginid可以从插件包的plugin.xml文件查看。
GUI是IntelliJIDEA提供的一个自动生成java布局代码的工具,它使用JDK中的Swing控件来实现UI界面。
使用步骤:
配置GUI首先打开Settings对话框,选择Editor|GUIDesigner,如图,在GenerateGUIinto:有两个选项,生成class文件或java代码,我们选择生成java代码,因为建好布局后可能需要修改代码。其他默认即可。
创建form文件form文件用于记录界面布局。在相应的package上右键,选择New|GUIForm,如图,输入form文件名,一般与java文件名相同,点击OK创建form与java文件。
编辑界面打开form文件,如图,通过拖拽控件来搭建布局。每个form文件布局的root控件都是一个JPanel,可将该root对象传给需要该布局的类。注意:左下角的属性面板,只有当填写了fieldname属性时该控件的对象才会被当成成员变量,否则为局部变量。
生成java代码搭建好布局后,点击build
编译按钮,即可生成java的源码文件。
SmartConverter--POJOObjectConverter
在分层开发中,我们总是面临着各种POJO(DTO,DO,JO,VO)对象之间的相互转换。当对象比较复杂时,编写转换代码耗时较多,且非常容易出错。以至于可能会出现写一天代码,半天在写各种convert的囧境。
为了实现自动转换,出现了BeanUtil和ModelMapper等解决方案。这些方案,在少量对象转换时,性能损耗可以忽略,但是当转换数量达到一定量级时,这种损耗会对性能产生影响。
本插件可以自动生成POJO之间的转换代码,省去手工转换的麻烦,也不会损失性能。
下载SmartConverter.zip,并在IntellijIdea中安装;
publicstaticList
插件自动从转换函数的参数和返回值推断出转换POJO;
支持List之间的转换。
如果存在单个转换的函数,则直接使用
如果不存在单个转换的函数,创建单个转换函数
支持嵌套转换
因为使用SmartConvert是使用alt+insert弹出或者右键点击Generate显示SmartConvertAction,所以根据前文的添加位置不难推断添加在弹出菜单EditorPopupMenu下,这个时候我们可以从两个方向找他添加的位置。
首先从项目的配置文件进入找到plugin.xml下配置的action。由此不难看出它实际是添加在了GenerateGroup这个组上的
这个时候我们不难看出并没有地方引用这个组,这个时候我们不防从使用的地方入手,我们是右键点击Generate或者alt+insert弹出的EditorLangPopupMenu下的Generate的组。这个时候我们去全局搜索EditorPopupMenu
发现这里有一个添加到右键菜单下的
点击后跳转的是
GenerateAction的点击方法actionPerformed内动态生成了ActionGroup
JBPopupFactory.getInstance().createActionGroupPopup(CodeInsightBundle.message("generate.list.popup.title"),wrapGroup(getGroup(),dataContext,project),dataContext,JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,false);
而getGroup()通过指定groupid获取到GenerateGroup的Action组
return(DefaultActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_GENERATE);
2.1ProgramStructureInterface(PSI)
程序结构接口,通常简称为PSI,负责解析文件并创建语法和语义代码模型,为平台的众多功能提供支持。
PSI文件是结构的根,将文件内容表示为特定编程语言中元素的层次结构
PsiFile是所有PSI文件的公共基类,而特定语言的文件通常由其子类表示。例如PsiJavaFile类代表一个Java文件,类XmlFile代表一个XML文件。
2.2查看某一个文件的PSI结构
参考文档:PSIViewer
未配置开启查看PIS结构时如下图
开启查看PIS结构找到idea安装路径下的bin目录下的idea.properties配置如下
idea.is.internal=true
开启后显示了ViewPSIStructure和ViewPSIStructureofCurrentFile
进入要查看结构的文件后点击ViewPSIStructureofCurrentFile\查看某一个文件的psi结构
2.3查看插件源码
进入ConvertGeneratorAction的点击事件方法不难看到如下的根据PSI获取当前类和方法的代码
2.4继续跟踪生成方法转换代码
这里主要是根据返回类型获取到了一个MethodGenerator并执行对应的generateCode方法
2.5MethodGenerator下的generateCode
MethodGenerator下的generateCode主要获取了当前方法的入参fromClass与toClass,并进行了字符串的组装和生成代码块。
\PsiCodeBlockcodeBlock=elementFactory.\
createCodeBlockFromText("{"+String.join("\n",statementList)+"}",psiClass);\源码分析就到这里,如果有兴趣的同学可以自行深入分析并欢迎补充。
想编写一个什么样的插件(功能)
插件要实现的能力是什么,eg:进行方法入参快速转为出参、获取选择的文本添加为笔记、idea激活弹出框、数据库Database...等。
实现插件需要具备哪些能力(功能拆解)
需要页面操作交互能力(javaswing)
需要添加action的能力(插件需要放在哪里,插件的生命周期是什么等级的等。)
需要读写文件的能里(newBufferedWriter(newOutputStreamWriter(newFileOutputStream(file),"utf-8"));)
创建一个action并继承AnAction
packagecom.test.action;importcom.intellij.openapi.actionSystem.AnAction;importcom.intellij.openapi.actionSystem.AnActionEvent;publicclasstestActionextendsAnAction{@OverridepublicvoidactionPerformed(AnActionEvente){//TODO:insertactionlogichereSystem.out.println("action点击触发方法="+e);}
}
使用spring-web下的RestTemplate创建网络请求工具(也可以直接使用RestTemplate)
在需的地方触发网络请求获取数据
触发验证
首先创建一个回显显示的界面
packagecom.test.view;importcom.intellij.openapi.ui.DialogWrapper;importorg.jetbrains.annotations.Nullable;importjavax.swing.*;importjava.awt.*;publicclasstestDialogextendsDialogWrapper{JLabellabel;publictestDialog(booleancanBeParent){super(canBeParent);init();//初始化dialogsetTitle("标题");}@Overrideprotected@NullableJComponentcreateCenterPanel(){/*创建一个面板,设置其布局为边界布局*/JPanelcenterPanel=newJPanel(newBorderLayout());/*创建一个文字标签,来承载内容*/Stringtext="aaa11111测试回显内容";label=newJLabel(text);/*设置首先大小*/label.setPreferredSize(newDimension(100,100));/*将文字标签添加的面板的正中间*/centerPanel.add(label,BorderLayout.CENTER);returncenterPanel;}publicvoidsetLabelText(Stringtext){label.setText(text);}}
在action内触发请求网络获取内容并设置到显示的面板上。
像上图的标题等直接赋值汉字时会有乱码,重新编码进行处理(这种方式简单的汉字和汉字较少时可以)
StringencodeTitle=newString("标题".getBytes("gbk"),"UTF-8");title=newEditorTextField(encodeTitle);
我们从action中获取editor对象,在通过editor获取SelectionModel,在获取选中的文本。
弹窗提供一个重新设置选择文本的方法testDialog.setContent(selectedText);
@OverridepublicvoidactionPerformed(AnActionEvente){//TODO:insertactionlogicheretestDialogtestDialog=newtestDialog(true);//获取当前编辑器对象Editoreditor=e.getRequiredData(CommonDataKeys.EDITOR);//获取选择的数据模型SelectionModelselectionModel=editor.getSelectionModel();//获取当前选择的文本StringselectedText=selectionModel.getSelectedText();System.out.println(selectedText);testDialog.setContent(selectedText);testDialog.show();}
测试选中内容和回显内容如下图
可以使用java本身的流进行读写,也可以使用模板引擎进行,这里使用freemarker模版引擎\3.8.1获取按钮点击事件后弹出目录选择框选择要保存的文件夹,首先需要改造弹窗的构造器传入当前action的事件Event,从event获取当前的工程
3.8.2按钮点击事件创建文件选择器
有人会有疑问,为什么这样就弹出了文件选择器,其实最后是一个FileChooser->FileChooserDialog
finalFileChooserDialogchooser=FileChooserFactory.getInstance().createFileChooser(descriptor,project,parent);returnchooser.choose(project,toSelect);
3.8.3引入freemarker模版引擎依赖并进行文件创建保存
组织数据、获取模版、创建文件、执行创建文件
模版代码创建并获取上图中的组织数据model下的内容
NotificationGroupnotificationGroup=newNotificationGroup("testId",NotificationDisplayType.BALLOON,true);/***content:通知内容*type:通知的类型,warning,info,error*/Notificationnotification=notificationGroup.createNotification("测试通知保存成功",MessageType.INFO);Notifications.Bus.notify(notification);
添加一个自定义ToolWindow\3.10.1创建一个toolwindow
packagecom.test.view;importcom.intellij.openapi.wm.ToolWindow;importjavax.swing.*;importjava.text.SimpleDateFormat;importjava.util.Date;publicclassMyToolWindow{privateJButtonhideButton;privateJLabeldatetimeLabel;privateJPanelmyToolWindowContent;publicMyToolWindow(ToolWindowtoolWindow){init();hideButton.addActionListener(e->toolWindow.hide(null));}privatevoidinit(){datetimeLabel=newJLabel();datetimeLabel.setText(newSimpleDateFormat("yyyy-MM-ddHH:mm:ss").format(newDate()));hideButton=newJButton("取消");myToolWindowContent=newJPanel();myToolWindowContent.add(datetimeLabel);myToolWindowContent.add(hideButton);}publicJPanelgetContent(){returnmyToolWindowContent;}}
3.10.2创建ToolWindowFactory的实现类
packagecom.test.view;importcom.intellij.openapi.project.Project;importcom.intellij.openapi.wm.ToolWindow;importcom.intellij.openapi.wm.ToolWindowFactory;importcom.intellij.ui.content.Content;importcom.intellij.ui.content.ContentFactory;importorg.jetbrains.annotations.NotNull;publicclasstoolWindowExtimplementsToolWindowFactory{@OverridepublicvoidcreateToolWindowContent(@NotNullProjectproject,@NotNullToolWindowtoolWindow){MyToolWindowmyToolWindow=newMyToolWindow(toolWindow);//获取内容工厂的实例ContentFactorycontentFactory=ContentFactory.SERVICE.getInstance();//获取用于toolWindow显示的内容Contentcontent=contentFactory.createContent(myToolWindow.getContent(),"自定义toolwindow",false);//给toolWindow设置内容toolWindow.getContentManager().addContent(content);}}
idea插件开发经验总结(一):环境搭建
IDEA插件开发简明教程
【IDEA插件开发】快速入门系列01开发一个简单的Idea插件
IDEAPlugin插件怎么开发?
作者:京东健康马仁喜
上一篇:
下一篇:
在云环境中,你如何保证数据一致性
你对云计算中的计费模式有什么理解?能否详细解释一下按需付费和预留实例的区别?
描述一下云计算的几种服务模式(IaaS、PaaS、SaaS)以及它们之间的区别
介绍一下云计算中的冷启动、热迁移和容灾,以及它们的优缺点
解释一下什么是无服务器计算,以及它在云环境中的应用
介绍一下云计算中的多租户技术,以及它在云环境中的应用
请解释一下什么是Docker,以及它在云环境中的应用
2024-06-03
2024-05-13
2024-05-11
2024-06-18
公司地址:长沙高新开发区麓谷街道东方红中路23号神汉商业广场3004