Macs, Modularity and More

IntelliJ Plugin Development

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
Hurt 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.

Installing 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 name) by
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.

Update sites

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=&thinsp;http://path/to/updatePlugins.xml property, 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
of a 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 id is
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.

Plugin Development

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
IntelliJ:

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.

Swing development

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
trigger actions.

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.

Hello World

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
case, -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.

Summary

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.