首页 > 代码库 > 插件api
插件api
public class TextBoxes extends AnAction { // If you register the action from Java code, this constructor is used to set the menu item name // (optionally, you can specify the menu description and an icon to display next to the menu item). // You can omit this constructor when registering the action in the plugin.xml file. public TextBoxes() { // Set the menu item name. super("Text _Boxes"); // Set the menu item name, description and icon. // super("Text _Boxes","Item description",IconLoader.getIcon("/Mypackage/icon.png")); } public void actionPerformed(AnActionEvent event) { Project project = event.getData(PlatformDataKeys.PROJECT); String txt= Messages.showInputDialog(project, "What is your name?", "Input your name", Messages.getQuestionIcon()); Messages.showMessageDialog(project, "Hello, " + txt + "!\n I am glad to see you.", "Information", Messages.getInformationIcon()); } }
<actions> <group id="MyPlugin.SampleMenu" text="_Sample Menu" description="Sample menu"> <add-to-group group-id="MainMenu" anchor="last" /> <action id="Myplugin.Textboxes" class="Mypackage.TextBoxes" text="Text _Boxes" description="A test menu item" /> <action id="Myplugin.Dialogs" class="Mypackage.MyShowDialog" text="Show _Dialog" description="A test menu item" /> </group> </actions>
Persisting the state of components
The state of every component will be automatically saved and loaded if the component’s class implements theJDOMExternalizable (deprecated) or PersistentStateComponent interface.
When the component’s class implements the PersistentStateComponent interface, the component state is saved in an XML file that you can specify using the @State and @Storage annotations in your Java code.
When the component’s class implements the JDOMExternalizable interface, the components save their state in the following files:
-
Project level components save their state to the project (
.ipr
) file.However, if the workspace option in the
plugin.xml
file is set totrue
, the component saves its configuration to the workspace (.iws
) file instead. -
Module level components save their state to the module (
.iml
) file.
For more information and samples, refer to Persisting State of Components.
Defaults
The defaults (a component’s predefined settings) should be placed in the <component_name>.xml
file. Place this file in the plugin’s classpath in the folder corresponding to the default package. The readExternal()
method will be called on the <component>
root tag.
If a component has defaults, the readExternal()
method is called twice:
- The first time for defaults
- The second time for saved configuration
Plugin components lifecycle
The components are loaded in the following order:
- Creation - constructor is invoked.
- Initialization - the
initComponent
method is invoked (if the component implements the ApplicationComponentinterface). - Configuration - the
readExternal
method is invoked (if the component implements JDOMExternalizable interface), or theloadState
method is invoked (if the component implements PersistentStateComponent and has non-default persisted state). - For module components, the
moduleAdded
method of the ModuleComponent interface is invoked to notify that a module has been added to the project. - For project components, the
projectOpened
method of the ProjectComponent interface 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 the component implements the JDOMExternalizableinterface), or thegetState
method is invoked (if the component implements PersistentStateComponent). - Disposal - the
disposeComponent
method is invoked.
Note that you should not request any other components using the getComponent()
method 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 specify them as constructor parameters or access them in the initComponent
method.
Action System
Executing and updating actions
The system of actions allows plugins to add their own items to IDEA menus and toolbars. An action is a class, derived from the AnAction
class, whose actionPerformed
method is called when the menu item or toolbar button is selected. For example, one of the action classes is responsible for the File | Open File… menu item and for the Open Filetoolbar button.
Actions are organized into groups, which, in turn, can contain other groups. A group of actions can form a toolbar or a menu. Subgroups of the group can form submenus of the menu.
Every action and action group has an unique identifier. Identifiers of many of the standard IDEA actions are defined in the IdeActions class.
Every action can be included in multiple groups, and thus appear in multiple places within the IDEA user interface. Different places where actions can appear are defined by constants in the ActionPlaces
interface. For every place where the action appears, a new Presentation
is created. Thus, the same action can have different text or icons when it appears in different places of the user interface. Different presentations for the action are created by copying the presentation returned by the AnAction.getTemplatePresentation()
method.
To update the state of the action, the method AnAction.update()
is periodically called by IDEA. The AnActionEvent
object passed to this method carries the information about the current context for the action, and in particular, the specific presentation which needs to be updated.
To retrieve the information about the current state of the IDE, including the active project, the selected file, the selection in the editor and so on, the method AnActionEvent.getData()
can be used. Different data keys that can be passed to that method are defined in the CommonDataKeys
class.
The AnActionEvent
instance is also passed to the actionPerformed
method.
Registering Actions
There are two main ways to register an action: either by listing it in the <actions>
section of the plugin.xml
file, or through Java code.
Registering Actions in plugin.xml
Registering actions in plugin.xml
is demonstrated in the following example. The example section of plugin.xml
demonstrates all elements which can be used in the <actions>
section, and describes the meaning of each element.
<!-- Actions -->
<actions>
<!-- The <action> element defines an action to register.
The mandatory "id" attribute specifies an unique
identifier for the action.
The mandatory "class" attribute specifies the
full-qualified name of the class implementing the action.
The mandatory "text" attribute specifies the text of the
action (tooltip for toolbar button or text for menu item).
The optional "use-shortcut-of" attribute specifies the ID
of the action whose keyboard shortcut this action will use.
The optional "description" attribute specifies the text
which is displayed in the status bar when the action is focused.
The optional "icon" attribute specifies the icon which is
displayed on the toolbar button or next to the menu item. -->
<action id="VssIntegration.GarbageCollection" class="com.foo.impl.CollectGarbage" text="Collect _Garbage" description="Run garbage collector" icon="icons/garbage.png">
<!-- The <add-to-group> node specifies that the action should be added
to an existing group. An action can be added to several groups.
The mandatory "group-id" attribute specifies the ID of the group
to which the action is added.
The group must be implemented by an instance of the DefaultActionGroup class.
The mandatory "anchor" attribute specifies the position of the
action in the group relative to other actions. It can have the values
"first", "last", "before" and "after".
The "relative-to-action" attribute is mandatory if the anchor
is set to "before" and "after", and specifies the action before or after which
the current action is inserted. -->
<add-to-group group-id="ToolsMenu" relative-to-action="GenerateJavadoc" anchor="after"/>
<!-- The <keyboard-shortcut> node specifies the keyboard shortcut
for the action. An action can have several keyboard shortcuts.
The mandatory "first-keystroke" attribute specifies the first
keystroke of the action. The key strokes are specified according
to the regular Swing rules.
The optional "second-keystroke" attribute specifies the second
keystroke of the action.
The mandatory "keymap" attribute specifies the keymap for which
the action is active. IDs of the standard keymaps are defined as
constants in the com.intellij.openapi.keymap.KeymapManager class. -->
<keyboard-shortcut first-keystroke="control alt G" second-keystroke="C" keymap="$default"/>
<!-- The <mouse-shortcut> node specifies the mouse shortcut for the
action. An action can have several mouse shortcuts.
The mandatory "keystroke" attribute specifies the clicks and
modifiers for the action. It is defined as a sequence of words
separated by spaces:
"button1", "button2", "button3" for the mouse buttons;
"shift", "control", "meta", "alt", "altGraph" for the modifier keys;
"doubleClick" if the action is activated by a double-click of the button.
The mandatory "keymap" attribute specifies the keymap for which
the action is active. IDs of the standard keymaps are defined as
constants in the com.intellij.openapi.keymap.KeymapManager class. -->
<mouse-shortcut keystroke="control button3 doubleClick" keymap="$default"/>
</action>
<!-- The <group> element defines an action group. <action>, <group> and
<separator> elements defined within it are automatically included in the group.
The mandatory "id" attribute specifies an unique identifier for the action.
The optional "class" attribute specifies the full-qualified name of
the class implementing the group. If not specified,
com.intellij.openapi.actionSystem.DefaultActionGroup is used.
The optional "text" attribute specifies the text of the group (text
for the menu item showing the submenu).
The optional "description" attribute specifies the text which is displayed
in the status bar when the group is focused.
The optional "icon" attribute specifies the icon which is displayed on
the toolbar button or next to the group.
The optional "popup" attribute specifies how the group is presented in
the menu. If a group has popup="true", actions in it are placed in a
submenu; for popup="false", actions are displayed as a section of the
same menu delimited by separators. -->
<group class="com.foo.impl.MyActionGroup" id="TestActionGroup" text="Test Group" description="Group with test actions" icon="icons/testgroup.png" popup="true">
<action id="VssIntegration.TestAction" class="com.foo.impl.TestAction" text="My Test Action" description="My test action"/>
<!-- The <separator> element defines a separator between actions.
It can also have an <add-to-group> child element. -->
<separator/>
<group id="TestActionSubGroup"/>
<!-- The <reference> element allows to add an existing action to the group.
The mandatory "ref" attribute specifies the ID of the action to add. -->
<reference ref="EditorCopy"/>
<add-to-group group-id="MainMenu" relative-to-action="HelpMenu" anchor="before"/>
</group>
</actions>
Registering Actions from Code
To register an action from code, two steps are required.
- First, an instance of the class derived from
AnAction
must be passed to theregisterAction
method of theActionManager class, to associate the action with an ID. - Second, the action needs to be added to one or more groups. To get an instance of an action group by ID, it is necessary to call
ActionManager.getAction()
and cast the returned value to the DefaultActionGroup class.
You can create a plugin that registers actions on IDEA startup using the following procedure.
To register an action on IDEA startup
- Create a new class that implements the
ApplicationComponent
interface. - In this class, override the
getComponentName
,initComponent
, anddisposeComponent
methods. - Register this class in the
<application-components>
section of the plugin.xml file.
To clarify the above procedure, consider the following sample Java class MyPluginRegistration
that registers an action defined in a custom TextBoxes
class and adds a new menu command to the Window menu group on the main menu:
public class MyPluginRegistration implements ApplicationComponent {
// Returns the component name (any unique string value).
@NotNull public String getComponentName() {
return "MyPlugin";
}
// If you register the MyPluginRegistration class in the <application-components> section of
// the plugin.xml file, this method is called on IDEA start-up.
public void initComponent() {
ActionManager am = ActionManager.getInstance();
TextBoxes action = new TextBoxes();
// Passes an instance of your custom TextBoxes class to the registerAction method of the ActionManager class.
am.registerAction("MyPluginAction", action);
// Gets an instance of the WindowMenu action group.
DefaultActionGroup windowM = (DefaultActionGroup) am.getAction("WindowMenu");
// Adds a separator and a new menu command to the WindowMenu group on the main menu.
windowM.addSeparator();
windowM.add(action);
}
// Disposes system resources.
public void disposeComponent() {
}
}
Note, that the sample TextBoxes
class is described in Getting Started with Plugin Development.
To ensure that your plugin is initialized on IDEA start-up, make the following changes to the <application-components>
section of the plugin.xml
file:
<application-components>
<!-- Add your application components here -->
<component>
<implementation-class>MypackageName.MyPluginRegistration</implementation-class>
</component>
</application-components>
Building UI from Actions
If a plugin needs to include a toolbar or popup menu built from a group of actions in its own user interface, that can be accomplished through the ActionPopupMenu
and ActionToolbar
classes. These objects can be created through calls toActionManager.createActionPopupMenu
and ActionManager.createActionToolbar
. To get a Swing component from such an object, simply call the getComponent() method.
If your action toolbar is attached to a specific component (for example, a panel in a toolwindow), you usually need to call ActionToolbar.setTargetComponent()
and pass the instance of the related component as a parameter. This ensures that the state of the toolbar buttons depends on the state of the related component, and not on the current focus location within the IDE frame.
Persisting State of Components
The IntelliJ Platform provides an API that allows components or services to persist their state between restarts of the IDE. You can use either a simple API to persist a few values, or persist the state of more complicated components using the PersistentStateComponent interface.
Using PropertiesComponent for simple non-roamable persistence
If the only thing that your plugin needs to persist is a few simple values, the easiest way to do so is to use thecom.intellij.ide.util.PropertiesComponent
service. It can be used for saving both application level values and project level values (stored in the workspace file). Roaming is disabled for PropertiesComponent
, so use it only for temporary, non-roamable properties.
Use the PropertiesComponent.getInstance()
method for storing application level values, and thePropertiesComponent.getInstance(Project)
method for storing project-level values.
Since all plugins share the same namespace, it is highly recommended to prefix key names (e.g. using your plugin ID).
Using PersistentStateComponent
The com.intellij.openapi.components.PersistentStateComponent
interface gives you the most flexibility for defining the values to be persisted, their format and storage location. In order to use it, you should mark a service or a component as implementing the PersistentStateComponent
interface, define the state class, and specify the storage location using the @com.intellij.openapi.components.State
annotation.
Note that instances of extensions cannot persist their state by implementing PersistentStateComponent
. If your extension needs to have persistent state, you need to define a separate service responsible for managing that state.
Implementing the PersistentStateComponent interface
The implementation of PersistentStateComponent
needs to be parameterized with the type of the state class. The state class can either be a separate JavaBean class, or the class implementing PersistentStateComponent
itself.
In the former case, the instance of the state class is typically stored as a field in the PersistentStateComponent
class:
class MyService implements PersistentStateComponent<MyService.State> {
static class State {
public String value;
}
State myState;
public State getState() {
return myState;
}
public void loadState(State state) {
myState = state;
}
}
In the latter case, you can use the following pattern to implement getState()
and loadState()
methods:
class MyService implements PersistentStateComponent<MyService> {
public String stateValue;
public MyService getState() {
return this;
}
public void loadState(MyService state) {
XmlSerializerUtil.copyBean(state, this);
}
}
Implementing the state class
The implementation of PersistentStateComponent
works by serializing public fields, annotated private fields and bean properties into an XML format. The following types of values can be persisted:
- numbers (both primitive types, such as
int
, and boxed types, such asInteger
) - booleans
- strings
- collections
- maps
- enums
In order to exclude a public field or bean property from serialization, you can annotate the field or getter with the@com.intellij.util.xmlb.annotations.Transient
annotation.
Note that the state class must have a default constructor. It should return the default state of the component (one used if there is nothing persisted in the XML files yet).
State class should have a equals
method, but if it is not implemented, state objects will be compared by fields. If you write in Kotlin, use Data.
Defining the storage location
In order to specify where exactly the persisted values will be stored, you need to add a @State
annotation to thePersistentStateComponent
class. It has the following fields:
name
(required) — specifies the name of the state (name of the root tag in XML).storages
— one or more of@com.intellij.openapi.components.Storage
annotations to specify the storage locations. Optional for project level values — standard project file will be used in this case.reloadable
(optional) — if set to false, complete project (or application) reload is required when the XML file is changed externally and the state has changed.
The simplest ways of specifying the @Storage
annotation are as follows (since IntelliJ IDEA 16, for previous versions please see old version of this document):
-
@Storage("yourName.xml")
If component is project-level — for.ipr
based projects standard project file will be used automatically, you don’t need to specify anything. -
@Storage(StoragePathMacros.WORKSPACE_FILE)
for values stored in the workspace file.
By specifying a different value for the value
parameter (file
before IntelliJ IDEA 16), you can cause the state to be persisted in a different file. For application level components strongly recommended to use custom file, using ofother.xml
is deprecated.
The roamingType
parameter of the @Storage
annotation specifies the roaming type when the Settings Repository plugin is used.
Customizing the XML format of persisted values
Please consider to use annotation parameters only to achieve backward compatibility. Otherwise feel free to file issues about serialization cosmetics.
If you want to use the default bean serialization but need to customize the storage format in XML (for example, for compatibility with previous versions of your plugin or externally defined XML formats), you can use the @Tag
,@Attribute
, @Property
, @MapAnnotation
, @AbstractCollection
annotations.
You can look at the source code (com.intellij.util.xmlb
package) to get more information about the meaning of these annotations.
If the state that you need to serialize doesn’t map cleanly to a JavaBean, you can use org.jdom.Element
as the state class. In that case, you can use the getState()
method to build an XML element with an arbitrary structure, which will then be saved directly in the state XML file. In the loadState()
method, you can deserialize the JDOM element tree using any custom logic. But this way is not recommended and should be avoided.
Persistent component lifecycle
The loadState()
method is called after the component has been created (only if there is some non-default state persisted for the component), and after the XML file with the persisted state is changed externally (for example, if the project file was updated from the version control system). In the latter case, the component is responsible for updating the UI and other related components according to the changed state.
The getState()
method is called every time the settings are saved (for example, on frame deactivation or when closing the IDE). If the state returned from getState()
is equal to the default state (obtained by creating the state class with a default constructor), nothing is persisted in the XML. Otherwise, the returned state is serialized in XML and stored.
Legacy API (JDOMExternalizable)
Older IDEA components use the JDOMExternalizable
interface for persisting state. It uses the readExternal()
method for reading the state from a JDOM element, and writeExternal()
to write the state to it.
JDOMExternalizable
implementations can store the state in attributes and sub-elements manually, and/or use theDefaultJDOMExternalizer
class to store the values of all public fields automatically.
When the component’s class implements the JDOMExternalizable
interface, the components save their state in the following files:
- Project level components save their state to the project (
.ipr
) file. However, if the workspace option in theplugin.xml
file is set totrue
, the component saves its configuration to the workspace (.iws
) file instead. - Module level components save their state to the module (
.iml
) file.
PSI Cookbook
This page gives a list of recipes for the most common operations for working with the PSI (Program Structure Interface). Unlike Developing Custom Language Plugins, it talks about working with the PSI of existing languages (such as Java).
How do I find a file if I know its name but don’t know the path?
FilenameIndex.getFilesByName()
How do I find where a particular PSI element is used?
ReferencesSearch.search()
How do I rename a PSI element?
RefactoringFactory.createRename()
How can I cause the PSI for a virtual file to be rebuilt?
FileContentUtil.reparseFiles()
Java Specific
How do I find all inheritors of a class?
ClassInheritorsSearch.search()
How do I find a class by qualified name?
JavaPsiFacade.findClass()
How do I find a class by short name?
PsiShortNamesCache.getInstance().getClassesByName()
How do I find a superclass of a Java class?
PsiClass.getSuperClass()
How do I get a reference to the containing package of a Java class?
PsiJavaFile javaFile = (PsiJavaFile) psiClass.getContaningFile();
PsiPackage pkg = JavaPsiFacade.getInstance(project).findPackage(javaFile.getPackageName());
How do I find the methods overriding a specific method?
OverridingMethodsSearch.search()
Virtual File System
The virtual file system (VFS) is a component of IntelliJ Platform that encapsulates most of its activity for working with files. It serves the following main purposes:
- Providing a universal API for working with files regardless of their actual location (on disk, in archive, on a HTTP server etc.)
- Tracking file modifications and providing both old and new versions of the file content when a modification is detected.
- Providing a possibility to associate additional persistent data with a file in the VFS.
In order to provide the last two features, the VFS manages a persistent snapshot of some of the contents of the user’s hard disk. The snapshot stores only those files which have been requested at least once through the VFS API, and is asynchronously updated to match the changes happening on the disk.
The snapshot is application level, not project level - so, if some file (for example, a class in the JDK) is referenced by multiple projects, only one copy of its contents will be stored in the VFS.
All VFS access operations go through the snapshot.
If some information is requested through the VFS APIs and is not available in the snapshot, it is loaded from disk and stored into the snapshot. If the information is available in the snapshot, the snapshot data is returned. The contents of files and the lists of files in directories are stored in the snapshot only if that specific information was accessed - otherwise, only file metadata like name, length, timestamp, attributes is stored.
This means that the state of the file system and the file contents displayed in the IntelliJ Platform UI comes from the snapshot, which may not always match the actual contents of the disk.
For example, in some cases deleted files can still be visible in the UI for some time before the deletion is picked up by the IntelliJ Platform.
The snapshot is updated from disk during refresh operations, which generally happen asynchronously. All write operations made through the VFS are synchronous - i.e. the contents is saved to disk immediately.
A refresh operation synchronizes the state of a part of the VFS with the actual disk contents. Refresh operations are explicitly invoked by the IntelliJ Platform or plugin code - i.e. when a file is changed on disk while the IDE is running, the change will not be immediately picked up by the VFS. The VFS will be updated during the next refresh operation which includes the file in its scope.
IntelliJ Platform refreshes the entire project contents asynchronously on startup. By default, it performs a refresh operation when the user switches to it from another app, but users can turn this off via Settings | Appearance & Behavior | System Settings | Synchronize files on frame activation.
On Windows, Mac and Linux, IntelliJ Platform starts a native file watcher process that receives file change notifications from the file system and reports them to IntelliJ Platform. If a file watcher is available, a refresh operation looks only at the files that have been reported as changed by the file watcher. If no file watcher is present, a refresh operation walks through all directories and files in the refresh scope.
Refresh operations are based on file timestamps. If the contents of a file was changed but its timestamp remained the same, IntelliJ Platform will not pick up the updated contents.
There is currently no facility for removing files from the snapshot. If a file was loaded there once, it remains there forever unless it was deleted from the disk and a refresh operation was called on one of its parent directories.
The VFS itself does not honor ignored files listed in Settings | File Types | Files and folders to ignore and excluded folders listed in Project Structure | Modules | Sources | Excluded. If the application code accesses them, the VFS will load and return their contents. In most cases, the ignored files and excluded folders must be skipped from processing by higher level code.
During the lifetime of a running instance of an IntelliJ Platform IDE, multiple VirtualFile
instances may correspond to the same disk file. They are equal, have the same hashCode
and share the user data.
Synchronous and asynchronous refreshes
From the point of view of the caller, refresh operations can be either synchronous or asynchronous. In fact, the refresh operations are executed according to their own threading policy, and the synchronous flag simply means that the calling thread will be blocked until the refresh operation (which will most likely run on a different thread) is completed.
Both synchronous and asynchronous refreshes can be initiated from any thread. If a refresh is initiated from a background thread, the calling thread must not hold a read action, because otherwise a deadlock would occur. SeeIntelliJ Platform Architectural Overview for more details on the threading model and read/write actions.
The same threading requirements also apply to functions like LocalFileSystem.refreshAndFindFileByPath(), which perform a partial refresh if the file with the specified path is not found in the snapshot.
In nearly all cases, using asynchronous refreshes is strongly preferred. If there is some code that needs to be executed after the refresh is complete, the code should be passed as a postRunnable
parameter to one of the refresh methods:
- RefreshQueue.createSession()
- VirtualFile.refresh()
Synchronous refreshes can cause deadlocks in some cases, depending on which locks are held by the thread invoking the refresh operation.
Virtual file system events
All changes happening in the virtual file system, either as a result of refresh operations or caused by user’s actions, are reported as virtual file system events. VFS events are always fired in the event dispatch thread, and in a write action.
The most efficient way to listen to VFS events is to implement the BulkFileListener
interface and to subscribe with it to the VirtualFileManager.VFS_CHANGES topic.
This API gives you all the changes detected during the refresh operation in one list, and lets you process them in batch. Alternatively, you can implement the VirtualFileListener
interface and register it usingVirtualFileManager.addVirtualFileListener(). This will let you process the events one by one.
Note that the VFS listeners are application level, and will receive events for changes happening in all the projects opened by the user. You may need to filter out events which aren’t relevant to your task.
VFS events are sent both before and after each change, and you can access the old contents of the file in the before event. Note that events caused by a refresh are sent after the changes have already occurred on disk - so when you process the beforeFileDeletion
event, for example, the file has already been deleted from disk. However, it is still present in the VFS snapshot, and you can access its last contents using the VFS API.
Note that a refresh operation fires events only for changes in files that have been loaded in the snapshot. For example, if you accessed a VirtualFile
for a directory but never loaded its contents using VirtualFile.getChildren(), you may not get fileCreated
notifications when files are created in that directory.
If you loaded only a single file in a directory using VirtualFile.findChild()
, you will get notifications for changes to that file, but you may not get created/deleted notifications for other files in the same directory.
Tool Windows
Tool windows are child windows of the IDE used to display information. These windows generally have their own toolbars (referred to as tool window bars) along the outer edges of the main window containing one or more tool window buttons, which activate panels displayed on the left, bottom and right sides of the main IDE window. For detailed information about tool windows, please see IntelliJ IDEA Web Help .
Each side contains two tool window groups, the primary and the secondary one, and only one toolwindow from each group can be active at a time.
Each toolwindow can show multiple tabs (or “contents”, as they are called in the API). For example, the Run toolwindow displays a tab for each active run configuration, and the Changes toolwindow displays a fixed set of tabs depending on the version control system used in the project.
There are two main scenarios for the use of tool windows in a plugin. In the first scenario (used by the Ant and Commander plugins, for example), a toolwindow button is always visible, and the user can activate it and interact with the plugin functionality at any time. In the second scenario (used by the Analyze Dependencies
action, for example), the toolwindow is created to show the results of a specific operation, and can be closed by the user after the operation is completed.
In the first scenario, the toolwindow is registered in plugin.xml using the <toolWindow>
extension point. The extension point attributes specify all the data which is necessary to display the toolwindow button:
-
The
id
of the toolwindow (corresponds to the text displayed on the toolwindow button) -
The
anchor
, meaning the side of the screen on which the toolwindow is displayed (“left”, “right” or “bottom”) -
The
secondary
attribute, specifying whether the toolwindow is displayed in the primary or the secondary group -
The
icon
to display on the toolwindow button (13x13 pixels)
In addition to that, you specify the factory class - the name of a class implementing the ToolWindowFactory interface. When the user clicks on the toolwindow button, the createToolWindowContent()
method of the factory class is called, and initializes the UI of the toolwindow. This procedure ensures that unused toolwindows don’t cause any overhead in startup time or memory usage: if a user does not interact with the toolwindow of your plugin, no plugin code will be loaded or executed.
If the toolwindow of your plugin doesn’t need to be displayed for all projects, you can also specify the conditionClassattribute - the qualified name of a class implementing the Condition<Project> interface (this can be the same class as the toolwindow factory implementation). If the condition returns false
, the toolwindow will not be displayed. Note that the condition is evaluated only once when the project is loaded; if you’d like to show your and hide toolwindow dynamically while the user is working with the project, you need to use the second method for toolwindow registration.
The second method involves simply calling ToolWindowManager.registerToolWindow() from your plugin code. The method has multiple overloads that can be used depending on your task. If you use an overload that takes a component, the component becomes the first content (tab) displayed in the toolwindow.
Displaying the contents of many toolwindows requires access to the indexes. Because of that, toolwindows are normally disabled while building indices, unless you pass true as the value of canWorkInDumbMode
to theregisterToolWindow()
function.
As mentioned previously, toolwindows can contain multiple tabs, or contents. To manage the contents of a toolwindow, you can call ToolWindow.getContentManager(). To add a tab (content), you first need to create it by callingContentManager.getFactory().createContent(), and then to add it to the toolwindow usingContentManager.addContent().
You can control whether the user is allowed to close tabs either globally or on a per-tab basis. The former is done by passing the canCloseContents
parameter to the registerToolWindow()
function, or by specifyingcanCloseContents="true"
in plugin.xml. If closing tabs is enabled in general, you can disable closing of specific tabs by calling Content.setCloseable(false).
How to Create a Tool Window?
The IntelliJ Platform provides the toolWindow extension point that you can use to create and configure your custom tool windows. This extension point is declared using the ToolWindowEP bean class.
To create a tool window, first declare an extension to the toolWindow extension point.
Creation of Plugin
To create a plugin that displays a custom tool window, perform the following steps:
- In your plugin project, create a Java class that implements the ToolWindowFactoryinterface.
- In this class, override the
createToolWindowContent
method. This method specifies the content for your tool window. - In the plugin configuration file plugin.xml, create the
<extensions defaultExtensionNs="com.intellij">...</extensions>
section. - To this section, add the
<toolWindow>
element, and for this element, set the following attributes declared in the ToolWindowEP bean class:- id (required): specifies the tool window caption.
- anchor (required): specifies the tool window bar where the tool window button will be displayed. Possible values: “left”, “right”, “top”, “bottom.”
- secondary (optional): when true, the tool window button will be shown on the lower part of the tool window bar. Default value is false.
- factoryClass (required): specifies the Java class implementing the ToolWindowFactory interface (see Step 1).
- icon (optional): specifies path to the icon that identifies the tool window, if any.
- conditionClass (optional): specifies a Java class that implements the Condition interface. Using this class, you can define conditions to be met to display tool window button. In the Condition class, you should override the value method: if this method returns false, the tool window button is not displayed on tool window bar.
To clarify the above procedure, consider the following fragment of the plugin.xml
file:
<extensions defaultExtensionNs="com.intellij">
<toolWindow id="My Sample Tool Window" icon="/myPackage/icon.png" anchor="right" factoryClass="myPackage.MyToolWindowFactory"/>
</extensions>
Sample Plugin
To clarify how to develop plugins that create tool windows, consider the toolWindow sample plugin available in thecode_samples directory of the SDK documentation. This plugin creates the Sample Calendar tool window that displays the system date, time and time zone.
To run toolWindow plugin
- Start IntelliJ IDEA and open the tool_window project saved into the code_samples/tool_window directory.
- Ensure that the project settings are valid for your environment. If necessary, modify the project settings.
To view or modify the project settings, you can open the Project Structure. - Run the plugin by choosing the Run | Run on the main menu.
If necessary, change the Run/Debug Configurations.
The plugin creates the Sample Calendar tool window. When opened, this tool window is similar to the following screen:
插件api