Introducing DebouncedUpdates: a simpler replacement for MergingUpdateQueue

Hello everyone!

In IntelliJ Platform 2026.2, we are introducing DebouncedUpdates — a coroutine-based replacement for MergingUpdateQueue (which has been @Obsolete for a while). It is a thin wrapper around a Kotlin channel that covers the most common MergingUpdateQueue use case: batch events over a time window and process them all at once. It works from both Kotlin and Java. The API is currently experimental — it is usable now, but may see minor adjustments based on feedback.

A typical scenario: your plugin listens to document changes, file system events, or user input — and you don’t want to react to every single event immediately. Instead, you want to wait until the stream of events settles down and then process the result once. For example: debouncing a search field so the query only runs after the user stops typing, refreshing a UI panel after a batch of file changes, or coalescing editor repaints to avoid flickering.

Compared to MergingUpdateQueue, DebouncedUpdates makes the debouncing intent explicit, avoids the Update interface and Alarm machinery, and provides built-in testing utilities.

Here is what a typical usage looks like:

@Service(Service.Level.PROJECT)
class MyService(cs: CoroutineScope) {
  private val updateQueue = DebouncedUpdates.forScope<Unit>(cs, "my-update", 500.milliseconds)
    .runLatest { doUpdate() }

  fun scheduleUpdate() = updateQueue.queue(Unit)
}

And here is an example with forComponent — the queue automatically pauses while the component is not showing and resumes once it becomes visible:

class MySearchPanel : JPanel() {
  private val searchQueue = DebouncedUpdates.forComponent<Unit>(this, "search", 300.milliseconds)
    .restartTimerOnAdd(true)
    .runLatest { performSearch() }

  fun onSearchFieldChanged() = searchQueue.queue(Unit)
}

The API supports three execution modes — runLatest (keep only the most recent item), runBatched (collect all items as a list), and runBatchedDistinct (deduplicate by equality).

You can find the full documentation, including Java examples, migration hints from MergingUpdateQueue, and testing utilities, in the IntelliJ Platform SDK.

I love updates like this because they surface a variety of plugin audit tasks to make sure plugins are following either the old idiomatic pattern or an appropriate alternative since the new APIs underpinning the updated pattern may still be a year away from being usable on plugins that support older IDE versions.