Skip to content

Conversation

@rock3r
Copy link
Collaborator

@rock3r rock3r commented Oct 28, 2025

This adds four Flow-based APIs to RegistryValue to allow users to observe and react to value changes in flow-based scenarios, and allowing easy integration in Compose-based UIs:

fun asStringFlow(): Flow<String>
fun asBooleanFlow(): Flow<Boolean>
fun asIntegerFlow(): Flow<Int>
fun asDoubleFlow(): Flow<Double>

The existing listener-based API unfortunately cannot be used for this purpose, as wrapping it in callbackFlow causes an exception to be thrown at runtime. I used IdleTracker as my implementation reference, but have some slightly different choices:

  • The values are only emitted when they change — this is particularly useful in flow-based scenarios to avoid unnecessary re-evaluations (IdleTracker doesn't because it only deals in Unit emissions)
  • Contrary to IdleTracker, we have functions instead of properties, since these involve creating a cold Flow on each invocation, and we don't want to obfuscate that
  • The APIs return Flows and not SharedFlows, as the mapping and distinct operations create cold flows and it is then up to the user to shareIn/stateIn based on their needs
  • Contrary to IdleTracker we use DROP_OLDEST for the flow backpressure (we assume users are primarily interested in the most recent values, not the oldest), and we do not provide a replay as some users may not want/need it — and they will be able to easily use onStart to provide one, or will be required to pass an initial value to stateIn anyway
  • I followed the same approach as other RegistryValue APIs when it comes to parsing the underlying strings to other types: it will just throw and fail if it is not possible

I opted against providing a Color flow for now, as it can be trivially added later if needed, but am also expecting the flow-based APIs will be mostly useful for Compose UI surfaces. These would not particularly benefit from getting an AWT color anyway, and would probably prefer to parse strings to Compose colours using Jewel APIs.

This adds four Flow-based APIs to RegistryValue to allow users
to observe and react to value changes in flow-based scenarios,
and allowing easy integration in Compose-based UIs:

```kotlin
fun asStringFlow(): Flow<String>
fun asBooleanFlow(): Flow<Boolean>
fun asIntegerFlow(): Flow<Int>
fun asDoubleFlow(): Flow<Double>
```

The existing listener-based API unfortunately cannot be used
for this purpose, as wrapping it in callbackFlow causes an
exception to be thrown at runtime. I used `IdleTracker` as my
implementation reference, but have some slightly different
choices:
 * The values are only emitted when they change — this is
   particularly useful in flow-based scenarios to avoid
   unnecessary re-evaluations (`IdleTracker` doesn't because
   it only deals in `Unit` emissions)
 * Contrary to `IdleTracker`, we have functions instead of
   properties, since these involve creating a cold `Flow` on
   each invocation, and we don't want to obfuscate that
 * The APIs return `Flow`s and not `SharedFlow`s, as the
   mapping and distinct operations create cold flows and it
   is then up to the user to `shareIn`/`stateIn` based on
   their needs
 * Contrary to `IdleTracker` we use `DROP_OLDEST` for the
   flow backpressure (we assume users are primarily interested
   in the most recent values, not the oldest), and we do not
   provide a replay as some users may not want/need it — and
   they will be able to easily use `onStart` to provide one,
   or will be required to pass an initial value to `stateIn`
   anyway
 * I followed the same approach as other `RegistryValue` APIs
   when it comes to parsing the underlying strings to other
   types: it will just throw and fail if it is not possible

I opted against providing a `Color` flow for now, as it can be
trivially added later if needed, but am also expecting the
flow-based APIs will be mostly useful for Compose UI surfaces.
These would not particularly benefit from getting an AWT
color anyway, and would probably prefer to parse strings to
Compose colours using Jewel APIs.
@rock3r rock3r self-assigned this Oct 28, 2025
@rock3r rock3r requested review from develar and knisht October 28, 2025 13:43
@knisht
Copy link
Contributor

knisht commented Nov 18, 2025

I guess this code was not tested?
The following error appears on usage

java.lang.IllegalArgumentException: replay or extraBufferCapacity must be positive with non-default onBufferOverflow strategy DROP_OLDEST
	at kotlinx.coroutines.flow.SharedFlowKt.MutableSharedFlow(SharedFlow.kt:284)
	at kotlinx.coroutines.flow.SharedFlowKt.MutableSharedFlow$default(SharedFlow.kt:276)
	at com.intellij.openapi.util.registry.RegistryValue.<init>(RegistryValue.kt:34)
	at com.intellij.openapi.util.registry.Registry.valueProducer$lambda$0(Registry.kt:349)
	at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
	at com.intellij.openapi.util.registry.Registry.resolveValue(Registry.kt:352)
	at com.intellij.openapi.util.registry.Registry.access$resolveValue(Registry.kt:35)
	at com.intellij.openapi.util.registry.Registry$Companion.get(Registry.kt:63)

@knisht
Copy link
Contributor

knisht commented Nov 18, 2025

I'll fix the error and add tests

@rock3r rock3r deleted the sebp/IJPL-215413_flow-apis-for-registry branch November 19, 2025 10:36
@rock3r
Copy link
Collaborator Author

rock3r commented Nov 19, 2025

Thanks Kostia — no, it wasn't, I wanted to get at least a style/approach review first :) But thanks for picking it up

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants