IntelliJ Plugins

Revamping Plugins #2 – IDEA Feature Suggester

The Revamping Plugins series is about sharing the experience of updating outdated plugins to align with the latest IntelliJ Platform SDK guidelines. We hope this series will help you understand the process of doing this and the tools JetBrains provide to make it easier.

Creating plugins for the IDE of your choice is not just about developing killer features to help others in their day-to-day work – gaining attention through marketing is essential, whether you’re distributing it for free or selling it via Marketplace.

In Revamping Plugins #1, we discussed the .ignore plugin from the technical perspective of keeping the plugin up to date. In this episode, we’ll show you how to enhance the presentation layer to make your plugin more attractive, using the example of a plugin with only a bare GitHub repository: IDEA Feature Suggester.

The IDEA Feature Suggester was developed in 2013 during one of our internal JetBrains Hackathons. This plugin checks the possibility of making some features more discoverable to the user by analyzing their actions during their work with the IDE. When a task can be completed more efficiently, the plugin notifies you. Currently, it can suggest features related to working with a clipboard, comments, file structure, completion, refactoring, and debugging in Java, Kotlin, Python, and JavaScript.
Last year, the codebase was rewritten entirely from Scala to Kotlin, and since then, it has been actively maintained as an open-source project.

The revamping process

README file

Before revamping, the plugin didn’t have the README file provided, and the description on the JetBrains Marketplace plugin page was just one short sentence. Despite the lack of information, it gathered a decent user base over a few years. Our goal is to make it clear for potential users, both on GitHub and JetBrains Marketplace, what this plugin does and how it can help in day-to-day work.

The README file should briefly introduce the plugin and cover all the basic questions that people have. It should have an informative, concise description, usage instructions, and answers to questions on installing and using the plugin. If the plugin changes the default IDE interface behavior, it should be mentioned to inform the user of such modifications.
Having badges on the top is always nice, so we’ve added the GitHub Actions build status badge and others you can find in our Marketing – Readme Badges documentation section.

Marketplace page

Visual representation is essential when it comes to the Marketplace page of the plugin. It is often easier to understand the plugin’s behavior while looking at the screenshots rather than reading a plain text block.
The IDEA Feature Suggester didn’t have any icons or screenshots provided, so we’ve added them to meet the Quality Guidelines for the JetBrains Marketplace plugins. As a result, the plugin page now looks way more attractive than before.

IDEA Feature Suggesters

Build configuration

The first implementation was based on the obsolete DevKit approach. Later, during the migration to Kotlin, Gradle configuration was introduced. You can read more about Building Plugins with Gradle in our IntelliJ Platform Plugin SDK docs.

As optional steps, we’ve introduced the following tweaks:

  • Gradle Kotlin DSL in the build configuration file: 9ec9d5e
  • Gradle Changelog Plugin integration: 9c92547
  • detekt/ktlint integration for static code analyze: 5fdc0ec
  • Extracting project configuration values to the gradle.properties file: 873a105

The last commit brings a neat shorthand function for accessing properties stored in the gradle.properties, so instead of the following:

val pluginName_: String by project
val pluginVersion: String by project
val platformVersion: String by project
val pluginSinceBuild: String by project
val pluginUntilBuild: String by project

intellij {
    pluginName = pluginName_
    version = platformVersion
}

patchPluginXml {
    version(pluginVersion)
    sinceBuild(pluginSinceBuild)
    untilBuild(pluginUntilBuild)
}

you can just use:

fun properties(key: String) = project.findProperty(key).toString()

intellij {
    pluginName = properties("pluginName")
    version = properties("platformVersion")
}

patchPluginXml {
    version(properties("pluginVersion"))
    sinceBuild(properties("pluginSinceBuild"))
    untilBuild(properties("pluginUntilBuild"))
}

Continuous Integration

When this blog post was published, there was no continuous integration or deployment configured for this project, so we will quickly address that by reusing the preconfigured GitHub Actions provided by the IntelliJ Platform Plugin Template.

To set up our CI/CD for testing, building, and releasing the plugin to the JetBrains Marketplace, we’ll be providing two workflow configuration files: 35d00a3
In addition, GitHub’s DependaBot has been enabled to inform us about any dependency updates in the future.

Fixing deprecated APIs

After the IntelliJ Plugin Verifier became available via Gradle IntelliJ Plugin, a mandatory step for every plugin vendor is running the plugin verification locally using the runPluginVerifier task on the CI of your choice.
The minimal setup requires passing the IDE versions that will be used for verification against your plugin:

runPluginVerifier {
    ideVersions(“2020.3.2”, “IU-211.6222.4”)
}

This tool is also used directly by Marketplace, which schedules routine checks for you and sends out emails containing a brief outline of the plugin’s condition. All of the reports can be found on the plugin’s page, where you can dive into more details or request additional checks.

For the IDEA Feature Suggester plugin, we’ve received three deprecated API usages that will be addressed below and some internal API usages of the Feature Usage Statistics, which are expected regarding this plugin.
Note that FUS is not supposed to be used by third-party plugins – server collecting data check the allowed list of plugins, and data browsing is not available for external vendors.

Plugin org.intellij.featureSuggester:211.{exitValue=0, failure=null} against IU-211.6222.4: Compatible. 3 usages of deprecated API
Deprecated API usages (3): 
    #Deprecated constructor com.intellij.notification.NotificationGroup.<init>(String, NotificationDisplayType, boolean, String, Icon, int, DefaultConstructorMarker) invocation
        Deprecated constructor com.intellij.notification.NotificationGroup.<init>(java.lang.String arg0, com.intellij.notification.NotificationDisplayType arg1, boolean arg2, java.lang.String arg3, javax.swing.Icon arg4, int arg5, kotlin.jvm.internal.DefaultConstructorMarker arg6) is invoked in org.jetbrains.plugins.feature.suggester.ui.NotificationSuggestionPresenter.<init>()
    #Deprecated method com.intellij.lang.javascript.psi.JSBlockStatement.getStatements() invocation
        Deprecated method com.intellij.lang.javascript.psi.JSBlockStatement.getStatements() : com.intellij.lang.javascript.psi.JSStatement[] is invoked in org.jetbrains.plugins.feature.suggester.suggesters.lang.JavaScriptLanguageSupport.getStatements(PsiElement) : List
    #Deprecated method com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger.logEvent(String, String, FeatureUsageData) invocation
        Deprecated method com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger.logEvent(java.lang.String groupId, java.lang.String eventId, com.intellij.internal.statistic.eventLog.FeatureUsageData data) : void is invoked in org.jetbrains.plugins.feature.suggester.statistics.FeatureSuggestersStatisticsCollector.sendStatistics(String, String) : void

Deprecated constructor com.intellij.notification.NotificationGroup invocation

As of 2020.3, NotificationGroup is supposed to be registered in the plugin.xml using com.intellij.notificationGroup as an extension point. You can find more details about this change in the Notifications section of the IntelliJ SDK documentation.
04983f5

Deprecated method com.intellij.lang.javascript.psi.JSBlockStatement.getStatements() invocation

That deprecation notice doesn’t provide many details on how it should be handled. Hopefully, such deprecation has happened together with an introduction of the similar JSStatementList.getStatementListItems() method. Thanks to the debugging process, it became clear that it was just a change of the interfaces.
5cdae07

Deprecated method com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger.logEvent(String, String, FeatureUsageData) invocation

Feature Usage Statistics API is marked with @ApiStatus.Internal annotation, and third-party plugins should not use it directly because of its volatile nature. However, the deprecation notice was clear enough to replace the implementation with a more valid form.
c502f5e

Dynamic Plugin

Since the 2020.1 release, the process of installing, updating, or uninstalling plugins is possible without restarting the IDE. To make that happen, plugins should fulfill a set of requirements described in the Dynamic Plugins section of the IntelliJ SDK documentation.
To check the possibility of loading the plugin dynamically, look at the Plugin Verifier report. If verification does not find anything suspicious, you’ll see the following statement:

Plugin can be loaded/unloaded without IDE restart

Also, you can install and uninstall the plugin manually to double-check no memory leak occurred and IDE will continue without a restart prompt. Otherwise, follow the Diagnosing Leaks hints.
In the case of the IDEA Feature Suggester, the whole process goes smoothly right out of the box.

Conclusion

The IDEA Feature Suggester plugin was already in great shape, considering the software development aspects of building the plugin for IntelliJ-based IDEs. Compared to the .ignore plugin, the amount of work was barely noticeable and came down to reusing the CI/CD and Gradle configurations from the IntelliJ Platform Plugin Template.

The majority of the work during the revamping process was focused on the marketing aspects and its presentation layer.
You can find all the plugin quality rules and best practices in the Marketplace docs Quality Guidelines. On the Marketing page of the IntelliJ Platform Plugin SDK docs, you will find some Markdown badges and other valuable snippets that you can put on your own website, like the Plugin Card or Install Button.

If you get stuck at any point, please feel free to reach out to us using the IntelliJ IDEA Open API and Plugin Development Forum or #marketplace channel in our JetBrains Platform Slack.

Stay tuned and follow us on Twitter!
Jakub Chrzanowski and JetBrains Platform