Writing an IntelliJ IDEA plugin to manage window layouts
There are many IntelliJ plugins out there. Still, existing SDK documentation does not cover every development step. Here I describe how a basic IntelliJ plugin with menu and import/export functionality can be developed.
Plugin’s purpose
Sometimes, after working on some project for a long enough period, you end up with a particular window layout that is convenient for you, e.g., console on the right side, source code on the left, debug window at the top.
However, if you open the same project on a different machine, the layout you’ve so delicately configured will be lost. Same situation will happen if you want to reuse a layout from some other project.
Preserve Layout Plugin allows you to export the layout of any IntelliJ project and then import it back. Export is done using the XML format.
About this blog post
I wrote this post to address the issues I encountered during development of such plugin. As of current date (Oct 20, 2017), IntelliJ SDK documentation is way far from being complete. Below is my collection of findings regarding missing pieces.
Initial Setup
For initial setup, if you never developed a JetBrains’ plugin, I highly recommend to go through the Getting Started section of IntelliJ Platform SDK.
You will notice that some sections are missing and greyed out. However, just by following these initial setup steps you will get just enough to create your own plugin. It is important to mention that in order to debug a plugin you will need the source code of IntelliJ Community Edition checked out and attached to the project.
Prototyping
If you are familiar with plugin development for IntelliJ, it is safe to skip this section.
To play around a little bit, let’s create a very simple plugin with one action. Just to test the things and confirm that setup is working.
First thing you might look in to is the plugin.xml
file that contains the
basic configuration of your plugin.
After filling in the standard information fields like <vendor>
or <version>
we can jump straight to the <actions>
section.
Here we can statically position and initialize our actions.
plugin.xml
is sufficient enough for most needs. However, when doing complex
stuff, we can initialize actions dynamically in Java.
Let’s position our action in the Project View popup menu, the one that appears when you right-click the project name in IDEA.
Information tags:
Decribing actions:
You can see that we created a unique id
for this action. A class for the
should also be specified. A name and description will be LayoutExporter
. The
<add-to-group ...>
tag is the one responsible for tying our action to the
specific menu in IDEA.
Obviously, we will need a class responsible for this action. Let’s make it
super simple: it will print project’s name to the console. You can
notice that we inherit from AnAction
class and override the
actionPerformed
method.
After we run this code, IDEA will spawn a new instance of itself with our plugin installed by default. After we right click on the project (you will need a project to be created or open) and invoke context menu, we will see that LayoutExporter action appeared in this menu. Clicking this action will print a project name to the console output.
Of course, we can assign actions to a different menu. For example, in
preserve-layout-plugin
actions are added to the WindowMenu
.
Window layout in IDEA
Stepping into the actual logic, we might ask ourselves where is the layout of current project stored in IntelliJ IDEA? Turns out it can be stored in two ways:
directory based project
: inworkspace.xml
file under.idea
directoryfile based project
: in.iws
file in a project directory
JetBrains is planning to deprecate the file based format. But we still have to support both options.
Getting current window layout:
To obtain current layout state we obtain an instance of the
ToolWindowManagerImpl
:
Having the manager, we can now ask it for a current layout. An instance of
DesktopLayout
will be returned. Then we cas use DesktopLayout
to return
org.jdom.Element
representation of itself.
Next step is to save this XML/DOM Element somewhere.
File Saver dialog & Saving/writing a file
File Saving dialog:
Somehow, file saving dialog in IntelliJ is invoked in a different way,
comparing to File Chooser. It is also not mentioned yet in documentation.
However, you can come across a FileSaverDescriptor
class, which when
instantiated and passed to
FileChooserFactory
as a parameter will produce a correct dialog window.
Writing to a File in IntelliJ:
Actual data writing is simple, but not obvious. It is required to execute
“write” operations outside of the main thread. To address this,
IntelliJ provides
a very convenient WriteCommandAction
abstraction.
Besides that, best approach to write a file to a disk is through
the VirtualFile
abstraction layer.
Let’s take a look at a composed example of logic required to save an XML to a file in IntelliJ.
File Chooser dialog & importing layout
The best way to select a file in IntelliJ is to use the FileChooser
dialog
and the overloaded function chooseFile
that accepts a callback parameter to
be called after the file is selected. When using this approach IntelliJ is
able to invoke native file chooser dialog window on every platform.
Now we need a callback function importLayoutFileToProject()
.
As you can notice,
importLayoutFileToProject
makes 2 calls to the helping methods.
One to parse the imported XML file, and the other to apply the parsed
layout to IDE. Let’s implement them as well.
Having all that we can now import and export window layout’s of any project. However, it would be nice to notify a user about results of the export/import process.
IntelliJ Notifications
Basic notifications are very easy to fire in IntelliJ:
Here we can change the icon to emphasize the ERROR
status.
Make plugin runnable in every JetBrains IDE
Last thing but not the least important. To support every JetBrains IDE it is
required to specify the <depends>
tag in <plugin>
. More details about
plugin compatibility are available at SDK docs.
Special thanks
This plugin has been developed thanks to Alexander Zolotov from JetBrains team. He provided tons of invaluable tips and advices during the development of this plugin.