IntelliJ Platform Plugin SDK Help

Launching Coroutines

In the IntelliJ Platform, coroutines can be launched with one of the following approaches:

  1. Service with its own scope.

  2. The currentThreadCoroutineScope function for executing actions.

  3. The runBlockingCancellable function. (not recommended)

Launching Coroutine From Service Scope

The recommended approach is creating a service that receives its scope via the constructor injection and launching a coroutine from the service methods. Note that while creating a service instance does allocate additional resources, using a dedicated service and scope remains a lightweight and fundamentally safe solution for launching coroutines. It should be used whenever possible.

The pattern is as follows:

@Service class MyApplicationService( private val cs: CoroutineScope ) { fun scheduleSomething() { cs.launch { // do something } } }
@Service(Service.Level.PROJECT) class MyProjectService( private val project: Project, private val cs: CoroutineScope ) { fun scheduleSomething() { cs.launch { // do something } } }

The injected scope is created per service, so each instance has its own isolated scope with a common parent, which is an intersection scope. The injected scope is canceled when the container (application/project) is shut down or when the plugin is unloaded.

Using currentThreadCoroutineScope

Action behavior performed in AnAction.actionPerformed() can be executed in a coroutine via currentThreadCoroutineScope:

internal class MyAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { val file = e.getData(LangDataKeys.PSI_FILE) ?: return currentThreadCoroutineScope().launch { // use suspending APIs: val targets = readAction { // do something in read } withContext(Dispatchers.EDT) { // show some UI } } } // ... }

Compared to the service scope approach, using currentThreadCoroutineScope() enables Action System infrastructure to control the launched coroutine and cancel it if needed. In the case of service scopes, the infrastructure code can't control a coroutine launched from an action, as service scopes are "more global" and live longer than the action trigger.

Using runBlockingCancellable

In a standard coroutine-based application, the bridge between the regular blocking code and the suspending code is the runBlocking function.

In the IntelliJ Platform, a similar purpose is achieved by the runBlockingCancellable function. In addition to the same semantics as runBlocking, the action gets canceled when the current progress indicator or the current job is canceled.

11 August 2025