Making a plugin isn’t so hard…
It was always interesting for me is it difficult to build your plugin for IDE. Recently, I’ve decided to try. It turns out it’s not that complicated, and I would like to share my experience.
Disclaimer: I’m not a Kotlin or Java developer. Moreover, this is my first experience with these languages. So, the code below might be not fully optimized or doesn’t follow best practices.
In this post, I want to show that plugin creation isn’t rocket science and maybe inspire you to build your own. The more plugins we have, the more productive the whole community will be. This post isn’t a how-to because JetBrains has detailed documentation, and you can find answers there. My goal is to demonstrate that there is a short step from an idea to a worth solution.
Template
To start with, you could create a repository from an IntelliJ Platform Plugin Template. It simplifies the first setup, creates GitHub Actions for building and publishing plugin and more.
IntelliJ Platform Plugin Template
After that, you’ll have a new repository for your plugin. Let’s clone it and add some logic.
Project Tye
Lately, I published a post about Project Tye. It’s an experimental tool from Microsoft to develop distributed applications locally and deploy them to the Kubernetes cluster. So, I want to add support for this tool to the Rider.
Distributed application with Project Tye
I’ll briefly remind you how this tool works. First of all, let’s create a test solution for our experiments.
$ dotnet new sln --name TyeExperiments
$ dotnet new web --name TyeApi
$ dotnet new worker --name TyeWorker
$ dotnet sln TyeExperiments.sln add TyeApi/TyeApi.csproj
$ dotnet sln TyeExperiments.sln add TyeWorker/TyeWorker.csproj
Now, we have a solution with two projects. Next, install tye .NET tool. Note that you need first install .NET Core 3.1. After that, run the following command:
$ dotnet tool install -g Microsoft.Tye --version "0.6.0-alpha.21070.5"
Finally, go to the TyeExperiments
solution folder and execute a command:
$ tye run
Tye starts your projects, and you can find them on the dashboard http://127.0.0.1:8000/
.
Run Configuration
Excellent, now what I want to accomplish is to run this command directly from my IDE. IntelliJ Platform SDK gives us the ability to add custom Run Configuration. So, I think that it’s the best place to extend basic functionality with our plugin. There is a tutorial in the IntelliJ Platform documentation, and I show my implementation bellow.
Start a new extension from the plugin.xml
file and register configurationType
.
<extensions defaultExtensionNs="com.intellij">
<configurationType implementation="com.github.rafaelldi.tyeplugin.run.TyeConfigurationType"/>
</extensions>
Implement this ConfigurationType
with a new class.
class TyeConfigurationType : ConfigurationType {
override fun getDisplayName() = "Tye"
override fun getConfigurationTypeDescription() = "Tye run command"
override fun getIcon() = AllIcons.General.Information
override fun getId() = "TYE_RUN_CONFIGURATION"
override fun getConfigurationFactories(): Array<ConfigurationFactory> = arrayOf(TyeConfigurationFactory(this))
}
Introduce a new ConfigurationFactory
to be able to produce a run configuration.
class TyeConfigurationFactory(type: TyeConfigurationType) : ConfigurationFactory(type) {
companion object {
private const val FACTORY_NAME = "Tye configuration factory"
}
override fun createTemplateConfiguration(project: Project): RunConfiguration {
return TyeRunConfiguration(project, this, "Tye")
}
override fun getName() = FACTORY_NAME
override fun getId() = FACTORY_NAME
}
Create a RunConfiguration
class. With it, you can execute different actions, for example, call tye commands.
class TyeRunConfiguration(project: Project, factory: TyeConfigurationFactory, name: String) :
RunConfigurationBase<TyeCommandLineState>(project, factory, name) {
override fun getConfigurationEditor(): SettingsEditor<out RunConfiguration> = TyeSettingsEditor()
override fun checkConfiguration() {
}
override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState {
return TyeCommandLineState(environment, this, project)
}
}
Additionally, I create a TyeCommandLineState
class to locate operations with a command line. We’ll come back to this later.
open class TyeCommandLineState(
environment: ExecutionEnvironment,
private val runConfig: TyeRunConfiguration,
private val project: Project
) : CommandLineState(environment) {
override fun startProcess(): ProcessHandler {
val commandLine = GeneralCommandLine()
val handler = OSProcessHandler(commandLine)
return handler
}
}
After all, add SettingsEditor
class to handle UI form.
class TyeSettingsEditor : SettingsEditor<TyeRunConfiguration>() {
private lateinit var panel: JPanel
override fun createEditor(): JComponent {
createUIComponents()
return panel
}
override fun resetEditorFrom(runConfig: TyeRunConfiguration) {
}
override fun applyEditorTo(runConfig: TyeRunConfiguration) {
}
private fun createUIComponents() {
panel = JPanel().apply {
layout = VerticalFlowLayout(VerticalFlowLayout.TOP)
}
}
}
Pretty straightforward implementation. I won’t go into detail about each file. As I said before, you can find their description in the documentation. Eventually, if you run the plugin, you would see the new Run Configuration
type. For now, it doesn’t do anything; let’s go to the next section and add some behaviour.
Tye run command
To call tye run
command, we’ll modify TyeCommandLineState
class.
override fun startProcess(): ProcessHandler {
val arguments = mutableListOf<String>()
arguments.add("run")
val commandLine = GeneralCommandLine()
.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
.withWorkDirectory(project.basePath)
.withExePath("tye")
.withParameters(arguments)
val handler = OSProcessHandler(commandLine)
handler.startNotify()
ProcessTerminatedListener.attach(handler, environment.project)
return handler
}
Start the plugin again, select TyeExperiments
solution folder and run the new tye configuration type. You’ll see the same logs as before when we ran tye independently.
Conclusion
This post shows how to create a primitive plugin to envelope some command-line tool calls. Of course, the capabilities of the platform are much more significant. You can find a lot of complicated plugins in the marketplace. I’ve tried to show that if you want to add some simple functionality, it’s not hard to do.
The documentation is an excellent starting point for your exploration. If it isn’t enough, you can find how to implement some functionality in the other plugins.
I hope I was able to motivate you to try creating your own plugin. My source code you can find on GitHub.
References
- Project Tye on GitHub
- Introducing Project Tye
- IntelliJ Platform Plugin Template
- IntelliJ Platform SDK
- IntelliJ Platform Explorer
Image: Photo by Johanneke Kroesbergen-Kamps on Unsplash
Comments