My attention recently turned towards IntelliJ plugin development, and since information on the subject is incredibly scarce I thought I’d jot down a few notes here. I’ll also try and relate it to plugin development with more familiar environments (Eclipse) for those that are interested in making the switch over.
Firstly, it’s worth noting that I’m writing this with IDEA 10.5 as a base. Things can change over time, so if you find this on a Google Search ten years for now you can use common sense to see if it still applies.
IntelliJ IDEA has only (relatively) recently enabled plugin development at all, so things have changed over time. One of the most notable issues is that whilst other products (Eclipse, NetBeans) have focussed on a modular architecture from the beginning, IDEA has always been shipped as a Big Bag of
Jars. So both the plugin development – and the available plugins – reflect the youthfulness of the application, often with contradictory results. For example, updating IDEA itself is handled in a completely different way from updating the plugins.
When IDEA installs, it creates a
plugins directory at the root of
the application install, which is used to store the plugins themselves. Each
plugin gets its own directory, which uses the
id of the plugin
(and if the
id is not available, the
default. Underneath that is a mandated
lib directory, and inside
that are the plugin JARs themselves.
One common way of installing plugins is to just unzip the folder into the
plugins directory. The IDEA build process generates a
zip file which
contains the name of the project as part of the internal folder structure.
Each plugin has a single class loader, so whether it is shipped as a single JAR or as multiple JARs are a matter of convenience rather than anything else. If you consume downstream libraries then you can simply embed a copy of it in the plugin’s lib directory.
The existence of
lib is somewhat mystifying; it’s as if JetBrains had other
thought regarding resources which they could then put to use; or perhaps they
were just following web app development structure. Either way, any other
content appears to be ignored in the plugins directory.
You can’t have update sites in IntelliJ IDEA. Well, you can’t have sites, but you can have site – hardcoded into each IntelliJ install is a constant called DEFAULT_PLUGINS_HOST, which resolves to http://plugins.intellij.net. This is similar to Eclipse’s MarketPlace and is usually the de-facto method of installing plugins into IntelliJ. If you want to have your own update site, you’re out of luck. There’s no way to host an IntelliJ update site outside of this mechanism.
There is laughably something called “Enterprise Repository support” in which you can have a list of plugins, identified with a file called
updatePlugins.xml. This is nothing of the sort.
Yes, you can start IDEA with a
-Didea.host.url= http://path/to/updatePlugins.xmlproperty, and yes, it will download that file. However, when you put any plugin in this file, it will show up a dialog asking you to install all of plugins in that file.
It doesn’t integrate with the Plugin model (where people will be expecting it to be) and it forces you to install everything. Oh, and it doesn’t work either.
Instead, the update site has two URLs which it uses to convey information back to clients who ask. The first is the list of plugins that are available, which it accesses from http://plugins.intellij.net/plugins/list/. This is an XML file (not even compressed!) which contains a list of every plugin known to man (or to JetBrains, at least). The format looks like:
Rather than telling you where you could get all of these plugins from as part
url attribute, JetBrains makes you go back and get them via another URL,
which is http://plugins.intellij.net/pluginManager.
This seems to act as a redirector for plugins based on their (plugin) id, and
it redirects you to a URL where you can download the file from. For example,
the first entry in the IDEA repository above is the Ant Debugger, whose
com.handyedit.AntDebugger. Add this to the URL, and you get http://plugins.intellij.net/pluginManager?action=download&id=com.handyedit.AntDebugger&build=IC-107.322, which redirects you to the JAR, and which subsequently installs the
content. (The name of this file is derived from the plugin’s ID and a unique
number which corresponds to the download id in the plugins.intellij.net site –
it’s not related to the repository at all. Note the without the build
parameter, the redirection doesn’t work – a primitive form of server-side
detection of compatible version numbers.
The one ray of sunshine is that there is a way of overriding this default
host; if you run IDEA with
-Didea.plugins.host=http://somewhere/else then you
can use a different update site to the one compiled within IntelliJ. This can
do something slightly more intelligent (like; allowing you to use multiple
update sites through careful redirections) but it’s a significant piece of
missing functionality in IntelliJ IDEA that you have to do this.
It’s no wonder that IntelliJ users often just extract the contents of a plugin into their local install, or acquire it through the central update site. It’s almost impossible to do otherwise.
In order to do any kind of plugin development in IntelliJ, you have to configure an SDK. This is non-obvious and almost all of the Google searches turn up with nothing of any use whatsoever. Fresh out of the box, IDEA doesn’t even have a Java SDK defined.
You need to set up the Java SDK first, followed by the IDEA SDK (which it can
use as itself). Go into the Project Settings and find the Platform Settings,
which includes SDKs. There’s a small
[+] icon at the top of the second
column; click it and select Java SDK first; it should auto-detect which JDK
you’ve launched it from, but you can select any JDK on your system. Once that’s
done, click on the
[+] icon again and this time select IDEA SDK. It will
prompt you for the JDK you’ve just defined and select itself as the host.
Whilst it looks like it’s set everything up, unless you hit ‘OK’ down at the
bottom these changes won’t be remembered.
Once you have your SDKs defined, you can create a Plugin Module project. This
creates a file called
META-INF/plugin.xml which defines the name, id, and
version of the plugin. There are also several entries for hooking into
- Plugin metadata (name, version, id)
- Application components – global content
- Project components – things which are specific to a single project
- Actions – things that show up in menus
- Extensions – things that plug into extension points
The plugin also has a reference to something which can initialise the component (like a Plugin constructor, or an activator in OSGi/Eclipse). This appears to be called during IDEA startup, but it can block startup until it returns; so if there’s long-running operations that you need to do, consider running them in a background thread.
My eyes! My eyes! I had really forgotten how bad Swing development can be, until I was forced back into it. Still, it’s not as bad as on some platforms but if you’re used to IntelliJ you probably see past the UI widget mess in any case. (I’m sure that people will have similar diverging opinions of Eclipse and Xcode; at some level, there’s an aspect of familiarity which means you look over warts in your own IDE of choice.)
One advantage of developing IDE plugins for IDEA over that of Eclipse is you
get to use Swing. Whilst not a UI win, it is a programming win, since you don’t
have to worry about
dispose() or leaking resources. It’s also generally
easier to find tutorials on the subject – Eclipse has always been somewhat
cryptic with the way that menus and actions are contributed (not helped by the
fact that the recommended way has changed every couple of releases).
In any case, it’s relatively easy to create tool windows (similar to Eclipse
Views, except that they are always present in the window as either minimised
entries or showing in the screen somewhere). You get handed a Container in the
createToolWindowContent() method (which turns out to be a Swing container)
and you can throw what you like in there, wiring it up to the mouse events to
Deploying the plugin is a case of doing “Build → Prepare Plugin for Deployment”. This generates a ZIP in the same folder as your plugin (no option to install it elsewhere, it seems) which contains the above folder structure with your module in. Modularity in Java has actively been harmed by IntelliJ’s awful project structure, and is the main cause for pain in leaky implementations for code primarily developed in the IDEA.
If you want to package dependent libraries as well, you can do so by going to the Project Settings and creating a new library, which you then put the JAR into (confusingly, with the ‘classes’ button). It will then get exported with your plugin when it gets built.
With that out of the way, the process involved for a Hello World toolWindow is as follows:
The only thing I found significantly painful was that the runtime on OSX
immediately crashed with a lack of PermGenSpace. Fortunately, “Run → Edit
Configurations” whereupon you can add additional Java VM arguments (in this
-XX:MaxPermSize=256m) which solved that problem. One other annoying
feature; each time I added a key press in the -VM parameters field, I got a
dialog box flash up asking whether I wanted to accept incoming connections or
not. I think this is likely to be an issue with the recent Lion build (along
with the error message “
2011-08-12 08:34:56.286 java[10515:407] -[NSOpenPanel
_setIncludeNewFolderButton:] is deprecated. Please stop calling it.” when
invoking the Open dialog.
Once you get past the ugliness that is Swing, and resign yourself to manual installation of plugins, developing for IntelliJ isn’t that bad. Most of it uses vanilla Swing operations, though it does helpfully suggest some improved IntelliJ Swing classes in place of the standard Swing ones (though in my uses, it actually performed worse than the standard Swing ones did so I ignored that suggestion).
Once you have a displayable component, it’s relatively easy to pick up mouse events and respond to actions. As yet, I have not integrated with the ADT or have processed any source code which is a challenge for another day.