SECRET OF CSS

Integrate Settings Into a Custom IntelliJ Code Inspection Plugin | by Ruben Quadros | Aug, 2022


Add settings UI to the Kotlin data class inspection plugin

Photo by Javier Allegue Barros on Unsplash

Introduction

In the previous post I demonstrated how you can create your own plugin to check and add the missing SerializedName annotation to your data class params. If you have not gone through the article I would highly recommend doing it now.

In this post I want to show you how you can extend it to support different parsing libraries. Currently I have added support for Moshi and Kotlinx-Serialization.

1*Zaq3vnPwXkLVkVUnQCljqQ
Codespector in action

Declaring the settings

Settings persistently store states that control the behavior and appearance of IntelliJ Platform-based IDEs.

The plugin settings should be defined in the plugin configuration file. In the plugin workspace there are mainly 2 types of settings:

  • Application level settings — These settings are valid at the IDE level and remain same across all projects.
1*CCtk W6mqJWIn I6Ru9 bQ
Application level settings
  • Project level settings — These settings are valid on a project level and can differ for different projects.
Project level settings

Let’s try and understand the attributes required for declaring these settings.

parentId — Parent group under which this setting needs to be shown. You can refer the Parent ID Attribute to see all the acceptable values.

instance — This is the most important attribute. The setting class should provide an implementation of Configurable interface and the value of this attribute is the fully qualified name of the implementation.

id — Unique identifier for this setting. Fully qualified name can be provided to maintain uniqueness.

displayName — The name of the setting which is displayed to the user.

You can see all the available settings attributes here.

Having understood the basics about plugin settings, can you guess which type of setting is required for the Codespector plugin?

If you guessed project level settings then you are absolutely right! We want the user to select the parser that is used in the project so that the plugin can inspect for the correct missing annotation. It is possible that one project is using Gson to parse the JSON while another is using Moshi or some other library altogether.

Project level plugin settings

Let’s now go ahead and create the InspectionSettings class which implements the Configurable interface.

1*SaCamztRoCa4wV2kiK4ciw
Initial settings implementation

Let’s try and provide implementation for each of the methods.

Creating the settings UI

The best way to create UI for plugins is via Kotlin DSL Version 2. The caveat here is that this API is available in versions 2021.3 and later only. Currently the Codespector plugin can be installed in IDEs 2019.3 and later. Hence, we will be using Kotlin DSL version 1 to create our UI which has support since 2019.2 version.

Let’s dive right into the code:

Initial settings UI

We have a title, sub-title and then 3 radio buttons. Running this code, the UI looks as follows:

1*PpIsOuCUd3eEb4ywQPQQQQ
Initial settings UI

Coming back to our settings implementation, we can provide the component as follows:

1*8B
Providing the inspection component

At this stage, let’s try and answer the following questions:

  • What happens when an option is selected?
  • What happens when the user clicks on apply settings?
  • What happens when the user clicks on reset settings?
  • How can we actually persist the settings for a project?

Persisting the project settings

IntelliJ Platform provides an API called PersistentStateComponent for persisting states across projects and IDE restarts. According to the docs you can use PersistentStateComponent as follows:

  • Mark a service which implements the PersistentStateComponent interface
  • Define a state class — The implementation of PersistentStateComponent needs to be parameterized with the type of state class. The state class can either be a separate JavaBean class, or the class implementing PersistentStateComponent.
  • Define the storage location using the @Stateannotation.

The first step is to declare the service in the plugin configuration file. There are mainly 3 types of service: application level, project level and module level. Since we are using project level settings we would also want a project level service.

Project level service

The implementation is as follows:

PersistentStateComponent implementation

Now that the settings persistence is in place, let’s use this to select the correct radio button based on the user input and also to apply/reset the settings.

Applying the settings

The first thing we would want is to listen to the radio button selection. We can do this with the help of ActionListener. We can tell the radio button whether it is selected or not with the help of setSelected(Boolean).

Radio button

The updated InspectionComponent is as follows:

InspectionComponent.kt

Running the plugin code now gives the following output:

1*0ZZdBBKez4bMPt TWseKhQ
Selecting the settings

Now that we are able to listen to the radio button, let’s see how we can apply the settings.

In order for the apply button to be selectable, we will first have to provide the implementation for isModified() method in InspectionSettings and to apply the settings we have to implement the apply() method.

InspectionSettings.kt

Running the plugin code now, gives the following output:

1*DhSTgLYNPzvVMF9V6BPo7w
Applying the settings

There are 2 important things to note once a new setting is applied:

  • The existing editor notification should update — Add X annotation to class params
  • The highlighting should highlight the correct problem and provide the updated quick fix — Missing X annotation
Reset notification and code analysis



News Credit

%d bloggers like this: