[execution]: Implement temporal observables in execution module and get rid of ide dependency

GitOrigin-RevId: ba6aaf2be9308b856756a7d1e1687868c62e6e35
This commit is contained in:
Andrii Rublov
2024-02-22 01:30:36 +01:00
committed by intellij-monorepo-bot
parent 669de82138
commit 62574ecbba
3 changed files with 80 additions and 13 deletions

View File

@@ -10,7 +10,6 @@
<orderEntry type="library" name="kotlin-stdlib" level="project" /> <orderEntry type="library" name="kotlin-stdlib" level="project" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.platform.core" /> <orderEntry type="module" module-name="intellij.platform.core" />
<orderEntry type="module" module-name="intellij.platform.ide" />
<orderEntry type="module" module-name="intellij.platform.ide.core" /> <orderEntry type="module" module-name="intellij.platform.ide.core" />
<orderEntry type="module" module-name="intellij.platform.ide.util.io" /> <orderEntry type="module" module-name="intellij.platform.ide.util.io" />
<orderEntry type="module" module-name="intellij.platform.util.ui" /> <orderEntry type="module" module-name="intellij.platform.util.ui" />

View File

@@ -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<T> {
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<T>(initialValue: T) : ReactiveProperty<T> {
private val currentValue = AtomicReference<T>()
private val changeEvent = Event<T>()
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<T> {
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)
}
}

View File

@@ -9,8 +9,6 @@ import com.intellij.execution.process.ProcessHandler;
import com.intellij.ide.HelpIdProvider; import com.intellij.ide.HelpIdProvider;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction; 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.Computable;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Key;
@@ -32,8 +30,8 @@ public class RunContentDescriptor implements Disposable {
private ExecutionConsole myExecutionConsole; private ExecutionConsole myExecutionConsole;
private ProcessHandler myProcessHandler; private ProcessHandler myProcessHandler;
private JComponent myComponent; private JComponent myComponent;
private final AtomicProperty<@TabTitle String> myDisplayNameView = new AtomicProperty<>(null); private final MutableReactiveProperty<@TabTitle @Nullable String> myDisplayNameView = new MutableReactiveProperty<>(null);
private final AtomicProperty<Icon> myIconView = new AtomicProperty<>(null); private final MutableReactiveProperty<@Nullable Icon> myIconView = new MutableReactiveProperty<>(null);
private final String myHelpId; private final String myHelpId;
private RunnerLayoutUi myRunnerLayoutUi = null; private RunnerLayoutUi myRunnerLayoutUi = null;
private RunContentDescriptorReusePolicy myReusePolicy = RunContentDescriptorReusePolicy.DEFAULT; private RunContentDescriptorReusePolicy myReusePolicy = RunContentDescriptorReusePolicy.DEFAULT;
@@ -71,8 +69,8 @@ public class RunContentDescriptor implements Disposable {
myExecutionConsole = executionConsole; myExecutionConsole = executionConsole;
myProcessHandler = processHandler; myProcessHandler = processHandler;
myComponent = component; myComponent = component;
myDisplayNameView.set(displayName); myDisplayNameView.setValue(displayName);
myIconView.set(icon); myIconView.setValue(icon);
myHelpId = myExecutionConsole instanceof HelpIdProvider ? ((HelpIdProvider)myExecutionConsole).getHelpId() : null; myHelpId = myExecutionConsole instanceof HelpIdProvider ? ((HelpIdProvider)myExecutionConsole).getHelpId() : null;
myActivationCallback = activationCallback; myActivationCallback = activationCallback;
if (myExecutionConsole != null) { if (myExecutionConsole != null) {
@@ -144,19 +142,21 @@ public class RunContentDescriptor implements Disposable {
*/ */
@Nullable @Nullable
public Icon getIcon() { 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. * 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. * @return the icon property that can be observed for the most recent icon value.
*/ */
public ObservableProperty<Icon> getIconProperty() { @ApiStatus.Experimental
public ReactiveProperty<Icon> getIconProperty() {
return myIconView; return myIconView;
} }
@ApiStatus.Experimental
protected void setIcon(@Nullable Icon icon) { protected void setIcon(@Nullable Icon icon) {
myIconView.set(icon); myIconView.setValue(icon);
} }
@Nullable @Nullable
@@ -186,19 +186,21 @@ public class RunContentDescriptor implements Disposable {
*/ */
@BuildEventsNls.Title @BuildEventsNls.Title
public String getDisplayName() { 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. * 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. * @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; return myDisplayNameView;
} }
@ApiStatus.Experimental
protected void setDisplayName(@Nullable @TabTitle String displayName) { protected void setDisplayName(@Nullable @TabTitle String displayName) {
myDisplayNameView.set(displayName); myDisplayNameView.setValue(displayName);
} }
public String getHelpId() { public String getHelpId() {