IntelliJ IDEA Plugin Structure
Purpose
Plugins are the only possible way for third-parties to extend IDEA functionality. Every plugin uses API exposed by IDEA or other plugins to implement its functionality. This document is focused on plugin system architecture and plugin lifecycle. It doesn’t specify any of the other APIs which may be used by plugins.
Plugin Content
There are 3 ways how to organize plugin content:
1. Plugin consists of one .jar file placed in IDEA_HOME/plugins/. The archive should contain configuration file (META-INF/plugin.xml) and classes which implement the plugin functionality. Configuration file specifies plugin name, description, version, vendor, IDEA version plugin is supposed to be used with, plugin components, actions and action groups, action user interface placement.
IDEA_HOME
Plugins Sample.jar/ com/foo/..... ... ... META-INF plugin.xml
2. Plugin files are located in a folder:
IDEA_HOME
Plugins Sample lib libfoo.jar libbar.jar classes com/foo/..... ... ... META-INF plugin.xml
The 'classes' folder and all jars located in the 'lib' folder are automatically added to the classpath.
3. Plugin files are located in a jar-file which is placed to the lib folder:
IDEA_HOME
Plugins Sample lib libfoo.jar libbar.jar Sample.jar/ com/foo/..... ... ... META-INF plugin.xml
All the jars from 'lib' folder are automatically added to the classpath.
Plugin Class Loaders
For loading the classes of each plugin, IDEA uses a separate class loader. This allows each plugin to use a different version of some library, even if the same library is used by IDEA itself or another plugin.
By default, the classes not found in the plugin class loader are loaded through the main IDEA class loader. However, it is possible to use the depends element in plugin.xml to specify that a plugin depends on one or more other plugins. In this case, the class loaders of those plugins will be used for classes not found in the current plugin. This allows a plugin to reference classes from other plugins.
Plugin Components
Components are the fundamental concept of plugins integration. There are three kinds of components: application-level, project-level and module-level. Application-level components are created and initialized on IDEA start-up. Project-level components are created for each Project instance present in IDEA (please note that components might be created for even unopened projects). Module-level components are created for each Module in every project loaded in IDEA.
Application-level components can be acquired from the Application instance with getComponent(Class) method. Project-level components can be acquired from the Project instance with getComponent(Class) method. Module-level component can be acquired from Module instance with the same method.
Every component should have interface and implementation classes specified in the configuration file. The interface class will be used for retrieving the component from other components and the implementation class will be used for component instantiation. Note that two components of the same level (Application, Project or Module) cannot have the same interface class. Interface and implementation classes can be the same.
Each component has the unique name which is used for its externalization and other internal needs. The name of component is returned by its getComponentName() method.
Components Naming Notation
It's recommended to name components in plugin_name.component_name form.
Application Level Components
Application-level component's implementation class should implement the ApplicationComponent interface. It should have constructor with no parameters which will be used for its instantiation.
Project Level Components
Project-level component's implementation class should implement the ProjectComponent interface. It should have constructor with a single parameter of Project type or with no parameters. If a component needs the Project instance it should provide constructor of the first form and store its parameter into a field for later use.
Module Level Components
Module-level component's implementation class should implement the ModuleComponent interface. It should have constructor with a single parameter of Module type or with no parameters. If a component needs the Module instance it should provide constructor of the first form and store its parameter into a field for later use.
Plug-In Components Configuration
The state of every component will be automatically saved and loaded if the component implements the JDOMExternalizable interface.
The state of application-level components state is saved in the XML file with the same name as the plugin in the config/plugins subfolder of the IDEA settings folder.
Project-level components save their state to the project (.ipr) file. If workspace option in the plugin.xml is set to true, the component will save its configuration to the workspace (.iws) file instead of project (.ipr) file.
Module-level components save their state to the module (.iml) file.
Note that a component should always be ready to save its state.
Defaults
Defaults (components' predefined settings) should be placed in the component_name.xml file. Put this file in the plugin's classpath in the folder corresponding to the default package. readExternal() method will be called on the <component> root tag. If component has defaults, the readExternal() method is called twice: the first time for defaults and the second time for saved configuration.
Plug-In Components Lifecycle
The components are loaded in the following order:
- Creation - constructor is invoked
- Initialization - the initComponent method is invoked
- Configuration - the readExternal method is invoked (if component implements JDOMExternalizable interface)
- For module components, the moduleAdded method is invoked to notify that a module has been added to the project
- For project components, the projectOpened method is invoked to notify that a project has been loaded
The components are unloaded in the following order:
- Saving configuration - the writeExternal method is invoked (if component implements JDOMExternalizable interface)
- Disposal - disposeComponent method is invoked
Note that you should not request any other components in the constructor of your component, otherwise you'll get an assertion. If you need access to other components when initializing your component you can access them in the initComponent method.
Plug-In Configuration (plugin.xml)
Here is a sample plugin configuration file. This sample showcases and describes all elements which can be used in the plugin.xml file.
<!-- url="" specifies the URL of the plugin
homepage (displayed in the Welcome Screen and in "Plugins" settings dialog) -->
<idea-plugin url=”http://www.jetbrains.com/idea”>
<!--
Plugin name -->
<name>VssIntegration</name>
<!--
Unique identifier of the plugin. Must not change between plugin versions. If
not specified, assumed to be equal to <name>. -->
<id>VssIntegration</id>
<!--
Description of the plugin. -->
<description>Vss
integration plugin</description>
<!--
Description of changes in the latest version of the plugin. Displayed in the
"Plugins" settings dialog and in the plugin repository Web interface. -->
<change-notes>Initial
release of the plugin.</change-notes>
<!--
Plugin version -->
<version>1.0</version>
<!--
The vendor of the plugin. The optional "url" attribute specifies the URL of the
vendor homepage. The optional "email" attribute specifies
the e-mail address of the vendor. The optional "logo" attribute specifies the
path within the plugin JAR to a 16x16 icon which is displayed next to the plugin name in the welcome screen. -->
<vendor
url="http://www.jetbrains.com"
email="support@jetbrains.com"
logo="icons/plugin.png">Foo
Inc.</vendor>
<!-- The unique identifiers of
the plugins on which this plugin depends. -->
<depends>MyFirstPlugin</depends>
<depends>MySecondPlugin</depends>
<!-- Allows a plugin to integrate its
help system (in JavaHelp format) with the IDEA help system. The "file" attribute
specifies the name of the JAR file in the "help"
subdirectory of the plugin directory. The "path" attribute specifies the name of
the helpset file within the JAR file.-->
<helpset
file="myhelp.jar"
path="/Help.hs"
/>
<!--
Minimum and maximum build of IDEA compatible with the plugin -->
<idea-version
since-build=”3000”
until-build=”3999”/>
<!--
Plugin's application components -->
<application-components>
<component>
<!--
Component's interface class -->
<interface-class>com.foo.Component1Interface</interface-class>
<!--
Component's implementation class -->
<implementation-class>com.foo.impl.Component1Impl</implementation-class>
</component>
</application-components>
<!--
Plugin's project components -->
<project-components>
<component>
<!--
Interface and implementation classes are the same -->
<interface-class>com.foo.Component2</interface-class>
<!--
Save state to the .iws instead of .ipr -->
<option
name="workspace"
value="true"
/>
</component>
</project-components>
<!-- Plugin's module components -->
<module-components>
<component>
<interface-class>com.foo.Component3</interface-class>
</component>
</module-components>
<!--
Actions -->
<actions>
<action
id="VssIntegration.GarbageCollection"
class="com.foo.impl.CollectGarbage"
text="Collect
_Garbage" description="Run
garbage collector">
<keyboard-shortcut
first-keystroke="control
alt G" second-keystroke="C"
keymap="$default"/>
</action>
</actions>
<!--
Extension points defined by the plugin. Extension points are registered by a
plugin so that other plugins can provide this plugin with certain data.
The "beanClass" attribute specifies the class the implementations of which can
be used for the extension point. -->
<extensionPoints>
<extensionPoint
name="testExtensionPoint"
beanClass="com.foo.impl.MyExtensionBean"/>
</extensionPoints>
<!--
Extensions which the plugin adds to extension points defined by the IDEA core
or by other plugins. The "xmlns" attribute specifies the ID of the plugin
defining the extension point, or "com.intellij" if the extension point is
defined by the IDEA core. The name of the tag within the
<extensions> tag matches the name of the extension point, and the
"implementation" class specifies the name of the class added to the
extension point. -->
<extensions
xmlns="VssIntegration">
<testExtensionPoint
implementation="com.foo.impl.MyExtensionImpl"/>
</extensions>
</idea-plugin>
