首页 > 代码库 > 插件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 to true, 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 the loadState 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 the getState 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 AnActionEventobject 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 CommonDataKeysclass.

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.xmldemonstrates 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 the registerAction 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 getComponentNameinitComponent, and disposeComponent 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 as Integer)
  • 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 to true, 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:

  1. In your plugin project, create a Java class that implements the ToolWindowFactoryinterface.
  2. In this class, override the createToolWindowContent method. This method specifies the content for your tool window.
  3. In the plugin configuration file plugin.xml, create the <extensions defaultExtensionNs="com.intellij">...</extensions> section.
  4. 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

  1. Start IntelliJ IDEA and open the tool_window project saved into the code_samples/tool_window directory.
  2. 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.
  3. 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