Getting started with plugin development for JetBrains IDEs like IntelliJ and Goland

Building a custom editor plugin can make your workflows more productive.

No matter what your technical background as a developer looks like, you’ve probably come across or even used an IDE from JetBrains. Their product line features IDEs for various languages and technologies and they’re all built according to the same schema: Atop an extensible core (“IntelliJ platform”) sits a set of curated extensions that provides all of the “integrated” functionality of the IDE.

But what if you could extend your IDE further with your own plugins? Doing your own plugin for your workflows can improve your productivity drastically. In this post we’ll explore the IntelliJ API and build a simple plugin step-by-step to get you going.

The plugin we’re about to develop is called “Google This” and it provides a context menu item to search for the highlighted text on Google.

Prerequisites

IntelliJ plugins run on the Java virtual machine (“JVM”), so naturally they’re written in a JVM-compatible language. The plugin APIs are best suited for Java and Kotlin. We’ll be using Java in this blog post. For development itself, we’ll be using the community edition of IntelliJ IDEA.

Getting started

First, let’s create our project. The project creation wizard in IntelliJ IDEA provides a template called “IDE Plugin” which bootstraps an empty plugin built on top of Gradle. Make sure “Java” is selected and pick the JDK installed on your system from the “JDK” dropdown.

The "New Project" wizard with the "IDE Plugin" template highlighted

After clicking “Create” we’re greeted with a scaffold of everything we need to write and run our empty plugin on IntelliJ.

Initial file structure of a plugin.

Run and debug the plugin

Our project uses Gradle. The project creation wizard created a bunch of Gradle tasks for us that are available at the “Gradle” panel to the right.

Gradle panel showing a bunch of tasks related to plugin development; The "runIde" task is selected.

Clicking through these Gradle tasks shows tasks related to running, testing, signing and distribution but only a few of them are really relevant for day-to-day operations. For now, we’ll only need the runIde task, which starts a new instance of IntelliJ IDEA with our plugin loaded.

But we don’t even need to mess with this panel all the time. The project template includes a run configuration called “Run Plugin” which lets us use the “Run” and “Debug” buttons in the top right toolbar to execute the runIde task.

Run configurations drop down with the "Run Plugin" configuration selected next to the "Run" and "Debug" buttons.

Create the context menu action

In order to use our plugin we need to implement a so called “action”. Actions are commands in IntelliJ that can be triggered via the context menu, the action runner (CtrlShiftA) or a custom keyboard shortcut, depending on their configuration.

Let’s create a class called GoogleThis in the com.example.demo package and have it extend the AnAction class.

package com.example.demo;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import org.jetbrains.annotations.NotNull;

public class GoogleThis extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {

    }
}

As-is this is not doing anything yet. In order for IntelliJ to know that we are listening for the aforementioned events, we need to register our action inside the plugin.xml file. This file contains general metadata about a plugin as well as declarations of what the plugin contributes to the relevant implementation classes.

You could add your action declaration by hand but in IntelliJ we can also just hover over the class name and click “Register Action” to register our class within the plugin.xml file.

Mouseover-popup over the "GoogleThis" class name showing a clickable link titled "Register Action".

The name under which the entry should be displayed is “Google this please!”. EditorPopupMenu defines where this action should live and we anchored it last, so it’s always displayed on the bottom. Last but not least we are assigning the keyboard shortcut CtrlAltG to our action.

"New Action" wizard in IntelliJ with the class name "com.example.demo.GoogleThis" set as the value for "Action ID" and "Class Name", "Google this please!" set as the value for "Name", "EditorPopupMenu" highlighted in "Groups", "Last" highlighted in "Anchor" and "Ctrl+Alt+G" set as the value for "Keyboard Shortcuts > First".

After adding the action via the wizard we can verify that everything works, even if no implementation has been done yet, by running the plugin and checking the context menu. Just click the “Run” button in the top right corner and open any project in the new IDE instance. Then open any file and right-click to bring up the context menu.

The "Google this please!" item visible in a context menu.

So far so good!

Implement the functionality

With our action registered we can click its context menu item but nothing happens when we do because its implementation is still completely blank. What should happen is the plugin reads the selected text, then constructs a URL to Google and opens a browser with that. A good place to start looking for the right interfaces to use when working on a plugin is the IntelliJ Platform Plugin SDK documentation. Failing that there’s also the IntelliJ Platform Explorer for finding real-world usages of APIs in open source plugins.

In this case, we use the CaretModel of the editor, as described in the docs on the page “Editor Coordinates System. Positions and Offsets”. Invoking the browser is possible using a utility method in BrowserUtil.

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
	final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
	final CaretModel caretModel = editor.getCaretModel();
	String selectedText = caretModel.getCurrentCaret().getSelectedText();

	BrowserUtil.browse("https://www.google.com/search?q="+selectedText);
}

And with that we do have a working context menu item which searches for the highlighted text on Google. But there is now a bug where you can use the context menu even if you did not have any text highlighted which would simply search for null. To avoid this we could check if selectedText is null and do nothing, or we can use the update method of the AnAction class to hide the context menu entirely.

 @Override
public void update(AnActionEvent e) {
	final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
	final CaretModel caretModel = editor.getCaretModel();
	e.getPresentation().setEnabledAndVisible(caretModel.getCurrentCaret().hasSelection());
}

Now the Google this please! context menu item is not visible if no text is selected.

Conclusion

And that’s it. Congratulations, you’ve just written your first IntelliJ IDEA plugin! This plugin will not only work on IntelliJ IDEA: Almost all JetBrains IDEs build upon the IntelliJ platform, so your plugins can be installed in all of them! (Provided they don’t depend on any exclusive functionality.) If you want to read more about plugin development, you can subscribe to our newsletter. In any case make sure to follow us on Twitter, Facebook or LinkedIn as well to get notified on posts about software development, software testing or if you are simple into memes.

Technical | 2022-06-17