Making a Rider plugin, because JetBrains won't
Attaching the debugger to multiple processes in Rider is not easy in the UI. Let's make a plugin to fix that.
Imagine you are working on a .NET application which runs dozens of processes and you would like to debug it. Visual Studio allows you to attach to multiple processes at once using a single dialog. Even though Rider also has the ability to debug multiple processes at once, the UI won’t allow you to select multiple processes. You have to attach to them one by one. Meaning you can select at most one process in the UI dialog to attach to. I find this ridiculous.
This prevents one to use Rider for debugging multiple processes. The issue has been raised to JetBrains in October 2017. It’s categorized as a feature, but in my opinion, it is a UI bug. Rider already has the ability to do this, but the UI won’t allow you to.
The lack of this feature keeps me going back to VS. — Tijn van der Helm
Thankfully, there is a way to extend JetBrains IDEs’ functionality with plugins. That’s what I took on myself from frustration. In this article I will explain the journey and maybe motivate you to fix problems with software you use every day along the way.
I will not bore you with the code, but rather take a high-level approach describing the process.
Getting into it
Of course, the first thing you do is search for “Rider plugin development”. The first result is from JetBrains documentation and describes how to configure your environment to get started, but not in much detail. It refers to an article which advertises a template. It allows you to make Rider plugins in C#. Ok, you might say. Let’s get started.
After you set up the template, you will find yourself stuck. There is little to none documentation about the SDK. You search around and find at least an introductory 1 hour video about it, but it does not help you much. Also, the template and the SDK are a little behind the recent Rider version.
The problem is we want to make a plugin that just adds UI and interacts with existing IDE functionality, not code analysis. This template is great for working with the ReSharper API, which is what Rider uses on the backend to analyze, transform, and search code. Dead end.
Starting over
What you have to find out yourself is that you actually need to make an IntelliJ plugin, i.e. a plugin that extends the IntelliJ platform. Thankfully, there is much more documentation about this, albeit still incomplete. Also, you have to get ready to work in Java or Kotlin, because the IntelliJ platform runs on JVM, of course.
So, you setup your environment and get to work. You manage to create an action that opens your dialog with a list of processes and an Attach button. You implement the Attach button to attach the debugger to all the selected processes. You try it out, select multiple processes and… nothing. What? Then close the dialog and the debugger successfully attaches to… a single process? What?
Ok, so having a dialog open prevents the IDE from doing the requested action. Let’s remove the Attach button and request debugger to attach after the dialog is closed with the native Ok button.
Still, Rider attaches to a single process.
Parallelism
After some testing you figure out that the problem is Rider only listens to the first command to attach to a process and ignores the rest of the loop. We must resort to multi-threading.
Thankfully, there are no problems with calling methods on the debugger from your new thread. Now, we need to wait until Rider actually finishes attaching to one process, before we request another. Maybe there is an event we could subscribe for this? I did not find one.
Unfortunately, you must resort to active waiting. At least you are not blocking the UI thread now. You poll the list of attached processes and wait for the current one in the loop to be among them. Let’s put some Thread.sleep
in there to throttle this polling at least. Even though it’s not really necessary, you want to be gentle to the CPU.
To find out our process is among them, you can compare on PID. Every process in the attached list has PID of… null
. What? Through debugging and inspecting the list in runtime, you find that you can cast them to DotNetDebugProcess
, which contains the PID. Of course…
It works! You have a proof of concept you can iterate on.
Better UI
First of all, let’s make it a table view instead of a list view. This lets you add more information about the processes in pretty and sortable columns. JetBrains UI is based on Java Swing with some extended components, so figuring out how to do this is quite easy thanks to the variety of questions about this well-known framework.
Now, attaching to multiple processes one-by-one takes quite a while. Maybe that’s why JetBrains haven’t implemented this in the first place. They need to rework their debugger to allow for “parallel attaching”.
You would like to show the progress of this action, and potentially make it cancellable as a bonus. Rider has little progress bars in the status bar at the bottom of the UI. Maybe you could use this?
Yes, and even better the IntelliJ platform has an experimental abstraction over background tasks. You can ditch the system threads and use Kotlin coroutines instead, because the experimental API requires you to use them.
Not having any experience with Kotlin you find coroutines to be quite challenging, at least some advanced work with them, including cancellation. You need to learn about coroutines contexts and scopes. The IntelliJ platform offers coroutine scopes with different semantics and lifetimes. Using the wrong scope can cause a leak! So, you have to be careful. Or not. It’s JVM, so it would be a drop in an ocean. If someone finds a leak in the code and it bothers them, they can make a PR.
Conclusion
I’m happy I made something useful to myself and maybe a couple of other people. Even when (or if) JetBrains implements this themselves, I can be content with my new knowledge about plugin development and maybe make another, more complicated one, in the future. And who knows? Maybe this plugin will still have more and useful features even then.
The code could make use of many improvements, regarding UX and performance (UI freezes). But I don’t have all the motivation in the world for this pet project. I’m content for now and I made the plugin open-source, so you can have a go at it.
The plugin is published on the JetBrains Marketplace, you can download it here.