From 62574ecbbad652f5a296465f24b144451de4d1ce Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Thu, 22 Feb 2024 01:30:36 +0100 Subject: [PATCH] [execution]: Implement temporal observables in execution module and get rid of ide dependency GitOrigin-RevId: ba6aaf2be9308b856756a7d1e1687868c62e6e35 --- .../execution/intellij.platform.execution.iml | 1 - .../intellij/execution/ui/ReactiveProperty.kt | 66 +++++++++++++++++++ .../execution/ui/RunContentDescriptor.java | 26 ++++---- 3 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 platform/execution/src/com/intellij/execution/ui/ReactiveProperty.kt diff --git a/platform/execution/intellij.platform.execution.iml b/platform/execution/intellij.platform.execution.iml index 100452fdc851..46f36d967164 100644 --- a/platform/execution/intellij.platform.execution.iml +++ b/platform/execution/intellij.platform.execution.iml @@ -10,7 +10,6 @@ - diff --git a/platform/execution/src/com/intellij/execution/ui/ReactiveProperty.kt b/platform/execution/src/com/intellij/execution/ui/ReactiveProperty.kt new file mode 100644 index 000000000000..e114e1b3aa3c --- /dev/null +++ b/platform/execution/src/com/intellij/execution/ui/ReactiveProperty.kt @@ -0,0 +1,66 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.ui + +import com.intellij.openapi.Disposable +import com.intellij.openapi.util.Disposer +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.ApiStatus.Experimental +import java.util.concurrent.atomic.AtomicReference + +/** + * This is only a temporal API used to add reactivity possibilities into execution module until other options become available as a valid + * dependency. + * Do not use it. Use [com.intellij.openapi.observable.properties.ObservableProperty] instead. + */ +@ApiStatus.Internal +@Experimental +interface ReactiveProperty { + fun getValue(): T + + fun afterChange(parentDisposable: Disposable, listener: (T) -> Unit) +} + +/** + * This is only a temporal API used to add reactivity possibilities into execution module until other options become available as a valid + * dependency. + * Do not use it. Use [com.intellij.openapi.observable.properties.ObservableProperty] instead. + */ +@ApiStatus.Internal +@Experimental +class MutableReactiveProperty(initialValue: T) : ReactiveProperty { + private val currentValue = AtomicReference() + private val changeEvent = Event() + + override fun afterChange(parentDisposable: Disposable, listener: (T) -> Unit) { + val subscription = Disposable { changeEvent -= listener } + + changeEvent += listener + + Disposer.register(parentDisposable, subscription) + } + + override fun getValue(): T = currentValue.get() + fun setValue(value: T) { + if (value == currentValue.get()) return + currentValue.set(value) + changeEvent.observers.forEach { it(value) } + } +} + +private class Event { + private val listenersList = mutableListOf<(T) -> Unit>() + val observers: List<(T) -> Unit> get() = listenersList + + operator fun plusAssign(observer: (T) -> Unit) { + listenersList.add(observer) + } + + operator fun minusAssign(observer: (T) -> Unit) { + listenersList.remove(observer) + } + + operator fun invoke(value: T) { + for (observer in listenersList) + observer(value) + } +} \ No newline at end of file diff --git a/platform/execution/src/com/intellij/execution/ui/RunContentDescriptor.java b/platform/execution/src/com/intellij/execution/ui/RunContentDescriptor.java index d9035f1b79e7..acc7771f6df4 100644 --- a/platform/execution/src/com/intellij/execution/ui/RunContentDescriptor.java +++ b/platform/execution/src/com/intellij/execution/ui/RunContentDescriptor.java @@ -9,8 +9,6 @@ import com.intellij.execution.process.ProcessHandler; import com.intellij.ide.HelpIdProvider; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.observable.properties.AtomicProperty; -import com.intellij.openapi.observable.properties.ObservableProperty; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; @@ -32,8 +30,8 @@ public class RunContentDescriptor implements Disposable { private ExecutionConsole myExecutionConsole; private ProcessHandler myProcessHandler; private JComponent myComponent; - private final AtomicProperty<@TabTitle String> myDisplayNameView = new AtomicProperty<>(null); - private final AtomicProperty myIconView = new AtomicProperty<>(null); + private final MutableReactiveProperty<@TabTitle @Nullable String> myDisplayNameView = new MutableReactiveProperty<>(null); + private final MutableReactiveProperty<@Nullable Icon> myIconView = new MutableReactiveProperty<>(null); private final String myHelpId; private RunnerLayoutUi myRunnerLayoutUi = null; private RunContentDescriptorReusePolicy myReusePolicy = RunContentDescriptorReusePolicy.DEFAULT; @@ -71,8 +69,8 @@ public class RunContentDescriptor implements Disposable { myExecutionConsole = executionConsole; myProcessHandler = processHandler; myComponent = component; - myDisplayNameView.set(displayName); - myIconView.set(icon); + myDisplayNameView.setValue(displayName); + myIconView.setValue(icon); myHelpId = myExecutionConsole instanceof HelpIdProvider ? ((HelpIdProvider)myExecutionConsole).getHelpId() : null; myActivationCallback = activationCallback; if (myExecutionConsole != null) { @@ -144,19 +142,21 @@ public class RunContentDescriptor implements Disposable { */ @Nullable public Icon getIcon() { - return myIconView.get(); + return myIconView.getValue(); } /** * Returns the reactive property for an icon to show in the Run or Debug toolwindow tab corresponding to this content. * @return the icon property that can be observed for the most recent icon value. */ - public ObservableProperty getIconProperty() { + @ApiStatus.Experimental + public ReactiveProperty getIconProperty() { return myIconView; } + @ApiStatus.Experimental protected void setIcon(@Nullable Icon icon) { - myIconView.set(icon); + myIconView.setValue(icon); } @Nullable @@ -186,19 +186,21 @@ public class RunContentDescriptor implements Disposable { */ @BuildEventsNls.Title public String getDisplayName() { - return myDisplayNameView.get(); + return myDisplayNameView.getValue(); } /** * Returns the reactive property for a title to show in the Run or Debug toolwindow tab corresponding to this content. * @return the title property that can be observed for the most recent title value. */ - public ObservableProperty<@Nullable @BuildEventsNls.Title String> getDisplayNameProperty() { + @ApiStatus.Experimental + public ReactiveProperty<@Nullable @BuildEventsNls.Title String> getDisplayNameProperty() { return myDisplayNameView; } + @ApiStatus.Experimental protected void setDisplayName(@Nullable @TabTitle String displayName) { - myDisplayNameView.set(displayName); + myDisplayNameView.setValue(displayName); } public String getHelpId() {