首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >IDEA 插件开发实战

IDEA 插件开发实战

作者头像
CSDN技术头条
发布于 2020-02-19 03:36:27
发布于 2020-02-19 03:36:27
2.6K00
代码可运行
举报
文章被收录于专栏:CSDN技术头条CSDN技术头条
运行总次数:0
代码可运行

一. 简介

IntelliJ IDEA 是一款开发工具,提供很多插件功能,比如阿里规范插件(Alibaba Java Coding Guidelines),但是随着日常业务展开,很多工作重复性编码,浪费很多时间,需要自定义抽象出来一些插件,自动化的方式解决问题,这也是工程师文化的体现。

二.原理

2.1 背景

IntelliJ 平台是开源的,基于 Apache 许可协议,提供很多丰富的工具,提供组件驱动,基于跨平台 JVM,可以在创建菜单栏、列表、弹出菜单、对话框等等。可以适用于多种语言,提供相关解析器和 PSI 模型,解析文件,构建语义模型。

2.2 基本原理

组件模型

负责生命周期管理以及连接组件之间的相互依赖关系。

  • Application level components,在 IDEA 启动的时候创建和初始化,可以使用 getComponent(Class) 获取它们。
  • Project level components,在 IDEA 中每个 Project 实例创建的,甚至可以为未打开的项目创建组件,可以使用 getComponent(Class)方法从 Project 实例中获取它们。
  • Module level components,它们是为 IDEA 中加载的每个项目中每个模块创建,使用 getComponent(Class)方法可以从 Module 实例获取模块级别组件。

生命周期:

  • 创建,调用构造函数
  • 初始化,initComponent 调用该方法(如果组件实现 ApplicationComponent 接口)
  • 配置,保存和加载每个组件的状态。(PersistentStateComponent 和 JDOMExternalizable,实例化配置)。
  • 注册,对于模块组件,将调用接口的 moduleAdded 方法 ModuleComponent 将模块添加到项目中,对于项目组件,调用接口的 projectOpened 方法 ProjectComponent 加载项目。
  • 保存配置,JDOMExternalizable,PersistentStateComponent 的调用。
  • 输出,disposeComponent 调用输出。

线程模型

平台相关数据结构由读/写锁覆盖,适用于 PSI,VFS 和项目模型。允许从任何线程读取数据。从 UI 线程读取数据不需要任何特殊的工作。但是,从任何其他线程执行的读取操作都需要使用 ApplicationManager.getApplication().runReadAction()或 ReadAction.run/compute。

仅允许从 UI 线程写入数据,并且写入操作始终需要用 ApplicationManager.getApplication().runWriteAction()或 WriteAction.run()/compute()。

后台流程管理

后台进度由 ProgressManager 类管理,该类有很多方法可以使用模式(对话框),非模式(在状态栏中可见)或不可见进度来执行给定代码。在所有情况下,代码都是在与 ProgressIndicator 对象关联的后台线程上执行的。

讯息传递

平台中可用的消息传递基础结构,基于 Observer 设计模式扩展实现的,通过该模式能够更好的梳理的一对多关系,实现提供了附加功能,例如在层次结构上进行广播和特殊的嵌套事件处理(此处的嵌套事件是指从另一个事件的回调中(直接或间接)触发新事件的情况)。

2.3 小结

具体相关原理研究,可查看官网(sdk 地址)。

三.API

3.1 框架结构

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.IntelliJIDEA/

└──  plugins

    └── code_plugin

        └── lib

            ├── lib_foo.jar

            ├── lib_bar.jar

            │   ......

            └── sample.jar

                ├── com/foo/.........

                └── META-INF

                    ├── plugin.xml

                    ├── pluginIcon.svg

                    └── pluginIcon_dark.svg

└──src

├──com.code

基本的框架结构,如果要导入依赖放到 lib 文件夹中,还有另一种建立框架的方式,那个是基于 Gradle 管理。

META-INF,配置文件件,管理注册的类。

3.2 常用 API 介绍

VFS

  • 提供一个处理文件的通用 API,而不关心文件的具体位置(无论文件位于磁盘上、归档文件中还是 HTTP 服务器上)。
  • 追踪文件变化,并且在检测到文件内容发生更改时能提供新旧两个版本的文件。
  • 建立文件在 VFS 和持久化存储之间的关联。

从本地 IO 文件中获取

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
File ioFile = new File("./io.java")
VritualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile)
virtualFile.refresh(false, true)

对 VirtualFile 进行读写操作:和 Android 一样,Intellij Platform 不允许直接在主线程进行实时的文件写入,需要通过一个异步任务进行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
WriteCommandAction.runWriteCommandAction(project, new Runnable() {
     @Override
    public void run() {
    //   virtualFile.getInputStream() / virtualFile.getOutputStream()         
    }
 });

在异步任务结束后,切回 UI 线程进行 UI 更新:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ApplicationManager.getApplication().invokeLater(new Runnable(){ 
 ...
})

PSI

PSI(Program Structure Interface)是 Intellij Platform 中一个非常重要的概念,在 IDE 所管理的 Project 中,每个目录,Package,源代码和资源文件都会被抽象成相应的 PSI 对象。

常用子类:PsiDirectory、PsiJavaFile 和 XmlFile。

创建目录和文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//创建目录
PsiDirectory baseDir =PsiDirectoryFactory.getInstance(project).createDirectory(project.getBaseDir());
//创建 Java 文件
PsiJavaFile psiFile = (PsiJavaFile) PsiFileFactory.getInstance(project).createFileFromText("",StdFileTypes.JAVA, "");
//创建 Xml 文件
XmlFile psiFile = (XmlFile) PsiFileFactory.getInstance(project).createFileFromText("",StdFileTypes.XML, "");

读写文件:和写入 VirtualFile 一样,读写操作都需要在 WriteCommandAction 异步线程中进行。

创建 Class 文件类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PsiClass clazz =JavaDirectoryService.getInstance().createClass(subDir, className)
//还有通过 freemarker 模板建立 class 类。

psiClass 类中添加接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PsiClass view = myFactory.createInterface("View");
psiClass.add(view);

设置包名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PsiJavaFile javaFile = (PsiJavaFile) psiClass.getContainingFile();
PsiPackage psiPackage = myDirectoryService.getPackage(directory);
javaFile.setPackageName(psiPackage.getQualifiedName());

设置类权限:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
psiClass.getModifierList().setModifierProperty(PsiModifier.PUBLIC,true);

四.实例架构

平时开发过程中,代码结构会分层,类似 MVC 思想,这里面有很多可以抽象出来的公共类,比如 JavaBean,DTO,Service 等等,我这个实例结合类似场景,实现自动化插件。

五.准备工作

创建插件项目:

还可以用 Gradle 方式创建项目,我用的 idea 版本 2019.2.4,上述内容中提到框架结构,现在可以在 src 目录中编码。

六.编码

6.1 组成

总共有几个部分组成。

BaseAnAction

AnActionEvent 一些基本信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class BaseAnAction extends AnAction {
    private AnActionEvent anActionEvent;
    private DataContext dataContext;
    private Presentation presentation;
    private Module module;
    private IdeView view;
    private ModuleType moduleType;
    private Project project;
    private PsiDirectory psiDirectory;
    private DialogBuilder builder;
    private PsiFile file;
    private JavaDirectoryService javaDirectoryService;
    private MysqlJdbc mysqlJdbc = MysqlJdbc.getMysqlJdbc();
    private PropertiesUtil properties = PropertiesUtil.getConfigProperties();

    public void init(AnActionEvent anActionEvent) {
        this.javaDirectoryService = new JavaDirectoryServiceImpl();
        this.anActionEvent = anActionEvent;
        IdeView ideView = (IdeView)anActionEvent.getRequiredData(LangDataKeys.IDE_VIEW);
        this.psiDirectory = ideView.getOrChooseDirectory();
        this.project = this.psiDirectory.getProject();
    }

    public PropertiesUtil getProperties() {
        return this.properties;
    }

    public MysqlJdbc getMysqlJdbc() {
        return this.mysqlJdbc;
    }

    public PsiDirectory getPsiDirectory() {
        return this.psiDirectory;
    }

    public JavaDirectoryService getJavaDirectoryService() {
        return this.javaDirectoryService;
    }

    public AnActionEvent getAnActionEvent() {
        return this.anActionEvent;
    }

    public void setAnActionEvent(AnActionEvent anActionEvent) {
        this.anActionEvent = anActionEvent;
    }

    public DataContext getDataContext() {
        return this.dataContext;
    }

    public void setDataContext(DataContext dataContext) {
        this.dataContext = dataContext;
    }

    public Module getModule() {
        return this.module;
    }

    public void setModule(Module module) {
        this.module = module;
    }

    public IdeView getView() {
        return this.view;
    }

    public void setView(IdeView view) {
        this.view = view;
    }

    public ModuleType getModuleType() {
        return this.moduleType;
    }

    public void setModuleType(ModuleType moduleType) {
        this.moduleType = moduleType;
    }

    public Project getProject() {
        return this.project;
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public DialogBuilder getBuilder() {
        return this.builder;
    }

    public void setBuilder(DialogBuilder builder) {
        this.builder = builder;
    }

    public PsiFile getFile() {
        return this.file;
    }

    public void setFile(PsiFile file) {
        this.file = file;
    }

    public Presentation getPresentation() {
        return this.presentation;
    }

    public void setPresentation(Presentation presentation) {
        this.presentation = presentation;
    }

    @Override
    public void update(AnActionEvent e) {
        try {
            this.presentation = e.getPresentation();
            this.onMenuUpade(e, (PsiFile)e.getData(DataKeys.PSI_FILE), ((IdeView)LangDataKeys.IDE_VIEW.getData(e.getDataContext())).getOrChooseDirectory());
        }
        catch (Exception exception) {
            // empty catch block
        }
    }

    public void show() {
        this.presentation.setEnabled(true);
        this.presentation.setVisible(true);
    }

    public void hide() {
        this.presentation.setEnabled(false);
        this.presentation.setVisible(false);
    }

    public void onMenuUpade(AnActionEvent e, PsiFile file, PsiDirectory dir) {
    }
}

CodeComponent

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
应用管理。
public class CodeComponent implements ApplicationComponent {
    @Override
    public void initComponent() {

        // TODO: insert component initialization logic here

    }



    @Override
    public void disposeComponent() {

        // TODO: insert component disposal logic here

    }



    @Override
    @NotNull
    public String getComponentName() {
        if ("CreateMicroServiceProjectComponent" == null) {
            CodeComponent.reportNull(0);
        }
        return "CreateMicroServiceProjectComponent";
    }

    private static  void reportNull(int n) {
        throw new IllegalStateException(String.format("@NotNull method %s.%s must not return null", "com/code/action/CodeComponent", "getComponentName"));}}

还有一些工具类,比如操作 MySQL 数据库,操作字符串等等。一些 freemarker 模板,Action 动作。

MMS_DO.java.ft

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import lombok.Data;
import java.util.*;
import java.math.BigDecimal;

/**
 * @author ${USER} E-mail:${E_MAIL}
 * @version 创建时间:${DATE} ${TIME}
 *     ${doCalssName}DO 对象
 */
@Data
public class ${doCalssName}DO {
}

CreateServiceAction

创建 Service 接口和实现类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CreateServiceAction extends BaseAnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
        this.init(anActionEvent);
        String serviceName = Messages.showInputDialog((String)"Service name", (String)"Create service", (Icon)Messages.getInformationIcon());
        Map<String, String> param = new HashMap<String, String>();
        param.put("doCalssName", BaseUtils.firstLetterUpperCase(BaseUtils.markToHump(serviceName, "_", null)));
        param.put("tableName", serviceName);
        PsiDirectory implDir = this.getPsiDirectory().findSubdirectory("impl");
        if (implDir == null) {
            implDir = this.getPsiDirectory().createSubdirectory("impl");
        }
        PsiClass servicePsiClass = this.getJavaDirectoryService().createClass(this.getPsiDirectory(), "", "MMS_Service", false, param);
        String packagePath = servicePsiClass.getQualifiedName();
        String implT = "impl";
        param.put(implT, packagePath + ";");
        param.put("serviceName", serviceName);
        this.getJavaDirectoryService().createClass(implDir, "", "MMS_ServiceImpl", false, param);
    }
}

6.2 GitHub项目地址

项目内容放到 GitHub 中,地址:code_plugin

项目中缺失 jar,有些依赖得自行下载,

七.部署

在 code_plugin 项目鼠标右击,或者 build 点击 Prepare Plugin Module '插件名称(codeplugin)' For Deployment 生成插件包(zip/jar)。

在 IDEA 文件夹,File->Settings->Plugins->Install Plugin from Disk,安装打出插件,查看目录,重启。

导入插件

在这里插入图片描述

效果展示

插件位置

项目,鼠标右击,新建 New,有 CreateDO、CreateDTO、CreateService 三个功能窗口。

创建 DO

这个实体是跟 MySQL 业务表像映射的,窗口填的是数据库表名称。

创建 DTO

DTO 是跟 DO 相映射的,符合阿里的编程规范,用于处理 Service 层业务处理,这个代码中写上包名称(com.lm.model),DO 得在特定包名下,DTO 才能映射,DTO 创建窗口,填 DO 类名称。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CreateDTOAction extends BaseAnAction  {
    @Override
    public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
        this.init(anActionEvent);
        String doName = Messages.showInputDialog((String)"DO name", (String)"Create DTO", (Icon)Messages.getInformationIcon());
        final PsiElementFactory factory = JavaPsiFacade.getInstance((Project)this.getProject()).getElementFactory();
        HashMap<String, String> param = new HashMap<String, String>();
        String doClassName = BaseUtils.firstLetterUpperCase(BaseUtils.markToHump(doName.substring(0, doName.length() - 2), "_", null));
        param.put("doCalssName", doClassName);
        GlobalSearchScope searchScope = GlobalSearchScope.allScope((Project)this.getProject());
        PsiPackage psiPackage = JavaPsiFacade.getInstance((Project)this.getProject()).findPackage("com.lm.model");
        PsiClass[] doPsiClasss = psiPackage.findClassByShortName(doName, searchScope);
        PsiClass doPsiClass = doPsiClasss[0];
        PsiClass dtoPsiClass = JavaPsiFacade.getInstance((Project)this.getProject()).findClass(doPsiClass.getQualifiedName(), searchScope);
        final PsiField[] psiFields = dtoPsiClass.getFields();
        final PsiClass psiClass = this.getJavaDirectoryService().createClass(this.getPsiDirectory(), "", "MMS_DTO", false, param);
        WriteCommandAction.runWriteCommandAction((Project)this.getProject(), (Runnable)new Runnable(){

            @Override
            public void run() {
                for (PsiField psiField : psiFields) {
                    String comment = psiField.getDocComment().getText().replaceAll("\\*", "").replaceAll("/", "").replaceAll(" ", "").replaceAll("\n", "");
                    StringBuffer fieldStrBuf = new StringBuffer(psiField.getDocComment().getText()).append("\nprivate ").append(psiField.getType().getPresentableText()).append(" ").append(psiField.getName()).append(";");
                    psiClass.add((PsiElement)factory.createFieldFromText(fieldStrBuf.toString(), (PsiElement)psiClass));
                }
            }
        });
    }}

八.总结

总体的 IDEA 插件开发介绍完毕,这个可以基于模板快速拓展,有兴趣的朋友可以尝试下,毕竟授人以鱼不如授人以渔,自动化是工程师文化的一个重要体现。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GitChat精品课 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
小伙伴想写个 IDEA 插件么?这些 API 了解一下!
" 在看完 IDEA 插件开发简易教程后,小伙伴们是否迫不及待的想自己上手整一个插件了?心里规划好了一二三,但是却不知道从哪里开始下手。下面我分享下自己整理的一些常用的 API。 "
程序员小航
2020/11/23
2.3K0
小伙伴想写个 IDEA 插件么?这些 API 了解一下!
Idea插件开发
https://www.w3cschool.cn/intellij_idea_doc/
码客说
2024/08/04
4200
Idea插件开发
《IntelliJ IDEA 插件开发》第六节:选定对象批量织入“x.set(y.get)”代码,自动生成vo2dto
这些年从事编程开发以来,我好像发现了大部分研发那些不愿意干的事,都成就了别人。就像部署服务麻烦,有了Docker、简单CRUD不想开发,有了低代码、给方法代码加监控繁琐、有了非入侵的全链路监控。
小傅哥
2021/12/15
8590
《IntelliJ IDEA 插件开发》第六节:选定对象批量织入“x.set(y.get)”代码,自动生成vo2dto
为 TheRouter 开发一个 IDEA 插件
这篇文章是假定你已经有了 idea 插件开发的入门知识,来教你如何实现一个实际项目的功能。如果你还不知道如何开发一个插件,建议先从这个链接查看官网相关文档 https://plugins.jetbrains.com/docs/intellij/welcome.html。
用户1907613
2023/10/19
3790
为 TheRouter 开发一个 IDEA 插件
开发属于自己的插件 | IDEA &amp; Android Studio插件开发指南
谷轩宇——从事安卓开发,目前效力于通天塔技术开放组是否曾经被ide重复繁琐的操作所困扰,又或者没有心仪的UI控件而难受。那么请阅读这篇文章,掌握idea插件的开发流程,开发属于自己的插件,造福开源社区。
京东技术
2018/09/28
5K0
开发属于自己的插件 | IDEA &amp; Android Studio插件开发指南
idea插件开发记录
插件开发示例 ---- 功能开发代码示例 java package com.cjl.plugins.code.hints; import com.cjl.plugins.code.code.NavigatorPanel; import com.cjl.plugins.code.http.HttpUtils; import com.cjl.plugins.code.json.Json; import com.intellij.codeInsight.hint.HintManager; import com.
司夜
2023/03/31
7610
idea插件开发记录
只需三步实现Databinding插件化
首先为何我要实现Databinding这个小插件,主要是在日常开发中,发现每次通过Android Studio的Layout resource file来创建xml布局文件时,布局文件的格式都没有包含Databinding所要的标签。导致的问题就是每次都要重复手动修改布局文件,添加标签等。
Rouse
2019/07/22
1K0
只需三步实现Databinding插件化
《IntelliJ IDEA 插件开发》第四节:扩展创建工程向导步骤,开发DDD脚手架
你做这个东西的价值是什么?有竞品调研吗?能赋能业务吗?那不已经有同类的了,你为什么还自己造轮子?
小傅哥
2021/12/01
1.2K0
《IntelliJ IDEA 插件开发》第四节:扩展创建工程向导步骤,开发DDD脚手架
IDEA Cody 插件实现原理
近年来,智能编程助手 在开发者日常工作中变得越来越重要。IDEA Cody 插件是 JetBrains 生态中一个重要的插件,它可以帮助开发者 快速生成代码、自动补全、并提供智能提示,从而大大提升开发效率。今天我们将深入探讨 Cody 插件的实现原理,看看它是如何工作的。
井九
2024/10/12
3120
IDEA Cody 插件实现原理
Android MVP 代码自动生成插件开发
本文会出现的原因是,lucio在遵循Google的Android MVP示例代码的模式开发一个小的程序,发现我们会需要写很多重复的代码,更加麻烦的是,我们需要创建很多重复的文件。每开发一个小的模块,至少会需要创建Activity、Contract、Fragment和Presenter四个文件。
luciozhang
2023/04/22
5590
Android MVP 代码自动生成插件开发
IntelliJ插件开发-京东工程师教你改造你的IDE
王帅廷,京东 Android高级开发工程师,6年以上开发经验,对设计框架有着深刻的认识,负责京东商城研发工具的开发,设计并完成了多个IntelliJ插件的开发工作。
京东技术
2018/07/30
3.3K1
IntelliJ插件开发-京东工程师教你改造你的IDE
你们要的Intellij IDEA 插件开发秘籍,来了!
王昭霞,软件开发工程师,先后从事脚本工具编写、工具开发、Android基础模块开发等工作。
京东技术
2018/09/28
57.2K13
你们要的Intellij IDEA 插件开发秘籍,来了!
《IntelliJ IDEA 插件开发》第三节:开发工具栏和Tab页,展示股票行情和K线
以前,我不懂。写的技术就是技术内容,写的场景就是场景分析,但从读者的阅读我发现,大家更喜欢的是技术与场景结合,尤其是用技术结合那些羞羞答答的场景,虽然嘴上都不说。
小傅哥
2021/11/19
2.6K0
让代码自动补全的全套流程
作者:熊唯,黄飞 ,腾讯 PCG/QQ研发中心/CV应用研究组 AI 如果真的可以写代码了,程序员将何去何从?近几年,NLP 领域的生成式任务有明显的提升,那通过 AI 我们可以让代码自动完成后续补全吗?本文主要介绍了如何使用 GPT2 框架实现代码自动补全的功能。 如果 AI 真的可以自己写代码了,程序员将何去何从? 我去年做过一个代码补全的小功能,打包为 androidStudio 插件,使用效果如下: 代码补全模型预测出的结果有时的确会惊吓到我,这也能学到~ 那如果给它见识了全世界的优秀
腾讯技术工程官方号
2020/07/31
2.4K0
《IntelliJ IDEA 插件开发》第 五 节:IDEA工程右键菜单,自动生成ORM代码
几年前,大家并不是这样,那时候还有很多东西可以创新,乱世出英雄总能在一个方向深耕并做出一款款好用的产品功能、框架服务、技术组件等。但后来好像这样的情况开始减少了,取而代之的是重复、复刻、照搬,换个新的皮肤、换个新的样式、换个新的名字,就是取巧的新东西了。
小傅哥
2021/12/13
2.5K0
《IntelliJ IDEA 插件开发》第 五 节:IDEA工程右键菜单,自动生成ORM代码
p3c 插件,是怎么检查出你那屎山的代码?
虽然我们都被称为码农,也都是写着代码,但因为所处场景需求的不同,所以各类码农也都做着不一样都事情。
小傅哥
2021/10/08
1.1K0
IDEA插件:快速删除Java代码中的注释
本文针对Java语言,介绍一种利用第三方库的方式,可以方便快速地移除代码中的注释。
xiaoxi666
2021/02/17
3.1K0
IntelliJ IDEA/Android Studio插件开发指南
目前在为安卓手机QQ做自动化的相关工作,包括UI自动化,逻辑层自动化等。使用到的uiautomator等框架,需要在Android Studio进行编码工作。 其中很多工作如果做到插件化的话,可以有效地节省时间成本,提升大家的自动化效率。 比如运行自动化的时候,需要用到我们自定义的shell命令。我们可以通过插件来实现一键运行。 在运行adb shell am instrument命令的时候,需要编译出test APK和target APK。手Q整体的git仓库很大,编译耗时很久。我们想着通过一些方法来优化这个耗时。其中一个步骤就是,把我们代码目录下的变更,同步到一个编译目录下。 这个小功能的最合适的形态,自然就是Android Studio上的一个插件。点击一个按钮,一键同步,那可真是在米奇妙妙屋吃妙脆角——妙到家了! Android Studio是基于Intellij IDEA开发的,所以开发Android Studio的插件,其实就是开发IDEA的插件。 根据官方推荐,使用IDEA IDE来开发IDEA插件。
于果
2021/08/25
2.8K0
《IntelliJ IDEA 插件开发》第一节:两种方式创建插件工程
对于码农这一行业的编程学习生涯来说,会遇到很多的不会,不会搭建IDEA工程、不会写老师的案例、不会完成书中的效果、不会做项目的需求、不会实现复杂的逻辑、不会抽象工程的结构等等。但这些不会当中并不是所有的不会,都因为太复杂学不会,而是很大一部分内容因为找不到好的资料、没有清晰的文档、缺少完整的案例,导致不知道所以不会。
小傅哥
2021/10/20
3.1K0
技术调研,IDEA 插件怎么开发「脚手架、低代码可视化编排、接口生成测试」?
你觉得肯德基全家桶是什么?一家人一起吃的桶吗,就那么一点点?不是,肯德基全家桶说的是,鸡的全家桶!
小傅哥
2021/09/14
1.6K0
推荐阅读
相关推荐
小伙伴想写个 IDEA 插件么?这些 API 了解一下!
更多 >
交个朋友
加入腾讯云技术交流站
洞悉AI新动向 Get大咖技术交流群
加入HAI高性能应用服务器交流群
探索HAI应用新境界 共享实践心得
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档