# IntelliJ Platform SDK IntelliJ Platform SDK Extend the IntelliJ Platform by creating plugins, custom language support, or UI themes. # The IntelliJ Platform The IntelliJ Platform is not a product in and of itself but provides a platform for building IDEs. It is used to power JetBrains products such as [IntelliJ IDEA](https://www.jetbrains.com/idea/). It is also Open Source and can be used by third parties to build IDEs, such as [Android Studio](https://developer.android.com/studio/index.html) from Google. The IntelliJ Platform provides all the infrastructure that these IDEs need to provide rich language tooling support. It is a component-driven, cross-platform JVM-based application host with a high-level user interface toolkit for creating [tool windows](tool-windows.html), tree views, and lists (supporting fast search) as well as popup menus and [dialogs](dialog-wrapper.html). The IntelliJ Platform has a full-text [editor](editors.html) with abstract implementations of [syntax highlighting](analyzing.html), [code folding](folding-builder.html), [code completion](code-completion.html), and other rich text [editing features](editing.html). An image editor is also included. Furthermore, it includes open APIs to build standard IDE functionality, such as a [project model](project.html) and a [build system](external-system-integration.html). It also provides an infrastructure for a rich debugging experience, with language-agnostic advanced breakpoint support, call stacks, watch windows, and expression evaluation. But the IntelliJ Platform's real power comes from the Program Structure Interface ([PSI](psi.html)). It is a set of functionalities used to parse files, build rich syntactic and semantic models of the code, and build [indexes](indexing-and-psi-stubs.html) from this data. PSI powers a lot of functionalities, from quick navigating to [files](psi-files.html), types, and [symbols](symbols.html), to the contents of [code completion](code-completion.html) popups and [find usages](find-usages.html), [code inspections](code-inspections.html), and code rewriting, for quick fixes or [refactorings](rename-refactoring.html), as well as many other features. The IntelliJ Platform includes [parsers](implementing-parser-and-psi.html) and a PSI model for many languages, and its extensible nature means that it is possible to [add support for other languages](custom-language-support.html). ## Plugins Products built on the IntelliJ Platform are extensible applications, with the platform being responsible for creating [Extensions](plugin-extensions.html). The IntelliJ Platform fully supports [plugins](developing-plugins.html), and JetBrains hosts the [JetBrains Marketplace](https://plugins.jetbrains.com), which can be used to distribute plugins that support one or more of the products. It is also possible to distribute plugins using a [Custom Plugin Repository](custom-plugin-repository.html). Plugins can extend the platform in many ways, from adding a simple menu item to adding support for a complete language, build system, and debugger. Many of the existing IntelliJ Platform features are implemented as plugins that can be included or excluded depending on the needs of the end product. See the [Quick Start Guide](plugins-quick-start.html) for more details. Note: Plugin Alternatives In some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as [alternative solutions](plugin-alternatives.html) exist. ## Open Source The IntelliJ Platform is Open Source, under the [Apache License](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/LICENSE.txt), and [hosted on GitHub](https://github.com/JetBrains/intellij-community). While this guide refers to the IntelliJ Platform as a separate entity, there is no "IntelliJ Platform" GitHub repository. Instead, the platform is considered to be an almost complete overlap with the [IntelliJ Platform](idea.html) (the GitHub repository linked above is the [JetBrains/intellij-community](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/README.md) repository). The version of the IntelliJ Platform is defined by the version of the corresponding IntelliJ IDEA release. For example, to build a plugin against IntelliJ IDEA (2019.1.1), build #191.6707.61 means specifying the same build number tag to get the correct IntelliJ Platform files from the `intellij-community` repository. See the [Build Number Ranges](build-number-ranges.html) page for more information about build numbers corresponding to version numbering. Typically, an IDE that is based on the IntelliJ Platform will include the `intellij-community` repository as a Git submodule and provide configuration to describe which plugins from the `intellij-community`, and which custom plugins will make up the product. ## IDEs Based on the IntelliJ Platform The IntelliJ Platform underlies many JetBrains IDEs. [IntelliJ IDEA](idea.html) is a superset of the IntelliJ Platform but includes closed-source plugins ([see this feature comparison](https://www.jetbrains.com/idea/features/editions_comparison_matrix.html)). Similarly, other products such as [WebStorm](webstorm.html) and [DataGrip](data-grip.html) are based on the IntelliJ Platform, but with a different set of plugins included and excluding other default plugins. This allows plugins to target multiple products, as each product will include base functionality and a selection of plugins from the IntelliJ Platform repository. The following IDEs are based on the IntelliJ Platform: * [JetBrains](https://www.jetbrains.com) IDEs: * [AppCode](app-code.html) * [CLion](clion.html) * [DataGrip](data-grip.html) * [DataSpell](https://www.jetbrains.com/dataspell/) * [GoLand](goland.html) * [IntelliJ IDEA](idea.html) * [MPS](https://www.jetbrains.com/mps/) * [PhpStorm](phpstorm.html) * [PyCharm](pycharm.html) * [Rider](#rider) * [RubyMine](rubymine.html) * [RustRover](https://www.jetbrains.com/rust/) * [WebStorm](webstorm.html) * [Android Studio](android-studio.html) IDE from Google * [Jmix Studio](https://www.jmix.io/tools/) ### Rider JetBrains [Rider](rider.html) uses the IntelliJ Platform differently than other IntelliJ-based IDEs. It uses the IntelliJ Platform to provide the user interface for a C# and .NET IDE, with the standard IntelliJ editors, tool windows, debugging experience, etc. It also integrates into the standard Find Usages and Search Everywhere UI and uses code completion, syntax highlighting, and so on. However, Rider doesn't create a full [PSI](psi.html) (syntactic and semantic) model for C# files. Instead, it reuses [ReSharper](https://www.jetbrains.com/resharper/) to provide language functionality. All the C# PSI models, inspections, code rewritings, such as quick fixes, and refactorings are run out of the process, in a command-line version of ReSharper. This means that creating a plugin for Rider involves two parts: a plugin that lives in the IntelliJ "front end" to show user interface, and a plugin that lives in the ReSharper "back end" to analyze and work with the C# PSI. Fortunately, many plugins can simply work with the ReSharper backend. Rider takes care of displaying the results of inspections and code completion, and many plugins can be implemented without requiring an IntelliJ UI component. # Contributing to the IntelliJ Platform Please make sure to read the [Code of Conduct](intellij-sdk-docs-original-code-of-conduct.html). ## Participate in the Community ### Participate in Newsgroups and Forums There are several community [forums and newsgroups](https://intellij-support.jetbrains.com/hc/en-us/community/topics) you can join to discuss the IntelliJ Platform and IDEs. The forums are an excellent source for users and contributors interested in having technical discussions, answering questions, or resolving potential issues for newcomers. ### Link to IntelliJ Platform Homepage The success of any open-source project depends on the number of people who use the product and contribute back to the project. By linking to [https://www.jetbrains.com/opensource/idea](https://www.jetbrains.com/opensource/idea), you can increase the chances of a new user or contributor finding out about the project and joining the community. If you're as excited about IntelliJ IDEA as we are, you can show it by linking to us. Project logos and other assets are [also available](https://www.jetbrains.com/company/brand/logos/). ### Promote IntelliJ Platform Help promote the platform and IDE by using your blog, Twitter, Facebook, or submitting an article to your favorite local magazine. If you are a member of a different open-source community, why not mention IntelliJ IDEA on their discussion forums or at conferences? If you love IntelliJ IDEA, don't hold back — speak up! The more developers use IntelliJ IDEA, the more bugs will be caught, the more plugins will be written, the more visible the project, and the more benefits the community will get! ## Help Others Learn ### Write Documents We're always looking for new articles about IntelliJ IDEA features as well as documentation for the IntelliJ Platform. You can write tutorials, how-tos, sample applications, or share your experience with the IntelliJ Platform. You can publish your documentation on a website/blog or submit a [pull request](intellij-sdk-docs-original-contributing.html) to the SDK Docs. ### Produce Screencasts Screencasts have recently become very popular as a way to show other developers how to use the tool effectively. You can record a screencast about a particular feature or use case you discovered and would like to share it with the community. ## Contribute Code ### File a Bug Report Bug reports take little time to file and are very helpful to developers. This is one of the easiest contributions you can make. When you discover a problem with the IDE or the platform, please report it. Make sure you provide information about your environment (available from the `About` menu), steps to reproduce the issue, as well as a written description of the problem. You can file a bug in our [YouTrack issue tracker](https://youtrack.jetbrains.com/issues/IJPL). Before submitting an issue, please search for already submitted ones describing the same problem — and if you find one, feel free to vote for it. ### Help Triage Existing Bug Reports Over the years, users have submitted thousands of issues to the IntelliJ IDEA issue tracker. Many of the unresolved issues are no longer applicable to the latest version of IntelliJ IDEA, are duplicates, or require additional information to be resolved. Leaving comments notifying about the status of such issues helps the team keep the issue tracker clean and useful for everyone. ### Write a Plugin One of the best ways to contribute a larger piece of code, adding extra functionality to IntelliJ IDEA or any of the other IntelliJ Platform-based IDEs, is by writing a plugin. You can submit a plugin to the [JetBrains Marketplace](https://plugins.jetbrains.com/), making it available for all users. When writing a plugin, you have control over the code and don't need to sign the contribution agreement. ### Submit a Patch If you would like to improve the code in the IntelliJ Platform or the core functionality of IntelliJ IDEA, you can submit a pull request to the [IntelliJ Platform repository on GitHub](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/README.md). When preparing the change, please make sure to follow the [IntelliJ Platform Coding Guidelines](intellij-coding-guidelines.html). A developer will review your contribution and, if it meets the quality criteria and fits well with the rest of the code, you'll be notified about the acceptance of the patch. Looking for issues to work on? Issues marked with the [#patch_welcome](https://youtrack.jetbrains.com/issues/IDEA?q=%23patch_welcome%20%23unresolved) tag are looking for external contributors. Alternatively, you can attach a patch to the ticket in the [YouTrack bug database](https://youtrack.jetbrains.com/issues/IJPL). You can either file a new issue with the patch attached or attach a patch to an issue submitted by another user. In this case, you will also need to sign the [JetBrains Contributor License Agreement (CLA)](https://www.jetbrains.com/agreements/cla/) to complete your contribution. ### Become a Committer Developers with a long history of submitting high-quality patches can gain direct commit rights. # IntelliJ Platform Coding Guidelines If you are writing code that you would like to contribute to the IntelliJ Platform, following these guidelines will make it easier for the JetBrains development team to review and accept your changes. ## Following the Latest Source Code If you submit patches, we strongly recommend building your patches against the latest version of the code from the [intellij-community Git repository](intellij-platform.html#open-source). The easiest way to do so is to clone the repository, track your work in Git, and provide your changes as described in [Submit a Patch](platform-contributions.html#submit-a-patch). ## General Architectural Principles Please do your best to follow common Java architectural principles. "Effective Java" by Joshua Bloch is the right place to start. ## Tests Functional tests cover most of the existing functionality of IntelliJ IDEA. If tests cover the area you're modifying, you must run the tests and make sure that your changes do not introduce any new test failures. It's also strongly recommended that you provide new functional tests that cover the bugs you fix or the new features that you add. ## Code Formatting We're generally pretty lax about code formatting, but at least the following conventions must be observed: * 2 space indents in source files * Use `my` prefix for instance variables and `our` prefix for class variables. * New source code files must include a copyright statement with the Apache 2 license and the name of the contributor. The easiest way to follow our code formatting guidelines is to reformat your code submissions using the shared code style, which is included in the IntelliJ IDEA project directory. ## Inspections The IntelliJ IDEA project includes a shared inspection profile. We strongly recommend making sure that the code you submit does not contain any warnings highlighted by the inspections configured in that inspection profile. ## Javadoc Comments If your code adds new OpenAPI interfaces, classes, methods, or extension points, you must provide Javadoc comments describing the parameters and intended usage of the APIs. Providing Javadoc or other comments for other parts of the code is a good idea but isn't required. ## Commits To avoid unnecessary work when reviewing your changes, please follow these guidelines: * Look through all of your changes in your patch or pull request before you submit it to us. Make sure that everything you've changed is there for a reason. * Please don't include unfinished work in the patch. Make sure that it doesn't contain any TODO comments. If you added some code and ended up not needing it, please make sure that you delete it before you submit your patch. * Please don't include any changes that affect formatting, fixing "yellow code" (warnings), or code style along with actual changes that fix a bug or implement a feature. No one likes to leave poor code, but remember that having these changes mixed complicates the process of review. * Please don't fix multiple problems within a single patch or pull request. * Please don't commit your changes to configuration files (`runConfigurations/IDEA.xml`, `codeStyleSettings.xml`, `misc.xml`, etc.) unless it is essential for the fix itself. * Please avoid moving or renaming code unless it is necessary for the fix. Keeping backwards compatibility is critical for the platform. The ideal pull request would contain one commit with everything needed to fix the bug or implement a feature, but nothing else. "Commit early, commit often" perfectly applies only to local commits, but such "public commits" are hard to review (the reviewer needs either to go commit by commit spending more time to review work-in-progress, or to review all changes at once thus losing valuable information stored in commit messages). The best would be to commit early, but then to squash all commits into one with a descriptive commit message. Sometimes several commits for a single issue are also acceptable, but each of these should be self-contained "steps" to solve the problem. # About This Guide This guide is split into several parts, similar to a textbook. Each one builds on the content of the previous section, but it is not necessary to read the guide in order. The [Key Topics](key-topics.html) page aims to link to the pages that are necessary to be able to understand the architecture and get started building plugins. All source links and reference lists target IntelliJ Platform 2026.1.4. Note: While browsing this guide, you will notice that there are topics that are greyed out. Unfortunately, the guide is not complete and contains placeholders for specific topics. We are working on increasing the coverage, but if you get stuck due to missing content, please see the [Getting Help](getting-help.html) section for details on how to get moving again. The guide is also [Open Source on GitHub](https://github.com/JetBrains/intellij-sdk-docs), and Pull Requests for new content, corrections or updates are always gratefully received. Please see the [Contributing](intellij-sdk-docs-original-contributing.html) page for details. Tip: See also [Glossary](glossary.html) for a handy reference of common terms. ## Plugins Describes how to create a plugin that can extend the IntelliJ Platform. Includes details on how to set up the project, register extension points, target specific versions of the IntelliJ Platform, and how to package, deploy, and test your plugins. ## Base Platform Describes the foundational layer of the architecture, which provides many features and utilities, such as the component model, the user interface, documents and editors, the virtual file system, settings, threading, and background tasks. The Base Platform layer mainly comprises the functionality of the IntelliJ Platform that does not target language features or parsing. ## Project Model Documents the Project Model, which represents the files and configuration of the currently loaded project, as well as the build system used to build the project. ## PSI The Program Structure Interface (PSI) builds the syntactic and semantic models for lots of different file types. This section describes how to work with the PSI, navigating and manipulating the syntax trees, and also looks at the powerful references system, which allows a syntax tree node to reference an item in the semantic model. It also details how PSI creates and uses indexes. ## Features Describes how to extend and interact with various features that use the PSI layer, such as code completion, navigation, `Alt+Enter` items, intentions, refactorings, and more. See also the section on Custom Languages below for language-specific features that are only applicable when adding support for a new language. ## Testing Describes the available infrastructure for writing automated tests covering the functionality of plugins. ## Custom Languages Plugins frequently extend support for existing languages, such as adding inspections to Java files. This section describes how to add support to the IntelliJ Platform for a new language that isn't supported by default, creating parsers, syntactic and semantic models, and all the features that build on top. ## Product Specific A lot of the functionalities in the IntelliJ Platform are language and product agnostic. For example, code inspections work the same in Java as they do in Ruby; it is just the syntax trees and semantic information that is different. This section describes product-specific features, such as specific project model differences and how to target them in a plugin. ## Custom IDEs Documents how to use the IntelliJ Platform to create a new, custom IDE, rather than plugins to an existing product, e.g., WebStorm, or Android Studio. ## Themes Describes how to create a theme for IntelliJ Platform-based IDEs. Includes details on how to set up the theme project, customize, build, and publish it on JetBrains Marketplace. ## Resources Links to [useful resources](useful-links.html), a [Glossary](glossary.html), [IntelliJ Platform Extension Point and Listener List](intellij-platform-extension-point-list.html), tips on how to [Explore the IntelliJ Platform API](explore-api.html) and [Learning Resources](learning-resources.html). ## API and Compatibility Information on [Verifying Plugin Compatibility](verifying-plugin-compatibility.html) and list of [backwards-incompatible](api-changes-list.html) API changes as well as [notable changes and new features](api-notable.html) in each major release of the IntelliJ Platform. ## Tooling Reference and usage guides for commonly used tools like the [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html). ## UI Guidelines How to create consistent and usable user interfaces. # Key Topics The IntelliJ Platform is extensive and very capable, and its size and scope can initially be very daunting. This page is intended to list the key topics that a plugin author would be interested in, and provide quick links to the most common extension points. ## Essential Concepts * [Developing a Plugin](developing-plugins.html). * [Testing Overview](testing-plugins.html). * Component model - the IntelliJ Platform is a component-based application and is responsible for creating components and injecting dependencies. Understanding this is necessary for building plugins. * [Extension Points](plugin-extensions.html) - how to register components with extension points, and how to find out what extension points are available. * [Virtual Files](virtual-file.html) - all file access should go through the Virtual File System, which abstracts and caches the file system. It means you can work with files that are on the local file system, in a zip file or are old versions from version control. Tip: See also [Glossary](glossary.html) for a handy reference of common terms. ## Code Model The IntelliJ Platform's code model is called the PSI - the [Program Structure Interface](psi.html). The PSI parses code, builds indexes, and creates a semantic model. ## Common Extension Points The IntelliJ Platform is extremely extensible, and most features and services can be extended. Some common extension points are: * [Action System](action-system.html) - menu and toolbar items * [Code Inspections](code-inspections.html) - code analysis that looks at the syntax trees and semantic models and highlight issues in the editor. * [Intentions](code-intentions.html) - context-specific actions that are available in the `Alt+Enter` menu when the text caret is at a particular location. * [Code Completion](code-completion.html). # Contributing to the IntelliJ Platform SDK This document describes our contribution guidelines for the open-source IntelliJ Platform SDK documentation and sample code. Before you begin contributing content to the SDK, please read this page thoroughly as well as the [Code of Conduct](intellij-sdk-docs-original-code-of-conduct.html) and [License](https://github.com/JetBrains/intellij-sdk-docs/blob/main/LICENSE.txt) documents. For information about contributing to the IntelliJ Platform itself, please visit [Contributing to the IntelliJ Platform](platform-contributions.html). ## Creating IntelliJ Platform SDK Content Content contributions to the IntelliJ Platform SDK are very welcome! Before creating content for a new topic or adding large sections to the existing pages, please create an issue in the [IJSDK YouTrack project](https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=) to ensure we are not working on the same topic, and to agree on the form of the content draft. Checking with us before starting the work will reduce the risk of creating content that is unnecessary and won't be accepted. Please clone the intellij-sdk-docs project from [GitHub](https://github.com/JetBrains/intellij-sdk-docs), make additions or changes, and submit a pull request. Alternatively, start by clicking on the Edit page link on the top of each page. Before creating or altering content, please consult these guides: * [SDK Docs Style Guide](sdk-style.html). This guide describes documentation conventions in terms of content style and syntax. * [SDK Code Sample Guidelines](sdk-code-guidelines.html). Conventions for code sample organization, project settings, and naming conventions are described in this document. # SDK Docs Style Guide This document describes the writing style used in authoring open-source IntelliJ Platform SDK documentation. Before you begin, read this page thoroughly, as well as the [Code of Conduct](intellij-sdk-docs-original-code-of-conduct.html) and [License](https://github.com/JetBrains/intellij-sdk-docs/blob/main/LICENSE.txt) documents. See also [Contributing to the IntelliJ Platform SDK](intellij-sdk-docs-original-contributing.html) for some general remarks. For information about contributing to the IntelliJ Platform itself, visit [Contributing to the IntelliJ Platform](platform-contributions.html). First and foremost, we should keep in mind our audience and their objectives: Someone reading technical content is usually looking to answer a specific question. That question might be broad or narrowly-focused, but either way, our goal is to provide answers without distraction. To verify grammar and correct spelling, it is highly recommended to use [Grazie Professional](https://plugins.jetbrains.com/plugin/16136-grazie-professional) plugin to highlight any issues on-the-fly in the IDE. ## Documentation Markup The documentation project is using [Writerside](https://plugins.jetbrains.com/plugin/20158-writerside), so the plugin should be installed to have full support in the IDE. The topic files themselves are [Markdown](https://github.github.com/gfm/) files (`*.md`) using some Writerside-specific custom tags (see below). ### Page Format Each Markdown file must start with a copyright notice, formatted using HTML comment notation: ```HTML ``` It must be followed by a header defining its title using a level 1 heading: ```MD # Contributing to the IntelliJ Platform SDK ``` The page title should be as concise as possible, so it can be reused in the [Table of Contents](#table-of-contents) as is. #### Excerpt Every page should provide a short excerpt (usually one sentence) using the dedicated `` tag before the main page contents: ```HTML Defining groups of related settings. ``` #### Highlighted Links A page can highlight related topics and other important links before the actual content using the `` tag. Links must be grouped using "Bold Category Name: link1, link2, \[...]" ([Example](language-and-filetype.html)). Use Reference to link to other topics, Code to link to code/files, UI Guidelines for links to [UI Guidelines](ui-guidelines-welcome.html), and Product Help for links to [IntelliJ IDEA Help](https://www.jetbrains.com/help/idea). #### Introductory Text Do not use headings like Introduction, Overview, etc. for any introductory text. ## Content Style ### Terminology Consistent terminology helps the reader grasp new concepts more quickly: * The open-source code in the GitHub repository `intellij-community` is known as the IntelliJ Platform. Use the full phrase in the SDK documentation. * IDEs based on the IntelliJ Platform are described as IntelliJ Platform-based IDEs. Once that term is used on a page, authors may use IDEs. * When referring to JetBrains products, always use the full name such as IntelliJ IDEA Ultimate Edition. However, only use product names when extensibility or functionality is particular to a product. Tip: Do not use TODOs TODO/todo comments are discouraged in the main branch of `intellij-sdk-docs`. There are always exceptions, but the best practice is to resolve all TODOs before the final review. If immediate resolution isn't possible, write a YouTrack [SDK Issue](https://youtrack.jetbrains.com/issues/IJSDK) that captures the TODO and remove the comment from the document. ### Text Format Conventions Start every sentence on a new line. For very long sentences, add additional line breaks after `,`, `:` or other sensible places. Very long [links](#links) should also be on a separate line. Consistent text styles are used to standardize references and keywords: * Menu paths are wrapped using `` with pipe characters separating each level: `Settings | Editor`: `Settings | Editor` Inside tables, use `|` instead of `|` to prevent escaping problems. * User interface element names like labels, buttons, checkboxes, etc. are wrapped using ``: `Press Continue`: Press Continue * Non-code keywords and quotations, or the names of non-code files are formatted as italic style: _Theme_ (Theme), _README.md_ (README.md.) Examples of this file type include LICENSE.txt and README.md. * Code keywords and class names are formatted as code style: `interface`: `interface`, `AnAction`: `AnAction`, `name` attribute: `name` attribute. * Filenames are wrapped using ``: `build.gradle.kts` `build.gradle.kts`. * File formats are shown as all capital letters: PNG and XML. * Filename extensions are not capitalized when part of a full filename, path, or URL: `plugin.xml`. * When using $PLACEHOLDER$ in non-code parts, it must be escaped: `\$PLACEHOLDER\$/somePath`. * Keyboard shortcuts are wrapped using ``: `press Alt+Insert` becomes "press `Alt+Insert`". See [Guidelines for Highlighting Syntax](#guidelines-for-highlighting-syntax) for best practices for representing code fragments. See [Links to IntelliJ Platform Source](#links-to-intellij-platform-source) for more details about representing names of source files in links. ### Links Links are handled as standard Markdown links and can be anchored to external sites, pages within the site, or headings in the pages. When a Markdown header is converted to an HTML header, it is assigned an ID so that it can be linked. For example, `## Basics` gets the ID of `basics`, and can be linked either on the same page or cross-page as described below. In some cases (e.g., the same heading text appears multiple times on the same page), it is necessary to specify a distinct ID manually: ```MD ## Task 1 ### Properties {#task1-properties} [...] ## Task 2 ### Properties {#task2-properties} [...] ``` #### General Links General Markdown links have the default Markdown link style: * `[Gradle](https://gradle.org)` ([Gradle](https://gradle.org)) links to an external site, such as companies, articles, etc. If the URL contains a `%` character, append `{ignore-vars="true"}`. * Linking to pages and page sections within the SDK documentation: * `[Page Title](page.md)` or `[](page.md)` (use page title as link text) links to an SDK doc page (all located under `/topics`). Note that the extension is `.md`, NOT `.html`. * Specific sections on pages in the SDK documentation are linked by using section anchors. The anchor name will be all lower case, and spaces are replaced with `-`, e.g. `## Page setup` becomes `#page-setup`. Once the anchor (`#`) character of the link is entered, the IDE code completion feature shows the available sections. * `[Link to a section on the current page](#another-section)` links to a heading on the current page. * `[Link to the section on another page](other_page.md#another-section)` links to a heading on another page. If the desired link label is the same as an SDK doc page or section title, leave the label part empty, e.g., `[](test-page.md)` or `[](test-page.md#section-1)`. The empty link label will be automatically filled with the actual page or section title. #### Links to IntelliJ Platform Source Links to files in the IntelliJ Platform ([intellij-community](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/README.md)) repository use the `%gh-ic%` prefix instead of the full URL to the repository. Links to files in source code packages in other GitHub repositories follow the same rules, except the links use a different custom `gh-...` prefix defined in `v.list`. * `[README.md](%gh-ic%/README.md)` links to any general, non-code information files. ([README.md](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/README.md)) Examples of this file type include LICENSE.txt and README.md. * `[`IdeaPlugin.xml`](%gh-ic%/community-resources/resources/META-INF/IdeaPlugin.xml)` links to declarative source code files, use `code` style. ([IdeaPlugin.xml](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/community-resources/resources/META-INF/IdeaPlugin.xml)) Examples of this file type include: `settings.gradle`, `plugin.xml` or `theme_basics.theme.json`. * `[``AnAction``](%gh-ic%/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java)` links to source files for code objects like interfaces and classes, use `code` style but without the file extension. ([AnAction](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java)) Examples of this file type include Java and Kotlin sources. * Note the use of `` characters surrounding the class name in the link. * When linking to an API in this manner, the FQN isn't necessary in the link. * No file extension (*.java, *.kt, *.py, etc.) is used by convention. * Be judicious when using such links. Generally, only one link is needed for a given file on a documentation page. ### Guidelines for Highlighting Syntax In-paragraph code fragments and IntelliJ Platform APIs are formatted according to the following rules. #### Code * Avoid using qualifiers like "`Foo` interface" or "`Foo` abstract class". Instead, refer to `Foo`. * The FQN is used for the first reference to an interface, class, or package on a page. Rather than `AnAction`, introduce it as `com.intellij.openapi.actionSystem.AnAction`. Later references on the page can be `AnAction`. Exception: the FQN is not used with a GitHub [link](#links). * Method names always use empty parentheses: "call `bar()` to apply." Method names are prefixed with the class/interface name when needed for clarity: `Foo.bar()`. #### Extension Points Use `` snippet to introduce EP: * [com.intellij.annotator](https://jb.gg/ipe?extensions=com.intellij.annotator) extension point Use `` for variant without "extension point" suffix: * [com.intellij.annotator](https://jb.gg/ipe?extensions=com.intellij.annotator) #### XML For XML elements, use the tag notation with syntax highlighting: ``. Attributes are shown with syntax highlighting, and attribute values are shown in quotes: `since-build="191"` ### Source Code Source code is represented by using code fences, which are three backticks. Syntax highlighting is applied by specifying the language after the first set of ticks: ``` ```xml XML Text ``` ``` Supported languages include `xml`, `java`, `kotlin`, `groovy`, `bash`, `md`, `php`, and `text` for plaintext. Note: Source code blocks must have one blank line before and after them and must have a language specification for highlighting (use `text` as fallback). Whole files can be imported on a page using the `src` attribute after code fences specifying the full path relative to the `code_samples` root folder. `{src="simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFoldingBuilder.java"}` The advantage is the code can come from the `code_samples` directory, so it will be live code that isn't silently stale. The disadvantage is the file may contain a large class, too large for the documentation page to be useful. If possible, use `include-symbol="ClassName"` to show only the class body without any headers and imports. To include only a specific method, specify `include-symbol="methodName"` additionally. In any case, keep code samples concise and avoid any unnecessary "surrounding" code or import statements. ### Tables The syntax is to use the pipe (`|`) and hyphen symbols: ```MD | Column 1 | Column 2 | Column 3 | |----------|----------|----------| | Blah | Blah | Blah | ``` Use `|` instead of `|` to prevent escaping problems for `` elements inside cells. Use `

Line 1

Line 2

` for multi-line content in a cell. ### Notes and Callouts Notes and callouts can be specified using the blockquote syntax. The converter looks at the `type` attribute specified after the text block. If so, it applies a callout style. The example below will be displayed as a callout, styled as a "note": ```MD > This is a simple note. > {style="note"} ``` Note: This is a simple note. The styles available for callouts are: * `tip`—Information that makes the reader more productive (Default). * `note`—Information that is important for the reader to understand. This callout is reserved for essential points and concepts. * `warning`—Information that is critical for the user to understand to prevent failures or errors. Complex callouts can also specify the `title` attribute: ```MD > This is a note. > We have a lot of text. > Don't make everyone read it fully by adding a good title. > {title="A useful title"} ``` Tip: A useful title This is a note. We have a lot of text. Don't make everyone read it fully by adding a good title. ### Images Every page typically has a dedicated subdirectory within root `/images`. Images in this documentation are generally screenshots. For consistency, images should be 296, 460, or 706 pixels wide. The preferred image format is PNG at a resolution of 144 DPI. A resolution of 72 DPI is acceptable but may look blurry on high-resolution monitors. Use [Window Resizer](https://plugins.jetbrains.com/plugin/18045-window-resizer) plugin for exact resizing of the IDE application window. It is crucial to reduce the size of image files to prevent bloating the repository and impacting the performance of the documentation site. Optimize the image files using a tool such as the [PNG optimizer](https://plugins.jetbrains.com/plugin/7942-png-optimizer) plugin. Images are embedded in a document by adding a Markdown link to the image like so: ```MD ![Alt text](image.png) ``` If the width of an image needs to be adjusted, it can be specified as follows: ```MD ![Alt text](image.png){width="42"} ``` #### Zoom Popup Images too big to fit into the main content can have + overlay control to open a popup with the "zoomed" variant. PNG: For PNG images, provide an additional zoomed variant `image.zoomed.png` with this notation: ```MD ![Alt text](image.png){thumbnail="true"} ``` SVG: For SVG images, use this notation: ``` ![Alt text](image.svg){thumbnail-same-file="true"} ``` ### Other Use a concise and neutral voice when describing topics: * Avoid: "A custom icon for files with a substituted language would be a great addition to the plugin. It is easy as implementing the `xyz` extension point." * Prefer: "To add a custom icon for files with a substituted language, implement the `xyz` extension point." Avoid directly addressing the reader and the author with "you", "your", "we", etc. * Avoid: "Add a dependency in your plugin descriptor." * Prefer: "Add a dependency in the plugin descriptor." ## Table of Contents The table of contents for the site is displayed in the tree view on the left side of the site, and it is generated from the `ijs.tree` file. The list can have nested items, which are displayed as child items in the table of contents. If absolutely required, overriding the page title text to show in the table of contents is possible via the `toc-title` attribute. ### Placeholders If a node does not have its `id` attribute specified, it will still appear in the table of contents but will be greyed out and not clickable. It acts as a placeholder for a documentation item. A placeholder is useful to keep track of what should be documented but hasn't yet, and can be helpful to show readers that the topic exists but isn't yet documented (Pull Requests always welcome!). ### Redirects When renaming pages, redirects must be configured so existing bookmarks continue working. All existing links in other topics must be updated. Specify the previous path(s) including `.html` extension in `accepts-web-file-names` attribute: ```XML ``` # SDK Code Sample Guidelines This document describes the coding guidelines used for authoring open-source IntelliJ Platform SDK code samples. Before you begin, please read this page thoroughly, as well as the [Code of Conduct](intellij-sdk-docs-original-code-of-conduct.html) and [License](https://github.com/JetBrains/intellij-sdk-docs/blob/main/LICENSE.txt) documents. For information about contributing to the IntelliJ Platform itself, visit [Contributing to the IntelliJ Platform](platform-contributions.html). Tip: See also [Code Samples](code-samples.html) for an overview. ## Objectives Keep the following considerations in mind while authoring an SDK code sample: * The purpose of an SDK sample is to demonstrate an implementation pattern of the IntelliJ Platform. * SDK code samples are instructional code, intended to teach. * Instructional code differs from production code in some key aspects: * Sacrifice some robustness in the interest of simplicity and brevity. Use error checking where it is necessary to make a point about an implementation pitfall. * Keep implementations as simple as possible, but use the full features of the IntelliJ Platform, programming language, and libraries. * Use meaningful names for interfaces, classes, fields, methods, and extension points. * Write instructional Javadoc comments. * Code samples replace lots of documentation. * Aim for two levels of SDK samples: * A basic sample is focused on a particular subject by demonstrating a limited area of the IntelliJ Platform. It should show all the components and architecture but ultimately accomplish something elementary. For example, demonstrate persistence by storing only a `String`. * An advanced sample demonstrates how different parts of the IntelliJ Platform integrate and work together, such as combining inspections or intentions with non-trivial PsiTree manipulation. Ultimately, the goal is to provide developers with roadmaps for implementing functionality in an IntelliJ Platform-based plugin. Each roadmap should contain: * Pointers to SDK documentation about the IntelliJ Platform APIs needed to implement the functionality. * Pointers to relevant basic SDK sample plugins. * Pointers to related advanced SDK sample plugins. ## Plugin Copyright Statements Use the standard intellij-community copyright notice in all sample plugins authored by JetBrains: ```TEXT Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. ``` Note: The copyright statement must appear at the top of every source file. Use the [IntelliJ Platform SDK](https://github.com/JetBrains/intellij-sdk-docs/tree/main/.idea/copyright) copyright profile. ## Directory Naming Conventions for SDK Plugins For basic samples, the plugin directory name is derived from the IntelliJ Platform extension points demonstrated. For example, `foo_basics` where foo corresponds to an IntelliJ Platform framework, extension point, or component. Some naming examples include `facet_basics` and `editor_basics`. There is only one basic sample per IntelliJ Platform API area. For advanced code samples, the name should reflect the complex functionality delivered by the plugin rather than the IntelliJ Platform APIs. Advanced samples will be cross-referenced to the IntelliJ Platform APIs demonstrated in the sample. The only symbol characters allowed in the name of a plugin root directory are underscores, such as `foo_basics` or `max_opened_projects`. However, underscores should not be used in any other symbol names, such as `Artifact ID`, plugin ID, package names, etc. Instead, concatenate a long name into camelCase such as `maxOpenedProjects`. ## Group and Artifact ID When creating a Gradle-based IntelliJ Platform plugin, the plugin's Maven coordinates (`Group ID`, `Artifact ID`, `Version`) are defined. The `Group ID` for SDK plugins is always `org.intellij.sdk`. The `Artifact ID` is a brief derivative of the plugin directory name. An `Artifact ID` should not contain symbols. For basic code samples, it is not necessary to include "basic" in the `Artifact ID`. For example, the `foo_basics` directory name would have the `Artifact ID` `foo`. A plugin with a longer directory name, such as `conditional_operator_intention`, could have the more succinct `Artifact ID` of `conditionalOperatorIntention`. (For legacy reasons, the `conditional_operator_intention` plugin uses a more concise `Artifact ID`.) ## Plugin ID Conventions The plugin ID appears between [<id>](plugin-configuration-file.html#idea-plugin__id) elements in the `[plugin.xml](plugin-configuration-file.html)` file. In general, the plugin ID is the `Group ID` concatenated with the `Artifact ID`. For example, a plugin like `facet_basics` has the plugin ID `org.intellij.sdk.facet`. Plugin IDs do not contain underscores. ## Plugin Package Names Packages in plugins should begin with the plugin ID. If there is only one package in a plugin, then the package name is the same as the plugin ID. Additional suffix words, separated by "." characters, can be added to form more specific package names. Package names do not contain underscores. ## Plugin Directory Structure SDK sample code should have a standard directory footprint. The standardized structure not only makes the samples simpler to navigate and understand, but it builds on the default Gradle plugin project structure. Note that directories below the plugin root folder should not have underscore characters, and should use camelCase if needed. The following is an example directory structure for a `foo_basics` plugin. ```TEXT code_samples/ foo_basics/ gradle/ src/ main/ java/ org.intellij.sdk.foo/ icons/ SdkIcons.java # The standard SDK icon class resources/ icons/ sdk_16.svg # The standard SDK icon for menus, etc. META-INF/ plugin.xml # The plugin configuration file pluginIcon.svg # The standard SDK plugin icon test/ # Omit if there are no tests java/ org.intellij.sdk.foo/ resources/ build.gradle.kts gradlew gradle.bat settings.gradle.kts README.md ``` ## Gradle Build Script Conventions SDK code samples must be developed [using Gradle](creating-plugin-project.html). As of this writing, the use of Gradle in SDK code samples still relies heavily on the `plugin.xml` for specifying the plugin configuration. At a later, second phase, the SDK code samples will transition to rely more on the Gradle configuration. The default contents of a Gradle build script file are produced by the [New Project Wizard](creating-plugin-project.html#create-ide-plugin). A consistent structure for an SDK code sample's Gradle build script file is essential for clarity and is based on the default produced by the project wizard. Comments in SDK code sample Gradle build scripts should only draw attention to the parts of the Gradle configuration that are unique for a plugin. For SDK code samples, a few alterations are needed to the default `build.gradle.kts` file produced by the plugin wizard: * Maintain the Gradle properties `version` (`project.version`) and `group` (`project.group`). See the [Plugin Gradle Properties](creating-plugin-project.html#plugin-gradle-properties-and-plugin-configuration-file-elements) section for how these Gradle properties relate to the elements in `plugin.xml`. * Add the following statement to the [Patching DSL](tools-gradle-intellij-plugin.html#tasks-patchpluginxml) (`patchPluginXml {...}`) section: ```KOTLIN // Patches value in plugin.xml version.set(project.version) sinceBuild.set("221") untilBuild.set("223.*") ``` ## plugin.xml Conventions A consistent structure for the `plugin.xml` [configuration file](plugin-configuration-file.html) of an SDK code sample is important because we want to draw attention to the unique parts of the file for a plugin. Comment profusely about unique elements and configurations, and comment sparingly for the rest. The sequence of elements in an SDK code sample `plugin.xml` file is: * [<id>](plugin-configuration-file.html#idea-plugin__id) Use the fully qualified [Plugin ID](#plugin-id-conventions). * [<name>](plugin-configuration-file.html#idea-plugin__name) The name value does not have to match the [Plugin Directory Name](#directory-naming-conventions-for-sdk-plugins). The name is used for display purposes and should reflect the functionality of the plugin. The name must start with "SDK: ". * [<version>](plugin-configuration-file.html#idea-plugin__version) The code sample's version in MAJOR.MINOR.FIX format. * MAJOR corresponds to a significant upgrade in functionality. * MINOR corresponds to minor refactoring and small improvements in functionality. * FIX corresponds to changes that fix problems without significant refactoring. * [<depends>](plugin-configuration-file.html#idea-plugin__depends) Include at least one dependency with the module `com.intellij.modules.platform` to indicate basic plugin compatibility with IntelliJ Platform-based products. Add `` elements containing module FQNs as needed to describe more specialized [Compatibility with Multiple Products](plugin-compatibility.html), and any other [Plugin Dependencies](plugin-dependencies.html). * [<description>](plugin-configuration-file.html#idea-plugin__description) is a concise explanation of what is being demonstrated and how a user would access the functionality. If the plugin by default overrides IDE behavior (such as `tree_structure_provider`) it must be noted in the description. * [<change-notes>](plugin-configuration-file.html#idea-plugin__change-notes) is an ordered list by version numbers with a brief description of changes for each version. * [<vendor>](plugin-configuration-file.html#idea-plugin__vendor) Set the value to `IntelliJ Platform SDK`. Set the attributes: * `email` omit this attribute. * `url` to the JetBrains Marketplace `"https://plugins.jetbrains.com"` * The remainder of the [plugin configuration elements](plugin-configuration-file.html) should only appear if they are needed by a specific plugin. ## README File Each code sample provided within the IntelliJ Platform SDK should contain a README file describing the sample purpose and its content. The [SAMPLE_README.md](https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/SAMPLE_README.md) file contains a template that should be used as an initial draft for further writing. Each `README.md` file is supposed to have the same structure for better navigation and readability: * A header with the link to the main IntelliJ SDK documentation and a page that the sample refers to. * Quickstart section briefly describes the sample's purpose and significant parts of the IntelliJ SDK it implements. * Extension Points section (if implements) with a listing of implemented extension points, links to the implementation classes, and names of extended classes. * Actions section (if implements) with a listing of implemented actions, action IDs, links to the implementation classes, and names of base action classes. * Listeners section (if implements) with a listing of implemented application- or project-level listeners, links to the implementation classes, and names of base listener classes. * Each link that appears in the documentation has to be listed on the very bottom of the file with the clear link ID and proper prefix depending on the link context (`docs:`, `file:`, etc.). ## Testing IntelliJ Platform SDK code samples should be built and tested against the `since-build` version. Code samples should build cleanly, with no warnings or errors, and new code samples should pass all default IntelliJ IDEA code inspections. Testers should complete the following checklist. Here the term "IDE" means the IntelliJ Platform-based IDE in which the plugin is designed to run: * The plugin should load in the IDE. * The correct information about the plugin should display in the `Settings | Plugins` panel. * If applicable, the plugin UI, such as tool windows, menu additions, etc. should display correctly. * The functionality of the plugin should be tested with a sample file. * If applicable, the plugin should pass unit tests. # Code of Conduct The JetBrains Open Source and Community Code of Conduct. This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://github.com/jetbrains#code-of-conduct). Please make sure you read it. # Code Samples This guide comes with a number of sample plugins available from dedicated [intellij-sdk-code-samples](https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/) GitHub repository. Please see `README.md` which lists all available code samples with a short description. Each sample is stored in a dedicated folder and is accompanied by its own `README.md`. Links to the corresponding tutorial or reference page in this tutorial, as well as a list of relevant show-cased elements are provided. ## Using Gradle All sample plugins are based on Gradle, see [Creating a Plugin Gradle Project](creating-plugin-project.html) to get started. Additionally, the [Working with Gradle in IntelliJ IDEA](https://youtu.be/6V6G3RyxEMk) screencast offers a thorough introduction to Gradle functionality inside IntelliJ IDEA. ## Setting up Code Samples Make sure plugins Git, Gradle, and Plugin DevKit are enabled. Warning: Installing Plugin DevKit plugin Plugin DevKit plugin must be installed from JetBrains Marketplace ([Plugin Homepage](https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is not bundled since version 2023.3. Clone the [intellij-sdk-code-samples](https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/) GitHub repository via Git | Clone.... After successful cloning, the IDE suggests opening the project. Select the code sample(s) to import via the [Gradle tool window](https://www.jetbrains.com/help/idea/gradle.html#link_gradle_project). Alternatively, import all code samples available by choosing `_gradleCompositeBuild`, which links all Gradle projects in a Composite Build. After successful import, the project appears in the Gradle tool window tree as a new node. Assign a Java 17 SDK in `Settings | Build, Execution, Deployment | Build Tools | Gradle` for Gradle JVM. Invoke Reload All Gradle Projects from the Gradle tool window toolbar if necessary. ## Running Code Samples Run the plugin by using the Gradle [runIde](creating-plugin-project.html#executing-the-plugin) task shown under the corresponding project's Tasks node in the Gradle tool window. # Getting Help Note: This page is also available in Chinese (switch Language in the top bar). 本页面也提供简体中文版本,请在页面右上方切换 Language 为简体中文。 Missing some documentation? Or got stuck developing your plugin? See all the ways of obtaining support below for each case. Please use English in all communication. ## 获取帮助 文档不全?或者开发插件的时候被问题卡住了?请阅读下面各种情况下获得支持的所有方式。 请在所有的交流中(邮件,Bug 报告等)使用英语。 ## Problems with Code - Support Issues Tip: Please see [Explore the IntelliJ Platform API](explore-api.html) for tips & tricks. For questions, problems, or other discussions about plugin development itself (rather than the content of this guide), use our [JetBrains Platform](https://platform.jetbrains.com) community Discourse forum. The forum is monitored by JetBrains team members. Of course, all raised questions will be used to try and improve this guide. ## 代码方面的问题-支持问题 Tip: 请参考 [插件开发常见问题问答](faq.html) 和 [探索 IntelliJ Platform API](explore-api.html) 来获取相关技巧和窍门。 对于与插件开发本身有关的问题,难题或者讨论(和本指南内容无关),请使用我们的 [JetBrains Platform](https://platform.jetbrains.com) 社区论坛。请注意这个论坛只支持英文沟通, 请不要在帖子和聊天中使用中文。 JetBrains团队成员会关注此论坛。 当然,所有收到的问题都将用于尝试和改进本指南。 ## Problems with the Guide If you're having problems with the guide itself, such as missing, incorrect, or confusing content, please [raise an issue on YouTrack](https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=). If the problem is easily solved, you can also submit a [Pull Request](intellij-sdk-docs-original-contributing.html). If you want to report (smaller) issues for a specific page, you can also use the Feedback widget (visible on the right) by providing more details after clicking the No button. Please try to be as specific as possible and provide your e-mail address for further questions and getting notified of updates. If you just want to share feedback on the guide, again, [raise an issue](https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=), even if it's for a discussion, ideas on improvements or suggestions. Note: Please don't use the IJSDK YouTrack project for plugin development or [product support](#problems-with-products) requests – it's intended for the SDK and the documentation. ## 本指南的问题 如果你在使用本指南的时候遇到问题,例如内容缺失,错误或者内容混乱,请在 [YouTrack上提交问题](https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=)。如果问题很容易得到解决,你也可以提交 [拉取请求](intellij-sdk-docs-original-contributing.html)。 如果你想报告特定页面的(小)问题,你还可以使用页面右侧的 Feedback 小组件,方法是点击 No 按钮后提供更多详细信息。请尽量具体的描述您的问题并提供您的电子邮件地址,以便于后续问题沟通并在更新时获取通知。 如果你只是想对本指南提供反馈,同样也是通过[提交问题](https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=)来进行,即便只是为了讨论,提出改进的想法或建议。 Note: 请不要使用 IJSDK YouTrack 项目来发起插件开发或者[产品支持](#problems-with-products-CN)请求 — 这个项目的本意是关于SDK和文档的。如果使用下列方式之一,你会更好的得到响应。 ## Problems with Products If you have a problem with an IntelliJ Platform-based product, such as IntelliJ IDEA, WebStorm, Rider, etc., please use any of the regular [Product Support](https://www.jetbrains.com/support/) channels. ## 产品问题 如果在使用基于 IntelliJ 平台的产品(例如 IntelliJ IDEA、WebStorm、Rider 等)时遇到问题,请使用任何常规[产品支持](https://www.jetbrains.com/support/)渠道。 # Content Updates This page lists notable additions and updates to the SDK documentation and [Code Samples](code-samples.html). See [GitHub Changelog](https://github.com/JetBrains/intellij-sdk-docs/commits/main) ([RSS](https://github.com/JetBrains/intellij-sdk-docs/commits/main.atom)) for a detailed changelog. Tip: Staying up to date [Subscribe to Marketplace Developer News](https://jb.gg/mp-updates) to receive news and announcements. Also follow [@platform.jetbrains.com](https://bsky.app/profile/platform.jetbrains.com) on Bluesky (or [JBPlatform](https://x.com/JBPlatform) on X) and visit [JetBrains Platform Blog](https://blog.jetbrains.com/platform/) and [JetBrains Marketplace on LinkedIn](https://www.linkedin.com/showcase/jetbrains-marketplace/). ## 2026 ### March Split Mode (Remote Development) : Add a new section about [Split Mode and Remote Development](split-mode-and-remote-development.html). Top-Level Notifications : Describe [notification balloons](notification-balloons.html) showing relevant information without interrupting the developer's flow. ### January Coroutines on EDT and Locks : Removed most of the content from the [Coroutines on EDT and Locks](coroutine-edt-and-locks.html) page. The approach described there is obsolete as it was solved internally in the IntelliJ Platform. The page and the error pattern are preserved to confirm that no fixes are needed in plugins. ## 2025 ### November Islands Theme Support : Add a new section about [supporting the Islands Theme](supporting-islands-theme.html). Terminal API : Add a new section about [extending the Terminal](embedded-terminal.html). Modular Plugins : Add a new section about [Modular Plugins (Experimental)](modular-plugins.html) describing how to develop plugins ready for remote development contexts. Threading Kotlin Notebooks : Link [Kotlin Notebooks](threading-model.html#kotlin-notebooks) explaining core threading model concepts. Minor Changes and Additions : * Add [Using Custom Icons from the Dotnet Rider Plugin Part](rider.html#using-custom-icons-from-the-dotnet-rider-plugin-part) Using Custom Icons from the Dotnet Rider Plugin Part. * Update [LSP supported features](language-server-protocol.html#supported-features). ### October PSI Performance Introduction : Describe why [caching get method results](psi-performance.html#overview) is important when working with PSI. Avoiding `idea-version@until-build` : Add information about recommendation to [avoid using idea-version@until-build](plugin-configuration-file.html#idea-plugin__idea-version) for plugins. Minor Changes and Additions : * [Extending existing themes](themes-customize.html#extending-themes). * Clarify [reacting to closing non-modal dialogs](dialog-wrapper.html#displaying-the-dialog). * Add an [inspection code sample for Qodana](https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/code_inspection_qodana). * Hide the [Kotlin UI DSL Version 1 section](kotlin-ui-dsl.html) and make [Version 2](kotlin-ui-dsl-version-2.html) the default version in content. * Add a link to the [Investigating UI Freezes](threading-model.html#investigating-ui-freezes) blog post. ### September LSP Module Introduction : Add information about specifying a required [dependency on the LSP module](language-server-protocol.html#pluginxml) since 2025.2.1. ### August Minor Changes and Additions : * Add information about launching coroutines from actions with [currentThreadCoroutineScope](launching-coroutines.html). ### July Kotlin Notebook integration : Add a new section about [Kotlin Notebook Integration](tools-kotlin-notebook.html). Accessibility Guidelines : Add information about making UI more [accessible](accessibility.html). ### June Replacement of the Web Symbol API with Poly Symbol API : Replace the Web Symbol API documentation with [Poly Symbols](polysymbols.html). ### May New Project Wizard : Add a new section about implementing project wizards: [New Project Wizard API](new-project-wizard.html). Minor Changes and Additions : * Add [Editors FAQ](editors.html#editors-faq) section. * Extension Points and Listeners are now grouped per plugin, for example, [Java Plugin](intellij-community-plugins-extension-point-list.html#java-plugin). * Revise and expand [Code Completion](code-completion.html), [Working with Icons](icons.html), [UI FAQ: Icons](ui-faq.html#icons) sections. * Add [Debugger Entry Points](explore-api.html#debugger-entry-points) allowing identifying code responsible for invoked actions. ### March Integration Testing : Add new section [Integration Tests](integration-tests.html). Minor Changes and Additions : * Document [Action ID Code Insight](action-system.html#action-id-code-insight) added in 2025.1. ### February Support Channel Update : Updated support channels to the new [JetBrains Platform](https://platform.jetbrains.com) community forum. See the [The JetBrains Platform Gets a New Community Space](https://blog.jetbrains.com/platform/2025/02/the-jetbrains-platform-gets-a-new-community-space/) blog post for more context. Revamped Threading and Execution Context topics : Reorganized [threading](threading-model.html) and [coroutines](kotlin-coroutines.html) topics structure. Updated [Execution Contexts](execution-contexts.html) with the information about contexts available since 2024.2. ### January Coroutines on EDT and Locks : Added a [page](coroutine-edt-and-locks.html) describing the current implicit locking behavior on EDT launched from coroutines and planned changes with migration hints. ## 2024 ### December Live Templates Configuration File : Added a page describing the structure and elements of [live templates configuration files](live-templates-configuration-file.html). ### August Workspace Model : [Workspace Model](workspace-model.html) represents the project's structure and all its elements and replaces the existing [Project Model](project-model.html). Minor Changes and Additions : * Updated list of [supported features](language-server-protocol.html#supported-features) for Language Server Protocol (LSP). ### July Threading Model : Revamp the [Threading Model](threading-model.html) page (formerly General Threading Rules) and add a new page describing [background processes](background-processes.html), including cancellation and progress tracking. Coroutines Read Actions : Added FAQ section and explanation of [why suspending inside the block is not allowed](coroutine-read-actions.html#why-can-t-i-suspend-inside-the-block). ### June Code Formatter : Rework [Code Formatter](code-formatting.html) page, extending explanations and updating content. ### May Minor Changes and Additions : * How to support [grammar checks](spell-checking.html#grammar-checks) provided by Grazie plugin in custom languages. * How to provide [code vision provider](inlay-hints.html#code-vision-provider) name and description in the settings. * How to manage [Poly Symbols context](polysymbols-context.html) detection. ### April Plugin Internationalization : Add [Internationalization](internationalization.html) and [Providing Translations](providing-translations.html) pages describing IDE and plugin translation possibilities and best practices. Minor Changes and Additions : * How to mark functionality available during indexing via [DumbAware API](indexing-and-psi-stubs.html#DumbAwareAPI). * Move Extension Point and Listener Lists to the Resources section and split the main Extension Point and Listener List into: IntelliJ Platform, IntelliJ Platfom Plugins, and Android Plugin. ### March Kotlin Coroutines : Add [Kotlin Coroutines](kotlin-coroutines.html) describing how to write asynchronous code in an imperative style. Minor Changes and Additions : * Add [documentation](plugin-extension-points.html#error-handling) on how to handle errors and deprecations in extensions. * Note changes in how highlighting is now [performed more efficiently](syntax-highlighting-and-error-highlighting.html#order-of-running-highlighting) in 2024.1. ### February IntelliJ Platform Gradle Plugin 2.x (Early Access Preview) : Add [documentation](tools-intellij-platform-gradle-plugin.html) for the next generation of Gradle tooling for plugin development. ## 2023 ### December JCEF : Revamp [JCEF (Java Chromium Embedded Framework)](embedded-browser-jcef.html) page. User Interface FAQ : Added [User Interface FAQ](ui-faq.html). Language Server Protocol (LSP) : Added [Language Server Protocol (LSP)](language-server-protocol.html). ### November Minor Changes and Additions : * Add information about [executing actions programmatically](action-system.html#executing-actions-programmatically). * Please see [Attaching Sources](tools-gradle-intellij-plugin.html#attaching-sources) on how to set up 2023.2/3 IDEs for Gradle plugin projects. ### October Kotlin Persisting State Component : Add an [example](persisting-state-of-components.html#implementing-the-persistentstatecomponent-interface) of a persistent state component implemented in Kotlin. Minor Changes and Additions : * Add section [Storing a Reference to a Module](module.html#storing-a-reference-to-a-module). ### September Run Configuration Macros : Add [Macros](run-configurations.html#macros) section describing how to support dynamic expandable values in run configuration settings. Inlay Hints : Update [Inlay Hints](inlay-hints.html) page with the information about new APIs. Threading Model : Update [Threading Model](threading-model.html) to reflect changes in 2023.3 platform. Minor Changes and Additions : * Add information about ordering [quick fixes](code-inspections.html#quick-fix-implementation) and [intentions actions](code-intentions.html#techniques-used). * Clarify the information about [declarative inlay hints](inlay-hints.html#declarative-inlay-hints-provider) customization possibilities. ### July Run Configurations : Describe techniques for [simplifying run configuration settings editors](run-configurations.html#simplifying-settings-editors). ### June Execution : Rework [Execution](execution.html), [Run Configurations](run-configurations.html), and [Run Configurations Tutorial](run-configurations-tutorial.html) pages. Minor Changes and Additions : * Clarify [the syntax highlighting](testing-highlighting.html#syntax-highlighting) test file format and test implementation initial approach. * Clarify referencing icons by paths and icon holder class constants in [Working with Icons](icons.html). * Add information about requirements for persistent state components to be included in [the Settings Sync plugin synchronization mechanism](persisting-state-of-components.html#backup-and-sync-plugin). ### April Documentation : Rework [Documentation](documentation.html) page and adapt it to the new `DocumentationTargetProvider` framework. ### March Poly Symbols : Add [Poly Symbols](polysymbols.html) documentation, which is a framework that simplifies web technology development by utilizing the [Symbols](symbols.html) API and supporting custom syntaxes. Open Source Plugins Extension Points : Added [Open Source Plugins Extension Point and Listener List](oss-plugins-extension-point-list.html) for plugins bundled with [IntelliJ IDEA Plugin Development](idea.html) and other IDEs. ### February Inspection Options : Add a section on [Inspection Options](inspection-options.html), which allows extending inspection behavior based on the input provided by user at runtime. Minor Changes and Additions : * Add section on [New UI Icons](icons.html#new-ui-icons). * Document [RuntimeDictionaryProvider](spell-checking.html#runtimedictionaryprovider) EP for spellchecking. ### January Minor Changes and Additions : * Update IDE Support section in [Verifying Plugin Compatibility](verifying-plugin-compatibility.html). * UI Inspector: update [Specific Component Properties](internal-ui-inspector.html#specific-component-properties) and add section [Inspecting Settings](internal-ui-inspector.html#inspecting-settings). ## 2022 ### December Intention Preview : Add information about how to prepare intentions to show [Intention Action Preview](code-intentions-preview.html). Minor Changes and Additions : * Add information for new [Cell.align](kotlin-ui-dsl-version-2.html#cell-align) methods in Kotlin UI DSL Version 2. ### November Plugin User Experience : Add a new section about how to improve [plugin UX](plugin-user-experience.html) and overall plugin quality. Minor Changes and Additions : * Add information about threading in Actions in [Principal Implementation Overrides](action-system.html#principal-implementation-overrides). ### October Minor Changes and Additions : * Add information about [sharing settings](persisting-state-of-components.html#sharing-settings-between-ide-installations) between different IDEs installations. ### September Extract "Themes" part : All the content related to [themes customization](theme-structure.html) and creating a project using the [DevKit approach](developing-themes.html) has been moved to a new [Themes](themes-getting-started.html) part. Content has been refreshed to match the current state of the project and SDK wizards. Spell Checking : Add [Spell Checking](spell-checking.html) section with an [accompanying tutorial](spell-checking-strategy.html) showing how to implement a spell checking for a custom language. Minor Changes and Additions : * Add descriptions for the following EPs to [Additional Minor Features](additional-minor-features.html): [Prevent Error Highlighting of Files](additional-minor-features.html#prevent-error-highlighting-of-files), [Provide Fully Qualified Names (FQN) for Elements](additional-minor-features.html#provide-fully-qualified-names-fqn-for-elements), [Label Files as Test Files](additional-minor-features.html#label-files-as-test-files), [Move Statements Up and Down in the Editor](additional-minor-features.html#move-statements-up-and-down-in-the-editor). * Add section about [Power Save Mode](ide-infrastructure.html#power-save-mode). * Highlight references automatically via [Additional Highlighting](references-and-resolve.html#additional-highlighting) * Language injections: controlling [Formatting](language-injection.html#formatting) ### August Plugin Configuration Page : Update the [Plugin Configuration File](plugin-configuration-file.html) page to describe all the elements in detail. Source links migrated from Upsource to GitHub : All source links now point to GitHub instead of Upsource (which is going to be [sunset](https://blog.jetbrains.com/upsource/2022/01/31/upsource-end-of-sales-announcement/)). ### July Status Bar Widgets : Add section [Status Bar Widgets](status-bar-widgets.html) describing how to implement your own status bar widgets. Minor Changes and Additions : * Add overview of [Useful Action Base Classes](action-system.html#useful-action-base-classes). ### June PHP Type Providers : Add section [PHP Type Providers](php-open-api-php-type-providers.html) about type providers describing type inference in PhpStorm and how to implement your own type provider. Postfix Completion : Add [Postfix Completion](postfix-completion.html) section explaining how to implement generating or wrapping the existing code into additional constructs without navigating the caret back. Gradle IntelliJ Plugin : Add [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) documentation to Tooling. Bundling Plugin API Sources : Add the [Bundling Plugin API Sources](bundling-plugin-openapi-sources.html) section explaining how to expose plugin API sources to dependent plugin developers. Minor Changes and Additions : * Add a small section to [Utility Classes](php-open-api.html#utility-classes) describing `PhpFilePathUtils` utility class. * Add mention of the way to programmatically open an autocomplete popup to [Code Completion](code-completion.html). * Add a small section to [Animated Icons](icons.html#animated-icons) describing animated icons. * Moved [Gradle Grammar-Kit Plugin](tools-gradle-grammar-kit-plugin.html) documentation to Tooling. ### May Navigation Bar : Add [Navigation Bar](navbar.html) section with an [accompanying tutorial](structure-aware-navbar.html) showing how to implement a navigation bar for a custom language. Inlay Hints : Add section [Inlay Hints](inlay-hints.html) describing special markers that appear in the editor and provide additional information, like the names of the parameters that a called method expects. Minor Changes and Additions : * Add a small section to [Editor Components](editor-components.html) describing convenient `EditorTextField` subclasses. * Add descriptions for the following EPs to [Additional Minor Features](additional-minor-features.html): [Recognizing Complex Multi-Block Expressions](additional-minor-features.html#recognizing-complex-multi-block-expressions), [Breadcrumbs](additional-minor-features.html#breadcrumbs), [Plain Text Completion](additional-minor-features.html#plain-text-completion), [Splitting and Joining List Constructs](additional-minor-features.html#splitting-and-joining-list-constructs), [Suggesting Rename and Change Signature Refactorings](additional-minor-features.html#suggesting-rename-and-change-signature-refactorings), [Reader Mode](additional-minor-features.html#reader-mode). * Add small section to [Decorating Project View Nodes](project-view.html#decorating-project-view-nodes) describing how to modify the representation of nodes in the project view. * Add [Rename Refactoring](rename-refactoring.html) paragraphs mentioning `RenameInputValidator(Ex)`. ### April Internal API Migration : As API annotated with `@ApiStatus.Internal` must not be used in plugins, refer to [Internal API Migration](api-internal.html) for a list corresponding API replacements and additional information. ### March Code Samples Build Scripts Migrated to Kotlin : All [Code Samples](code-samples.html) now use Kotlin DSL in their Gradle build scripts. Android Studio Releases List : Add [Android Studio Releases List](android-studio-releases-list.html) section containing a complete list of the Android Studio releases with the relevant IntelliJ IDEA release version specified. Alternatives to Implementing a Plugin : Add [Alternatives to Implementing a Plugin](plugin-alternatives.html) section describing alternative approaches to extending IDE functionality without actual plugin development. ### February Parameter Info : Add [Parameter Info](parameter-info.html) section explaining how information about function parameters can be shown in the editor. File and Code Templates : Add [File and Code Templates](file-and-code-templates.html) section explaining how to implement functionality that allows generating files and code fragments containing repetitive text and patterns. ### January Highlighting : Add [Syntax Errors](syntax-errors.html) and [Controlling Highlighting](controlling-highlighting.html) sections explaining syntax highlighting basics and filtering highlighting information. IDE Infrastructure : Add sections [Application Events](ide-infrastructure.html#application-events) and [Plugin Management](ide-infrastructure.html#plugin-management). ## 2021 ### December Trusted Project : Potentially unsafe features must be guarded using [Trusted Project API](trusted-projects.html). ### November Language Injection : Add [Language Injection](language-injection.html) section that shows how the IntelliJ Platform handles different languages within the same source file. ### September IDE Infrastructure : [IDE Infrastructure](ide-infrastructure.html) handles Logging, Error Reporting, Runtime Information, and how to provide Context Help. Extension Point Lists: Listeners, Deprecation status : [IntelliJ Platform Extension Point and Listener List](intellij-platform-extension-point-list.html) now contains sections listing all provided [Listener](plugin-listeners.html) Topics. See also corresponding Extension Point Lists under Product Specific. Also, all deprecated API now has a dedicated tag. ### July Plugin Signing : [Plugin Signing](plugin-signing.html) describes the plugin signing process, explains how to generate a certificate, configure the Gradle [signPlugin](tools-gradle-intellij-plugin.html#tasks-signplugin) task, and introduces a standalone CLI tool. ### June Testing FAQ : [Testing FAQ](testing-faq.html) page lists common issues, useful classes, and techniques for writing and maintaining tests. Documentation Provider : Add [Documentation](documentation.html) section with an [accompanying tutorial](documentation-provider.html) that show how to implement a `DocumentationProvider` for custom languages. ### May IDE specific Extension Point Lists : See Product Specific. New Guide - Explore the IntelliJ Platform API : Add a new section [Explore the IntelliJ Platform API](explore-api.html) that describes how plugin authors work with the IntelliJ Platform API and what tools they use. ### March Element Patterns : Add a new section about [Element Patterns](element-patterns.html) that are used when implementing [Completion Contributors](completion-contributor.html) or [PSI Reference Contributors](psi-references.html#contributed-references). Editor - Text Selection : Add a new section about [Text Selection EPs](text-selection.html) and describe [ExtendWordSelectionHandler](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/codeInsight/editorActions/ExtendWordSelectionHandler.java). SDK Setup Assistance : Added a code sample to the SDK tutorial that expands on [assisting in the setup of an SDK](sdk.html#assisting-in-setting-up-an-sdk). Unified AST : [Unified Abstract Syntax Tree (UAST)](uast.html) allows providing features that will work across all supported JVM languages (Java, Kotlin, Scala, Groovy). ## 2020 ### December IntelliJ Platform Explorer : Explore usages of [Extension Points](intellij-platform-extension-point-list.html) in open-source plugins using [IntelliJ Platform Explorer](https://jb.gg/ipe). ### November Extension Point List : All EPs [available in IJ Platform and Android](intellij-platform-extension-point-list.html) can now be browsed conveniently. ### August README added to Code Samples : All code samples used in this guide now come with `README`, making it easier to browse them. They can be conveniently accessed via a [separate GitHub repository](https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/). ### June Dynamic Plugins update : Added new sections Code and Troubleshooting to [Dynamic Plugins](dynamic-plugins.html). GitHub IntelliJ Platform Plugin Template : Create new plugins with a preconfigured project scaffold and CI in [one click](plugin-github-template.html). Disposer & Disposable : Added [reference](disposers.html) discussing resource cleanup/management. ### May Settings (Preferences) : Added [guide](settings-guide.html) and [tutorial](settings-tutorial.html) on integrating with IDE Settings dialog. UI Inspector : Inspect Swing components and associated data (like `AnAction` for menu item) using [UI Inspector](internal-ui-inspector.html). ### March JCEF Support : Allows [embedding](embedded-browser-jcef.html) Chromium-based browser in the IDE. ### February All Code Samples converted to Gradle : [All samples](https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples) now use the [recommended solution](creating-plugin-project.html) of setting up plugin projects. ### January Custom Language Support Tutorial converted to Gradle : The [corresponding tutorial](custom-language-support-tutorial.html) and [Testing a Custom Language Plugin](writing-tests-for-plugins.html) have been updated and enhanced as well. Targeting specific IDEs : [Product Specific](plugin-compatibility.html) has been expanded massively, now also covering each IDE with its dedicated page. ## 2019 ### December Dynamic Plugins support : Added starting point [Dynamic Plugins](dynamic-plugins.html) for migrating plugins (IntelliJ Platform 2020.1 and later). Plugin Components migration : Components being a legacy feature, the [updated page](plugin-components.html) describes migrating them to modern replacement API. ### October Plugin Repository moved : All contents have been moved to [JetBrains Marketplace Documentation](https://plugins.jetbrains.com/docs/marketplace/). ### July New page: Optimizing Performance : Optimizing performance when [working with PSI](psi-performance.html), [during indexing](indexing-and-psi-stubs.html#improving-indexing-performance), and [Avoiding UI Freezes](threading-model.html#avoiding-ui-freezes). ### May New Page: Kotlin UI DSL : [Describes preferred way](kotlin-ui-dsl.html) of building UI/dialogs for IntelliJ Platform 2019.2 and later. # Quick Start Guide Note: Plugin Alternatives In some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as [alternative solutions](plugin-alternatives.html) exist. Note: UI Themes See [Themes](themes-getting-started.html) for information about UI theme development. This section covers the basics of working with the IntelliJ Platform. It will familiarize you with the working environment, project structure, and frequently used API components. * [Required Experience](plugin-required-experience.html) * [Plugin Types](plugin-types.html) * [Developing a Plugin](developing-plugins.html) * [Split Mode (Remote Development)](split-mode-and-remote-development.html) * [Plugin Structure](plugin-structure.html) * [Implementing Plugin in Kotlin](using-kotlin.html) * [Plugin Signing](plugin-signing.html) * [IDE Development Instance](ide-development-instance.html) * [Plugin Development FAQ](faq.html) # Alternatives to Implementing a Plugin In some cases, implementing an actual IntelliJ Platform plugin can be overkill, and using one of the alternative approaches listed below may provide you with the required value in a much shorter time. If you need a functionality that is specific to your project domain, conventions, or practices, you can avoid all the steps that are required to implement and publish a plugin and provide these features as a part of your project or IDE configuration files. Before you start the IntelliJ Platform plugin development, define your requirements and verify if they can be covered with any of the alternatives described below. Consider implementing an actual plugin only when the described solutions are insufficient in your case and there is a significant number of developers who can benefit from it. ## Kotlin Notebook Integration [Kotlin Notebook Integration](tools-kotlin-notebook.html) offers an interactive environment for testing IntelliJ Platform APIs directly within your IDE. By declaring `%use intellij-platform` in a notebook cell and switching to "Run in IDE Process" mode, you can run IntelliJ Platform code within the active IDE runtime without needing to create a full plugin project. This integration is especially helpful for: * Quickly prototyping IDE features and UI components * Testing platform APIs and behaviors * Building interactive documentation and tutorials * Trying out plugin ideas before formal development The notebook environment provides direct access to the IntelliJ Platform APIs, UI rendering features, resource management via the Disposer mechanism, and the ability to load and interact with installed plugins. ## Structural Search and Replace Inspections The [Structural Search and Replace (SSR)](https://www.jetbrains.com/help/idea/structural-search-and-replace.html) functionality allows defining search patterns which are based not only on textual information but also on the structure of the searched code fragments, no matter how it is formatted or commented. The SSR templates can be used for [creating custom inspections](https://www.jetbrains.com/help/idea/creating-custom-inspections.html), which can be an alternative for programmatic [code inspections](code-inspections.html). Depending on requirements, an inspection can report an issue for a code fragment matching a given template, but also provide a quick fix replacing the reported fragment with the configured replacement template. All inspection metadata like name, problem tooltip, and description are configurable. A single inspection can use multiple search and replacement templates. Once SSR inspections are created and configured, they can be shared with other team members via [inspection profiles](https://www.jetbrains.com/help/idea/customizing-profiles.html). SSR inspections can be created only for languages providing SSR support. To verify if a given language supports SSR, invoke the `Edit | Find | Search Structurally...` action in an IDE supporting the language, and check if it is present in the Language select list. Note: See the [I(J)nspector](https://ijnspector.wordpress.com/) blog for practical SSR templates examples. ## IDE Scripting Console The [IDE scripting console](https://www.jetbrains.com/help/idea/ide-scripting-console.html) can be used for automating IDE's features and extracting required information, e.g., about a current project. Scripts can access the IntelliJ Platform APIs and can be implemented in Kotlin, JavaScript, or Groovy by default, but it is also possible to use other languages compliant with the [JCR-223 specification](https://www.jcp.org/en/jsr/detail?id=223). Created scripts are stored in the [IDE configuration directory](https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#config-directory) and can't be shared as part of project files or configuration. ## Flora Plugin The [Flora](https://plugins.jetbrains.com/plugin/17669-flora-beta-) plugin allows for developing project-specific extensions as Kotlin Script (`*.kts`) or JavaScript (`*.js`) files. Flora extensions have access to all available IntelliJ Platform APIs, just like a regular plugin. Every extension is represented by a single file and stored directly in a project's `.plugins` directory. Extensions can be easily shared with other team members by adding the `.plugins` directory to VCS. Also, adding the Flora plugin in the `Settings | Build, Execution, Deployment | Required Plugins` and sharing this configuration as part of a project makes it effortless to deliver additional IDE functionalities to your team without any manual setup. Tip: Please note that the Flora plugin is in an experimental state. ## LivePlugin The [LivePlugin](https://plugins.jetbrains.com/plugin/7282-liveplugin) allows for extending IntelliJ-based IDEs functionalities at the runtime, without the need of restarting IDE. It adds a new Plugins tool window that lists all available extensions and allows managing them. Extensions can be implemented in Kotlin or Groovy and edited directly in the IDE. Extensions can use all IntelliJ Platform APIs and additional LivePlugin API that shorten common use cases. Created extensions are stored on the IDE level and can be shared with other team members as plain files, GitHub gists, or repositories. Additionally, if they are stored in a project's `.live-plugins` directory and LivePlugin's Run Project Specific Plugins option is enabled, all extensions from this directory will be loaded automatically when the project is opened and unloaded when the project is closed. Note: See the LivePlugin [description](https://dmitrykandalov.com/liveplugin), [presentation](https://www.youtube.com/watch?v=GcYa4lMRta0), and [extensions examples](https://github.com/dkandalov/live-plugin#more-examples) for more information. ## PhpStorm Advanced Metadata [PhpStorm](https://www.jetbrains.com/phpstorm/) supports special [metadata files](https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html) describing the behavior of methods and functions. This information is used for using the existing IDE features such as code completion, navigation, finding usages, and others. The metadata files can be [part of project files](https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#create-metadata-files-inside-your-project), which makes it easy to share it between team members via version control. # Required Experience The IntelliJ Platform is a JVM application, implemented mostly in [Java](https://www.oracle.com/java/) and [Kotlin](https://kotlinlang.org). At this time, it's not possible to develop plugins for the IntelliJ Platform in non-JVM languages). Note: Plugin Alternatives In some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as [alternative solutions](plugin-alternatives.html) exist. Developing a plugin for the IntelliJ Platform requires knowledge and experience with the following technologies and concepts: * Java, Kotlin, or any other JVM language, and its standard and third-party libraries * [Gradle](https://gradle.org/) or a similar build system (for example, Maven) * [Swing](https://en.wikipedia.org/wiki/Swing_(Java)) for building user interfaces * [Java Concurrency Model](https://docs.oracle.com/javase/tutorial/essential/concurrency/index.html) * experience with IntelliJ Platform-based IDE (for example, [IntelliJ IDEA](https://www.jetbrains.com/idea/)) Keep in mind that the IntelliJ Platform is a large project, and while we are doing our best to cover as many topics as possible, it is not possible to include every feature and use-case in the documentation. Developing a plugin will sometimes require digging into the [IntelliJ Platform code](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/README.md) and analyzing the [example implementations in other plugins](https://jb.gg/ipe). Tip: It is highly recommended to get familiar with the [Explore the IntelliJ Platform API](explore-api.html) section before starting with the plugin implementation. # Plugin Types Products based on the IntelliJ Platform can be modified and adjusted for custom purposes by adding plugins. All downloadable plugins are available from the [JetBrains Marketplace](https://plugins.jetbrains.com/). The most common types of plugins include: * Custom language support * Framework integration * Tool integration * User interface add-ons * Themes Note: Plugin Alternatives In some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as [alternative solutions](plugin-alternatives.html) exist. ## Custom Language Support Custom language support provides basic functionality for working with a particular programming language, that includes: * File type recognition * Lexical analysis * Syntax highlighting * Formatting * Code insight and code completion * Inspections and quick fixes * Intention actions Plugins can also augment existing (bundled) custom languages, e.g., by providing additional inspections, intentions, or any other features. Refer to the [Custom Language Support Tutorial](custom-language-support-tutorial.html) to learn more about the topic. ## Framework Integration Framework integration consists of improved code insight features, which are typical for a given framework, as well as the option to use framework-specific functionality directly from the IDE. Sometimes it also includes language support elements for a custom syntax or DSL. * Specific code insight * Direct access to framework-specific functionality Refer to the [IntelliJ-HCL](https://github.com/JetBrains/intellij-plugins/tree/idea/261.26222.65/terraform) as an example of framework integration. More reference plugins can be found on [JetBrains Marketplace](https://plugins.jetbrains.com/search?orderBy=update%20date&shouldHaveSource=true&tags=Framework). ## Tool Integration Tool integration makes it possible to manipulate third-party tools and components directly from the IDE without switching contexts, that implies: * Implementation of additional actions * Related UI components * Access to external resources Refer to the [Gerrit integration](https://plugins.jetbrains.com/plugin/7272) plugin as an example. ## User Interface Add-Ons Plugins in this category apply various changes to the standard user interface of the IDE. Some newly added components are interactive and provide new functionality, while others are limited to visual modifications only. The [Foldable ProjectView](https://plugins.jetbrains.com/plugin/17288-foldable-projectview) plugin may serve as an example. ## Themes [Themes](themes-getting-started.html) give designers the ability to customize the appearance of built-in IDE UI elements. # Developing a Plugin IntelliJ Platform plugins can be developed by using [IntelliJ IDEA](https://www.jetbrains.com/idea/download/) as your IDE. It is highly recommended to always use the latest available version, as the plugin development tooling support from Plugin DevKit continues supporting new features. Before starting with the actual development, make sure to understand all requirements to achieve best [Plugin User Experience (UX)](plugin-user-experience.html). Note: Plugin Alternatives In some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as [alternative solutions](plugin-alternatives.html) exist. ## Gradle Plugin The recommended solution for building IntelliJ Platform plugins is using [Gradle](https://www.gradle.org) with a dedicated Gradle plugin: [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) or [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) (obsolete). Warning: Gradle Plugin The Gradle plugin must be chosen depending on the target [platform version](build-number-ranges.html#platformVersions). 2024.2+ : Requires [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) 2022.3+ : Recommended [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) : Requires [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) (Obsolete) 1.10.1+ The IntelliJ IDEA provides the necessary plugins to support Gradle-based plugin development: Gradle and Plugin DevKit. To verify these plugins are installed and enabled, see the help section about [Managing Plugins](https://www.jetbrains.com/help/idea/managing-plugins.html). Warning: Installing Plugin DevKit plugin Plugin DevKit plugin must be installed from JetBrains Marketplace ([Plugin Homepage](https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is not bundled since version 2023.3. The Gradle plugin manages the dependencies of a plugin project – both the base IDE and other [plugin dependencies](plugin-dependencies.html). It provides tasks to run the IDE with your plugin and to package and [publish](publishing-plugin.html#publishing-plugin-with-gradle) your plugin to the [JetBrains Marketplace](https://plugins.jetbrains.com). To make sure that a plugin is not affected by [API changes](api-changes-list.html), which may happen between major releases of the platform, you can quickly verify your plugin against other IDEs and releases. There are two main ways of creating a new Gradle-based IntelliJ Platform plugin project: * dedicated generator available in the [New Project Wizard](https://www.jetbrains.com/help/idea/new-project-wizard.html) – it creates a minimal plugin project with all the required files * [IntelliJ Platform Plugin Template](plugin-github-template.html) available on GitHub – in addition to the required project files, it includes configuration of the GitHub Actions CI workflows This documentation section describes the plugin structure generated with the New Project wizard, but the project generated with IntelliJ Platform Plugin Template covers all the described files and directories. See [IntelliJ Platform Plugin Template](plugin-github-template.html) for more information about the advantages of this approach and instructions on how to use it. ### Alternatives The old DevKit project model and workflow are still supported in existing projects and are recommended for [creating theme plugins](developing-themes.html). See how to [migrate a DevKit plugin to Gradle](migrating-plugin-devkit-to-gradle.html). A dedicated [SBT plugin](https://github.com/JetBrains/sbt-idea-plugin) is available for plugins implemented in Scala. ## Plugin Development Workflow * [Creating a Plugin Gradle Project](creating-plugin-project.html) * [IntelliJ Platform Plugin Template](plugin-github-template.html) * [Configuring IntelliJ Platform Gradle Plugin (2.x)](configuring-gradle.html) * [Configuring Gradle IntelliJ Plugin (1.x)](configuring-plugin-project.html) (Obsolete) * [Configuring a Plugin for Split Mode](configuring-split-mode.html) * [Configuring Kotlin Support](using-kotlin.html) * [Plugin Signing](plugin-signing.html) * [Publishing a Plugin](publishing-plugin.html) # Creating a Plugin Gradle Project This documentation page describes a Gradle-based plugin project generated with the [New Project Wizard](https://www.jetbrains.com/help/idea/new-project-wizard.html) in IntelliJ IDEA, but the project generated with [IntelliJ Platform Plugin Template](plugin-github-template.html) covers all the described files and directories. Note: Plan for Split Mode New plugin projects should be implemented in a way that supports working in Remote Development mode or so-called [Split Mode](split-mode-and-remote-development.html). Review [Split Mode (Remote Development)](split-mode-and-remote-development.html) before settling on module boundaries. ## Creating a Plugin with New Project Wizard To enable the IDE Plugin wizard, make sure Gradle and Plugin DevKit plugins are installed and enabled. Warning: Installing Plugin DevKit plugin Plugin DevKit plugin must be installed from JetBrains Marketplace ([Plugin Homepage](https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is not bundled since version 2023.3. Procedure: Create IDE Plugin ### Components of a Wizard-Generated Gradle IntelliJ Platform Plugin For the `my_plugin` example created with the steps described above, the following directory content is created: ```PLANTUML @startuml skinparam TitleFontName JetBrains Sans skinparam TitleFontStyle plain skinparam TitleFontSize 16 skinparam DefaultTextAlignment left title my_plugin |_ .run |_ Run IDE with Plugin.run.xml |_ gradle |_ wrapper |_ gradle-wrapper.jar |_ gradle-wrapper.properties |_ src |_ main |_ kotlin |_ resources |_ META-INF |_ plugin.xml |_ pluginIcon.svg |_ .gitignore |_ build.gradle.kts |_ gradle.properties |_ gradlew |_ gradlew.bat |_ settings.gradle.kts end title @enduml ``` * The default IntelliJ Platform `build.gradle.kts` file (see [below](#build-gradle-kts)). * The `gradle.properties` file, containing properties used by Gradle build script. * The `settings.gradle.kts` file, containing a definition of the `rootProject.name` and required repositories. * The Gradle Wrapper files in the `gradle` directory. The `gradle-wrapper.properties` file specifies the version of Gradle to be used to build the plugin. If needed, the IDE downloads the version of Gradle specified in this file automatically. * The `META-INF` directory under the default `main` [source set](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_project_layout) contains the [plugin configuration file](plugin-configuration-file.html) and [plugin logo](plugin-icon-file.html). * The Run IDE with Plugin [run configuration](https://www.jetbrains.com/help/idea/run-debug-configuration.html). #### `build.gradle.kts` Gradle Build File The generated `my_plugin` project `build.gradle.kts` file depends on the IDE version used to generate the project: * 2025.1+: [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) variant * earlier IDE versions: [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) variant (deprecated) IntelliJ Platform Gradle Plugin (2.x): ```KOTLIN plugins { id("java") id("org.jetbrains.kotlin.jvm") version "1.9.25" id("org.jetbrains.intellij.platform") version "2.11.0" } group = "org.example" version = "1.0-SNAPSHOT" repositories { mavenCentral() intellijPlatform { defaultRepositories() } } // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin.html dependencies { intellijPlatform { intellijIdea("2025.2.6.1") testFramework(org.jetbrains.intellij.platform.gradle.TestFrameworkType.Platform) // Add necessary plugin dependencies for compilation here, example: // bundledPlugin("com.intellij.java") } } intellijPlatform { pluginConfiguration { ideaVersion { sinceBuild = "252" } changeNotes = """ Initial version """.trimIndent() } } tasks { // Set the JVM compatibility versions withType { sourceCompatibility = "21" targetCompatibility = "21" } withType { kotlinOptions.jvmTarget = "21" } } ``` * Three Gradle plugins are explicitly declared: * [Gradle Java](https://docs.gradle.org/current/userguide/java_plugin.html) plugin (`java`) * [Kotlin Gradle](https://kotlinlang.org/docs/gradle-configure-project.html#apply-the-plugin) plugin (`org.jetbrains.kotlin.jvm`) * [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) (`org.jetbrains.intellij.platform`) * The Group from the [New Project](#create-ide-plugin) wizard is the `project.group` value * `repositories`: setup required repositories ([Repositories Extension](tools-intellij-platform-gradle-plugin-repositories-extension.html)) * `dependencies`: * define target IDE `intellijIdea("2025.2.6.1")` ([Target Versions](tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions)) * add dependency on the platform testing framework ([Testing](tools-intellij-platform-gradle-plugin-dependencies-extension.html#testing)) * `pluginConfiguration`: [since-build](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-pluginConfiguration-ideaVersion) and initial [change notes](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-pluginConfiguration-changeNotes) * `sourceCompatibility` enforces using a 21 JDK Gradle IntelliJ Plugin (1.x): ```KOTLIN plugins { id("java") id("org.jetbrains.kotlin.jvm") version "1.9.21" id("org.jetbrains.intellij") version "1.17.4" } group = "com.example" version = "1.0-SNAPSHOT" repositories { mavenCentral() } // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html intellij { version.set("2022.2.5") type.set("IC") // Target IDE Platform plugins.set(listOf(/* Plugin Dependencies */)) } tasks { // Set the JVM compatibility versions withType { sourceCompatibility = "17" targetCompatibility = "17" } withType { kotlinOptions.jvmTarget = "17" } patchPluginXml { sinceBuild.set("222") untilBuild.set("232.*") } signPlugin { certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) privateKey.set(System.getenv("PRIVATE_KEY")) password.set(System.getenv("PRIVATE_KEY_PASSWORD")) } publishPlugin { token.set(System.getenv("PUBLISH_TOKEN")) } } ``` * Three Gradle plugins are explicitly declared: * The [Gradle Java](https://docs.gradle.org/current/userguide/java_plugin.html) plugin (`java`). * The [Kotlin Gradle](https://kotlinlang.org/docs/gradle-configure-project.html#apply-the-plugin) plugin (`org.jetbrains.kotlin.jvm`). * The [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) (`org.jetbrains.intellij`). * The Group from the [New Project](#create-ide-plugin) wizard is the `project.group` value. * The `sourceCompatibility` line is injected to enforce using Java 17 JDK to compile Java sources. * The values of the [intellij.version](tools-gradle-intellij-plugin.html#intellij-extension-version) and [intellij.type](tools-gradle-intellij-plugin.html#intellij-extension-type) properties specify the version and type of the IntelliJ Platform to be used to build the plugin. * The empty placeholder list for [plugin dependencies](tools-gradle-intellij-plugin.html#intellij-extension-plugins). * The values of the [patchPluginXml.sinceBuild](tools-gradle-intellij-plugin.html#tasks-patchpluginxml-sincebuild) and [patchPluginXml.untilBuild](tools-gradle-intellij-plugin.html#tasks-patchpluginxml-untilbuild) properties specifying the minimum and maximum versions of the IDE build the plugin is compatible with. * The initial [signPlugin](tools-gradle-intellij-plugin.html#tasks-signplugin) and [publishPlugin](tools-gradle-intellij-plugin.html#tasks-publishplugin) tasks configuration. See the [Publishing Plugin With Gradle](publishing-plugin.html#publishing-plugin-with-gradle) section for more information. #### Plugin Gradle Properties and Plugin Configuration File Elements The Gradle properties `rootProject.name` and `project.group` will not, in general, match the respective [plugin configuration file](plugin-configuration-file.html) `plugin.xml` elements [<name>](plugin-configuration-file.html#idea-plugin__name) and [<id>](plugin-configuration-file.html#idea-plugin__id). There is no IntelliJ Platform-related reason they should as they serve different functions. The `` element (used as the plugin's display name) is often the same as `rootProject.name`, but it can be more explanatory. The `` value must be a unique identifier over all plugins, typically a concatenation of the specified Group and Artifact. Please note that it is impossible to change the `` of a published plugin without losing automatic updates for existing installations. ## Running a Plugin With the `runIde` Gradle task Gradle projects are run from the IDE's Gradle tool window. ### Adding Code to the Project Before running [my_plugin](#components-of-a-wizard-generated-gradle-intellij-platform-plugin), some code can be added to provide basic functionality. See the [Creating Actions](creating-actions-tutorial.html) tutorial for step-by-step instructions for adding a menu action. ### Executing the Plugin The generated project contains the Run IDE with Plugin run configuration that can be executed via the `Run | Run...` action or can be found in the Gradle tool window under the Run Configurations node. To execute the Gradle `runIde` task directly, open the Gradle tool window and search for the runIde task under the Tasks node. If it's not on the list, click the Sync All Gradle Projects button on the [toolbar](https://www.jetbrains.com/help/idea/jetgradle-tool-window.html#gradle_toolbar) at the top of the Gradle tool window. Then double-click it to execute. To debug your plugin in a standalone IDE instance, please see [How to Debug Your Own IntelliJ IDEA Instance](https://medium.com/agorapulse-stories/how-to-debug-your-own-intellij-idea-instance-7d7df185a48d) blog post. Tip: For more information about how to work with Gradle-based projects see the [Working with Gradle in IntelliJ IDEA](https://www.youtube.com/watch?v=6V6G3RyxEMk) screencast and working with [Gradle tasks](https://www.jetbrains.com/help/idea/work-with-gradle-tasks.html) in the IntelliJ IDEA help. # IntelliJ Platform Plugin Template The IntelliJ Platform Plugin Template is the alternative solution for creating a new Gradle-based IntelliJ Platform plugin with the [New Project Wizard](creating-plugin-project.html). ## Modular Plugin Template Note: Experimental The modular template is intended for plugins that are designed for [Split Mode](split-mode-and-remote-development.html) and modular plugin packaging from the start. With increasing demand for the remote development support, JetBrains advices plugin developers to design their plugins modularly where UI and business logic are detached from each other. Use the modular template when a plugin is expected to provide optimal low-latency UX and correct project interaction in [Split Mode](split-mode-and-remote-development.html) as well as in a monolithic IDE. To start developing a [split plugin](split-mode-and-remote-development.html), use the [IntelliJ Platform Modular Plugin Template](https://github.com/JetBrains/intellij-platform-modular-plugin-template). The modular template provides: * frontend, backend, and shared module layout * split-mode-oriented Gradle configuration * ready-to-use examples of RPC-based communication ## Classic Plugin Template [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template) is a GitHub repository that provides a pure boilerplate template to make it easier to create a new Gradle-based plugin project. The main goal of this template is to speed up the setup phase of plugin development for both new and experienced developers by preconfiguring the project scaffold and CI, linking to the proper documentation pages, and keeping everything organized. Tip: Note: This template uses [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html). GitHub Template allows you to create a new repository from the scaffold without having to copy and paste content, clone repositories, or clear the history manually. All you have to do is click the Use this template button on the GitHub project page (you must be logged in with your GitHub account). After that, the GitHub Actions workflow will be triggered to override or remove any template-specific configurations, such as the plugin name, current changelog, etc. Once this is complete, the project is ready to be cloned to your local environment and opened with [IntelliJ IDEA](https://www.jetbrains.com/idea/download). For more details, refer to the [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template) project documentation. Note: The recording of the Busy Plugin Developer. Episode 0 webinar describes and shows [how to use the IntelliJ Platform Plugin Template](https://youtu.be/-6D5-xEaYig?t=230) in detail. # Migrating DevKit Plugin to Gradle Tip: See [Revamping Plugins #3 – Migrating from DevKit to the Gradle build system](https://blog.jetbrains.com/platform/2021/12/migrating-from-devkit-to-the-gradle-build-system/) blog post for a step-by-step walk-through. Converting a plugin created with the old DevKit approach (which can be used for [creating themes](creating-theme-project.html)) to a Gradle-based plugin project can be done using the New Project wizard to create a Gradle-based project around the existing DevKit-based project: * Ensure the directory containing the DevKit-based IntelliJ Platform plugin project can be fully recovered if necessary. * Delete all the artifacts of the DevKit-based project: * `.idea` directory * `[modulename].iml` file * `out` directory * Arrange the existing source files within the project directory in the Gradle [source set](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_project_layout) format. * Use the [New Project](creating-plugin-project.html#create-ide-plugin) wizard as though creating a new Gradle-based plugin project from scratch. On the New Project page choose the IDE Plugin generator and set the values of: * Group to the existing package in the initial source set. * Artifact to the name of the existing plugin. * Name to the name of the directory where the existing plugin is located, e.g. if the plugin project base directory is `/Users/john/Projects/old_plugin`, it should be the `old_plugin`. * Location to the name of the plugin's parent directory, e.g. if the plugin project base directory is `/Users/john/Projects/old_plugin`, it should be the `/Users/john/Projects`. * Click Finish to create the new Gradle-based plugin. * [Add more modules](https://www.jetbrains.com/help/idea/gradle.html#gradle_add_module) using Gradle [source sets](https://www.jetbrains.com/help/idea/gradle.html#gradle_source_sets) as needed. # Configuring IntelliJ Platform Gradle Plugin (2.x) This section presents a short overview of the most important Gradle plugin configuration elements to achieve commonly desired functionality. For more advanced options and topics, see: * [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) reference * [IntelliJ Platform Gradle Plugin – FAQ](tools-intellij-platform-gradle-plugin-faq.html) * [Recipes](tools-intellij-platform-gradle-plugin-recipes.html) * [Migrating from Gradle IntelliJ Plugin (1.x)](tools-intellij-platform-gradle-plugin-migration.html) Warning: Gradle Plugin The Gradle plugin must be chosen depending on the target [platform version](build-number-ranges.html#platformVersions). 2024.2+ : Requires [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) 2022.3+ : Recommended [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) : Requires [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) (Obsolete) 1.10.1+ ## Keep Up To Date IntelliJ Platform Gradle Plugin (2.x) and [Gradle](https://gradle.org/install/) build system are constantly developed, and every new release brings important bug fixes, new features, and improvements that make the development more efficient. It is strongly recommended to keep updating both Gradle and IntelliJ Platform Gradle Plugin to the latest versions. Newer IDE releases might not be supported fully in older releases of the IntelliJ Platform Gradle Plugin. Note: The current IntelliJ Platform Gradle Plugin (2.x) version is 2.17.0 ## Setup See [Configuration](tools-intellij-platform-gradle-plugin.html#configuration) for the necessary declaration of repositories. ## Target Platform and Dependencies Tip: Which versions should your plugin support? We've collected some insights based on download statistics in [Statistics: Product Versions in Use](https://plugins.jetbrains.com/docs/marketplace/product-versions-in-use-statistics.html). If a matching version of the specified IntelliJ Platform is not present on the local machine, the Gradle plugin will download it. IntelliJ IDEA then indexes the build and any associated source code and [JetBrains Java Runtime](tools-intellij-platform-gradle-plugin-jetbrains-runtime.html). To build a plugin for more than one target platform version, see [Targeting Multiple IDE Versions](build-number-ranges.html#multipleIDEVersions) for important notes. ### IntelliJ Platform Configuration The target IDE platform is set in the `dependencies {}` block, see [Setting Up IntelliJ Platform](tools-intellij-platform-gradle-plugin.html#setting-up-intellij-platform) for a minimal sample. [Target Platforms](tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-platforms) lists all available extension functions for known target IDE platforms. ### Plugin Dependencies IntelliJ Platform plugin projects may depend on either bundled or third-party plugins defined in the `dependencies {}` block, see [Plugins](tools-intellij-platform-gradle-plugin-dependencies-extension.html#plugins) for details. The runtime dependency must be added in the [Plugin Configuration](plugin-configuration-file.html) (`plugin.xml`) file as described in [Plugin Dependencies](plugin-dependencies.html#dependency-declaration-in-pluginxml). ## Run IDE Task By default, the [runIde](tools-intellij-platform-gradle-plugin-tasks.html#runIde) task will use the same version of the IntelliJ Platform for the IDE Development instance as was used for building the plugin. ## Verifying Plugin The following tasks allow running integrity and compatibility tests: * [verifyPlugin](tools-intellij-platform-gradle-plugin-tasks.html#verifyPlugin) - runs the [Plugin Verifier](verifying-plugin-compatibility.html#plugin-verifier) tool to check the binary compatibility with the specified IDE builds * [verifyPluginStructure](tools-intellij-platform-gradle-plugin-tasks.html#verifyPluginStructure) — validates completeness and contents of [plugin.xml descriptors](plugin-configuration-file.html) as well as the plugin's archive structure * [verifyPluginProjectConfiguration](tools-intellij-platform-gradle-plugin-tasks.html#verifyPluginProjectConfiguration) — validates the plugin project configuration ## Publishing Plugin Review the [Publishing a Plugin](publishing-plugin.html) page before using the [publishPlugin](tools-intellij-platform-gradle-plugin-tasks.html#publishPlugin) task. Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Configuring Gradle IntelliJ Plugin (1.x) This page presents a guided tour of Gradle plugin attributes to achieve the commonly desired functionality. For more advanced options, see the full [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) reference. Warning: Obsolescence Notice Gradle IntelliJ Plugin (1.x) is no longer under active development. Whenever possible, use [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) instead. Warning: Gradle Plugin The Gradle plugin must be chosen depending on the target [platform version](build-number-ranges.html#platformVersions). 2024.2+ : Requires [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) 2022.3+ : Recommended [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) : Requires [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) (Obsolete) 1.10.1+ ## Keep Up To Date Gradle IntelliJ Plugin and [Gradle](https://gradle.org/install/) build system are constantly developed, and every new release brings important bug fixes, new features, and improvements that makes the development more efficient. It is strongly recommended to keep updating both Gradle and Gradle IntelliJ Plugin to the latest versions. Newer IDE releases might not be supported fully in older releases of Gradle IntelliJ Plugin. Note: Current Gradle IntelliJ Plugin version is 1.17.4 ## Target Platform and Dependencies Tip: Which versions should your plugin support? We've collected some insights based on download statistics in [Statistics: Product Versions in Use](https://plugins.jetbrains.com/docs/marketplace/product-versions-in-use-statistics.html). By default, the Gradle plugin will build a plugin project against the IntelliJ Platform defined by the latest EAP snapshot of the IntelliJ IDEA. If a matching version of the specified IntelliJ Platform is not available on the local machine, the Gradle plugin downloads the correct version and type. IntelliJ IDEA then indexes the build and any associated source code and JetBrains Java Runtime. To build a plugin for more than one target platform version, see [Targeting Multiple IDE Versions](build-number-ranges.html#multipleIDEVersions) for important notes. ### IntelliJ Platform Configuration Explicitly setting the [intellij.version](tools-gradle-intellij-plugin.html#intellij-extension-version) and [intellij.type](tools-gradle-intellij-plugin.html#intellij-extension-type) properties tells the Gradle plugin to use that configuration of the IntelliJ Platform to create the plugin project. Tip: See the [Developing for Multiple Products](dev-alternate-products.html) page for information about how to develop a plugin that is compatible with multiple IntelliJ-based IDEs. All available platform versions can be browsed in the [IntelliJ Platform Artifacts Repositories](intellij-artifacts.html). If the chosen platform version is not available in the repositories, or a local installation of the target IDE is the desired type and version of the IntelliJ Platform, use [intellij.localPath](tools-gradle-intellij-plugin.html#intellij-extension-localpath) to point to that installation. If the `intellij.localPath` attribute is set, do not set the `intellij.version` and `intellij.type` attributes as this could result in undefined behavior. ### Plugin Dependencies IntelliJ Platform plugin projects may depend on either bundled or third-party plugins. In that case, a project should build against a version of those plugins that match the IntelliJ Platform version used to build the plugin project. The Gradle plugin will fetch any plugins in the list defined by [intellij.plugins](tools-gradle-intellij-plugin.html#intellij-extension-plugins). See the Gradle plugin [IntelliJ Extension](tools-gradle-intellij-plugin.html#configuration-intellij-extension) for information about specifying the plugin and version. Note that this attribute describes a dependency so that the Gradle plugin can fetch the required artifacts. The runtime dependency must be added in the [Plugin Configuration](plugin-configuration-file.html) (`plugin.xml`) file as described in [Plugin Dependencies](plugin-dependencies.html#dependency-declaration-in-pluginxml). ## Run IDE Task By default, the Gradle plugin will use the same version of the IntelliJ Platform for the IDE Development Instance as was used for building the plugin. Using the corresponding JetBrains Runtime is also the default, so for this use-case no further configuration is required. ### Running Against Alternate Versions and Types of IntelliJ Platform-Based IDEs The IntelliJ Platform IDE used for the [Development Instance](ide-development-instance.html) can be different from that used to build the plugin project. Setting the [runIde.ideDir](tools-gradle-intellij-plugin.html#tasks-runide-idedir) property will define an IDE to be used for the Development Instance. This attribute is commonly used when running or debugging a plugin in an [alternate IntelliJ Platform-based IDE](intellij-platform.html#ides-based-on-the-intellij-platform). ### Running Against Alternate Versions of the JetBrains Runtime Every version of the IntelliJ Platform has a corresponding version of the [JetBrains Runtime](ide-development-instance.html#using-a-jetbrains-runtime-for-the-development-instance). A different version of the runtime can be used by specifying the [runIde.jbrVersion](tools-gradle-intellij-plugin.html#tasks-runide-jbrversion) attribute, describing a version of the JetBrains Runtime that should be used by the IDE Development Instance. The Gradle plugin will fetch the specified JetBrains Runtime as needed. ## Patching the Plugin Configuration File A plugin project's `plugin.xml` file has element values that are "patched" at build time from the attributes of the [patchPluginXml](tools-gradle-intellij-plugin.html#tasks-patchpluginxml) task. As many as possible of the attributes in the Patching DSL will be substituted into the corresponding element values in a plugin project's `plugin.xml` file: * If a `patchPluginXml` attribute default value is defined, the attribute value will be patched in `plugin.xml` regardless of whether the `patchPluginXml` task appears in the Gradle build script. * For example, the default values for the attributes [patchPluginXml.sinceBuild](tools-gradle-intellij-plugin.html#tasks-patchpluginxml-sincebuild) and [patchPluginXml.untilBuild](tools-gradle-intellij-plugin.html#tasks-patchpluginxml-untilbuild) are defined based on the declared (or default) value of [intellij.version](tools-gradle-intellij-plugin.html#intellij-extension-version). So by default `patchPluginXml.sinceBuild` and `patchPluginXml.untilBuild` are substituted into the [<idea-version>](plugin-configuration-file.html#idea-plugin__idea-version) element's `since-build` and `until-build` attributes in the `plugin.xml` file. * If a [patchPluginXml](tools-gradle-intellij-plugin.html#tasks-patchpluginxml) task's attribute value is explicitly defined, the attribute value will be substituted in `plugin.xml`. * If both `patchPluginXml.sinceBuild` and `patchPluginXml.untilBuild` attributes are explicitly set, both are substituted in `plugin.xml`. * If one attribute is explicitly set (e.g. `patchPluginXml.sinceBuild`) and one is not (e.g. `patchPluginXml.untilBuild` has a default value), both attributes are patched at their respective (explicit and default) values. * For no substitution of the `` element's `since-build` and `until-build` attributes, set [intellij.updateSinceUntilBuild](tools-gradle-intellij-plugin.html#intellij-extension-updatesinceuntilbuild) to `false`, and do not provide `patchPluginXml.sinceBuild` and `patchPluginXml.untilBuild` values. The best practice to avoid confusion is to replace the elements in `plugin.xml` that will be patched by the Gradle plugin with a comment. That way, the values for these parameters do not appear in two places in the source code. The Gradle plugin will add the necessary elements as part of the patching process. For those [patchPluginXml](tools-gradle-intellij-plugin.html#tasks-patchpluginxml) attributes that contain descriptions such as [patchPluginXml.changeNotes](tools-gradle-intellij-plugin.html#tasks-patchpluginxml-changenotes) and [patchPluginXml.pluginDescription](tools-gradle-intellij-plugin.html#tasks-patchpluginxml-plugindescription), a `CDATA` block is not necessary when using HTML elements. Tip: To maintain and generate an up-to-date changelog, try using [Gradle Changelog Plugin](https://github.com/JetBrains/gradle-changelog-plugin). As discussed in [Components of a Wizard-Generated Gradle IntelliJ Platform Plugin](creating-plugin-project.html#components-of-a-wizard-generated-gradle-intellij-platform-plugin), the Gradle properties `project.version`, `project.group`, and `rootProject.name` are all generated based on the input to the Wizard. However, the [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) does not combine and substitute those Gradle properties for the default [<id>](plugin-configuration-file.html#idea-plugin__id) and [<name>](plugin-configuration-file.html#idea-plugin__name) elements in the `plugin.xml` file. The best practice is to keep `project.version` current. By default, if you modify `project.version` in Gradle build script, the Gradle plugin will automatically update the [<version>](plugin-configuration-file.html#idea-plugin__version) value in the `plugin.xml` file. This practice keeps all version declarations synchronized. ## Verifying Plugin The Gradle plugin provides tasks that allow for running integrity and compatibility tests: * [verifyPluginConfiguration](tools-gradle-intellij-plugin.html#tasks-verifypluginconfiguration) - validates the versions of SDK, target platform, APIs, etc., configured in a plugin project, * [verifyPlugin](tools-gradle-intellij-plugin.html#tasks-verifyplugin) - validates completeness and contents of `plugin.xml` descriptors as well as plugin's archive structure, * [runPluginVerifier](tools-gradle-intellij-plugin.html#tasks-runpluginverifier) - runs the [IntelliJ Plugin Verifier](https://github.com/JetBrains/intellij-plugin-verifier) tool to check the binary compatibility with specified IntelliJ IDE builds. Plugin Verifier integration task allows for configuring the exact IDE versions that your plugin will be checked against. See [Plugin Verifier](verifying-plugin-compatibility.html#plugin-verifier) for more information. ## Publishing Plugin Please review the [Publishing a Plugin](publishing-plugin.html) page before using the [publishPlugin](tools-gradle-intellij-plugin.html#tasks-publishplugin) task. That documentation explains different ways to use Gradle for plugin uploads without exposing account credentials. # Configuring a Plugin for Split Mode Split Mode configuration in the Gradle build script allows for running development sandbox IDEs in mode emulating the [remote development](split-mode-and-remote-development.html) scenario, with both frontend and backend processes running locally. To make a plugin work natively in split mode, see [plugin modularization](modular-plugins.html). ## Basic Configuration Warning: Split Mode requires [IntelliJ Platform Gradle Plugin 2.x](tools-intellij-platform-gradle-plugin.html). Enable Split Mode in the `intellijPlatform {}` extension: ```KOTLIN intellijPlatform { splitMode = true pluginInstallationTarget = SplitModeAware.PluginInstallationTarget.BOTH } ``` The two relevant properties are: * [splitMode](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-splitMode) – starts separate frontend and backend processes * [pluginInstallationTarget](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-pluginInstallationTarget) – selects where the plugin is installed ## Choosing the Installation Target Choose `pluginInstallationTarget` according to the code being exercised: * `BACKEND` for backend-only functionality * `FRONTEND` for frontend-only functionality * `BOTH` for typical modular plugins that contain frontend, backend, and shared modules For split plugin development, `BOTH` is the most common choice. Note: `pluginInstallationTarget` only controls where the plugin is placed in locally run development sandboxes. It doesn't describe end-user installation or synchronization behavior in split mode. See [Plugin Management](plugin-management-in-split-mode.html). ## Custom Run and Test Tasks Custom split-mode tasks can be declared with `intellijPlatformTesting`: ```KOTLIN val runIdeSplitMode by intellijPlatformTesting.runIde.registering { splitMode = true pluginInstallationTarget = SplitModeAware.PluginInstallationTarget.BOTH } val testIdeUiSplitMode by intellijPlatformTesting.testIdeUi.registering { splitMode = true pluginInstallationTarget = SplitModeAware.PluginInstallationTarget.BOTH } ``` These tasks get dedicated sandboxes and can be used like other development or test tasks. See [IntelliJ Platform Testing Extension](tools-intellij-platform-gradle-plugin-testing-extension.html) for more details. ## Configuring Debug and Trace Logs Starting with IntelliJ Platform Gradle Plugin 2.15.0, Split Mode provides dedicated `runIdeBackend` and `runIdeFrontend` tasks, so additional log categories can be enabled separately for each process. ```KOTLIN tasks { // Set up the additional debug/trace log categories if needed. runIdeBackend { systemProperty("idea.log.debug.categories", "com.example.plugin.backend") } runIdeFrontend { systemProperty("idea.log.trace.categories", "com.example.plugin.frontend") } } ``` Set `idea.log.debug.categories` or `idea.log.trace.categories` to the package or logger category names that should be enabled in the corresponding IDE process. Use a comma-separated list to enable multiple categories. If needed, either property can be applied to either task. ## Typical Next Step After the Gradle configuration is in place, the next step is deciding how the plugin code should be distributed between frontend, backend, and shared modules. See [Implementing a Feature for Split Mode](split-mode-feature-development.html). ## Required Gradle Plugin and Library Versions The following tables list recommended versions of Gradle plugins and libraries for split mode development, tailored for compatibility with the specified IntelliJ Platform versions. ### Gradle Plugins | IntelliJ Platform |`rpc` |`org.jetbrains.kotlin.plugin.serialization` | | 2026.1 |2.3.20-RC2-0.1 |2.3.20 | | 2025.3 |2.1.20-0.1 |2.1.20 | ### Libraries | IntelliJ Platform |`kotlinx-serialization-core-jvm` |`kotlinx-serialization-json-jvm` | | 2026.1 |1.9.0 |1.9.0 | | 2025.3 |1.7.3 |1.7.3 | # Configuring Kotlin Support Homepage: [Kotlin](https://kotlinlang.org) Project Template: [IntelliJ Platform Plugin Template](plugin-github-template.html) This page describes developing plugins using the [Kotlin](https://kotlinlang.org) programming language. Tip: Operating on Kotlin code To implement a plugin operating on Kotlin code ([PSI](psi.html)) in the IDE, see [Kotlin Plugin](idea.html#kotlin-plugin). ## Advantages of Developing a Plugin in Kotlin Using Kotlin to write plugins for the IntelliJ Platform is very similar to writing plugins in Java. Existing Java classes can be converted to their Kotlin equivalents by using the [J2K converter](https://kotlinlang.org/docs/mixing-java-kotlin-intellij.html#converting-an-existing-java-file-to-kotlin-with-j2k) (part of the Kotlin plugin). In addition to [null safety](https://kotlinlang.org/docs/null-safety.html), [type-safe builders](https://kotlinlang.org/docs/type-safe-builders.html), and [Kotlin Coroutines](kotlin-coroutines.html) the Kotlin language offers many convenient features for plugin development, which make plugins easier to read and simpler to maintain. Much like [Kotlin for Android](https://kotlinlang.org/docs/android-overview.html), the IntelliJ Platform makes extensive use of callbacks, which are straightforward to express as [lambdas](https://kotlinlang.org/docs/lambdas.html) in Kotlin. Kotlin classes can be mixed in a project with existing Java code. This might come in handy when certain APIs require the use of mentioned Kotlin Coroutines. ### Adding Extensions Likewise, it is possible to customize the behavior of internal classes in the IntelliJ Platform using [extensions](https://kotlinlang.org/docs/extensions.html). For example, it is common practice to [guard logging statements](https://www.slf4j.org/faq.html#logging_performance) to avoid the cost of parameter construction, leading to the following ceremony when using the log: ```JAVA if (logger.isDebugEnabled()) { logger.debug("..."+expensiveComputation()); } ``` We can achieve the same result more succinctly in Kotlin by declaring the following extension method: ```KOTLIN inline fun Logger.debug(lazyMessage: () -> String) { if (isDebugEnabled) { debug(lazyMessage()) } } ``` Now we can directly write: ```KOTLIN logger.debug { "..." + expensiveComputation() } ``` to receive all the benefits of lightweight logging while reducing the code verbosity. With practice, you will be able to recognize many idioms in the IntelliJ Platform that can be simplified with Kotlin. ### UI Forms in Kotlin The IntelliJ Platform provides a [type-safe DSL](kotlin-ui-dsl-version-2.html) to build UI forms declaratively. Tip: Using UI Designer plugin with Kotlin is [not supported](https://youtrack.jetbrains.com/issue/KTIJ-791). ### Kotlin Coroutines [Kotlin Coroutines](kotlin-coroutines.html) are a lightweight and convenient alternative to threads with many [advantages](kotlin-coroutines.html#coroutines-advantages). ## Adding Kotlin Support Tip: The [IntelliJ Platform Plugin Template](plugin-github-template.html) provides a preconfigured project using Kotlin. IntelliJ IDEA bundles the necessary Kotlin IDE plugin, requiring no further configuration. For detailed instructions, refer to the [Kotlin documentation](https://kotlinlang.org/docs/getting-started.html). Using Kotlin 2.x is recommended for plugins targeting 2024.3 or later and required for 2025.1 or later. ### Kotlin Gradle Plugin Adding Kotlin source files compilation support to a Gradle-based project requires adding and configuring the [Kotlin JVM Gradle plugin](https://kotlinlang.org/docs/gradle.html#targeting-the-jvm). ### Kotlin Standard Library (`stdlib`) A dependency on the standard library `stdlib` is added automatically ([API Docs](https://kotlinlang.org/api/latest/jvm/stdlib/)). In nearly all cases, it is not necessary to include it in the plugin distribution as the platform already bundles it. To opt out, add this line in `gradle.properties`: ```PROPERTIES kotlin.stdlib.default.dependency = false ``` To bundle `stdlib` in the plugin distribution, set the property to `true` explicitly. #### Gradle Check The presence of this Gradle property is checked with the corresponding Gradle task: IntelliJ Platform Gradle Plugin (2.x): [verifyPlugin](tools-intellij-platform-gradle-plugin-tasks.html#verifyPluginProjectConfiguration) task Gradle IntelliJ Plugin (1.x): [verifyPluginConfiguration](tools-gradle-intellij-plugin.html#tasks-verifypluginconfiguration) task If the property is not present, a warning will be reported during the plugin configuration verification. #### `stdlib` – Miscellaneous If a plugin supports [multiple platform versions](build-number-ranges.html), it must either target the lowest bundled `stdlib` version (see [table below](#bundled-stdlib-versions)) or the specific version must be [provided in plugin distribution](plugin-content.html#plugin-with-dependencies). See [Dependency on the standard library](https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library) for more details. Tip: Adding stdlib in tests If you need to add the Kotlin Standard Library to your test project dependencies, see the [How to test a JVM language?](testing-faq.html#how-to-test-a-jvm-language) section. #### Bundled `stdlib` Versions | IntelliJ Platform version (latest update) |Bundled `stdlib` version | ----------------------------------------------------------------------- | 2026.1 |2.3.20 | | 2025.3 |2.2.20 | | 2025.2 |2.1.20 | | 2025.1 |2.1.10 | #### Earlier Versions | IntelliJ Platform version (latest update) |Bundled stdlib version | --------------------------------------------------------------------- | 2024.3 |2.0.21 | | 2024.2 |1.9.24 | | 2024.1 |1.9.22 | | 2023.3 |1.9.21 | | 2023.2 |1.8.20 | | 2023.1 |1.8.0 | | 2022.3 |1.7.22 | | 2022.2 |1.6.21 | | 2022.1 |1.6.10 | See [here](https://www.jetbrains.com/legal/third-party-software/) for earlier versions. ### Kotlin Coroutines Libraries (`kotlinx.coroutines`) Warning: Use Bundled Library Plugins must always use the bundled library from the target IDE and not provide their own version. Make sure it is not added via transitive dependencies, see [View and Debug Dependencies](https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html) in the Gradle user guide. See [Kotlin Coroutines](kotlin-coroutines.html) on how to use them in plugins. #### Bundled `kotlinx-coroutines` Versions | IntelliJ Platform version (latest update) |Bundled `kotlinx-coroutines` version | ----------------------------------------------------------------------------------- | 2025.2 |1.10.1-intellij-4 | | 2025.1 |1.8.0-intellij-13 | | 2024.3 |1.8.0-intellij-11 | | 2024.2 |1.8.0-intellij-9 (*) | | 2024.1 |1.7.3 | (*) Since 2024.2, a custom [fork](https://github.com/JetBrains/intellij-deps-kotlinx.coroutines) with additional patches is bundled. ### Other Bundled Kotlin Libraries In general, it is strongly advised to always use the bundled library version. See [Third-Party Software and Licenses](https://www.jetbrains.com/legal/third-party-software/) for an overview of all bundled libraries. ### Incremental compilation The Kotlin Gradle plugin supports [incremental compilation](https://kotlinlang.org/docs/gradle-compilation-and-caches.html#incremental-compilation), which allows tracking changes in the source files, so the compiler handles only updated code. Kotlin 1.9.0 and later: No action is required. Remove the additional `kotlin.incremental.useClasspathSnapshot=false` property in `gradle.properties` if present. Kotlin 1.8.20: Tip: Consider using Kotlin 1.9.0 or later where this issue has been resolved. Kotlin `1.8.20` has a [new incremental compilation approach](https://kotlinlang.org/docs/gradle-compilation-and-caches.html#a-new-approach-to-incremental-compilation) which is enabled by default. Unfortunately, it is not compatible with the IntelliJ Platform when reading large JAR files (like `app.jar` or `3rd-party-rt.jar`), leading to an `Out of Memory` exception: ``` Execution failed for task ':compileKotlin'. > Failed to transform app.jar to match attributes {artifactType=classpath-entry-snapshot, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}. > Execution failed for ClasspathEntrySnapshotTransform: .../lib/app.jar. > Java heap space ``` To avoid this exception, add the following line to `gradle.properties`: ``` kotlin.incremental.useClasspathSnapshot=false ``` ## Plugin Implementation Notes ### Do not use `object` but `class` Plugins may use [Kotlin classes](https://kotlinlang.org/docs/classes.html) (`class` keyword) to implement declarations in the [plugin configuration file](plugin-configuration-file.html). When registering an [extension](plugin-extensions.html), the platform uses a dependency injection framework to instantiate these classes at runtime. For this reason, plugins must not use [Kotlin objects](https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview) (`object` keyword) to implement any `[plugin.xml](plugin-configuration-file.html)` declarations. Managing the lifecycle of extensions is the platform's responsibility, and instantiating these classes as Kotlin singletons may cause issues. A notable exception is [FileType](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/fileTypes/FileType.java) ([com.intellij.fileType](https://jb.gg/ipe?extensions=com.intellij.fileType) extension point ), see also the inspection descriptions below. Problems are highlighted via these inspections (2023.2): * Plugin DevKit | Code | Kotlin object registered as extension for Kotlin code * Plugin DevKit | Plugin descriptor | Extension class is a Kotlin object for `plugin.xml` ### Do not use `companion object` in extensions A Kotlin `companion object` is always created once you try to load its containing class, and [extension point implementations](plugin-extensions.html) are supposed to be cheap to create. To avoid unnecessary classloading (and thus slowdown in IDE startup), `companion object` in extensions must only contain simple constants or [logger](ide-infrastructure.html#logging). Anything else must be a top-level declaration or stored in an `object`. Use inspection Plugin DevKit | Code | Companion object in extensions to highlight such problems (2023.3). ## Example Plugins Implemented in Kotlin Browse all [open-source Kotlin plugins](https://jb.gg/ipe?language=kotlin) built on the IntelliJ Platform. # IDE Development Instance A plugin project can be run or debugged from within the development instance of IntelliJ IDEA. Selecting the `runIde` task for a Gradle-based project (or [Run](running-and-debugging-a-theme.html) menu for a Plugin DevKit-based project) will launch a Development Instance of the target IDE with the current development version of the plugin enabled. This page describes how to control some settings for the Development Instance. Tip: See also `runIde` task (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#runIde), [1.x](tools-gradle-intellij-plugin.html#tasks-runide)) properties and [Advanced Configuration](https://www.jetbrains.com/help/idea/tuning-the-ide.html) for general VM options and properties. ## Using a JetBrains Runtime for the Development Instance Tip: IntelliJ Platform Gradle Plugin (2.x) See [JetBrains Runtime (JBR)](tools-intellij-platform-gradle-plugin-jetbrains-runtime.html) when using [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html). An everyday use case is to develop (build) a plugin project against a JDK, e.g., Java 17, and then run or debug the plugin in a Development Instance of the IDE. In such a situation, Development Instance must use a [JetBrains Runtime (JBR)](https://www.jetbrains.com/jetbrains-runtime) rather than the JDK used to build the plugin project. The JetBrains Runtime is an environment for running IntelliJ Platform-based IDEs on Windows, macOS, and Linux. It has some modifications by JetBrains, such as fixes for native crashes not present in official JDK builds. A version of the JetBrains Runtime is bundled with all IntelliJ Platform-based IDEs. To produce accurate results while running or debugging a plugin project in a Development Instance, follow the procedures below to ensure the Development Instance uses a JetBrains Runtime. ### Using JetBrains Runtime Gradle IntelliJ Plugin (1.x): By default, the Gradle plugin will fetch and use the version of the JetBrains Runtime for the Development Instance corresponding to the version of the IntelliJ Platform used for building the plugin project. If required, an alternative version can be specified using the [runIde.jbrVersion](tools-gradle-intellij-plugin.html#tasks-runide-jbrversion) task property. Plugin DevKit: The [Run Configuration](https://www.jetbrains.com/help/idea/run-debug-configuration.html) for a DevKit-based plugin project controls the JDK used to run and debug a plugin project in a Development Instance. The default Run Configuration uses the same JDK for building the plugin project and running the plugin in a Development Instance. To change the runtime for the Development Instance, set the JRE field in the Run Configuration edit dialog to download a JetBrains Runtime. ### Determining a JetBrains Runtime Version The JetBrains Runtime is determined by the JDK version used to build the plugin project, regardless of whether it is built on macOS, Windows, or Linux. Procedure: Determine an Example JetBrains Runtime Version ### JetBrains Runtime Variants The JetBrains Runtime is delivered in various variants used for different purposes, like debugging, running for development purposes, or bundling with the IDE. Available JBR variants are: * `jcef` - the release bundles with the [JCEF](embedded-browser-jcef.html) browser engine * `sdk` - JBR SDK bundle used for development purposes * `fd` - the fastdebug bundle which also includes the `jcef` module * `dcevm` - bundles DCEVM (Dynamic Code Evolution Virtual Machine) * `nomod` – the release bundled without any additional modules Note: For `JBR 17`, `dcevm` is bundled by default. As a consequence, separated `dcevm` and `nomod` variants are no longer available. ## Enabling Auto-Reload Auto-Reload is available for compatible [dynamic plugins](dynamic-plugins.html). This allows a much faster development cycle by avoiding a full restart of the development instance after detecting code changes (when JARs are modified). Please note that any unloading problems in a production environment will ask the user to restart the IDE. Warning: Debugging Auto-Reload does not work when the sandbox IDE instance is running under a debugger. ### IntelliJ Platform Gradle Plugin (2.x) Auto-Reload is enabled by default. Set property [intellijPlatform.autoReload](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-autoReload) to `false` to disable it explicitly, see [How to disable the automatic reload of dynamic plugins?](tools-intellij-platform-gradle-plugin-faq.html#how-to-disable-the-automatic-reload-of-dynamic-plugins) After starting the sandbox IDE instance, run the [buildPlugin](tools-intellij-platform-gradle-plugin-tasks.html#buildPlugin) task after modifications in the plugin project and switch back focus to the sandbox instance to trigger reload. Warning: [buildSearchableOptions](tools-intellij-platform-gradle-plugin-tasks.html#buildSearchableOptions) task must currently be [disabled explicitly](tools-intellij-platform-gradle-plugin-faq.html#how-to-disable-building-the-searchable-options) to work around Only one instance of IDEA can be run at a time problem. ### Gradle IntelliJ Plugin (1.x) Warning: Obsolescence Notice Gradle IntelliJ Plugin (1.x) is no longer under active development. Whenever possible, use [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) instead. Auto-Reload is enabled by default when targeting 2020.2 or later. Set the property [runIde.autoReloadPlugins](tools-gradle-intellij-plugin.html#tasks-runide-autoreloadplugins) to `true` for enabling it in earlier platform versions or `false` to disable it explicitly, see [How to disable automatic reload of dynamic plugins?](tools-gradle-intellij-plugin-faq.html#how-to-disable-automatic-reload-of-dynamic-plugins) After starting the sandbox IDE instance, run the [buildPlugin](tools-gradle-intellij-plugin.html#tasks-buildplugin) task after modifications in the plugin project and switch focus back to the sandbox instance to trigger reload. Warning: [buildSearchableOptions](tools-gradle-intellij-plugin.html#tasks-buildsearchableoptions) task must currently be [disabled explicitly](tools-gradle-intellij-plugin-faq.html#how-to-disable-building-searchable-options) to work around Only one instance of IDEA can be run at a time problem. ### Plugin DevKit Add system property `idea.auto.reload.plugins` in the Plugin DevKit [run configuration](running-and-debugging-a-theme.html). To disable auto-reload, set `idea.auto.reload.plugins` to `false` explicitly. ## The Development Instance Sandbox Directory The Sandbox Home directory contains the [settings, caches, logs, and plugins](#development-instance-settings-caches-logs-and-plugins) for a Development Instance of the IDE. This information is stored in a different location than for the [installed IDE itself](https://intellij-support.jetbrains.com/hc/en-us/articles/206544519-Directories-used-by-the-IDE-to-store-settings-caches-plugins-and-logs). ### IntelliJ Platform Gradle Plugin (2.x) The default Sandbox Home location in a [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) plugin project is: * Windows: `$PROJECT_DIRECTORY$\build\$TARGET_IDE$\idea-sandbox` * Linux/macOS: `$PROJECT_DIRECTORY$/build/$TARGET_IDE$/idea-sandbox` The Sandbox Home location can be configured with the [intellijPlatform.sandboxContainer](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-sandboxContainer) property. ### Gradle IntelliJ Plugin (1.x) Warning: Obsolescence Notice Gradle IntelliJ Plugin (1.x) is no longer under active development. Whenever possible, use [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) instead. The default Sandbox Home location in a [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) plugin project is: * Windows: `$PROJECT_DIRECTORY$\build\idea-sandbox` * Linux/macOS: `$PROJECT_DIRECTORY$/build/idea-sandbox` The Sandbox Home location can be configured with the [intellij.sandboxDir](tools-gradle-intellij-plugin.html#intellij-extension-sandboxdir) property. ### Plugin DevKit For Plugin DevKit-based plugins, the default Sandbox Home location is defined in the IntelliJ Platform Plugin SDK. See the [Setting Up a Theme Development Environment](setting-up-theme-environment.html#add-intellij-platform-plugin-sdk) for information about how to set up Sandbox Home in IntelliJ Platform SDK. The default Sandbox Home directory location is: * Windows: `$USER_HOME$\.$PRODUCT_SYSTEM_NAME$$PRODUCT_VERSION$\system\plugins-sandbox\` * Linux: `~/.$PRODUCT_SYSTEM_NAME$$PRODUCT_VERSION$/system/plugins-sandbox/` * macOS: `~/Library/Caches/$PRODUCT_SYSTEM_NAME$$PRODUCT_VERSION$/plugins-sandbox/` ### Development Instance Settings, Caches, Logs, and Plugins Within the Sandbox Home directory are subdirectories of the Development Instance: * `config` contains settings for the IDE instance. * `plugins` contains folders for each plugin being run in the IDE instance. * `system/caches` or `system\caches` holds the IDE instance data. * `system/log` or `system\log` contains the `idea.log` file for the IDE instance. Each of these Sandbox Home subdirectories can be manually cleared to reset the IDE Development Instance. At the next launch of a Development Instance, the subdirectories will be repopulated with the appropriate information. # Plugin Signing Plugin Signing is a mechanism introduced in the 2021.2 release cycle to increase security on the [JetBrains Marketplace](https://plugins.jetbrains.com) and all IntelliJ-based IDEs. The JetBrains Marketplace signing process is designed to ensure that plugins are not modified over the course of the publishing and delivery pipeline. If the author does not sign the plugin or has a revoked certificate, a warning dialog will appear in the IDE during installation. On our side, we will check if the public part of a key is present, and we will verify the signature. This is similar to the [Google upload key](https://developer.android.com/studio/publish/app-signing#generate-key) mechanism. ## How Signing Works To be sure a file has not been modified, the file will be signed twice – first by the plugin author, then by JetBrains Marketplace. Procedure: Signing and Verification Process The intermediate certificate issues a certificate that will be used to sign plugins. This way, it will be possible to re-generate this certificate without access to JetBrains CA's supersecret private key. The private key of the intermediate certificate is issued and kept in the AWS Certificate Manager, and no application has access to it; people's access is also limited. So now we have an AWS-based Intermediate CA. The public part of the intermediate certificate will be added to the plugin distribution file together with the signing certificate. The certificate used to sign plugins is stored securely, too. JetBrains Marketplace uses AWS KMS as a signature provider to sign plugin distribution files. ## Signing Methods For signing, the [Marketplace ZIP Signer](https://github.com/JetBrains/marketplace-zip-signer) library is used. For Gradle-based projects, it can be used conveniently using the provided tasks via [Gradle Integration](#gradle-integration). Alternatively, a standalone [CLI Tool](#cli-tool) can be used. Both methods require a private certificate key to be present. ### Generate Private Key Warning: Storing Secrets Never commit your credentials to the Version Control System! Instead, use environment variables, like: ``` token.set(providers.environmentVariable("PUBLISH_TOKEN")) password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD")) ``` To generate an RSA `private.pem` private key, run the `openssl genpkey` command in the terminal, as below: ```BASH openssl genpkey\ -aes-256-cbc\ -algorithm RSA\ -out private_encrypted.pem\ -pkeyopt rsa_keygen_bits:4096 ``` After that, it's required to convert it into the RSA form with: ```BASH openssl rsa\ -in private_encrypted.pem\ -out private.pem ``` At this point, the generated `private.pem` content should be provided to the `signPlugin.privateKey` property ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-privateKey), [1.x](tools-gradle-intellij-plugin.html#tasks-signplugin-privatekey)). The provided password should be specified as the `signPlugin.password` property ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-password), [1.x](tools-gradle-intellij-plugin.html#tasks-signplugin-password)). As a next step, we will generate a `chain.crt` certificate chain with: ```BASH openssl req\ -key private.pem\ -new\ -x509\ -days 365\ -out chain.crt ``` The content of the `chain.crt` file will be used for the `signPlugin.certificateChain` property ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-certificateChain), [1.x](tools-gradle-intellij-plugin.html#tasks-signplugin-certificatechain)). Tip: Information about generating a public key based on the private key will be added later when uploading public keys to JetBrains Marketplace becomes available. ### Gradle Integration #### IntelliJ Platform Gradle Plugin (2.x) The [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) provides the [signPlugin](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin) task, which will be executed automatically right before the [publishPlugin](tools-intellij-platform-gradle-plugin-tasks.html#publishPlugin) task when [signPlugin.certificateChain](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-certificateChain) and [signPlugin.privateKey](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-privateKey) properties are specified. Otherwise, it'll be skipped. See [intellijPlatform.signing](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-signing) for configuration reference. ##### Configuration using Files Instead of using the `signPlugin.certificateChain` and `signPlugin.privateKey` properties which expect the certificate chain and key to be provided directly, it's also possible to specify the paths to the files containing the certificate chain and key content. Use [signPlugin.certificateChainFile](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-certificateChainFile) and [signPlugin.privateKeyFile](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin-privateKeyFile) properties instead. #### Gradle IntelliJ Plugin (1.x) The [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) provides the [signPlugin](tools-gradle-intellij-plugin.html#tasks-signplugin) task, which will be executed automatically right before the [publishPlugin](tools-gradle-intellij-plugin.html#tasks-publishplugin) task when [signPlugin.certificateChain](tools-gradle-intellij-plugin.html#tasks-signplugin-certificatechain) and [signPlugin.privateKey](tools-gradle-intellij-plugin.html#tasks-signplugin-privatekey) properties are specified. Otherwise, it'll be skipped. An example [signPlugin](tools-gradle-intellij-plugin.html#tasks-signplugin) task configuration may look like this: Kotlin: ```KOTLIN signPlugin { certificateChain.set(""" -----BEGIN CERTIFICATE----- MIIElgCCAn4CCQDo83LWYj2QSTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ ... gdZzxCN8t1EmH8kD2Yve6YKGFCRAIIzveEg= -----END CERTIFICATE----- """.trimIndent()) privateKey.set(""" -----BEGIN RSA PRIVATE KEY----- MIIJKgIBAAKCAgEAwU8awS22Rw902BmwVDDBMlTREX440BAAVM40NW3E0lJ7YTJG ... EnNBfIVFhh6khisKqTBWSEo5iS2RYJcuZs961riCn1LARztiaXL4l17oW8t+Qw== -----END RSA PRIVATE KEY----- """.trimIndent()) password.set("8awS22%#3(4wVDDBMlTREX") } publishPlugin { token.set("perm:a961riC....l17oW8t+Qw==") } ``` Groovy: ```GROOVY signPlugin { certificateChain = """ -----BEGIN CERTIFICATE----- MIIElgCCAn4CCQDo83LWYj2QSTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ ... gdZzxCN8t1EmH8kD2Yve6YKGFCRAIIzveEg= -----END CERTIFICATE----- """.stripIndent() privateKey = """ -----BEGIN RSA PRIVATE KEY----- MIIJKgIBAAKCAgEAwU8awS22Rw902BmwVDDBMlTREX440BAAVM40NW3E0lJ7YTJG ... EnNBfIVFhh6khisKqTBWSEo5iS2RYJcuZs961riCn1LARztiaXL4l17oW8t+Qw== -----END RSA PRIVATE KEY----- """.stripIndent() password = "8awS22%#3(4wVDDBMlTREX" } publishPlugin { token = "perm:a961riC....l17oW8t+Qw==" } ``` ##### Using Files Instead of using the [signPlugin.privateKey](tools-gradle-intellij-plugin.html#tasks-signplugin-privatekey) and [signPlugin.certificateChain](tools-gradle-intellij-plugin.html#tasks-signplugin-certificatechain) properties which expect the key and certificate chain content to be provided directly, it's also possible to specify the paths to the files containing the key and certificate chain content. To do that, use the [signPlugin.privateKeyFile](tools-gradle-intellij-plugin.html#tasks-signplugin-privatekeyfile) and [signPlugin.certificateChainFile](tools-gradle-intellij-plugin.html#tasks-signplugin-certificatechainfile) properties instead. Kotlin: ```KOTLIN signPlugin { certificateChainFile.set(file("certificate/chain.crt")) privateKeyFile.set(file("certificate/private.pem")) password.set("8awS22%#3(4wVDDBMlTREX") } publishPlugin { token.set("perm:a961riC....l17oW8t+Qw==") } ``` Groovy: ```GROOVY signPlugin { certificateChainFile = file("certificate/chain.crt") privateKeyFile = file("certificate/private.pem") password = "8awS22%#3(4wVDDBMlTREX" } publishPlugin { token = "perm:a961riC....l17oW8t+Qw==" } ``` ### Provide Secrets to IDE To avoid storing hard-coded values in the project configuration, the most suitable method for local development would be using environment variables provided within the Run/Debug Configuration. To specify secrets like `PUBLISH_TOKEN` and values required for the `signPlugin` Gradle task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-signplugin)), modify your Gradle configuration as follows: Kotlin: ```KOTLIN signPlugin { certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN")) privateKey.set(providers.environmentVariable("PRIVATE_KEY")) password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD")) } publishPlugin { token.set(providers.environmentVariable("PUBLISH_TOKEN")) } ``` Groovy: ```GROOVY signPlugin { certificateChain = providers.environmentVariable("CERTIFICATE_CHAIN") privateKey = providers.environmentVariable("PRIVATE_KEY") password = providers.environmentVariable("PRIVATE_KEY_PASSWORD") } publishPlugin { token = providers.environmentVariable("PUBLISH_TOKEN") } ``` In the Run/Debug Configuration for `publishPlugin` Gradle task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#publishPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-publishplugin)), provide Environment Variables using relevant environment variable names: ![Run/Debug Configuration Environment Variables](images/plugin_singing_env_variables.png) Warning: Encoding Values Note that both the private key and certificate chain are multi-line values. It is necessary to transform them first using Base64 encoding before providing the single-line field in the Environment Variables panel. The corresponding Gradle task properties will automatically detect and decode the Base64-encoded values. ### CLI Tool Current Release: 0.1.43 GitHub: [Releases & Changelog](https://github.com/JetBrains/marketplace-zip-signer/releases), [Issue Tracker](https://github.com/JetBrains/marketplace-zip-signer/issues) JetBrains Platform Forum: [Marketplace](https://platform.jetbrains.com/c/marketplace/8) category CLI tool is required if you don't rely on either Gradle plugin – i.e., when working with [Themes](developing-themes.html). Get the latest Marketplace ZIP Signer CLI Tool version from the GitHub Releases page. After downloading the `marketplace-zip-signer-cli.jar`, execute it as below: ```BASH java -jar marketplace-zip-signer-cli.jar sign\ -in "unsigned.zip"\ -out "signed.zip"\ -cert-file "/path/to/chain.crt"\ -key-file "/path/to/private.pem"\ -key-pass "PRIVATE_KEY_PASSWORD" ``` ## Signing for Custom Repositories Signing plugins hosted on a [Custom Plugin Repository](custom-plugin-repository.html) can be achieved for added trust between the repository and installation. However, unlike JetBrains Marketplace, the custom repository will not re-sign the plugin with the JetBrains key. Instead, a trusted private CA or self-signed certificate can be used to sign and validate plugins. ### Verification Before looking at how we can sign a plugin, let's first review how verification works when a non-JetBrains certificate is used. As of 2021.2, during verification, IntelliJ-based IDEs check if the plugin was signed with the JetBrains CA certificate or any public key provided by the user via `Settings | Plugins | Manage Plugin Certificates`. In 2021.2.1, a system property has been added: `intellij.plugins.truststore`, pointing to a trusted JKS TrustStore. During verification, the plugin's public key is extracted from the signature. The last certificate entry in the chain matched against the certificates stored in one of the storages from above. ### Using a Trusted Internal CA If an internal CA is available, it can be used to generate certificates for signing. When choosing this route, the certificate chain includes the root CA public key at the end of the chain. With this approach, existing internal TrustStores may exist and could be used. Be sure when choosing a TrustStore that the CAs are limited to the internal CAs you trust. Using a TrustStore with public CAs can expose users to an attack vector. If adding a TrustStore to a user's environment is not possible, the user may also add the root CAs public key to `Settings | Plugins | Manage Plugin Certificates`. ### Using Self-Signed Certificates Using a self-signed certificate is an option if no internal CAs exist. To generate the key and public key, see [Generate Private Key](#generate-private-key). If providing users with a TrustStore, you can generate one with the public key using `keytool`: ```BASH keytool -import -alias IdeaPlugin -file chain.crt -keystore pluginKeystore.jks -storepass changeit ``` (note: the TrustStore password must remain `changeit`) Otherwise, users may add the public key manually to `Settings | Plugins | Manage Plugin Certificates`. ## Plugin Signature Verification The signature of a plugin can be verified using the `verifyPluginSignature` Gradle task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#verifyPluginSignature), [1.x](tools-gradle-intellij-plugin.html#tasks-verifypluginsignature)). By default, it will use the same certificate chain as provided to the `signPlugin` Gradle task (see [Gradle Integration](#gradle-integration)). To verify the signature using the [CLI tool](#cli-tool), execute the `verify` command as below: ```BASH java -jar marketplace-zip-signer-cli.jar verify\ -in "signed.zip"\ -cert "/path/to/chain.crt" ``` # Publishing a Plugin When a plugin is ready, it can be published to the [JetBrains Marketplace](https://plugins.jetbrains.com) plugin repository so that other users can install it in their IDE. Tip: When publishing a plugin to a repository other than the [JetBrains Marketplace](https://plugins.jetbrains.com), refer to the [Custom Plugin Repository](custom-plugin-repository.html) documentation. The first plugin publication must always be [uploaded manually](#uploading-a-plugin-to-jetbrains-marketplace). Procedure: Before Publishing Checklist Warning: Do Not Repackage Libraries Do not repackage libraries into the main plugin JAR file. Otherwise, [Plugin Verifier](verifying-plugin-compatibility.html) will yield false positives for unresolved classes and methods. ## Uploading a Plugin to JetBrains Marketplace Before publishing a plugin, make sure it is signed. For more details on generating a proper certificate and configuring the signing process, check the [Plugin Signing](plugin-signing.html) article. Procedure: Creating a JetBrains Account Procedure: Uploading plugin ### Uploading a New Version New versions can be uploaded manually on the plugin's detail page, see [Marketplace Docs](https://plugins.jetbrains.com/docs/marketplace/plugin-updates.html) for details. See [Deploying a Plugin with Gradle](#deploying-a-plugin-with-gradle) on how to publish new versions using Gradle. ## Publishing Plugin With Gradle Once [Gradle support](creating-plugin-project.html) has been configured, and the plugin has been [uploaded manually](#uploading-a-plugin-to-jetbrains-marketplace) to the plugin repository at least once, it can be built and deployed to the [JetBrains Marketplace](https://plugins.jetbrains.com) automatically using dedicated Gradle tasks. Tip: In the following sections * 2.x refers to [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) * 1.x refers to [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) ### Building Distribution For the initial upload, manual distribution, or local installation, invoke the `buildPlugin` Gradle task (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#buildPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-buildplugin)) to create the plugin distribution. If the project is configured to rely on [Plugin Signing](plugin-signing.html), use the `signPlugin` task instead (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-signplugin)). The resulting ZIP file is located in `build/distributions` and can then be installed in the IDE via [Install Plugin from Disk...](https://www.jetbrains.com/help/idea/managing-plugins.html#install_plugin_from_disk) action or uploaded to a [Custom Plugin Repository](custom-plugin-repository.html). ### Providing Your Personal Access Token to Gradle To deploy a plugin to the JetBrains Marketplace, supply the Personal Access Token, which can be found on your profile page in the [My Tokens](https://plugins.jetbrains.com/author/me/tokens) section. To create a new token, provide its name and click the Generate Token button. A new token will be created and displayed right below. Warning: Copy it before closing this page and keep it in a secure location. This is the only time the token is visible. This section describes two options to supply the Personal Access Token via Gradle using: * Environment variables, * Parameters to the Gradle task. #### Using Environment Variables Start by defining an environment variable such as: ```BASH export ORG_GRADLE_PROJECT_intellijPlatformPublishingToken='YOUR_TOKEN' ``` Note: macOS Note On macOS systems, environment variables set in `.bash_profile` are only visible to processes run from bash. Environment variables visible to all processes need to be defined in [Environment.plist](https://developer.apple.com/library/archive/qa/qa1067/_index.html). Now provide the environment variable in the run configuration for running the `publishPlugin` task locally (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#publishPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-publishplugin)). To do so, create a Gradle run configuration (if not already done), select the Gradle project, specify the `publishPlugin` task, and then add the environment variable. IntelliJ Platform Gradle Plugin (2.x): ```KOTLIN intellijPlatform { publishing { token = providers.gradleProperty("intellijPlatformPublishingToken") } } ``` Gradle IntelliJ Plugin (1.x): ```KOTLIN tasks { publishPlugin { token = providers.gradleProperty("intellijPlatformPublishingToken") } } ``` Note that it's still required to put some default values (can be empty) in the Gradle properties. Otherwise, there can be a compilation error. #### Using Parameters for the Gradle Task Like using environment variables, the token can also be passed as a parameter to the Gradle task. For example, provide the parameter on the command line or by putting it in the arguments of the Gradle run configuration. ```BASH -PintellijPlatformPublishingToken=YOUR_TOKEN ``` Note that in this case also, it's still required to put some default values (can be empty) in the Gradle properties ### Deploying a Plugin with Gradle The first step when deploying a plugin is to confirm that it works correctly. Verify this by [installing the plugin from disk](https://www.jetbrains.com/help/idea/managing-plugins.html) in a fresh instance of the target IDE(s). #### Signing a Plugin The Marketplace signing is designed to ensure that plugins are not modified over the course of the publishing and delivery pipeline. The `signPlugin` Gradle task (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#signPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-signplugin)), will be executed automatically right before the `publishPlugin` task (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#publishPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-publishplugin)). For more details on generating a proper certificate and configuring the `signPlugin` task, see [Plugin Signing](plugin-signing.html). #### Publishing a Plugin Once the plugin works as intended, make sure the plugin version is updated, as the JetBrains Marketplace won't accept multiple artifacts with the same version. To deploy a new version of the plugin to the JetBrains Marketplace, invoke the `publishPlugin` Gradle task. Now check the most recent version of the plugin on the [JetBrains Marketplace](https://plugins.jetbrains.com/). If successfully deployed, any users who currently have this plugin installed on an available version of the IntelliJ Platform are notified of a new update available as soon as the update has been verified. ### Specifying a Release Channel IntelliJ Platform Gradle Plugin (2.x): It's possible to deploy plugins to a chosen release channel by configuring the [intellijPlatform.publishing.channels](tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-publishing-channels) extension property. Kotlin: ```KOTLIN intellijPlatform { publishing { channels = listOf("beta") } } ``` Groovy: ```GROOVY intellijPlatform { publishing { channels = ['beta'] } } ``` Gradle IntelliJ Plugin (1.x): It's possible to deploy plugins to a chosen release channel by configuring the [publishPlugin.channels](tools-gradle-intellij-plugin.html#tasks-publishplugin-channels) task property. Kotlin: ```KOTLIN tasks { publishPlugin { channels = listOf("beta") } } ``` Groovy: ```GROOVY tasks { publishPlugin { channels = ['beta'] } } ``` When empty, this uses the default plugin repository, available to all [JetBrains Marketplace](https://plugins.jetbrains.com/) users. However, it's possible to publish it to an arbitrarily named channel. These non-default release channels are treated as separate repositories. When using a non-default release channel, users need to configure a new [custom plugin repository](https://www.jetbrains.com/help/idea/managing-plugins.html#repos) in their IDE to install the plugin. For example, when specifying `'canary'` as channel name, users will need to add the `https://plugins.jetbrains.com/plugins/canary/list` repository to install the plugin and receive updates. Popular channel names include: * `alpha`: https://plugins.jetbrains.com/plugins/alpha/list * `beta`: https://plugins.jetbrains.com/plugins/beta/list * `eap`: https://plugins.jetbrains.com/plugins/eap/list # Split Mode (Remote Development) Code: [IntelliJ Platform Modular Plugin Template](https://github.com/JetBrains/intellij-platform-modular-plugin-template) Video: [Making an IntelliJ Plugin Remote Development-friendly](https://www.youtube.com/watch?v=T2VvY6kgALY) ## Split Mode Overview The documentation uses the following basic terms: * Remote Development: a workflow where the IDE frontend connects to a backend running locally or on another machine * Split Mode: essentially a synonym for Remote Development, a JetBrains IDE setup where the frontend and backend run as separate processes * Split Plugin (remote development-native plugin): a plugin consisting of dedicated frontend, backend, and shared modules so each part runs on the appropriate side in Split Mode Tip: See the [Modular Plugins](modular-plugins.html) section for detailed information about plugin modules. Remote development changes where plugin code runs. In Split Mode, the frontend process renders the user interface, while the backend process hosts the project model, indexing, analysis, execution, and other heavy work. The backend may run on the same machine, on another host, in a container, or in the cloud. Plugins that provide user interface, typing assistance, or other latency-sensitive features should account for this model from the beginning. This article explains the terminology, motivation, and architecture behind remote development in JetBrains IDEs, also known as Split Mode. It also describes how to run, debug, and test an IDE in Split Mode. ## Remote Development Support Benefits Remote development support in the IDE provides several benefits for end users: * Use more powerful hardware than a local laptop. * Work on a different OS than the one you’re running locally (useful for cross-platform development). * Use the laptop as a thin client (often no need to keep source code locally). * Keep sensitive code on company servers while still working “from anywhere”. ## Benefits of Applying Split Mode in Plugins Applying the Split Mode architecture provides the following benefits for plugins: * Responsive UX is not affected by latency or unstable connection issues * Some APIs naturally belong to either the backend or the frontend, and it is crucial that they work on the proper side. Take a file system events listener, for instance: the plugin would likely want to observe events in the file system where the project source code is located, not on the client side. * Split Mode becomes more and more popular and is demanded by JetBrains IDEs customers, who in turn are potential plugin customers as well. ## Problems with Non-Split Plugins Some features do not require anything specific to work properly in Split Mode. These include: * LSP * MCP * Inspections and annotators * Quick fixes and intentions * Completion contributors * Reference providers * Run configurations * Find Usages providers * Inlays Usually, non-split plugins depend on plugins and modules that can be run only on the backend, forcing the plugin to be loaded on the backend only. If a plugin enabled on the backend provides other functionality, such as UI or editing assistance, it will be available on the frontend side as well, but will be affected by latency and result in suboptimal UX. For example: * Language support, lexers, and parsers get processed on the backend and expose their output, such as the PSI tree, only there. The frontend does not have information about the file structure and only renders the highlighting received from the backend by applying it to the corresponding ranges in an edited file. * Typing assistance gets processed on the backend and then sent to the frontend, making it subject to latency issues. * UI components, actions, dialogs, and popups get rendered on the backend side, and rendering commands are then transmitted to the frontend. They do not require additional effort to work, but they are subject to latency issues, and the resulting UX is usually of insufficient quality. ## Split Mode Architecture The high-level architecture of the Split Mode can be summarized on the following diagram: ![High-level architecture overview](images/split-mode-high-level-architecture.png) A Split Plugin is a plugin consisting of modules that implement different parts of its functionality and are loaded either on the backend of a JetBrains IDE, on the frontend, or everywhere. These modules are referred to as backend, frontend, and shared plugin modules. An IDE running in Split Mode is essentially two separate processes. A monolithic IDE is a single process. A split plugin works just fine in a monolithic IDE. The only difference is that both frontend and backend functionality are combined in the same process. IntelliJ Platform determines whether a particular module belongs to the frontend or the backend by examining its dependencies on the corresponding `intellij.platform.frontend` and `intellij.platform.frontend.backend` modules. If a dependency is not satisfied, the module does not get loaded. Both frontend and backend dependencies are satisfied when an IDE runs in monolithic mode. See [Plugin Management](plugin-management-in-split-mode.html) for more details. A more detailed architecture is presented on the following diagram: ![Low-level architecture overview](images/split-mode-low-level-architecture.png) Frontend and backend code communicate with each other via RPC calls. The RPC framework provided by the IntelliJ Platform implies the existence of a shared plugin module, which gets loaded regardless of which IDE mode is active. Relations between modules are as follows: * The shared module defines RPC interfaces. * The frontend module depends on the shared module and calls RPC via these interfaces. * The backend module depends on the shared module and implements RPC interfaces. Thus, the primary flow is to get or send data from the frontend to the backend via RPC. Data sent through RPC must be serializable; the kotlinx.serialization framework is used for that purpose. The data is sent over a secure protocol under the hood, so there is no need to apply additional measures. In a monolithic IDE, the whole machinery works inside a single process, and there is no network access during RPC execution. In fact, one can consider the RPC function call a regular suspend function call in Kotlin, and thus features properly implemented for Split Mode naturally work well in a monolithic IDE. ## Running the Sandbox IDE in Split Mode See [configuring a plugin for Split Mode](configuring-split-mode.html#basic-configuration) for details. ## Running Tests in Split Mode There are two common needs here: 1. Plugin business logic can be tested by regular unit tests using the IntelliJ test framework (see [IntelliJ Platform Testing Extension](tools-intellij-platform-gradle-plugin-testing-extension.html#intellijPlatformTesting)). Tests that verify the functionality of a specific class can be placed in any content module. However, testing the plugin as a whole requires placing test classes into the root plugin module This is necessary for the correct classpath assembly, which will include the `plugin.xml` file and properly register all plugin extensions from all content modules. 2. Plugin UI in Split Mode can be tested by the integrated UI test framework (see [Integration Tests: UI Testing](integration-tests-ui.html#interaction-with-components)). For example, see [PluginTest](https://github.com/JetBrains/intellij-ide-starter/blob/master/intellij.tools.ide.starter.examples.plugins/src/integrationTest/kotlin/PluginTest.kt). ## Debugging the IDE in Split Mode Use the Run IDE (Split Mode) run configuration that will start both frontend and backend processes locally. The [modular plugin template](https://github.com/JetBrains/intellij-platform-modular-plugin-template) already provides such a run configuration out of the box. To add it in an existing project, use the steps below. Procedure: Creating the Run IDE (Split Mode) Run Configuration To enable additional `DEBUG` or `TRACE` logging categories for the split-mode backend or frontend process, configure the `runIdeBackend` and `runIdeFrontend` tasks in the Gradle build script as described in [Configuring Debug and Trace Logs](configuring-split-mode.html#configuring-debug-and-trace-logs). ## Testing Split Mode Manually and Emulating Latency Deploying the backend to a real remote machine is not the easiest or fastest way to check that the feature has been properly split. The major problem with features working in a Split Mode IDE is that they are exposed to latency issues. This can be easily emulated within a locally running Split Mode. Procedure: Emulating Slow Connection to the Backend Be careful not to set delay too high – the UX will be very poor if a particular feature has not yet been properly split. Specifying the delay allows experiencing something close to the real feel of the features provided by the plugin. If the plugin works correctly and fast, it is likely well-prepared for Split Mode and remote development context. Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Modular Plugins (Experimental) Code: [IntelliJ Platform Modular Plugin Template](https://github.com/JetBrains/intellij-platform-modular-plugin-template) A classic plugin has a single [class loader](plugin-class-loaders.html) which is used to load all classes of the plugin. This approach works well for many plugins, but there are cases when more granularity is needed: * To work properly in remote development mode, different parts of the plugin should be loaded in the backend and frontend processes; these parts have different dependencies. * If some classes of a plugin `A` depend on classes from a plugin `B`, they should be loaded by a separate class loader to allow unloading the plugin `B` without restarting the IDE. In such cases, it's possible to use the new modular plugin format: Plugin Model Version 2. Note: The new format is still experimental and may change in the future. It is not recommended for cases other than writing plugins for remote development, where the new format is required. Modular plugins can be developed only with [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html). ## Typical Split Mode Layout A [split plugin](split-mode-and-remote-development.html) commonly contains at least three modules: * shared module: DTOs, RPC interfaces, remote topics, and other code used on both sides * frontend module: user interface, editor-side interactions, and other latency-sensitive behavior * backend module: PSI, VFS, indexing, project model, execution, and other project-local logic See the [Split Mode feature guide](split-mode-feature-development.html) for the migration flow, the [RPC guide](remote-procedure-calls.html) for communication between modules, and the [API placement guide](frontend-backend-shared-apis.html) for common API placement guidance. ## Plugin Modules A plugin consists of one or more modules registered in the `content` tag in the [plugin descriptor file](plugin-configuration-file.html): ```XML ``` The name of the module is specified by a required `name` attribute and must be unique within the plugin. When the IntelliJ Platform loads a plugin, it tries to load all its modules. A module can be loaded if and only if all its dependencies are available. If some module cannot be loaded, the behavior depends on the value of an optional `loading` attribute: * `required`: module is the required part of the plugin; if the module cannot be loaded, the whole plugin isn't loaded, and an error is shown to the user. * `optional`: module is an optional part of the plugin; if the module cannot be loaded, it is skipped and doesn't prevent other modules from the plugin from being loaded. * other options are currently for internal use only. If the `loading` attribute is not specified, the module is treated as optional by default. If the module is optional, it's possible to specify that it should be treated as required when the IDE is running in a specific mode via the `required-if-available` attribute: * `intellij.platform.backend`: the module is required if the current process is a regular IDE process, or it's a remote development backend process; * `intellij.platform.frontend`: the module is required if the current process is a regular IDE process, or it's a remote development frontend process (JetBrains Client). These dependencies control whether a module loads after the plugin is installed. For how the IDE determines where a plugin can be installed and how backend/frontend plugin sets are synchronized, see [Plugin Management](plugin-management-in-split-mode.html). Source code and resource files of modules must be located in separate Gradle projects registered in the main module as [submodules](tools-intellij-platform-gradle-plugin-plugins.html#module). ## Module Descriptor File A module must have a descriptor file whose name is equal to the module name and has the `xml` extension (for example, `example.my.module.xml`), located directly in the `main/resources` directory of the corresponding Gradle project (not in the `META-INF` directory!). The module descriptor file uses the same format as the [plugin configuration file](plugin-configuration-file.html), but only the following top-level tags are allowed: * `` * `` * `` * `` * `` * `` The `` tag is also not allowed. A special `` tag can be used to specify dependencies on other modules and plugins: ```XML ``` `` subtag specifies dependency on another module with the given name. `` subtag specifies dependency on a classic plugin with the given ID. Dependencies are used at runtime to determine whether a module can be loaded or not, and to configure the class loader of the module. Like with `` tags in classic plugins, it's necessary to specify [dependencies in the Gradle build script](plugin-dependencies.html#intellij-platform-gradle-plugin-2x) as well to have them in the compilation classpath. If no dependencies are specified, the module will always be loaded when the plugin is loaded, regardless of the current IDE and the mode it runs in. ### Class Loaders Each module has its own class loader. The class loader has the class loaders of modules and plugins specified in the `` tag of the module descriptor as its parents, so it delegates loading of classes to them if they aren't found in the module itself. Also, the core class loader of the IntelliJ Platform is automatically added as a parent class loader. If a classic plugin declares a dependency on a modular plugin using `` tag, class loaders of all plugin modules will be added as parent class loaders. ## Plugin Configuration File The plugin configuration file for modular plugins uses the same format as a [plugin configuration file for classic plugins](plugin-configuration-file.html). However, the following top-level tags related to registration of classes aren't allowed in it; they must be located in the module descriptor files where the referenced classes are defined: * `` * `` * `` * `` * `` Also, `` tags are not allowed. Dependencies must be specified in the module descriptor files using `` tag. The IDE will automatically treat dependencies of modules marked as `required` as necessary dependencies of the plugin and will suggest installing or enabling corresponding plugins when the plugin is installed. Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Implementing a Feature for Split Mode Code: [IntelliJ Platform Modular Plugin Template](https://github.com/JetBrains/intellij-platform-modular-plugin-template) This page describes a practical flow for implementing new or refactoring an existing feature to make it work natively in [Split Mode](split-mode-and-remote-development.html) and behave the same in a monolithic IDE. The steps apply both when migrating an existing plugin and when designing a new one: 1. [Module Structure](#1-identify-or-create-necessary-plugin-modules) — Ensure shared, frontend, and backend modules are present. 2. [Code Distribution](#2-put-the-existing-or-newly-written-code-into-appropriate-module-types) — Verify that code is properly distributed across shared, frontend, and backend modules. 3. [Data Serialization](#3-create-dto-classes-required-for-data-exchange-between-the-frontend-ui-and-backend-logic) — Make sure that shared data is serializable. 4. [Data Transfer](#4-add-the-transport-layer-to-connect-the-ui-to-the-backend-model) — Implement RPC for data exchange. 5. [Feature Behavior](#5-verify-and-polish) — Verify correctness in both monolith and split modes. 6. [Optimization](#6-review-common-issues) — Address issues related to empty state and large state loading. 7. [Tests](#7-add-tests) — Cover the functionality with regular unit tests and integration UI tests running in both monolith and split mode. ## 1. Identify or Create Necessary Plugin Modules 1. Refer to the [modular plugin template](https://github.com/JetBrains/intellij-platform-modular-plugin-template) for module structure and necessary dependencies and [Modular Plugins](modular-plugins.html) for modular plugin concept description. 2. Create at least those three modules: * `.Shared` – with as few dependencies as possible * `.Backend` - with `intellij.platform.backend` dependency * `.Frontend` - with `intellij.platform.frontend` dependency 3. Make sure the dependencies are properly described in the build scripts – again, refer to the plugin template. 4. Put the `plugin.xml` file into the root plugin module’s `resources/META-INF` directory. 5. Put the `..xml` file directly into the `resources` directory in all three freshly created modules. 6. Reference content modules in the root `plugin.xml` file. Expected Outcome The plugin consists of at least three modules, namely frontend, backend, and shared, plus possibly the existing code that resides in a root plugin module or in other submodules. ## 2. Put the Existing or Newly Written Code Into Appropriate Module Types 1. Identify which kind of features dominate in the plugin: 1. See which APIs it uses more and which type of module they belong to by referring to the [list of frontend/backend/shared APIs](frontend-backend-shared-apis.html). 2. If there are mostly backend extensions in the plugin, consider it backend functionality; otherwise, frontend functionality. 2. Extract the analyzed functionality into the module type determined during the previous step. 3. If some APIs are still used in the wrong module type, that’s expected and will be addressed later. 4. Continue moving the code between modules with a higher level of granularity: Example: consider a plugin that provides a tool window which receives data to display from an external CLI tool that analyzes a project. Steps to perform: 1. Move the tool window implementation to the frontend and register it in its XML descriptor. 2. Extract the code responsible for external process spawning and reading its output to the backend module. 3. Ensure no more APIs are reported as being used in an inappropriate module type. Expected Outcome The plugin is not functioning properly since the UI is now completely detached from the business logic. The code, though, is properly distributed between modules that do not communicate with each other yet. The root plugin module carries only the `plugin.xml` descriptor in its `resources` folder. All the code from it is extracted to one of the freshly created modules. ## 3. Create DTO Classes Required For Data Exchange Between the Frontend UI and Backend Logic 1. Investigate what information is necessary for the extracted UI components but is known only on the backend side. 2. Create classes representing the data and annotate them with `@Serializable`. 3. Consider using one of the primitive types as a DTO property, or use custom structure with a proper custom serializer implementation (see [Data Transfer Objects](remote-procedure-calls.html#data-transfer-objects)). 4. Consider using the serializable form of some major platform primitives if necessary (see [ID Types](remote-procedure-calls.html#id-types)). Expected Outcome There are serializable (in terms of `kotlinx.serialization` framework) DTO classes in the shared module representing the data to be sent over RPC calls. ## 4. Add the Transport Layer to Connect the UI to the Backend Model Tip: For more details about RPC refer to [RPC guideline](remote-procedure-calls.html). 1. Introduce an RPC interface in the shared plugin module. The RPC interface must: * be annotated with `@Rpc` * implement [RemoteApi<Unit>](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/fleet/rpc/srcCommonMain/fleet/rpc/FleetApi.kt) * have only `suspend` functions * use only `@Serializable` DTO classes as function parameters and return type 2. Introduce RPC implementation in the backend plugin module. The RPC implementation must: * implement the corresponding RPC interface * be registered in the backend module XML descriptor via the [com.intellij.platform.rpc.backend.remoteApiProvider](https://jb.gg/ipe?extensions=com.intellij.platform.rpc.backend.remoteApiProvider) extension point 3. Use DTOs created in [step 3](#3-create-dto-classes-required-for-data-exchange-between-the-frontend-ui-and-backend-logic) as input parameters and return values. Get back to step 3 if some data is missing. 4. Call the RPC where the backend data is required. * It is a crucial detail that RPC calls are always suspending. It may be impossible to use suspending code in a particular place in the frontend functionality, either because it is an old implementation written in Java and is not ready for suspend functions at all, or because the data must be available immediately, otherwise causing poor UX or even freezes. Remember that a proper UX is one of the main reasons we initiated the entire splitting process for, see the [split-mode introduction](split-mode-and-remote-development.html). * RPC can't be called on [EDT](threading-model.html). Avoid wrapping it in `runBlockingCancellable` unless it is absolutely necessary, and you understand all the consequences of such a decision, namely blocking the caller thread and breaking the structured concurrency and suspending API concepts. * Consider using the existing platform abstraction for shared state as a reference: [FlowWithHistory.kt](https://github.com/JetBrains/intellij-community/blob/1c3952828ff3af2d18f99a6721c48bb22f97bd57/platform/lang-impl/src/com/intellij/build/FlowWithHistory.kt). 5. RPC is designed to be initiated by the frontend, which implies that users always interact with one of the IDE UI components that naturally belong to the frontend. In some cases, however, it is required to initiate some UI display from within the backend code. For example, a long backend process may want to show a notification after it finishes. Consider using the Remote Topic API in such cases: * Declare a project- or application-level topic in the shared plugin module by using the [ProjectRemoteTopic](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/remote-topics/shared/src/com/intellij/platform/rpc/topics/ProjectRemoteTopic.kt) or [ApplicationRemoteTopic](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/remote-topics/shared/src/com/intellij/platform/rpc/topics/ApplicationRemoteTopic.kt), respectively * Subscribe to the topic in the frontend plugin module via the [com.intellij.platform.rpc.applicationRemoteTopicListener](https://jb.gg/ipe?extensions=com.intellij.platform.rpc.applicationRemoteTopicListener) extension point – code to display the desired notification can be invoked here * Push the serializable DTO class into the topic in the backend plugin module where necessary – as soon as the DTO is delivered, the frontend topic listener will do its job * Example: [ShowStructurePopupRemoteTopicListener.kt](https://github.com/JetBrains/intellij-community/blob/1b63f9058d6285980c1eac14b8b59fca251751b7/platform/structure-view-impl/frontend/src/ShowStructurePopupRemoteTopicListener.kt) Expected Outcome Frontend UI exchanges serializable data with backend via RPC or RemoteTopic API. ## 5. Verify and Polish After all infrastructure has been implemented, it is time to verify the feature behavior and polish it. Refer to [Introduction into Split Mode / Remote Development](split-mode-and-remote-development.html) on how to manually test Split Mode and check the monolithic IDE as well – the behavior is expected to be exactly the same. Expected Outcome The code is valid from the point of view of this guide, and the feature works as expected in both Split Mode and a monolithic IDE. ## 6. Review Common Issues Now that the general functionality works as expected, consider reviewing the list of frequently occurring problems and suggested solutions for them. Necessity of tuning the code depends on the specifics of the feature. 1. Handle reconnection: wrap RPC calls in [durable {}](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/fleet/rpc/srcCommonMain/fleet/rpc/client/Durable.kt); this wrapper will restart the call if a network error occurs. Be careful with any side effects your code inside the durable block produces – ideally, avoid them. Example: [RecentFileModelSynchronizer.kt](https://github.com/JetBrains/intellij-community/blob/1c3952828ff3af2d18f99a6721c48bb22f97bd57/platform/recentFiles/frontend/src/com/intellij/platform/recentFiles/frontend/model/RecentFileModelSynchronizer.kt) 2. Register actions in proper plugin modules 1. We strongly encourage registering actions on the frontend side, if possible. The possibility is determined by the action's update method: if it requires backend entities, the action belongs on the backend. Otherwise – on the frontend. 2. Frontend actions are rendered immediately and do not introduce delays when displaying context menus, popups, or toolbars. If the frontend action decides to touch backend entities in the `AnAction.actionPerformed` method, it is completely fine to call RPC there. 3. (advanced) In some complicated cases, the `action.update()` method might require both frontend and backend entities to be available simultaneously. Such cases must be addressed individually depending on a specific feature description. There are examples of shared, eventually synchronized state implementations in the IntelliJ Platform codebase, for example, in the [Recent Files implementation](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/recentFiles). There, an eventually consistent mutable shared state is implemented to make it possible to access the data model on both the frontend and the backend with no RPC calls required for access. 4. Consider approaching us on the [JetBrains Platform Forum](https://platform.jetbrains.com) to discuss what could be done in your specific case. 3. Display empty state: UI must render without waiting for backend; show placeholders and progressively fill in data. 4. Load large state: avoid “send everything at once” RPC implementations; use paging/lazy loading, and request only what the UI needs now. Example: [BackendRecentFileEventsModel.kt](https://github.com/JetBrains/intellij-community/blob/1c3952828ff3af2d18f99a6721c48bb22f97bd57/platform/recentFiles/backend/src/com/intellij/platform/recentFiles/backend/BackendRecentFileEventsModel.kt#L164) 5. Do not load data too frequently: avoid chatty RPC (per keystroke, per scroll tick). Batch requests, cache results, debounce UI events. Example: [RecentFilesEditorTypingListener.kt](https://github.com/JetBrains/intellij-community/blob/1c3952828ff3af2d18f99a6721c48bb22f97bd57/platform/recentFiles/frontend/src/com/intellij/platform/recentFiles/frontend/RecentFilesEditorTypingListener.kt) Expected Outcome Known issues are mitigated, and the plugin quality is now good enough. ## 7. Add Tests Fix the split feature behavior and quality with unit and integration tests if you have not used the TDD approach earlier. See the [Running Tests in Split Mode](split-mode-and-remote-development.html#running-tests-in-split-mode) section. We suggest paying attention to: * Data transformations: correct (de)serialization * Data consistency: correct data merging in case of complicated features with eventually consistent backend/frontend state * Proper behavior under latency: artificial delay in a test implementation backend service does not bring freezes or broken UX Expected Outcome The feature implementation has tests covering its correct behavior in remote and local scenarios. ## Getting Help In case of any questions or uncertainties regarding the splitting process, please post a question on the [JetBrains Platform Forum](https://platform.jetbrains.com). We will try to provide as much help as possible there and reconsider and adjust for unexpected cases. Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Remote Procedure Calls (RPC) This article walks through how remote calls (RPC) are set up in [Split Mode](split-mode-and-remote-development.html) and refers to code in the publicly available [modular plugin template](https://github.com/JetBrains/intellij-platform-modular-plugin-template). ## Modules Overview The plugin is split into three modules: shared, frontend, and backend. The following sections explain how the module dependencies are configured. ### Shared Module The shared module defines the RPC interface. It needs the `rpc` and `kotlinx.serialization` plugins. [shared/build.gradle.kts](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/shared/build.gradle.kts): ```KOTLIN plugins { id("rpc") id("org.jetbrains.kotlin.jvm") id("org.jetbrains.kotlin.plugin.serialization") } dependencies { intellijPlatform { intellijIdea(libs.versions.intellij.platform) } } ``` ### Frontend Module The frontend module depends on `:shared` and needs the `rpc` plugin as well. [frontend/build.gradle.kts](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/frontend/build.gradle.kts): ```KOTLIN plugins { id("rpc") id("org.jetbrains.kotlin.jvm") id("org.jetbrains.kotlin.plugin.serialization") } dependencies { intellijPlatform { intellijIdea(libs.versions.intellij.platform) bundledModule("intellij.platform.frontend") // ... } compileOnly(project(":shared")) } ``` ### Backend Module The backend module depends on `:shared` and requires `intellij.platform.kernel.backend` and `intellij.platform.rpc.backend`. [backend/build.gradle.kts](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/build.gradle.kts): ```KOTLIN plugins { id("rpc") id("org.jetbrains.kotlin.jvm") id("org.jetbrains.kotlin.plugin.serialization") } dependencies { intellijPlatform { intellijIdea(libs.versions.intellij.platform) bundledModule("intellij.platform.kernel.backend") bundledModule("intellij.platform.rpc.backend") bundledModule("intellij.platform.backend") } compileOnly(project(":shared")) } ``` Also declare the backend platform module dependency in [backend/src/main/resources/modular.plugin.backend.xml](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/src/main/resources/modular.plugin.backend.xml): ```XML ``` ## Implementing RPC Communication ### RPC Interface Introduce the RPC interface in the shared module. [ChatRepositoryRpcApi](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/shared/src/main/kotlin/org/jetbrains/plugins/template/ChatRepositoryRpcApi.kt): ```KOTLIN @Rpc interface ChatRepositoryRpcApi : RemoteApi { companion object { suspend fun getInstance(): ChatRepositoryRpcApi { return RemoteApiProviderService.resolve( remoteApiDescriptor() ) } } suspend fun getMessagesFlow(projectId: ProjectId): Flow> suspend fun sendMessage(projectId: ProjectId, messageContent: String) } ``` RPC interface must follow the rules: 1. The interface must be annotated with [@Rpc](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/fleet/rpc/srcCommonMain/fleet/rpc/FleetApi.kt). 2. All functions must be [suspend](https://kotlinlang.org/docs/coroutines-basics.html#suspending-functions). 3. All parameters and return types must be `@Serializable`. They are essentially data transfer objects (DTOs) widely used in client-server application development. * Primitives, `String`, `Flow`, `Deferred` are serializable by default. * Enums are not serializable by default. Mark them as `@Serializable` explicitly. * Classes must be annotated with `@Serializable` and must contain only other serializable fields For convenience, introduce `suspend getInstanceAsync()` so the frontend can easily acquire the instance. ### RPC Backend Implementation Add a class implementing the RPC interface in the backend module. [BackendChatRepositoryRpcApi](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/src/main/kotlin/org/jetbrains/plugins/template/BackendChatRepositoryRpcApi.kt): ```KOTLIN class BackendChatRepositoryRpcApi : ChatRepositoryRpcApi { override suspend fun getMessagesFlow(projectId: ProjectId): Flow> { val backendProject = projectId.findProjectOrNull() ?: return emptyFlow() return BackendChatRepositoryModel.getInstance(backendProject) .getMessagesFlow() } override suspend fun sendMessage( projectId: ProjectId, messageContent: String ) { val backendProject = projectId.findProjectOrNull() ?: return return BackendChatRepositoryModel.getInstance(backendProject) .sendMessage(messageContent) } } ``` Implement [RemoteApiProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/kernel/rpc.backend/src/RemoteApiProvider.kt), which registers the RPC implementation with the platform. [BackendRpcApiProvider](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/src/main/kotlin/org/jetbrains/plugins/template/BackendRpcApiProvider.kt): ```KOTLIN internal class BackendRpcApiProvider : RemoteApiProvider { override fun RemoteApiProvider.Sink.remoteApis() { remoteApi(remoteApiDescriptor()) { BackendChatRepositoryRpcApi() } } } ``` Register the provider in the [com.intellij.platform.rpc.backend.remoteApiProvider](https://jb.gg/ipe?extensions=com.intellij.platform.rpc.backend.remoteApiProvider) extension point . [backend/src/main/resources/modular.plugin.backend.xml](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/src/main/resources/modular.plugin.backend.xml): ```XML ``` ### Calling RPC from Frontend [ChatRepositoryRpcApi](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/shared/src/main/kotlin/org/jetbrains/plugins/template/ChatRepositoryRpcApi.kt) can be called on the frontend: ```KOTLIN val chatRepositoryApi = ChatRepositoryRpcApi.getInstance() chatRepositoryApi.getMessagesFlow(project.projectId()) chatRepositoryApi.sendMessage(project.projectId(), messageContent) ``` Note that all `getInstanceAsync()`, `getMessagesFlow()`, and `sendMessage()` functions are `suspend`. They must be called in some [coroutine](kotlin-coroutines.html) context. #### RPC Error Handling Calling an RPC API may fail with an `RpcClientException`, for example, if there are communication problems, service is not ready, etc. For instance, this may happen if the client tries to execute the call while the backend is not fully initialized, if a network problem occurs, or if the backend is restarted while a call is being executed. Such errors can be mitigated by using the [durable](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/fleet/rpc/srcCommonMain/fleet/rpc/client/Durable.kt) wrapper function. ```KOTLIN durable { ChatRepositoryRpcApi.getInstanceAsync() .getMessagesFlow(project.projectId()) .collect { valueFromBackend -> ... // process the message } } ``` It will retry the call in case discovery of the backend RPC implementation fails. Tip: Consider using `durable` especially when working with long-lived RPC flows, so that an exception there is handled properly and the corresponding feature keeps working. ## Serializable Types All parameters and return types must be `@Serializable`. Tip: See the [Kotlin serialization documentation](https://kotlinlang.org/docs/serialization.html) for more information about `kotlinx.serialization`. General rules: * Primitives, `String`, `Flow`, `Deferred` are serializable by default. * Enums are not serializable by default and must be annotated with `@Serializable`. * For types like `LocalDateTime`, implement a custom `KSerializer` (see [Data Transfer Objects](#data-transfer-objects)). ### Data Transfer Objects Domain objects are not always directly serializable for transport over RPC. In the example plugin, [ChatMessage](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/shared/src/main/kotlin/org/jetbrains/plugins/template/ChatMessage.kt) is the domain model used on both sides, but it contains `LocalDateTime`, which is not natively supported by `kotlinx.serialization`. The solution is a dedicated DTO class in the shared module, for example, [ChatMessageDto](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/shared/src/main/kotlin/org/jetbrains/plugins/template/dtos.kt): ```KOTLIN @Serializable data class ChatMessageDto( val id: String, val content: String, val author: String, val isMyMessage: Boolean, @Serializable(with = LocalDateTimeSerializer::class) val timestamp: LocalDateTime, val type: ChatMessage.ChatMessageType ) fun ChatMessageDto.toChatMessage(): ChatMessage { /* ... */ } fun ChatMessage.toChatMessageDto(): ChatMessageDto { /* ... */ } ``` Custom `KSerializer` implementations can handle types that are not natively serializable, for example, [LocalDateTimeSerializer](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/shared/src/main/kotlin/org/jetbrains/plugins/template/serializers.kt): ```KOTLIN object LocalDateTimeSerializer : KSerializer { private val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: LocalDateTime) { encoder.encodeString(value.format(formatter)) } override fun deserialize(decoder: Decoder): LocalDateTime { return LocalDateTime.parse(decoder.decodeString(), formatter) } } ``` The RPC interface uses the DTO, and each side converts to and from the domain model as needed: * Backend: converts `ChatMessage -> ChatMessageDto` before emitting via `getMessagesFlow()` * Frontend: converts `ChatMessageDto -> ChatMessage` after receiving via `toChatMessage()` ### ID Types To pass classes commonly used in IntelliJ Platform through RPC, use ID types and helper functions allowing to serialize and deserialize between the full and ID types: | Full Type |ID Type |Serialization |Deserialization | ------------------------------------------------------ | [Project](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/project/Project.java) |[ProjectId](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/project/shared/src/ProjectId.kt) |`Project.projectId()` `Project.projectIdOrNull()` |`ProjectId.findProject()` `ProjectId.findProjectOrNull()` | | [VirtualFile](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java) |[VirtualFileId](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/rpc/src/com/intellij/ide/vfs/VirtualFileId.kt) |`VirtualFile.rpcId()` |`VirtualFileId.virtualFile()` | | [Editor](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/editor/Editor.java) |[EditorId](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorId.kt) |`Editor.editorId()` `Editor.editorIdOrNull()` |`EditorId.findEditor()` `EditorId.findEditorOrNull()` | | [Icon](https://docs.oracle.com/en/java/javase/21/docs/api/java.desktop/javax/swing/Icon.html) |[IconId](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/rpc/src/com/intellij/ide/ui/icons/IconId.kt) |`Icon.rpcId()` `Icon.rpcIdOrNull()` |`IconId.icon()` | Note that these objects are not fully serializable, so the frontend only receives parts of the backend object. If possible, use only IDs on the frontend and work with the full objects on the backend side. ## RPC Examples ### Subscribing to the Backend State [BackendChatRepositoryModel](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/src/main/kotlin/org/jetbrains/plugins/template/BackendChatRepositoryModel.kt) holds a `MutableStateFlow` of messages on the backend: ```KOTLIN @Service(Service.Level.PROJECT) class BackendChatRepositoryModel { private val _messages = MutableStateFlow( listOf(/* initial messages */) ) fun getMessagesFlow(): Flow> { return _messages.map { messagesList -> messagesList.map(ChatMessage::toChatMessageDto) } } suspend fun sendMessage(messageContent: String) { // appends to _messages, triggering flow emissions } } ``` The [BackendChatRepositoryRpcApi](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/backend/src/main/kotlin/org/jetbrains/plugins/template/BackendChatRepositoryRpcApi.kt) RPC implementation simply delegates to this service: ```KOTLIN class BackendChatRepositoryRpcApi : ChatRepositoryRpcApi { override suspend fun getMessagesFlow(projectId: ProjectId): Flow> { val backendProject = projectId.findProjectOrNull() ?: return emptyFlow() return BackendChatRepositoryModel.getInstance(backendProject) .getMessagesFlow() } } ``` On the frontend, [FrontendChatRepositoryModel](https://github.com/JetBrains/intellij-platform-modular-plugin-template/blob/main/frontend/src/main/kotlin/org/jetbrains/plugins/template/chatApp/viewmodel/FrontendChatRepositoryModel.kt) subscribes to this flow and exposes it as a `StateFlow`: ```KOTLIN @Service(Level.PROJECT) class FrontendChatRepositoryModel( private val project: Project, coroutineScope: CoroutineScope ) : ChatRepositoryApi { override val messagesFlow: StateFlow> = flow { durable { ChatRepositoryRpcApi.getInstanceAsync() .getMessagesFlow(project.projectId()) .collect { valueFromBackend -> val mappedValue = valueFromBackend.map { messageDto -> messageDto.toChatMessage() } emit(mappedValue) } } }.stateIn( coroutineScope, initialValue = emptyList(), started = SharingStarted.Lazily ) } ``` In the code above, `messagesFlow` is initialized with an empty list. Since services are initialized lazily, the first subscriber will trigger the RPC connection, but the state will be `emptyList()` until the first backend emission arrives. If this matters for an implemented feature, make sure the service is initialized before its first use — for example, via subscribing to updates from the backend inside a dedicated [ProjectActivity](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt). ### Pushing Events from Backend to Frontend To push events from the backend to the frontend without an explicit request, use [ApplicationRemoteTopic](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/remote-topics/shared/src/com/intellij/platform/rpc/topics/ApplicationRemoteTopic.kt) or [ProjectRemoteTopic](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/remote-topics/shared/src/com/intellij/platform/rpc/topics/ProjectRemoteTopic.kt) instead of a regular RPC call. Example: 1. Define topic and event in the shared module: ```KOTLIN // shared module @Serializable data class NewMessageEvent( val projectId: ProjectId, val messageId: String ) val NEW_MESSAGE_TOPIC: ProjectRemoteTopic = ProjectRemoteTopic("chat.newMessage", NewMessageEvent.serializer()) ``` 1. Send events from the backend module: ```KOTLIN // backend module class BackendChatRepositoryModel { suspend fun sendMessage(messageContent: String) { val userMessage = chatMessageFactory.createUserMessage(messageContent) _messages.value += userMessage NEW_MESSAGE_TOPIC.send( NewMessageEvent(project.projectId(), userMessage.id) ) } } ``` 1. Handle events in the frontend module: ```KOTLIN // frontend module class NewMessageEventListener : ProjectRemoteTopicListener { override val topic = NEW_MESSAGE_TOPIC override fun handleEvent(event: NewMessageEvent) { // React to the new message event } } ``` 1. Register the listener via extension point in `modular.plugin.frontend.xml`: ```XML ``` Tip: See `ApplicationRemoteTopic`/`ProjectRemoteTopic`'s docs for more details. ## FAQ ### What to do with `AbstractMethodError`? If an error like this occurs: ``` java.lang.AbstractMethodError: Receiver class InterfaceApiClientStub does not define or inherit an implementation of the resolved method 'abstract void foo()' of interface InterfaceApi. ``` Make sure that all the functions in the interface are `suspend`. ### How to efficiently transfer `byte[]`? Wrap the data into a [fleet.rpc.core.Blob](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/fleet/rpc/srcCommonMain/fleet/rpc/core/Serialization.kt) for the sake of reducing the serialization overhead. Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Frontend, Backend, and Shared APIs This page lists APIs that commonly indicate where plugin code should live in a split architecture: * [Frontend APIs](#frontend-apis) * [Backend APIs](#backend-apis) * [Shared APIs](#shared-apis) The lists are intentionally conservative. Treat them as placement defaults rather than an exhaustive type system. If a feature needs a different placement, review the specific API behavior and validate the result in [Split Mode](split-mode-and-remote-development.html). Inspection `Plugin DevKit | Code | Inappropriate Frontend/Backend API usage in module` highlights inappropriate API usage. See also [Modular Plugins](modular-plugins.html) and [Split Mode Feature Development Guideline](split-mode-feature-development.html). ## Frontend APIs The following APIs usually belong in the frontend module: * [com.intellij.openapi.wm.ToolWindowFactory](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowFactory.kt) * [com.intellij.openapi.wm.ToolWindow](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/wm/ToolWindow.java) * [com.intellij.openapi.editor.toolbar.floating.FloatingToolbarProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/openapi/editor/toolbar/floating/FloatingToolbarProvider.kt) * [com.intellij.openapi.ui.DialogWrapper](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/openapi/ui/WindowWrapperBuilder.java) * [com.intellij.openapi.fileEditor.FileEditorProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/openapi/fileEditor/FileEditorProvider.java) * [com.intellij.openapi.ui.popup.JBPopupFactory](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/ui/popup/JBPopupFactory.java) * [com.intellij.codeInsight.editorActions.TypedHandlerDelegate](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/codeInsight/editorActions/TypedHandlerDelegate.java) * [com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterHandlerDelegate.java) * [com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterHandlerDelegateAdapter.java) * [com.intellij.codeInsight.highlighting.BraceMatcher](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-impl/src/com/intellij/codeInsight/highlighting/BraceMatcher.java) * [com.intellij.openapi.fileEditor.FileEditorManager.getFocusedEditor()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/openapi/fileEditor/FileEditorManager.java) * [com.intellij.execution.services.ServiceViewContributor](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/execution/services/ServiceViewContributor.java) * [com.intellij.ui.tree.AsyncTreeModel](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java) * [com.intellij.ui.jcef.JBCefClient](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ui.jcef/jcef/JBCefClient.java) * [com.intellij.notification.NotificationGroup](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/notification/NotificationGroupManager.java) * [com.intellij.openapi.wm.StatusBarWidgetFactory](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidgetFactory.java) * [com.intellij.openapi.options.Configurable](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/options/Configurable.java) * [com.intellij.codeInsight.editorActions.CopyPastePostProcessor](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/codeInsight/editorActions/CopyPastePostProcessor.java) * [com.intellij.openapi.editor.actionSystem.EditorActionHandler](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionHandler.java) * [com.intellij.openapi.editor.event.CaretListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/editor/event/CaretListener.java) * [com.intellij.openapi.editor.event.EditorMouseMotionListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/editor/event/EditorMouseMotionListener.java) * [com.intellij.openapi.editor.event.EditorMouseListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/editor/event/EditorMouseListener.java) * [com.intellij.openapi.editor.event.SelectionListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/editor/event/SelectionListener.java) * [com.intellij.openapi.editor.event.VisibleAreaListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/editor/event/VisibleAreaListener.java) * [com.intellij.openapi.editor.ex.FoldingListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/openapi/editor/ex/FoldingListener.java) * [com.intellij.openapi.ui.popup.JBPopupListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/ui/popup/JBPopupListener.java) * [com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/CommonDataKeys.java) * [com.intellij.openapi.actionSystem.PlatformDataKeys.TOOL_WINDOW](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/actionSystem/PlatformDataKeys.java) * [com.intellij.openapi.actionSystem.PlatformCoreDataKeys.SELECTED_ITEM](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/actionSystem/PlatformCoreDataKeys.java) * [com.intellij.openapi.actionSystem.PlatformCoreDataKeys.SELECTED_ITEMS](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/actionSystem/PlatformCoreDataKeys.java) * [com.intellij.openapi.actionSystem.PlatformCoreDataKeys.CONTEXT_COMPONENT](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/actionSystem/PlatformCoreDataKeys.java) ## Backend APIs The following APIs usually belong in the backend module: * [com.intellij.openapi.vfs.VirtualFileManager](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileManager.java) * [com.intellij.openapi.vfs.VfsUtil](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/openapi/vfs/VfsUtil.java) * [com.intellij.psi.stubs.StubIndex](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/psi/stubs/StubIndexKey.java) * [com.intellij.util.indexing.IndexExtension](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/util/src/com/intellij/util/indexing/IndexExtension.java) * [com.intellij.execution.configurations.ConfigurationType](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/execution/src/com/intellij/execution/configurations/ConfigurationType.java) * [com.intellij.psi.search.searches.ReferencesSearch](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/indexing-api/src/com/intellij/psi/search/searches/ReferencesSearch.java) * [com.intellij.model.search.SearchService](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/indexing-api/src/com/intellij/model/search/SearchService.kt) * [com.intellij.util.Query](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/util/Query.kt) * [com.intellij.ide.FileIconProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/ide/FileIconProvider.java) * [com.intellij.codeInsight.hints.InlayHintsProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.java) * [com.intellij.codeInsight.hints.declarative.InlayHintsProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/codeInsight/hints/declarative/InlayHintsProvider.kt) * [com.intellij.codeInsight.daemon.LineMarkerProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/codeInsight/daemon/LineMarkerProvider.java) * [com.intellij.codeInspection.LocalInspectionTool](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/codeInspection/CleanupLocalInspectionTool.java) * [com.intellij.psi.PsiReferenceContributor](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java) * [com.intellij.codeInsight.completion.CompletionContributor](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/codeInsight/completion/CompletionItemContributor.java) * [com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/json/backend/src/com/jetbrains/jsonSchema/extension/JsonSchemaProviderFactory.java) * [com.intellij.execution.actions.RunConfigurationProducer](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-api/src/com/intellij/execution/actions/CompatibleRunConfigurationProducer.java) * [com.intellij.ide.structureView.StructureViewBuilder](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewBuilderProvider.java) * [com.intellij.openapi.vfs.AsyncFileListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java) * [com.intellij.openapi.vfs.VirtualFileListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileListener.java) * [com.intellij.platform.backend.workspace.WorkspaceModelChangeListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/backend/workspace/src/WorkspaceModelTopics.kt) * [com.intellij.openapi.project.ProjectManagerListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/projectModel-api/src/com/intellij/openapi/project/ProjectManagerListener.java) * [com.intellij.execution.process.ProcessHandler](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/util/src/com/intellij/execution/process/ProcessHandler.java) * [com.intellij.openapi.actionSystem.CommonDataKeys.SYMBOLS](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/CommonDataKeys.java) * [com.intellij.openapi.actionSystem.PlatformCoreDataKeys.MODULE](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/openapi/actionSystem/PlatformCoreDataKeys.java) * [com.intellij.openapi.actionSystem.LangDataKeys.MODULE_CONTEXT](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-core/src/com/intellij/openapi/actionSystem/LangDataKeys.java) * [com.intellij.openapi.actionSystem.LangDataKeys.MODIFIABLE_MODULE_MODEL](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-core/src/com/intellij/openapi/actionSystem/LangDataKeys.java) ## Shared APIs The following APIs can be used in both frontend and backend modules. This list currently includes two categories of APIs: * language support–related extensions * project/application lifecycle listeners Language support should be implemented in the shared module so that PSI can be used on both sides. The frontend benefits from language support extensions because they enable a fast local editing experience and make it possible to build features on top of them. For example, a typing handler naturally belongs in the frontend, and it requires PSI for the file type it supports. The backend also benefits from language support because backend-specific extensions depend on it as well. For example, an inspection that naturally belongs in the backend requires PSI to traverse the file. The other category consists of application and project lifecycle listeners. Since their implementations may produce arbitrary side effects, it is up to the plugin developer to decide whether those effects should run in the backend or the frontend. For example, if a plugin needs to install and run an external tool after a project is loaded, it should be registered in the backend. If it needs to display a survey UI with a feedback form after a user changes a registry key, it should run in the frontend. * [com.intellij.lang.ParserDefinition](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/lang/ParserDefinition.java) * [com.intellij.openapi.util.registry.RegistryValueListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/util/src/com/intellij/openapi/util/registry/RegistryValueListener.java) * [com.intellij.openapi.application.ApplicationListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/application/ApplicationListener.java) * [com.intellij.openapi.project.ProjectCloseListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/projectModel-api/src/com/intellij/openapi/project/ProjectCloseListener.kt) * [com.intellij.openapi.project.ProjectManagerListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/projectModel-api/src/com/intellij/openapi/project/ProjectManagerListener.java) * [com.intellij.openapi.project.VetoableProjectManagerListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/projectModel-api/src/com/intellij/openapi/project/VetoableProjectManagerListener.java) * [com.intellij.ide.plugins.DynamicPluginListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/ide/plugins/DynamicPluginListener.kt) * [com.intellij.openapi.fileEditor.FileEditorManagerListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/openapi/fileEditor/FileEditorManagerListener.java) * [com.intellij.openapi.editor.event.DocumentListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/editor/event/DocumentListener.java) Shared code should stay lightweight. If shared logic starts depending on frontend-only or backend-only APIs, it usually belongs in a dedicated content module instead. Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Persistent State Component in Split Mode This article shows how to make a [Persistent State Component](persisting-state-of-components.html) synchronize correctly between the frontend and the backend. At a high level, it requires: 1. [Creating a persistent state component.](#1-create-a-persistentstatecomponent) 2. [Registering sync metadata with RemoteSettingInfoProvider.](#2-register-a-remotesettinginfoprovider) 3. [Declaring the settings in XML so the initial synchronization can happen.](#3-declare-the-settings-in-xml) 4. [Choosing the right sync direction for a use case.](#4-choose-the-right-sync-direction) This setup is especially important in [split mode](split-mode-and-remote-development.html), where settings may exist on both sides and need to stay in sync. ## 1. Create a `PersistentStateComponent` Start by implementing a settings component in a standard way. In the case of extending [SimplePersistentStateComponent](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/projectModel-api/src/com/intellij/openapi/components/SimplePersistentStateComponent.kt), it is recommended to override `noStateLoaded()`. This helps handle the case in which the remote side sends an empty state: instead of leaving the component in an unexpected state, it is explicitly reset it to its defaults. ```KOTLIN @State( name = "MySettings", storages = [Storage("my-settings.xml")] ) class MySettings : SimplePersistentStateComponent(State()) { class State { var mySetting: Boolean = true } override fun noStateLoaded() { // Reset to defaults when remote sends empty state: loadState(State()) } } ``` It is important, because during synchronization, one side may receive no previously stored state. If that happens, `noStateLoaded()` provides a safe and predictable fallback. ## 2. Register a `RemoteSettingInfoProvider` The next step is informing the platform that the persistent state component should participate in frontend/backend synchronization. This is achieved by implementing [RemoteSettingInfoProvider](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/ide/settings/RemoteSettingInfoProvider.kt) in the shared module, and registering it on both the frontend and the backend. In practice, this usually means either: * registering it in a shared XML module descriptor, or * registering the same provider in both frontend and backend XML module descriptors. Example: ```KOTLIN class MySettingsRemoteInfoProvider : RemoteSettingInfoProvider { override fun getRemoteSettingsInfo() = mapOf( "MySettings" to RemoteSettingInfo(direction = Direction.InitialFromFrontend) // Use InitialFromBackend for project-level settings ) } ``` Then register it in `plugin.xml`: ```XML ``` `RemoteSettingInfoProvider` supplies synchronization metadata for a persistent state component. In particular, it tells the platform which component should be synced and in which direction the initial state should flow. ## 3. Declare the Settings in XML This step is required for initial synchronization. If settings are not declared in XML, synchronization will not start until the user changes the setting manually for the first time. Use one of the following declarations, depending on the scope of settings: ```XML ``` These declarations make the settings visible to the synchronization infrastructure from the start. Without them, the platform does not know that the settings should be included in the initial synchronization. ## 4. Choose the Right Sync Direction When registering settings, it is required to define the [RemoteSettingInfo.direction](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-impl/src/com/intellij/ide/settings/RemoteSettingInfo.kt) determining where the initial value should come from. In most cases, the correct choice depends on whether the settings are application-level or project-level. Use the following guidance for choosing the right direction: | Direction |Recommended Use | ------------------------------ | `InitialFromFrontend` |The default choice for application-level settings. Use it unless there is a specific reason not to. | | `InitialFromBackend` |The default choice for project-level settings. Use it unless there is a specific reason not to. | | `OnlyFromFrontend` |Use when the setting is owned entirely by the frontend module and the backend module is not able to interpret and manage the setting. | | `OnlyFromBackend` |Use when the setting is owned entirely by the backend module and the frontend module is not able to interpret and manage the setting. | Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Plugin Management in Split Mode A plugin can be installed on the backend, on the frontend, or on both sides. This article explains how the platform determines that and how plugin synchronization works. A separate question is which modules of an installed plugin are actually loaded in each process. For split plugins, module loadability is controlled by content-module dependencies such as `intellij.platform.backend` and `intellij.platform.frontend`. See [Modular Plugins](modular-plugins.html) and [Split Mode](split-mode-and-remote-development.html) for details. ## Compatibility Check A plugin may be: * compatible only with the backend * compatible only with the frontend * compatible with both sides Compatibility is determined by the dependencies declared by the plugin and its content modules. By default, content modules are optional, which means that if a particular module's dependencies are not satisfied, that module is not loaded, but this does not affect the loading of other plugin modules. As a result, if the plugin dependencies are satisfied, the plugin is loaded. If a plugin consists of multiple modules and the dependencies of at least one module are satisfied, the plugin is loaded. For more details about modules and dependencies, see [Modular Plugins](modular-plugins.html). When a plugin is installed from JetBrains Marketplace, the plugin manager checks its compatibility with both the frontend and the backend and installs it wherever its dependencies are satisfied. ## Plugin Sync Flow When a project is opened, the frontend and backend plugin sets are compared. If a difference is detected, the IDE shows a plugin synchronization notification, allowing the user to choose which plugins to install on each side. ### Current Limitation Plugin synchronization currently works only for plugins that are available on [JetBrains Marketplace](https://plugins.jetbrains.com). If a plugin is installed only from a local ZIP or JAR file, the synchronization flow cannot resolve it for the other side through Marketplace metadata. For a more convenient installation method than ad hoc local archives, consider publishing the plugin to JetBrains Marketplace or using a [custom plugin repository](custom-plugin-repository.html). Note: Something missing? If a topic is not covered in the above sections, let us know via the Feedback widget displayed on the right, or [other channels](getting-help.html#problems-with-the-guide). Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback! # Plugin Structure Plugin Structure Learn about the plugin system structure and plugin components lifecycle. # Plugin Content Plugin distribution is built using the dedicated Gradle `buildPlugin` task (Reference: [2.x](tools-intellij-platform-gradle-plugin-tasks.html#buildPlugin), [1.x](tools-gradle-intellij-plugin.html#tasks-buildplugin)) or [Plugin DevKit](deploying-theme.html). The plugin distribution `.jar` file contains: * configuration file (`META-INF/plugin.xml`) ([Plugin Configuration File](plugin-configuration-file.html)) * classes implementing the plugin functionality * recommended: plugin logo file(s) (`META-INF/pluginIcon*.svg`) ([Plugin Logo](plugin-icon-file.html)) Tip: See [Distribution Size](plugin-user-experience.html#distribution-size) for important steps to optimize the plugin distribution file. Targeting a plugin distribution to a specific OS is not possible ([issue](https://youtrack.jetbrains.com/issue/MP-1896)). ## Plugin Without Dependencies A plugin consisting of a single `.jar` file is placed in the `/plugins` directory. ```PLANTUML @startuml skinparam TitleFontName JetBrains Sans skinparam TitleFontStyle plain skinparam TitleFontSize 16 skinparam DefaultTextAlignment left title |_ plugins |_ sample.jar // (Plugin distribution) // |_ com |_ company |_ Sample.class // (Class ""com.company.Sample"") // |_ ... |_ META-INF |_ plugin.xml // (Plugin Configuration File) // |_ pluginIcon.svg // (Plugin Logo) // |_ pluginIcon_dark.svg // (Plugin Logo, dark variant) // end title @enduml ``` ## Plugin With Dependencies The plugin `.jar` file is placed in the `/lib` folder under the plugin's "root" folder, together with all required bundled libraries. All JARs from the `/lib` folder are automatically added to the classpath (see also [Plugin Class Loaders](plugin-class-loaders.html)). Warning: Do Not Repackage Libraries Do not repackage libraries into the main plugin JAR file. Otherwise, [Plugin Verifier](verifying-plugin-compatibility.html) will yield false positives for unresolved classes and methods. ```PLANTUML @startuml skinparam TitleFontName JetBrains Sans skinparam TitleFontStyle plain skinparam TitleFontSize 16 skinparam DefaultTextAlignment left title |_ plugins |_ sample |_ lib |_ lib_foo.jar // (Required bundled library #1) // |_ lib_bar.jar // (Required bundled library #2) // |_ ... |_ sample.jar // (Plugin distribution) // |_ com |_ company |_ Sample.class // (Class ""com.company.Sample"") // |_ ... |_ META-INF |_ plugin.xml // (Plugin Configuration File) // |_ pluginIcon.svg // (Plugin Logo) // |_ pluginIcon_dark.svg // (Plugin Logo, dark variant) // end title @enduml ``` # Bundling Plugin API Sources If a plugin exposes its own API that is meant to be used by other plugins, it is worth considering bundling the plugin's API sources in the ZIP distribution. Being able to see API sources drastically improves the development experience, and it is highly recommended to bundle them. If a third-party plugin uses [IntelliJ Platform Gradle Plugin (2.x)](tools-intellij-platform-gradle-plugin.html) or [Gradle IntelliJ Plugin (1.x)](tools-gradle-intellij-plugin.html) (1.7.0+) and adds a dependency to a plugin bundling sources in the ZIP distribution, sources will be automatically attached to the plugin library and visible in IDE when developers navigate to the API classes. ## API Sources Location The API source JARs must be located in the `example-plugin.zip!/plugin/lib/src` directory in the plugin ZIP distribution, e.g.: ```PLANTUML @startuml skinparam TitleFontName JetBrains Sans skinparam TitleFontStyle plain skinparam TitleFontSize 16 skinparam DefaultTextAlignment left title example-plugin.zip // (Plugin distribution file) // |_ example-plugin |_ lib |_ example-plugin.jar // (Plugin) // |_ src |_ example-plugin-api-src.jar // (API sources JAR) // end title @enduml ``` The plugin distribution ZIP file can contain multiple source JARs, and there are no strict rules for the source JAR names. ## Defining Plugin API Usually, the following classes are considered as plugin API: * [Extension Point](plugin-extension-points.html) and related classes * [Listener](plugin-listeners.html) and related classes * [Services](plugin-services.html) and utilities that provide access to the plugin data/behavior Keep in mind that API should be stable and change very rarely as every incompatible change will break the client plugins. It is also recommended to organize the plugin code in multiple modules with clear responsibilities, e.g.: * `example-plugin-api` - a module containing API * `example-plugin-impl` - a module containing plugin features code that are not meant to be extended or used by client plugins General rule to define API is to include classes that are likely to be consumed by the client plugins code. Of course, more complex plugins may require more fine-grained structure. See [Gradle IntelliJ Plugin - Usage Examples](tools-gradle-intellij-plugin-examples.html). ## Bundling API Sources in Gradle Build Script In the simplest case, if a project consists of a single module and plugin API is clearly isolated in a package, e.g. `com.example.plugin.openapi`, including the source JAR can be achieved by adding the following snippet to the `tasks` section of the Gradle build script: Kotlin: ```KOTLIN tasks { val createOpenApiSourceJar by registering(Jar::class) { // Java sources from(sourceSets.main.get().java) { include("**/com/example/plugin/openapi/**/*.java") } // Kotlin sources from(kotlin.sourceSets.main.get().kotlin) { include("**/com/example/plugin/openapi/**/*.kt") } destinationDirectory.set(layout.buildDirectory.dir("libs")) archiveClassifier.set("src") } buildPlugin { dependsOn(createOpenApiSourceJar) from(createOpenApiSourceJar) { into("lib/src") } } } ``` Groovy: ```GROOVY task createOpenApiSourceJar(type: Jar) { // Java sources from(sourceSets.main.java) { include '**/com/example/plugin/openapi/**/*.java' } // Kotlin sources from(sourceSets.main.kotlin) { include '**/com/example/plugin/openapi/**/*.kt' } destinationDirectory = layout.buildDirectory.dir('libs') archiveClassifier = 'src' } buildPlugin { dependsOn(createOpenApiSourceJar) from(createOpenApiSourceJar) { into 'lib/src' } } ``` The above configuration will create a source JAR containing Java and Kotlin source files from the `com.example.plugin.openapi` package and add it to the final plugin ZIP distribution in the required `example-plugin.zip!/example-plugin/lib/src` directory. If your plugin is a Gradle project and there is no clear open API package separation, it is recommended to restructure the plugin project to a Gradle multi-project variant and create a dedicated open API subproject that contains all API sources to be included in the final distribution created by the main plugin Gradle project. # Class Loaders Each plugin has a dedicated class loader, which is used to load the plugin's classes. This allows each plugin to use a different library version, even if the same library is used by the IDE itself or by another plugin. ## Overriding IDE Dependencies Gradle 7 introduced the `implementation` scope that replaced the `compile` scope. For this setup, to use a library dependency declared by a plugin instead of the version bundled in the IDE, add the following snippet to the Gradle build script: ```KOTLIN configurations.all { resolutionStrategy.sortArtifacts(ResolutionStrategy.SortOrder.DEPENDENCY_FIRST) } ``` Tip: Bundled Libraries [Third-Party Software and Licenses](https://www.jetbrains.com/legal/third-party-software/) list all bundled libraries and their versions for each IDE. ## Loading Classes from Plugin Dependencies By default, the main IDE class loader loads classes that are not found in the plugin class loader. However, in the `[plugin.xml](plugin-configuration-file.html)` file, you may use the [<depends>](plugin-configuration-file.html#idea-plugin__depends) element to specify that a [plugin depends](plugin-dependencies.html) 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. ## Using `ServiceLoader` Some libraries use [ServiceLoader](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/ServiceLoader.html) to detect and load implementations. To make it work in a plugin, the context class loader must be set to the plugin's classloader and restored afterward with the original one around initialization code: Kotlin: ```KOTLIN val currentThread = Thread.currentThread() val originalClassLoader = currentThread.contextClassLoader val pluginClassLoader = this.javaClass.classLoader try { currentThread.contextClassLoader = pluginClassLoader // code working with ServiceLoader here } finally { currentThread.contextClassLoader = originalClassLoader } ``` Java: ```JAVA Thread currentThread = Thread.currentThread(); ClassLoader originalClassLoader = currentThread.getContextClassLoader(); ClassLoader pluginClassLoader = this.getClass().getClassLoader(); try { currentThread.setContextClassLoader(pluginClassLoader); // code working with ServiceLoader here } finally { currentThread.setContextClassLoader(originalClassLoader); } ``` # Actions The IntelliJ Platform provides the concept of actions. An action is a class derived from [AnAction](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java), whose `actionPerformed()` method is called when its menu item or toolbar button is selected. Actions are the most common way for a user to invoke the functionality of your plugin. An action can be invoked from a menu or a toolbar, using a keyboard shortcut or the `Help | Find Action...` lookup. 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 a menu. The user can customize all registered actions via [Menus and Toolbars](https://www.jetbrains.com/help/idea/customize-actions-menus-and-toolbars.html) settings. Please see [Action System](action-system.html) on how to create and register actions in the IDE. # Extensions Extensions are the most common way for a plugin to extend the IntelliJ-based IDE's functionality. They are implementations of specific interfaces or classes that are [registered](#declaring-extensions) in the plugin descriptor. Provided extension implementations are called by the platform or other plugins to customize and extend the IDE's functionality. ## Common Extension Use Cases The following are some of the most common tasks achieved using extensions: * The [com.intellij.toolWindow](https://jb.gg/ipe?extensions=com.intellij.toolWindow) extension point allows plugins to add [tool windows](tool-windows.html) (panels displayed at the sides of the IDE user interface); * The [com.intellij.applicationConfigurable](https://jb.gg/ipe?extensions=com.intellij.applicationConfigurable) extension point and [com.intellij.projectConfigurable](https://jb.gg/ipe?extensions=com.intellij.projectConfigurable) extension point allow plugins to add pages to the [Settings dialog](settings.html); * [Custom language plugins](custom-language-support.html) use many extension points to extend various language support features in the IDE. There are more than 1700 extension points available in the platform and the bundled plugins, allowing customizing different parts of the IDE behavior. ## Exploring Available Extensions ### Documentation * [IntelliJ Platform Extension Point and Listener List](intellij-platform-extension-point-list.html) * [IntelliJ Platform Plugins Extension Point and Listener List](intellij-community-plugins-extension-point-list.html) (bundled plugins in IntelliJ IDEA) * [Open Source Plugins Extension Point and Listener List](oss-plugins-extension-point-list.html) Lists for other IDEs are available under Product Specific (for example, [PhpStorm](php-extension-point-list.html)). ### IntelliJ Platform Explorer Browse usages inside existing implementations of open-source IntelliJ Platform plugins via [IntelliJ Platform Explorer](https://jb.gg/ipe). ### Code Insight Alternatively (or when using 3rd party extension points), all available extension points for the specified namespace (`defaultExtensionNs`) can be listed using auto-completion inside the [<extensions>](plugin-configuration-file.html#idea-plugin__extensions) block in `[plugin.xml](plugin-configuration-file.html)`. Use `View | Quick Documentation` in the lookup list to access more information about the extension point and implementation (if applicable). See [Explore the IntelliJ Platform API](explore-api.html) for more information and strategies. ## Declaring Extensions Tip: Auto-completion, Quick Documentation, and other code insight features are available on extension point tags and attributes in `plugin.xml`. Procedure: Declaring Extension To clarify this procedure, consider the following sample section of the `plugin.xml` file that defines two extensions designed to access the [com.intellij.appStarter](https://jb.gg/ipe?extensions=com.intellij.appStarter) and [com.intellij.projectTemplatesFactory](https://jb.gg/ipe?extensions=com.intellij.projectTemplatesFactory) extension points in the IntelliJ Platform, and one extension to access the `another.plugin.myExtensionPoint` extension point in another plugin `another.plugin`: ```XML ``` Procedure: Implementing Extension ### Extension Properties Code Insight Several tooling features are available to help configure bean class extension points in `plugin.xml`. #### Required Properties Properties annotated with [RequiredElement](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/extensions/RequiredElement.java) are inserted automatically and validated. If the given property is allowed to have an explicit empty value, set `allowEmpty` to `true`. #### Class names Property names matching the following list will resolve to a fully qualified class name: * `implementation` * `className` * ending with `Class` (case-sensitive) * `serviceInterface`/`serviceImplementation` A required parent type can be specified in the [extension point declaration](plugin-extension-points.html) via [<with>](plugin-configuration-file.html#idea-plugin__extensionPoints__extensionPoint__with): ```XML ``` #### Custom resolve Property name `language` (or ending in `*Language`) resolves to all present [Language](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/lang/Language.java) IDs. Similarly, `action` and `actionId` (2024.3+) resolve to all registered [<action>](plugin-configuration-file.html#idea-plugin__actions__action) IDs. #### Deprecation/ApiStatus Properties marked as `@Deprecated` or annotated with any of [ApiStatus](https://github.com/JetBrains/java-annotations/tree/24.0.0/common/src/main/java/org/jetbrains/annotations/ApiStatus.java) `@Internal`, `@Experimental`, `@ScheduledForRemoval`, or `@Obsolete` will be highlighted accordingly. #### Enum properties `Enum` attributes support code insight with lowerCamelCased notation. Note: The `Enum` implementation must not override `toString()`. #### I18n Annotating with [@Nls](https://github.com/JetBrains/java-annotations/tree/24.0.0/common/src/main/java/org/jetbrains/annotations/Nls.java) validates a UI `String` capitalization according to the text property `Capitalization` enum value. # Services A service is a plugin component loaded on demand when your plugin calls the `getService()` method of corresponding [ComponentManager](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/extensions/src/com/intellij/openapi/components/ComponentManager.java) instance (see [Types](#types)). The IntelliJ Platform ensures that only one instance of a service is loaded even though it is called several times. Services are used to encapsulate logic operating on a set of related classes or to provide some reusable functionality that can be used across the plugin project. Conceptually, they don't differ from the service classes in other languages or frameworks. A service must have an implementation class used for service instantiation. A service may also have an interface class used to obtain the service instance and provide the service's API. A service needing a shutdown hook/cleanup routine can implement [Disposable](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/util/src/com/intellij/openapi/Disposable.java) and perform necessary work in `dispose()` (see [Automatically Disposed Objects](disposers.html#automatically-disposed-objects)). Note: Services as API If declared services are intended to be used by other plugins depending on your plugin, consider [bundling their sources](bundling-plugin-openapi-sources.html) in the plugin distribution. ## Types The IntelliJ Platform offers three types of services: application-level services (global singleton), project-level services, and module-level services. For the latter two, a separate instance of the service is created for each instance of its corresponding scope, see [Project Model Introduction](project-model.html). Note: Avoid using module-level services as it can increase memory usage for projects with many modules. ## Constructor To improve startup performance, avoid any heavy initializations in the constructor. Project/Module-level service constructors can have a [Project](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/project/Project.java)/[Module](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/module/Module.java) argument. Warning: Do not use Constructor Injection Using constructor injection of dependency services is deprecated (and not supported in [Light Services](#light-services)) for performance reasons. Other service dependencies must be [acquired only when needed](#retrieving-a-service) in all corresponding methods, e.g., if you need a service to get some data or execute a task, retrieve the service before calling its methods. Do not retrieve services in constructors to store them in class fields. Use inspection Plugin DevKit | Code | Non-default constructors for service and extension class to verify code. ### Kotlin Coroutines When using [Kotlin Coroutines](kotlin-coroutines.html), a distinct service [scope](coroutine-scopes.html) can be injected as parameter. The Application Service and Project Service scopes are bound to an application and project [service](#types) lifetimes accordingly. They are children of the [Intersection Scopes](coroutine-scopes.html#intersection-scopes), which means that they are canceled when the application/project is closed or a plugin is unloaded. The service scope is provided to services via constructor injection. The following constructor signatures are supported: * `MyService(CoroutineScope)` for application and project services * `MyProjectService(Project, CoroutineScope)` for project services Each service instance receives its own scope instance. The injected scopes' contexts contain [Dispatchers.Default](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html) and [CoroutineName(serviceClass)](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/). See [Launching Coroutine From Service Scope](launching-coroutines.html#launching-coroutine-from-service-scope) for full samples. ## Light Services A service not going to be overridden or exposed as API to other plugins does not need to be registered in `[plugin.xml](plugin-configuration-file.html)` (see [Declaring a Service](#declaring-a-service)). Instead, annotate the service class with [@Service](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/components/Service.java) (see [Examples](#examples)). The service instance will be created in the scope according to the caller (see [Retrieving a Service](#retrieving-a-service)). ### Light Service Restrictions * None of these attributes/restrictions (available for [registration of non-light services](#declaring-a-service)) is allowed: `id`, `os`, `client`, `overrides`, `configurationSchemaKey`/`preload` (Internal API). * There is no separate headless/test implementation required. * Service class must be `final`. * [Constructor injection](#ctor) of dependency services is not supported. * If an application-level service is a [PersistentStateComponent](persisting-state-of-components.html), roaming must be disabled (`roamingType = RoamingType.DISABLED`). Use these inspections to verify above restrictions and highlight non-light services that can be converted (2023.3): * Plugin DevKit | Code | Light service must be final * Plugin DevKit | Code | Mismatch between light service level and its constructor * Plugin DevKit | Code | A service can be converted to a light one and corresponding Plugin DevKit | Plugin descriptor | A service can be converted to a light one for `plugin.xml` ### Examples Java: Application-level light service: ```JAVA @Service public final class MyAppService { public void doSomething(String param) { // ... } } ``` Project-level light service example: ```JAVA @Service(Service.Level.PROJECT) public final class MyProjectService { private final Project myProject; MyProjectService(Project project) { myProject = project; } public void doSomething(String param) { String projectName = myProject.getName(); // ... } } ``` Kotlin: Application-level light service: ```KOTLIN @Service class MyAppService { fun doSomething(param: String) { // ... } } ``` Project-level light service example: ```KOTLIN @Service(Service.Level.PROJECT) class MyProjectService(private val project: Project) { fun doSomething(param: String) { val projectName = project.name // ... } } ``` ## Declaring a Service To register a non-[Light Service](#light-services), distinct extension points are provided for each type: * `com.intellij.applicationService` – application-level service * `com.intellij.projectService` – project-level service * `com.intellij.moduleService` – module-level service (not recommended, see [Note](#types)) The service implementation is specified in the required `serviceImplementation` attribute. ### Service API To expose a service's API, create a separate class for `serviceInterface` and extend it in the corresponding class registered in `serviceImplementation`. If `serviceInterface` isn't specified, it is supposed to have the same value as `serviceImplementation`. Use inspection Plugin DevKit | Plugin descriptor | Plugin.xml extension registration to highlight redundant `serviceInterface` declarations. ### Additional Attributes A service can be restricted to a certain OS via the `os` attribute. To provide a custom implementation for test or headless environment, specify `testServiceImplementation` or `headlessImplementation` respectively. ### Examples Java: Application-level service: * Interface: ```JAVA public interface MyAppService { void doSomething(String param); } ``` * Implementation: ```JAVA final class MyAppServiceImpl implements MyAppService { @Override public void doSomething(String param) { // ... } } ``` Project-level service: * Interface: ```JAVA public interface MyProjectService { void doSomething(String param); } ``` * Implementation: ```JAVA final class MyProjectServiceImpl implements MyProjectService { private final Project myProject; MyProjectServiceImpl(Project project) { myProject = project; } public void doSomething(String param) { String projectName = myProject.getName(); // ... } } ``` Kotlin: Application-level service: * Interface: ```KOTLIN interface MyAppService { fun doSomething(param: String) } ``` * Implementation: ```KOTLIN internal class MyAppServiceImpl : MyAppService { override fun doSomething(param: String) { // ... } } ``` Project-level service: * Interface: ```KOTLIN interface MyProjectService { fun doSomething(param: String) } ``` * Implementation: ```KOTLIN internal class MyProjectServiceImpl(private val project: Project) : MyProjectService { fun doSomething(param: String) { val projectName = project.name // ... } } ``` Registration in `plugin.xml`: ```XML ``` ## Retrieving a Service Warning: Correct Service Retrieval Never acquire service instances prematurely or store them in fields for later use. Instead, always obtain service instances directly and only at the location where they're needed. Failing to do so will lead to unexpected exceptions and severe consequences for the plugin's functionality. Such problems are highlighted via inspections (2023.3): * Plugin DevKit | Code | Application service assigned to a static final field or immutable property * Plugin DevKit | Code | Incorrect service retrieving * Plugin DevKit | Code | Simplifiable service retrieving Getting a service doesn't need a read action and can be performed from any thread. If a service is requested from several [threads](threading-model.html), it will be initialized in the first thread, and other threads will be blocked until it is fully initialized. Java: ```JAVA MyAppService applicationService = ApplicationManager.getApplication().getService(MyAppService.class); MyProjectService projectService = project.getService(MyProjectService.class); ``` Service implementations can wrap these calls with convenient static `getInstance()` or `getInstance(Project)` method: ```JAVA MyAppService applicationService = MyAppService.getInstance(); MyProjectService projectService = MyProjectService.getInstance(project); ``` Kotlin: ```KOTLIN val applicationService = service() val projectService = project.service() ``` ### Getting Service Flow ```PLANTUML @startuml skinparam monochrome true skinparam DefaultFontName JetBrains Sans skinparam DefaultFontSize 13 skinparam DefaultTextAlignment center skinparam NoteTextAlignment left ' default 1.5 skinparam ActivityBorderThickness 1 ' default 2 skinparam PartitionBorderThickness 1.5 :getService; note right Allowed in any thread. Call on demand only. Never cache the result. Do not call in constructors unless needed. end note if (Is Light Service) then (yes) else (no) if (Is Service Declaration Found) then (yes) else (no) :Return null; detach endif endif if (Is Created and Initialized?) then (yes) else (no) if (Is Container Active?) then (yes) partition "synchronized\non service class" { if (Is Created and Initialized?) then (yes) else (no) if (Is Initializing?) then (yes) :Throw PluginException (Cyclic Service Initialization); detach else (no) partition "non-cancelable" { :Create Instance] note right Avoid getting other services to reduce the initialization tree. The fewer the dependencies, the faster and more reliable initialization. end note :Register to be Disposed on Container Dispose (Disposable only)] :Load Persistent State (PersistentStateComponent only)] } endif endif } else (disposed or dispose in progress) :Throw ProcessCanceledException; detach endif endif :Return Instance; @enduml ``` ## Sample Plugin To clarify how to use services, consider the maxOpenProjects sample plugin available in the [code samples](https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/max_opened_projects). This plugin has an application service counting the number of currently opened projects in the IDE. If this number exceeds the maximum number of simultaneously opened projects allowed by the plugin (3), it displays an information message. See [Code Samples](code-samples.html) on how to set up and run the plugin. # Listeners Listeners allow plugins to subscribe to events delivered through the message bus (see [Messaging infrastructure](messaging-infrastructure.html) for details). Listeners are defined at the application (global) or [project](project.html) level. Tip: Locating Listeners/Topics All available listeners/topics are listed on [IntelliJ Platform Extension Point and Listener List](intellij-platform-extension-point-list.html) and [IntelliJ Platform Plugins Extension Point and Listener List](intellij-community-plugins-extension-point-list.html) under Listeners sections. Browse usages inside existing implementations of open-source IntelliJ Platform plugins via [IntelliJ Platform Explorer](https://jb.gg/ipe). Listener implementations must be stateless and may not implement life-cycle (e.g., `Disposable`). Use inspection Plugin DevKit | Code | Listener implementation implements 'Disposable' to verify (2023.3). Declarative registration of listeners allows achieving better performance than registering listeners from code. The advantage is because listener instances get created lazily — the first time an event is sent to the topic — and not during application startup or project opening. ## Defining Application-Level Listeners To define an application-level listener, add the [<applicationListeners>](plugin-configuration-file.html#idea-plugin__applicationListeners) section to `[plugin.xml](plugin-configuration-file.html)`: ```XML ``` The `topic` attribute specifies the listener interface corresponding to the type of events to receive. Usually, this is the interface used as the type parameter of the [Topic](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/extensions/src/com/intellij/util/messages/Topic.java) instance for the type of events. The `class` attribute specifies the class in the plugin that implements the listener interface and receives the events. As a specific example, to receive events about all [Virtual File System](virtual-file-system.html) changes, implement the `BulkFileListener` interface, corresponding to the topic `VirtualFileManager.VFS_CHANGES`. To subscribe to this topic from code, use something like the following snippet: ```JAVA messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { @Override public void after(@NotNull List events) { // handle the events } }); ``` To use declarative registration, it's no longer required to reference the `Topic` instance. Instead, refer directly to the listener interface class: ```XML ``` Then provide the listener implementation: ```JAVA package myPlugin; final class MyVfsListener implements BulkFileListener { @Override public void after(@NotNull List events) { // handle the events } } ``` ## Defining Project-Level Listeners [Project](project.html)-level listeners are registered in the same way, except that the top-level tag is [<projectListeners>](plugin-configuration-file.html#idea-plugin__projectListeners). They can be used to listen to project-level events, for example, [tool window](tool-windows.html) operations: ```XML ``` The class implementing the listener interface can define a one-argument constructor accepting a [Project](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/project/Project.java), and it will receive the instance of the project for which the listener is created: ```JAVA package myPlugin; final class MyToolWindowListener implements ToolWindowManagerListener { private final Project project; MyToolWindowListener(Project project) { this.project = project; } @Override public void stateChanged(@NotNull ToolWindowManager toolWindowManager) { // handle the state change } } ``` ## Additional Attributes Registration of listeners can be restricted using the following attributes. `os` : Allows restricting listener to a given OS, e.g., `os="windows"` for Windows only. `activeInTestMode` : Set to `false` to disable listener if [Application.isUnitTestMode()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/application/Application.java) returns `true` `activeInHeadlessMode` : Set to `false` to disable the listener if [Application.isHeadlessEnvironment()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/application/Application.java) returns `true`. Also covers `activeInTestMode` as test mode implies headless mode. Note: If declared listener topics are intended to be used by other plugins depending on your plugin, consider [bundling their sources](bundling-plugin-openapi-sources.html) in the plugin distribution. # Extension Points Note: See [Plugin Extensions](plugin-extensions.html) for using extension points in your plugin. By defining extension points in your plugin, you can allow other plugins to extend your plugin's functionality. There are two types of extension points: * Interface extension points allow other plugins to extend your plugins with code. When defining an interface extension point, specify an interface, and other plugins will provide classes implementing that interface. The providing plugin can then invoke methods on this interface. In most cases, the interface can be annotated with `@ApiStatus.OverrideOnly` (see [Override-Only API](verifying-plugin-compatibility.html#override-only-api)). * Bean extension points allow other plugins to extend a plugin with data. Specify the fully qualified name of an extension class, and other plugins will provide data that will be turned into instances of that class. Procedure: Declaring Extension Point The plugin that contributes to the extension point will read the specified properties from the `plugin.xml` file. If extension implementations are filtered according to [dumb mode](indexing-and-psi-stubs.html#dumb-mode), the base class should be marked with [PossiblyDumbAware](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/project/PossiblyDumbAware.java) to highlight this. Use [DumbService.getDumbAwareExtensions()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/project/DumbService.kt) to retrieve dumb-aware implementations. Base classes for extensions requiring a key: * [LanguageExtension](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/lang/LanguageExtension.java) * [FileTypeExtension](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeExtension.java) * [ClassExtension](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/util/ClassExtension.java) * [KeyedExtensionCollector](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/util/KeyedExtensionCollector.java) Note: See the [Bundling Plugin API Sources](bundling-plugin-openapi-sources.html) section explaining how to expose extension points sources to other plugins. ## Example Consider example extension points declarations: `myPlugin/META-INF/plugin.xml` ```XML my.plugin ``` The `com.example.MyBeanClass` bean class used in the above `plugin.xml` file is implemented as follows: `myPlugin/src/com/myplugin/MyBeanClass.java` ```JAVA public final class MyBeanClass extends AbstractExtensionPointBean { @Attribute("key") public String key; @Attribute("implementationClass") public String implementationClass; public String getKey() { return key; } public String getClass() { return implementationClass; } } ``` Tip: See [Extension properties code insight](plugin-extensions.html#extension-properties-code-insight) on how to provide smart completion/validation. For the above extension points, their usage in anotherPlugin would look like this (see also [Declaring Extensions](plugin-extensions.html#declaring-extensions)): `anotherPlugin/META-INF/plugin.xml` ```XML another.plugin my.plugin ``` ## Using Extension Points To refer to all registered extension instances at runtime, declare an [ExtensionPointName](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPointName.kt) with private visibility passing in the fully qualified name matching its [declaration in plugin.xml](#declaring-extension-points). If needed, provide a public method to query registered extensions (Sample: [TestSourcesFilter.isTestSources()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java)). `myPlugin/src/com/myplugin/MyExtensionUsingService.java` ```JAVA @Service public final class MyExtensionUsingService { private static final ExtensionPointName EP_NAME = ExtensionPointName.create("my.plugin.myExtensionPoint1"); public void useRegisteredExtensions() { for (MyBeanClass extension : EP_NAME.getExtensionList()) { String key = extension.getKey(); String clazz = extension.getClass(); // ... } } } ``` A gutter icon for the `ExtensionPointName` declaration allows navigating to the corresponding [<extensionPoint>](plugin-configuration-file.html#idea-plugin__extensionPoints__extensionPoint) declaration in `plugin.xml`. Code insight is available for the extension point name String literal (2022.3). ### Error Handling When processing extension implementations or registrations, there might be errors, compatibility and configuration issues. Use [PluginException](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/diagnostic/PluginException.java) to log and correctly attribute the causing plugin for [builtin error reporting](ide-infrastructure.html#error-reporting). To report use of a deprecated API, use `PluginException.reportDeprecatedUsage()` methods. Examples: * [CompositeFoldingBuilder.assertSameFile()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/lang/folding/CompositeFoldingBuilder.java) * [InspectionProfileEntry.getDisplayName()](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java) ## Dynamic Extension Points To support [Dynamic Plugins](dynamic-plugins.html), an extension point must adhere to specific usage rules: * extensions are enumerated on every use, and extension instances are not stored anywhere * alternatively, an [ExtensionPointListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPointListener.kt) can perform any necessary updates of data structures (register via `ExtensionPointName.addExtensionPointListener()`) Extension points matching these conditions can then be marked as dynamic by adding `dynamic="true"` in their declaration: ```XML ``` All non-dynamic extension points are highlighted via Plugin DevKit | Plugin descriptor | Plugin.xml dynamic plugin verification inspection. # Components (Deprecated) Warning: Deprecation Notice When writing new plugins, creating Components must be avoided. Any existing Components should be migrated to services, extensions, or listeners (see below). Plugin Components are a legacy feature supported for compatibility with plugins created for older versions of the IntelliJ Platform. Plugins using Components don't support [dynamic loading](dynamic-plugins.html) (the ability to install, update, and uninstall plugins without restarting the IDE). Plugin Components are defined in the ``, ``, and `` sections in a [Plugin Configuration File](plugin-configuration-file.html). ## Migration To migrate existing code from Components to modern APIs, see the following guidelines. ### Manage State To manage some state or logic that is only necessary when the user performs a specific operation, use a [Service](plugin-services.html). ### Persisting State To store the state of your plugin at the application or project level, use a [Service](plugin-services.html) and implement the `PersistentStateComponent` interface. See [Persisting State of Components](persisting-state-of-components.html) for details. ### Subscribing to Events To subscribe to events, use a [listener](plugin-listeners.html) or create an [extension](plugin-extensions.html) for a dedicated extension point (for example, [com.intellij.editorFactoryListener](https://jb.gg/ipe?extensions=com.intellij.editorFactoryListener) ) if one exists for the event to subscribe to. ### Application Startup Warning: Executing code on application startup should be avoided whenever possible because it slows down startup. Plugin code should only be executed when projects are opened (see [Project Open](#project-open)) or when the user invokes an action of a plugin. If this can't be avoided, add a [listener](plugin-listeners.html) subscribing to the [AppLifecycleListener](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/ide-core/src/com/intellij/ide/AppLifecycleListener.java) topic. See also [Running Tasks Once](ide-infrastructure.html#running-tasks-once). ### Project Open 2023.1 and later: Using [Kotlin coroutines](kotlin-coroutines.html), implement [ProjectActivity](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt) and register in [com.intellij.postStartupActivity](https://jb.gg/ipe?extensions=com.intellij.postStartupActivity) extension point . Examples: * [PowerSaveModeNotifier](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/lang-impl/src/com/intellij/ide/actions/PowerSaveModeNotifier.kt) * [TipOfTheDayStartupActivity](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/tips-of-the-day/src/com/intellij/ide/TipOfTheDayStartupActivity.kt) Implementation in [Kotlin](using-kotlin.html) is required because Java doesn't support suspending functions. Pre-2023.1: To execute code when a project is being opened, use one of these two [extensions](plugin-extensions.html): `com.intellij.postStartupActivity` : [StartupActivity](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt) for immediate execution on [EDT](threading-model.html). Implement `DumbAware` to indicate activity can run in a background thread (in parallel with other such tasks). `com.intellij.backgroundPostStartupActivity` : [StartupActivity.Background](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt) for execution with a 5-second delay in a background thread. Any long-running or CPU-intensive tasks should be made visible to users by using `ProgressManager.run(Task.Backgroundable)` (see [Background Processes](background-processes.html)). Access to indexes must be wrapped with [DumbService](indexing-and-psi-stubs.html#dumb-mode), see also [Threading Model](threading-model.html). See also [Running Tasks Once](ide-infrastructure.html#running-tasks-once). ### Project and Application Close To execute code on project closing or application shutdown, implement the [Disposable](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/util/src/com/intellij/openapi/Disposable.java) interface in a [Service](plugin-services.html) and place the code in the `dispose()` method. Alternatively, use `Disposer.register()` passing a `Project` or `Application` service instance as the `parent` argument (see [Choosing a Disposable Parent](disposers.html#choosing-a-disposable-parent)). # Plugin Configuration File The `plugin.xml` configuration file contains all the information about the plugin, which is displayed in the [plugins' settings dialog](https://www.jetbrains.com/help/idea/managing-plugins.html), and all registered extensions, actions, listeners, etc. The sections below describe all the elements in detail. The example `plugin.xml` files can be found in the [IntelliJ SDK Docs Code Samples](https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/README.md) repository. ## Additional Plugin Configuration Files A plugin can contain additional configuration files beside the main `plugin.xml`. They have the same format, and they are included with the `config-file` attribute of [<depends>](#idea-plugin__depends) elements specifying [plugin dependencies](plugin-dependencies.html). However, some elements and attributes required in `plugin.xml` are ignored in additional configuration files. If the requirements differ, the documentation below will state it explicitly. One use case for additional configuration files is when a plugin provides optional features that are only available in some IDEs and require [certain modules](plugin-compatibility.html#modules-specific-to-functionality). ## Useful Resources Please make sure to follow the guidelines from [Best practices for listing your plugin](https://plugins.jetbrains.com/docs/marketplace/best-practices-for-listing.html) for an optimal presentation of your plugin on JetBrains Marketplace. The Busy Plugin Developers. Episode 2 discusses [5 tips for optimizing JetBrains Marketplace plugin page](https://youtu.be/oB1GA9JeeiY?t=52) in more detail. See also [Marketing](marketing.html) about widgets and badges. ## Configuration Structure Overview Warning: Private Configuration Elements If an element or an attribute is not documented on this page, consider them as configuration items intended to be used by JetBrains only. They must not be used by third-party plugins. Deprecated elements are omitted in the list below. Note: Elements described on this page are available in [quick documentation](https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation) since IntelliJ IDEA 2025.1. The [Plugin DevKit](https://plugins.jetbrains.com/plugin/22851-plugin-devkit) plugin must be installed and enabled. * [<idea-plugin>](#idea-plugin) * [<id>](#idea-plugin__id) * [<name>](#idea-plugin__name) * [<version>](#idea-plugin__version) * [<product-descriptor>](#idea-plugin__product-descriptor) * [<idea-version>](#idea-plugin__idea-version) * [<vendor>](#idea-plugin__vendor) * [<description>](#idea-plugin__description) * [<change-notes>](#idea-plugin__change-notes) * [<depends>](#idea-plugin__depends) * [<incompatible-with>](#idea-plugin__incompatible-with) * [<extensions>](#idea-plugin__extensions) * [An Extension](#idea-plugin__extensions__-) * [<extensionPoints>](#idea-plugin__extensionPoints) * [<extensionPoint>](#idea-plugin__extensionPoints__extensionPoint) * [<with>](#idea-plugin__extensionPoints__extensionPoint__with) * [<resource-bundle>](#idea-plugin__resource-bundle) * [<actions>](#idea-plugin__actions) * [<action>](#idea-plugin__actions__action) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<keyboard-shortcut>](#idea-plugin__actions__action__keyboard-shortcut) * [<mouse-shortcut>](#idea-plugin__actions__action__mouse-shortcut) * [<override-text>](#idea-plugin__actions__action__override-text) * [<synonym>](#idea-plugin__actions__action__synonym) * [<abbreviation>](#idea-plugin__actions__action__abbreviation) * [<group>](#idea-plugin__actions__group) * [<action>](#idea-plugin__actions__action) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<keyboard-shortcut>](#idea-plugin__actions__action__keyboard-shortcut) * [<mouse-shortcut>](#idea-plugin__actions__action__mouse-shortcut) * [<override-text>](#idea-plugin__actions__action__override-text) * [<synonym>](#idea-plugin__actions__action__synonym) * [<abbreviation>](#idea-plugin__actions__action__abbreviation) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<override-text>](#idea-plugin__actions__action__override-text) * [<reference>](#idea-plugin__actions__group__reference) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<separator>](#idea-plugin__actions__group__separator) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<reference>](#idea-plugin__actions__group__reference) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<applicationListeners>](#idea-plugin__applicationListeners) * [<listener>](#idea-plugin__applicationListeners__listener) * [<projectListeners>](#idea-plugin__projectListeners) * [<listener>](#idea-plugin__applicationListeners__listener) * [<xi:include>](#idea-plugin__xi:include) * [<xi:fallback>](#idea-plugin__xi:include__xi:fallback) ## `idea-plugin` The `plugin.xml` file root element. Required : yes Attributes : * `url` (optional; ignored in an [additional config file](#additional-plugin-configuration-files)) The link to the plugin homepage displayed on the plugin page in the [JetBrains Marketplace](https://plugins.jetbrains.com). * `require-restart` (optional) The boolean value determining whether the plugin installation, update, or uninstallation requires an IDE restart (see [Dynamic Plugins](dynamic-plugins.html) for details). Default value: `false`. Children : * [<actions>](#idea-plugin__actions) * [<applicationListeners>](#idea-plugin__applicationListeners) * [<change-notes>](#idea-plugin__change-notes) * [<depends>](#idea-plugin__depends) * [<description>](#idea-plugin__description) * [<extensionPoints>](#idea-plugin__extensionPoints) * [<extensions>](#idea-plugin__extensions) * [<id>](#idea-plugin__id) * [<idea-version>](#idea-plugin__idea-version) * [<incompatible-with>](#idea-plugin__incompatible-with) * [<name>](#idea-plugin__name) * [<product-descriptor>](#idea-plugin__product-descriptor) * [<projectListeners>](#idea-plugin__projectListeners) * [<resource-bundle>](#idea-plugin__resource-bundle) * [<vendor>](#idea-plugin__vendor) * [<version>](#idea-plugin__version) * [<xi:include>](#idea-plugin__xi:include) * [<application-components>](#idea-plugin__application-components) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<module-components>](#idea-plugin__module-components) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<project-components>](#idea-plugin__project-components) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) ### `id` A unique identifier of the plugin. It should be a fully qualified name similar to Java packages and must not collide with the ID of existing plugins. The ID is a technical value used to identify the plugin in the IDE and [JetBrains Marketplace](https://plugins.jetbrains.com). Please use characters, numbers, and `'.'`/`'-'`/`'_'` symbols only and keep it reasonably short. Warning: Make sure to pick a stable ID, as the value cannot be changed later after public release. Required : no; ignored in an [additional config file](#additional-plugin-configuration-files) It is highly recommended to set in `plugin.xml` file. The element can be skipped in the source `plugin.xml` file if the Gradle plugin `patchPluginXml` task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#patchPluginXml), [1.x](tools-gradle-intellij-plugin.html#tasks-patchpluginxml)) is enabled and configured. Default value : Value of the [<name>](#idea-plugin__name) element. Example : ```XML com.example.framework ``` ### `name` Reference: [JetBrains Marketplace: Plugin Name](https://plugins.jetbrains.com/docs/marketplace/best-practices-for-listing.html#plugin-name) The user-visible plugin display name (Title Case). Required : yes; ignored in an [additional config file](#additional-plugin-configuration-files) Example : ```XML My Framework Support ``` ### `version` Reference: [JetBrains Marketplace: Semantic Versioning](https://plugins.jetbrains.com/docs/marketplace/semver.html) The plugin version displayed in the Plugins settings dialog and on the [JetBrains Marketplace](https://plugins.jetbrains.com) plugin page. Plugins uploaded to the JetBrains Marketplace must follow semantic versioning. Required : yes; ignored in an [additional config file](#additional-plugin-configuration-files) The element can be skipped in the source `plugin.xml` file if the Gradle plugin `patchPluginXml` task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#patchPluginXml), [1.x](tools-gradle-intellij-plugin.html#tasks-patchpluginxml)) is enabled and configured. Example : ```XML 1.3.18 ``` ### `product-descriptor` Reference: [JetBrains Marketplace: How to add required parameters for paid plugins](https://plugins.jetbrains.com/docs/marketplace/add-required-parameters.html) [Paid](https://plugins.jetbrains.com/build-and-market) or [Freemium](https://plugins.jetbrains.com/docs/marketplace/freemium.html) plugin descriptor. Required : only for paid or freemium plugins; ignored in an [additional config file](#additional-plugin-configuration-files) Do not add `` element in a free plugin. Attributes : * `code` (required) The plugin product code used in the JetBrains Sales System. The code must be agreed with JetBrains in advance and follow [the requirements](https://plugins.jetbrains.com/docs/marketplace/add-required-parameters.html). * `release-date` (required) Date of the major version release in the `YYYYMMDD` format. * `release-version` (required) A major version in a specific number format, for example, `20242` for the 2024.2 major release. See [release-version constraints](https://plugins.jetbrains.com/docs/marketplace/versioning-of-paid-plugins.html#release-version-constraints) for more details. * `optional` (optional) The boolean value determining whether the plugin is a [Freemium](https://plugins.jetbrains.com/docs/marketplace/freemium.html) plugin. Default value: `false`. * `eap` (optional) Specifies the boolean value determining whether the plugin is an EAP release. Default value: `false`. ### `idea-version` Reference: [Build Number Ranges](build-number-ranges.html) The plugin's range of compatible IntelliJ-based IDE versions. Required : yes; ignored in an [additional config file](#additional-plugin-configuration-files) The element can be skipped in the source `plugin.xml` file if the Gradle plugin `patchPluginXml` task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#patchPluginXml), [1.x](tools-gradle-intellij-plugin.html#tasks-patchpluginxml)) is enabled and configured. Attributes : * `since-build` (required) The lowest IDE version compatible with the plugin. * `until-build` (optional) The highest version of the IDE the plugin is compatible with. It's highly recommended not to set this attribute, so the plugin will be compatible with all IDE versions since the version specified by the `since-build`. If it becomes necessary to specify the highest compatible IDE version later, it'll be possible to do that via JetBrains Marketplace. Only if the publishing process for the plugin is configured to upload a new version for each major IDE version, it makes sense to limit the highest compatible IDE version from the beginning. In that case, use `strict-until-build` instead. * `strict-until-build` (optional; available since 2025.3) The highest version of the IDE the plugin is compatible with. Use this attribute only if the publishing process for the plugin is configured to upload a new version for each major IDE version. Otherwise, skip this attribute. If it becomes necessary to specify the highest compatible IDE version later, it'll be possible to do that via JetBrains Marketplace. Examples : * Compatibility with a specific build number (2021.3.3) and higher versions: ```XML ``` * Compatibility with versions from any of `213` branches to any of `221` branches: ```XML ``` ### `vendor` Reference: [JetBrains Marketplace: Contacts and Resources](https://plugins.jetbrains.com/docs/marketplace/best-practices-for-listing.html#contacts-and-resources) The vendor name or organization ID (if created) in the Plugins settings dialog and on the [JetBrains Marketplace](https://plugins.jetbrains.com) plugin page. Required : yes; ignored in an [additional config file](#additional-plugin-configuration-files) Attributes : * `url` (optional) The URL to the vendor's homepage. Supports `https://` and `http://` scheme links. * `email` (optional) The vendor's email address. Examples : * Personal vendor with an email address provided: ```XML Joe Doe ``` * Organizational vendor with a website URL and email address provided: ```XML My Company ``` ### `description` Reference: [JetBrains Marketplace: Plugin Description](https://plugins.jetbrains.com/docs/marketplace/best-practices-for-listing.html#plugin-description) The plugin description displayed on the [JetBrains Marketplace](https://plugins.jetbrains.com) plugin page and in the Plugins settings dialog. Simple HTML elements, like text formatting, paragraphs, lists, etc., are allowed and must be wrapped into `` section. Required : yes; ignored in an [additional config file](#additional-plugin-configuration-files) The element can be skipped in the source `plugin.xml` file if the Gradle plugin `patchPluginXml` task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#patchPluginXml), [1.x](tools-gradle-intellij-plugin.html#tasks-patchpluginxml)) is enabled and configured. Example : ```XML
  • code completion
  • references
  • For more information visit the project site. ]]>
    ``` ### `change-notes` Reference: [JetBrains Marketplace: Change Notes](https://plugins.jetbrains.com/docs/marketplace/best-practices-for-listing.html#change-notes) A short summary of new features, bugfixes, and changes provided with the latest plugin version. Change notes are displayed on the [JetBrains Marketplace](https://plugins.jetbrains.com) plugin page and in the Plugins settings dialog. Simple HTML elements, like text formatting, paragraphs, lists, etc., are allowed and must be wrapped into `` section. Required : no; ignored in an [additional config file](#additional-plugin-configuration-files) The element can be skipped in the source `plugin.xml` file if the Gradle plugin `patchPluginXml` task ([2.x](tools-intellij-platform-gradle-plugin-tasks.html#patchPluginXml), [1.x](tools-gradle-intellij-plugin.html#tasks-patchpluginxml)) is enabled and configured. Example : ```XML New Features
    • Feature 1
    • Feature 2

    Bug Fixes

    • Fixed issue 1
    • Fixed issue 2
    ]]>
    ``` ### `depends` Reference: [Plugin Dependencies](plugin-dependencies.html) , [Modules Specific to Functionality](plugin-compatibility.html#modules-specific-to-functionality) Specifies a dependency on another plugin or a module of an IntelliJ Platform-based product. A single [<idea-plugin>](#idea-plugin) element can contain multiple `` elements. Required : no; in most cases dependency on the [platform](plugin-compatibility.html#modules-available-in-all-products) module is needed Attributes : * `optional` (optional) Boolean value defining whether the dependency is optional to load the plugin in the IDE. If the dependency plugin is not installed in the current IDE, and `optional` is: * `true` - the plugin will be loaded * `false` (default) - the plugin will not be loaded * `config-file` (required when `optional` is `true`) Relative path to an [additional configuration file](#additional-plugin-configuration-files), loaded only if the dependency plugin is installed in the current IDE. Examples : * Required plugin dependency: ```XML com.example.dependencypluginid ``` * Required dependency on the IntelliJ IDEA Java Module: ```XML com.intellij.modules.java ``` * Required module dependency with additional configuration: ```XML com.intellij.modules.java ``` * Optional module dependency with additional configuration: ```XML org.jetbrains.kotlin ``` ### `incompatible-with` Reference: [Declaring Incompatibility with Plugin](plugin-compatibility.html#declaring-incompatibility-with-module) The [ID](#idea-plugin__id) or alias of the plugin the current plugin is incompatible with. The plugin is not loaded if the incompatible plugin is installed in the current IDE. Required : no; ignored in an [additional config file](#additional-plugin-configuration-files) Examples : * Incompatibility with the Java plugin: ```XML com.intellij.java ``` * Incompatibility with the AppCode plugin referenced via its alias: ```XML com.intellij.modules.appcode.ide ``` ### `extensions` Reference: [Extensions](plugin-extensions.html) Defines the plugin extensions. Required : no Attributes : * `defaultExtensionNs` (optional) Default extensions namespace. It allows skipping the common prefix in fully qualified extension point names. Usually, the `com.intellij` namespace is used when the plugin implements IntelliJ Platform extensions. Children : The children elements are registrations of instances of [extension points](#idea-plugin__extensionPoints__extensionPoint) provided by the IntelliJ Platform or plugins. An extension element name is defined by its extension point via `name` or `qualifiedName` attributes. An extension element attributes depend on the extension point implementation, but all extensions support basic attributes: `id`, `order`, and `os`. Examples : * Extensions' declaration with a default namespace: ```XML ``` * Extensions' declaration using the fully qualified extension name: ```XML ``` #### An Extension An extension instance registered under [<extensions>](#idea-plugin__extensions). Listed attributes are basic attributes available for all extensions. The list of actual attributes can be longer depending on the extension point implementation. Attributes : * `id` (optional) Unique extension identifier. It allows for referencing an extension in other attributes, for example, in `order`. To not clash with other plugins defining extensions with the same identifier, consider prepending the identifier with a prefix related to the plugin [<id>](#idea-plugin__id) or [<name>](#idea-plugin__name), for example, `id="com.example.myplugin.myExtension"`. * `order` (optional) Allows for ordering the extension relative to other instances of the same extension point. Supported values: * `first` - orders the extension as first. It is not guaranteed that the extension will be the first if multiple extensions are defined as `first`. * `last` - orders the extension as last. It is not guaranteed that the extension will be the last if multiple extensions are defined as `last`. * `before extension_id` - orders the extension before an extension with the given `id` * `after extension_id` - orders the extension after an extension with the given `id` Values can be combined, for example, `order="after extensionY, before extensionX"`. * `os` (optional) Allows restricting an extension to a given OS. Supported values: * `freebsd` * `linux` * `mac` * `unix` * `windows` For example, `os="windows"` registers the extension on Windows only. ### `extensionPoints` Reference: [Extension Points](plugin-extension-points.html) Extension points defined by the plugin. Required : no Children : * [<extensionPoint>](#idea-plugin__extensionPoints__extensionPoint) #### `extensionPoint` Reference: [Declaring Extension Points](plugin-extension-points.html#declaring-extension-points) A single extension point entry of the [<extensionPoints>](#idea-plugin__extensionPoints) defined by the plugin. A single [<extensionPoints>](#idea-plugin__extensionPoints) element can contain multiple `` elements. Required : no Attributes : * `name` (`name` or `qualifiedName` is required) The extension point name that should be unique in the scope of the plugin, e.g., `myExtension`. The fully qualified name of the extension point is built at runtime by prepending the value of the `name` attribute with the plugin [<id>](#idea-plugin__id) + `.` prefix. Example: when the `name` is `myExtension` and plugin ID is `com.example.myplugin`, the fully qualified name of the EP will be `com.example.myplugin.myExtension`. Only one of the `name` and `qualifiedName` attributes can be specified. * `qualifiedName` (`name` or `qualifiedName` is required) The fully qualified name of the extension point. It should be unique between different plugins, and it is recommended to include a plugin ID to guarantee uniqueness, e.g., `com.example.myplugin.myExtension`. Only one of the `name` and `qualifiedName` attributes can be specified. * `interface` (`interface` or `beanClass` is required) The fully qualified name of the interface to be implemented for extending the plugin's functionality. Only one of the `interface` and `beanClass` attributes can be specified. * `beanClass` (`interface` or `beanClass` is required) The fully qualified name of the extension point bean class providing additional information to the plugin. The bean class specifies one or several properties annotated with the [@Attribute](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/util/src/com/intellij/util/xmlb/annotations/Attribute.java) annotation. Note that bean classes do not follow the JavaBean standard. Implement [PluginAware](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/extensions/src/com/intellij/openapi/extensions/PluginAware.java) to obtain information about the plugin providing the actual extension (see [Error Handling](plugin-extension-points.html#error-handling)). Only one of the `interface` and `beanClass` attributes can be specified. * `dynamic` (optional) Boolean value defining whether the extension point meets the requirements to be [dynamic](plugin-extension-points.html#dynamic-extension-points), which is a prerequisite for [dynamic plugins](dynamic-plugins.html). Default value: `false`. * `area` (optional) The scope in which the [extension](plugin-extensions.html) is instantiated. Allowed values: * `IDEA_APPLICATION` (default) * `IDEA_PROJECT` * `IDEA_MODULE` (deprecated) It is strongly recommended not to introduce new project- and module-level extension points. If an extension point needs to operate on a `Project` or `Module` instance, declare an application-level extension point and pass the instance as a method parameter. Children : * [<with>](#idea-plugin__extensionPoints__extensionPoint__with) ##### `with` Specifies the required parent type for class names provided in extension point tags or attributes. A single [<extensionPoint>](#idea-plugin__extensionPoints__extensionPoint) element can contain multiple `` elements. Required : no Attributes : * `tag` (`tag` or `attribute` is required) The name of the tag holding the fully qualified name of the class which parent type will be limited by the type provided in the `implements` attribute. Only one of the `tag` and `attribute` attributes can be specified. * `attribute` (`tag` or `attribute` is required) The name of the attribute holding the fully qualified name of the class which parent type will be limited by the type provided in the `implements` attribute. Only one of the `tag` and `attribute` attributes can be specified. * `implements` (required) The fully qualified name of the parent type limiting the type provided in the place specified by `tag` or `attribute`. Example : An extension point which restricts the type provided in a `myClass` attribute to be an instance of `com.example.ParentType`, and the type provided in a `someClass` element to be an instance of `java.lang.Comparable`: : ```XML ``` : When using the above extension point, an implementation could be registered as follows: : ```XML com.example.MyComparable ``` : where: : * `com.example.MyCustomType` must be a subtype of `com.example.ParentType` * `com.example.MyComparable` must be a subtype of `java.lang.Comparable` ### `resource-bundle` A resource bundle to be used with message key attributes in extension declarations and for [action and group localization](action-system.html#localizing-actions-and-groups). A single [<idea-plugin>](#idea-plugin) element can contain multiple `` elements. Required : no Example : To load the content of `messages/Bundle.properties` bundle, declare: : ```XML messages.Bundle ``` ### `actions` Reference: [Actions](action-system.html) Defines the plugin actions. Required : no Attributes : * `resource-bundle` (optional; available since 2020.1) Defines the dedicated actions resource bundle. See [Localizing Actions and Groups](action-system.html#localizing-actions-and-groups) for more details. Children : * [<action>](#idea-plugin__actions__action) * [<group>](#idea-plugin__actions__group) * [<reference>](#idea-plugin__actions__group__reference) Example : ```XML ``` #### `action` Reference: [Registering Actions in plugin.xml](action-system.html#registering-actions-in-pluginxml) A single action entry of the [<actions>](#idea-plugin__actions) implemented by the plugin. A single `` element can contain multiple `` elements. Required : no Attributes : * `id` (optional; defaults to the action class short name if not specified) A unique action identifier. It is recommended to specify the `id` attribute explicitly. The action identifier must be unique across different plugins. To ensure uniqueness, consider prepending it with the value of the plugin's [<id>](#idea-plugin__id). * `class` (required) The fully qualified name of the action implementation class. * `text` (required if the action is not [localized](action-system.html#localizing-actions-and-groups)) The default long-version text to be displayed for the action (tooltip for toolbar button or text for menu item). * `description` (optional) The text which is displayed in the status bar when the action is focused. * `icon` (optional) The icon that is displayed on the toolbar button or next to the action menu item. See [Working with Icons](icons.html) for more information about defining and using icons. * `use-shortcut-of` (optional) The ID of the action whose keyboard shortcut this action will use. Children : * [<abbreviation>](#idea-plugin__actions__action__abbreviation) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<keyboard-shortcut>](#idea-plugin__actions__action__keyboard-shortcut) * [<mouse-shortcut>](#idea-plugin__actions__action__mouse-shortcut) * [<override-text>](#idea-plugin__actions__action__override-text) * [<synonym>](#idea-plugin__actions__action__synonym) Examples : * Action declaring explicit `text`: ```XML ``` * Action without the `text` attribute must use the texts from the resource bundle declared with the [<resource-bundle>](#idea-plugin__resource-bundle) element, or the `resource-bundle` attribute of the [<actions>](#idea-plugin__actions) element: ```XML ``` ##### `add-to-group` Specifies that the action should be added to an existing [<group>](#idea-plugin__actions__group). A single action can be added to multiple groups. Required : no Attributes : * `group-id` (required) Specifies the ID of the [<group>](#idea-plugin__actions__group) to which the action is added. The group must be an implementation of the [DefaultActionGroup](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java) class. * `anchor` (optional) Specifies the position of the action relative to other actions. Allowed values: * `first` - the action is placed as the first in the group * `last` (default) - the action is placed as the last in the group * `before` - the action is placed before the action specified by the `relative-to-action` attribute * `after` - the action is placed after the action specified by the `relative-to-action` attribute * `relative-to-action` (required if `anchor` is `before`/`after`) The action before or after which the current action is inserted. Example : ```XML ``` ##### `keyboard-shortcut` Specifies the keyboard shortcut for the action. A single action can have several keyboard shortcuts. Required : no Attributes : * `keymap` (required) Specifies the keymap for which the action shortcut is active. IDs of the standard keymaps are defined as constants in the [KeymapManager](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/keymap/KeymapManager.java) class. * `first-keystroke` (required) Specifies the first keystroke of the action shortcut. The keystrokes are specified according to the regular Swing rules. * `second-keystroke` (optional) Specifies the second keystroke of the action shortcut. * `remove` (optional) Removes a shortcut from the specified action. * `replace-all` (optional) Removes all keyboard and mouse shortcuts from the specified action before adding the specified shortcut. Examples : * Add the first and second keystrokes to all keymaps: ```XML ``` * Remove the given shortcut from the Mac OS X keymap: ```XML ``` * Remove all existing keyboard and mouse shortcuts and register one for the Mac OS X 10.5+ keymap only: ```XML ``` ##### `mouse-shortcut` Specifies the mouse shortcut for the action. A single action can have several mouse shortcuts. Required : no Attributes : * `keymap` (required) Specifies the keymap for which the action shortcut is active. IDs of the standard keymaps are defined as constants in the [KeymapManager](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/keymap/KeymapManager.java) class. * `keystroke` (required) Specifies the clicks and modifiers for the action. It is defined as a sequence of words separated by spaces: * modifier keys: `shift`, `control`, `meta`, `alt`, `altGraph` * mouse buttons: `button1`, `button2`, `button3` * button double-click: `doubleClick` * `remove` (optional) Removes a shortcut from the specified action. * `replace-all` (optional) Removes all keyboard and mouse shortcuts from the specified action before adding the specified shortcut. Examples : * Add the shortcut to all keymaps: ```XML ``` * Remove the given shortcut from the Mac OS X keymap: ```XML ``` * Remove all existing keyboard and mouse shortcuts and register one for the Mac OS X 10.5+ keymap only: ```XML ``` ##### `override-text` Defines an alternate menu action or group text depending on context: menu location, toolbar, and other. Supported : 2020.1+ for actions 2020.3+ for groups Required : no Attributes : * `place` (required) Declares where the alternate text should be used. * `text` (`text` or `use-text-of-place` is required) Defines the text to be displayed for the action. * `use-text-of-place` (`text` or `use-text-of-place` is required) Defines a location whose text should be displayed for this action. Examples : * Explicitly overridden text: ```XML ``` * Overridden text reused from the `MainMenu` place: ```XML ``` ##### `synonym` Defines an alternative text for searching the action in `Help | Find Action...` or `Navigate | Search Everywhere` popups. A single action can have multiple synonyms. Required : no Attributes : * `key` (`key` or `text` is required) The key of the synonym text provided in a [message bundle](action-system.html#localizing-actions-and-groups). * `text` (`key` or `text` is required) The synonym text. Example : ```XML ``` ##### `abbreviation` Defines an abbreviation for searching the action in `Help | Find Action...` or `Navigate | Search Everywhere` popups. A single action can have multiple abbreviations. Required : no Attributes : * `value` (required) The abbreviation value. Example : ```XML ``` #### `group` Reference: [Grouping Actions](action-system.html#grouping-actions) Defines an action group. The [<action>](#idea-plugin__actions__action), `` and [<separator>](#idea-plugin__actions__group__separator) elements defined inside the group are automatically included in it. The `` elements can be nested. Required : no Attributes : * `id` (required) A unique group identifier. The group identifier must be unique between different plugins. Thus, it is recommended to prepend it with the value of the plugin [<id>](#idea-plugin__id). * `class` (optional) The fully qualified name of the group implementation class. If not specified, [DefaultActionGroup](https://github.com/JetBrains/intellij-community/tree/idea/261.26222.65/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java) is used. * `text` (required if the `popup` is `true` and the group is not [localized](action-system.html#localizing-actions-and-groups)) The default long-version text to be displayed for the group (text for the menu item showing the submenu). * `description` (optional) The text which is displayed in the status bar when the group is focused. * `icon` (optional) The icon that is displayed next to the group menu item. See [Working with Icons](icons.html) for more information about defining and using icons. * `popup` (optional) Boolean flag defining whether the group items are presented in the submenu popup. * `true` - group actions are placed in a submenu * `false` (default) - actions are displayed as a section of the same menu delimited by separators * `compact` (optional) Boolean flag defining whether disabled actions within this group are hidden. If the value is: * `true` - disabled actions are hidden * `false` (default) - disabled actions are visible * `use-shortcut-of` (optional) The ID of the action whose keyboard shortcut this group will use. * `searchable` (optional; available since 2020.3) Boolean flag defining whether the group is displayed in `Help | Find Action...` or `Navigate | Search Everywhere` popups. Default value: `true`. Children : * [<action>](#idea-plugin__actions__action) * [<add-to-group>](#idea-plugin__actions__action__add-to-group) * [<group>](#idea-plugin__actions__group) * [<override-text>](#idea-plugin__actions__action__override-text) * [<reference>](#idea-plugin__actions__group__reference) * [<separator>](#idea-plugin__actions__group__separator) Examples : * Group declaring explicit `text`: ```XML ``` * A popup group without the `text` attribute must use the texts from the resource bundle declared with the [<resource-bundle>](#idea-plugin__resource-bundle) element, or the `resource-bundle` attribute of the [<actions>](#idea-plugin__actions) element: ```XML ``` * A group with custom implementation and icon: ```XML ``` ##### `reference` Allows adding an existing action to the group. The element can be used directly under the [<actions>](#idea-plugin__actions) element, or in the [<group>](#idea-plugin__actions__group) element. Required : no Attributes : * `ref` (required) The ID of the action to add to a group. * `id` (optional) Deprecated: Use `ref` instead. The ID of the action to add to a group. Children : * [<add-to-group>](#idea-plugin__actions__action__add-to-group) Examples : * An action reference in a group: ```XML ``` * An action reference registered directly in the [<actions>](#idea-plugin__actions) element: ```XML ``` ##### `separator` Defines a separator between actions in a group. The element can be used directly under the [<actions>](#idea-plugin__actions) element with the child [<add-to-group>](#idea-plugin__actions__action__add-to-group) element defining the target group, or in the [<group>](#idea-plugin__actions__group) element. Required : no Attributes : * `text` (optional) Text displayed on the separator. Separator text is displayed only in specific contexts such as popup menus, toolbars, etc. * `key` (optional) The [message key](action-system.html#localizing-actions-and-groups) for the separator text. The message bundle for use should be registered via the `resource-bundle` attribute of the [<actions>](#idea-plugin__actions) element. The attribute is ignored if the `text` attribute is specified. Children : * [<add-to-group>](#idea-plugin__actions__action__add-to-group) Examples : * A separator dividing two actions in a group: ```XML ``` * A separator registered directly in the [<actions>](#idea-plugin__actions) element: ```XML ``` * A separator with a defined text: ```XML ``` * A separator with a text defined by a message key: ```XML ``` ### `applicationListeners` Reference: [Defining Application-Level Listeners](plugin-listeners.html#defining-application-level-listeners) Defines the application-level listeners. Required : no Children : * [<listener>](#idea-plugin__applicationListeners__listener) #### `listener` Reference: [Listeners](plugin-listeners.html) Defines a single application or project-level listener. A single [<applicationListeners>](#idea-plugin__applicationListeners) or [<projectListeners>](#idea-plugin__projectListeners) can contain multiple `` elements. Required : no Attributes : * `topic` (required) The fully qualified name of the listener interface corresponding to the type of received events. * `class` (required) The fully qualified name of the class implementing the listener interface that receives and handles the events. * `os` (optional; available since 2020.1) Restricts listener instantiation to a specific operating system. Allowed values: * `freebsd` * `mac` * `linux` * `unix` * `windows` * `activeInTestMode` (optional) Boolean flag defining whether the listener should be instantiated in test mode. Default value: `true`. * `activeInHeadlessMode` (optional) Boolean flag defining whether the listener should be instantiated in headless mode. Default value: `true`. Example : ```XML ``` ### `projectListeners` Reference: [Defining Project-Level Listeners](plugin-listeners.html#defining-project-level-listeners) Defines the project-level listeners. Required : no Children : * [<listener>](#idea-plugin__applicationListeners__listener) ### `xi:include` Allows including content of another plugin descriptor in this descriptor with [XInclude](http://www.w3.org/2001/XInclude) standard. Namespace : `xi="http://www.w3.org/2001/XInclude"` Required : no Attributes : * `href` (optional) Path of the plugin descriptor file to include. * `xpointer` (optional) Deprecated since 2021.2: The `xpointer` attribute must be `xpointer(/idea-plugin/*)` or not defined. Elements pointer to include. Default value: `xpointer(/idea-plugin/*)`. Children : * [<xi:fallback>](#idea-plugin__xi:include__xi:fallback) Example : Given a plugin descriptor: : ```XML com.example.myplugin Example ... ``` : and `/META-INF/another-plugin.xml`: : ```XML ... ... ``` : The effective plugin descriptor loaded to memory will contain the following elements: : ```XML com.example.myplugin Example ... ... ... ``` #### `xi:fallback` Indicates that including the specified file is optional. If the file referenced in `href` is not found and the `xi:fallback` element is missing, the plugin will fail to load. Namespace : `xi="http://www.w3.org/2001/XInclude"` Required : no Example : ```XML ... ... ``` ### `application-components` Warning: Do not use it in new plugins. See [Components](plugin-components.html) for the migration guide. Defines a list of application [components](plugin-components.html). Deprecated : since 2020.1 Required : no Children : * [<component>](#idea-plugin__application-components__component) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) #### `component` Warning: Do not use it in new plugins. See [Components](plugin-components.html) for the migration guide. Defines a single application, project, or module [component](plugin-components.html). A single [<application-components>](#idea-plugin__application-components), [<project-components>](#idea-plugin__project-components), or [<module-components>](#idea-plugin__module-components) element can contain multiple `` elements. Deprecated : since 2020.1 Required : no Children : * [<headless-implementation-class>](#idea-plugin__application-components__component__headless-implementation-class) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<implementation-class>](#idea-plugin__application-components__component__implementation-class) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<interface-class>](#idea-plugin__application-components__component__interface-class) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<loadForDefaultProject>](#idea-plugin__application-components__component__loadForDefaultProject) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<option>](#idea-plugin__application-components__component__option) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) * [<skipForDefaultProject>](#idea-plugin__application-components__component__skipForDefaultProject) ![Deprecated](https://img.shields.io/badge/-Deprecated-7f7f7f?style=flat-square) ##### `implementation-class` Warning: Do not use it in new plugins. See [Components](plugin-components.html) for the migration guide. The fully qualified name of the component implementation class. Deprecated : since 2020.1 Required : yes ##### `interface-class` Warning: Do not use it in new plugins. See [Components](plugin-components.html) for the migration guide. The fully qualified name of the component interface class. If not specified, the interface will be the same as defined by [<implementation-class>](#idea-plugin__application-components__component__interface-class) element. Deprecated : since 2020.1 Required : no ##### `headless-implementation-class` Warning: Do not use it in new plugins. See [Components](plugin-components.html) for the migration guide. The fully qualified name of the component implementation class to be used when the IDE runs in headless mode. Deprecated : since 2020.1 Required : no ##### `option` Warning: Do not use it in new plugins. See [Components](plugin-components.html) for the migration guide. Allows to provide additional component options. A single [<component>](#idea-plugin__application-components__component) element can contain multiple `