跳至主要内容

How to Develop Intellij Plugin

Recently, when using a plugin of idea, it reports the NPE on the latest version and fail to work. Because the original author haven’t update the repo for a relative long time, we decide to fix this by ourselves.

Setup

In order to develop idea plugin, we first have to setup some environment. Here is the environment setting tutorial and following are some basic steps:

  • Start IntelliJ IDEA.
  • Configure the IntelliJ Platform Plugin SDK. (prefer to choose community version as SDK because we can have source code of community version to assist debug)
  • Create a project for plugin development.
  • Create the necessary source elements and write the source code.
  • Run and debug your plugin.
  • Prepare the plugin for publishing.

We come across some problems at step three because we are developing a plugin on the fork of other people’s repository and there is no option to choose plugin development project type when create project from existing source. So, on step three, we have to first create project then edit projectName.iml like following as this answer said:

# change type from java_module to plugin_module
- type="JAVA_MODULE" 
+ type="PLUGIN_MODULE"

Development

In the process of development, we come across some problems and we share here to help the future readers.

Examples: Debug Plugin NPE

In order to fix the problem, which resides in a not small code base (a plugin code and some idea source code), in a relative short time, the first thing is to understand how this plugin works and why it doesn’t work on newer version.

The first convenient tool, if exists, is the doc from plugin writer. It can give us a general idea of how his code works and help us reading source code.

The second tool is debugging. We can start the plugin from idea in debug mode. In this way, we can

  • find the stack trace, in our cases, where NPE comes from
  • follow the execution flow to understand the logic

By viewing the stack trace, we find the NPE is located in the source of idea when we create a file chooser dialog.

VirtualFile virtualFile = 
     FileChooser.chooseFile(descriptor, project, null);

public static VirtualFile chooseFile(@NotNull final FileChooserDescriptor descriptor,
                                       @Nullable final Project project,
                                       @Nullable final VirtualFile toSelect) {...}

Reading code more times, we understand that the NPE arises because the frame that dialog needed (only needed in newer version) disappeared (notice this conclusion can also get from running plugin in both old working version and newer NPE version, and compare the change.)

So the solution is either to make it not disappear, or choose another frame:

VirtualFile virtualFile = 
    FileChooser.chooseFile(descriptor, WindowManager.getInstance().findVisibleFrame(), project, null);

Here, we choose the idea’s largest frame to make sure it will still work in the future version.

// we get this frame by compare the init process of our file chooser dialog with idea the tip dialog 
WindowManager.getInstance().findVisibleFrame()

In conclusion, in order to get familiar with many code and find the right way to do things, there exists some ways:

  • debug and follow the execution to understand logic
  • compare similar situation

Examples: Generate Getter/Setter

Then, we are going to implement a new feature in this plugin: generate getter and setter for a batch of model class.

When come across generation of getter and setter, we can recall that this a conenient method when using Intellij. So we think that there may be some public method/api also for pulgin to use.

So the question changes to how to find the needed api?

We first try to search the different terms like:

idea plugin openapi generate getter setter
idea plugin development getter setter generation

but found no useful resouces. Doesn’t want to give the road, we start to search in community edition code. And lucky enough, we found it like this link shows (thanks to the good naming convention of idea development team).

The following task is much easier:

  • choose one of the utility function to generate getter/setter
  • add the method abstraction PsiMethod to class abstraction PsiClass
PsiClass dist = dataSet.get(clsName);
...
PsiMethod getter = GenerateMembersUtil.generateGetterPrototype(field);
dist.add(getter);

Working example can be found here.

Ref

评论

此博客中的热门博文

Spring Boot: Customize Environment

Spring Boot: Customize Environment Environment variable is a very commonly used feature in daily programming: used in init script used in startup configuration used by logging etc In Spring Boot, all environment variables are a part of properties in Spring context and managed by Environment abstraction. Because Spring Boot can handle the parse of configuration files, when we want to implement a project which uses yml file as a separate config file, we choose the Spring Boot. The following is the problems we met when we implementing the parse of yml file and it is recorded for future reader. Bind to Class Property values can be injected directly into your beans using the @Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via @ConfigurationProperties. As the document says, there exists three ways to access properties in *.properties or *.yml : @Value : access single value Environment : can access multi

Elasticsearch: Join and SubQuery

Elasticsearch: Join and SubQuery Tony was bothered by the recent change of search engine requirement: they want the functionality of SQL-like join in Elasticsearch! “They are crazy! How can they think like that. Didn’t they understand that Elasticsearch is kind-of NoSQL 1 in which every index should be independent and self-contained? In this way, every index can work independently and scale as they like without considering other indexes, so the performance can boost. Following this design principle, Elasticsearch has little related supports.” Tony thought, after listening their requirements. Leader notice tony’s unwillingness and said, “Maybe it is hard to do, but the requirement is reasonable. We need to search person by his friends, didn’t we? What’s more, the harder to implement, the more you can learn from it, right?” Tony thought leader’s word does make sense so he set out to do the related implementations Application-Side Join “The first implementation

Implement isdigit

It is seems very easy to implement c library function isdigit , but for a library code, performance is very important. So we will try to implement it and make it faster. Function So, first we make it right. int isdigit ( char c) { return c >= '0' && c <= '9' ; } Improvements One – Macro When it comes to performance for c code, macro can always be tried. #define isdigit (c) c >= '0' && c <= '9' Two – Table Upper version use two comparison and one logical operation, but we can do better with more space: # define isdigit(c) table[c] This works and faster, but somewhat wasteful. We need only one bit to represent true or false, but we use a int. So what to do? There are many similar functions like isalpha(), isupper ... in c header file, so we can combine them into one int and get result by table[c]&SOME_BIT , which is what source do. Source code of ctype.h : # define _ISbit(bit) (1 << (