IJPL-158073 Introduce DataSink.lazyValue

(cherry picked from commit cb3a4d617192638c05ec24feca1128e1625931da)

IJ-CR-158811

GitOrigin-RevId: 345df15b9e296be66a6a540af058058bcf982b4a
This commit is contained in:
Gregory.Shrago
2025-03-27 03:50:29 +04:00
committed by intellij-monorepo-bot
parent 0ff4a1342d
commit 1189cb9281
8 changed files with 110 additions and 57 deletions

View File

@@ -57,13 +57,16 @@ f:com.intellij.openapi.actionSystem.DataKey
- f:is(java.lang.String):Z
f:com.intellij.openapi.actionSystem.DataKey$Companion
- f:create(java.lang.String):com.intellij.openapi.actionSystem.DataKey
com.intellij.openapi.actionSystem.DataMap
- a:get(com.intellij.openapi.actionSystem.DataKey):java.lang.Object
com.intellij.openapi.actionSystem.DataProvider
- a:getData(java.lang.String):java.lang.Object
com.intellij.openapi.actionSystem.DataSink
- sf:Companion:com.intellij.openapi.actionSystem.DataSink$Companion
- a:dataSnapshot(com.intellij.openapi.actionSystem.DataSnapshotProvider):V
- a:lazy(com.intellij.openapi.actionSystem.DataKey,kotlin.jvm.functions.Function0):V
- lazy(com.intellij.openapi.actionSystem.DataKey,kotlin.jvm.functions.Function0):V
- a:lazyNull(com.intellij.openapi.actionSystem.DataKey):V
- a:lazyValue(com.intellij.openapi.actionSystem.DataKey,kotlin.jvm.functions.Function1):V
- a:set(com.intellij.openapi.actionSystem.DataKey,java.lang.Object):V
- a:setNull(com.intellij.openapi.actionSystem.DataKey):V
- a:uiDataSnapshot(com.intellij.openapi.actionSystem.DataProvider):V
@@ -74,7 +77,7 @@ f:com.intellij.openapi.actionSystem.DataSink$Companion
- f:uiDataSnapshot(com.intellij.openapi.actionSystem.DataSink,com.intellij.openapi.actionSystem.DataProvider):V
- f:uiDataSnapshot(com.intellij.openapi.actionSystem.DataSink,java.lang.Object):V
com.intellij.openapi.actionSystem.DataSnapshot
- a:get(com.intellij.openapi.actionSystem.DataKey):java.lang.Object
- com.intellij.openapi.actionSystem.DataMap
com.intellij.openapi.actionSystem.DataSnapshotProvider
- a:dataSnapshot(com.intellij.openapi.actionSystem.DataSink):V
com.intellij.openapi.actionSystem.EdtNoGetDataProvider

View File

@@ -5,7 +5,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.Component;
/**
* Allows an action to retrieve information about the context in which it was invoked.

View File

@@ -52,15 +52,21 @@ fun interface UiDataProvider {
}
}
/**
* A data map representation.
*/
@ApiStatus.NonExtendable
interface DataMap {
operator fun <T : Any> get(key: DataKey<T>): T?
}
/**
* A data snapshot representation.
* [UiDataRule] operates on it.
*/
@ApiStatus.NonExtendable
interface DataSnapshot {
operator fun <T : Any> get(key: DataKey<T>): T?
}
interface DataSnapshot: DataMap
/**
* A non-EDT version of [UiDataProvider].
@@ -152,7 +158,14 @@ interface DataSink {
/**
* Put the [PlatformCoreDataKeys.BGT_DATA_PROVIDER] lambda in the sink
*/
fun <T : Any> lazy(key: DataKey<T>, data: () -> T?)
fun <T : Any> lazy(key: DataKey<T>, data: () -> T?) {
lazyValue(key) { data() }
}
/**
* Put the [PlatformCoreDataKeys.BGT_DATA_PROVIDER] lambda in the sink
*/
fun <T : Any> lazyValue(key: DataKey<T>, data: (provider: DataMap) -> T?)
/**
* Put the [com.intellij.openapi.actionSystem.CustomizedDataContext.EXPLICIT_NULL] value in the sink

View File

@@ -33,15 +33,22 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.AsyncPromise;
import org.jetbrains.concurrency.Promise;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.util.*;
import static com.intellij.openapi.actionSystem.PlatformCoreDataKeys.BGT_DATA_PROVIDER;
/** @noinspection removal*/
public class DataManagerImpl extends DataManager {
private static final Logger LOG = Logger.getInstance(DataManagerImpl.class);
private static final ThreadLocal<int[]> ourGetDataLevel = ThreadLocal.withInitial(() -> new int[1]);
private static class ThreadState { int depth; Set<String> ids; }
private static final ThreadLocal<ThreadState> ourGetDataLevel = ThreadLocal.withInitial(() -> new ThreadState());
private final Map<Pair<String, GetDataRuleType>, GetDataRule> myRulesCache = ConcurrentFactoryMap.createMap(o -> getDataRule(o.first, o.second));
@@ -71,60 +78,56 @@ public class DataManagerImpl extends DataManager {
public @Nullable Object getDataFromProviderAndRules(@NotNull String dataId,
@Nullable GetDataRuleType ruleType,
@NotNull DataProvider provider) {
return getDataFromProviderAndRulesInner(dataId, ruleType, null, provider);
return getDataFromProviderAndRulesInner(dataId, ruleType, provider);
}
@ApiStatus.Internal
public @Nullable Object getDataFromRules(@NotNull String dataId,
@NotNull GetDataRuleType ruleType,
@NotNull DataProvider provider) {
return getDataFromRulesInner(dataId, ruleType, null, provider);
return getDataFromRulesInner(dataId, ruleType, provider);
}
private @Nullable Object getDataFromProviderAndRulesInner(@NotNull String dataId,
@Nullable GetDataRuleType ruleType,
@Nullable Set<String> alreadyComputedIds,
@NotNull DataProvider provider) {
ProgressManager.checkCanceled();
if (alreadyComputedIds != null && alreadyComputedIds.contains(dataId)) {
ThreadState state = ourGetDataLevel.get();
if (state.ids != null && state.ids.contains(dataId)) {
return null;
}
int[] depth = ourGetDataLevel.get();
state.depth++;
try {
depth[0]++;
Object data = getDataFromProviderInner(dataId, provider);
Object data = getDataFromProviderInner(dataId, provider, null);
if (data != null) {
return data;
}
return ruleType == null ? null : getDataFromRulesInner(dataId, ruleType, alreadyComputedIds, provider);
return ruleType == null ? null : getDataFromRulesInner(dataId, ruleType, provider);
}
finally {
depth[0]--;
if (alreadyComputedIds != null) alreadyComputedIds.remove(dataId);
state.depth--;
}
}
private @Nullable Object getDataFromRulesInner(@NotNull String dataId,
@NotNull GetDataRuleType ruleType,
@Nullable Set<String> alreadyComputedIds,
@NotNull DataProvider provider) {
GetDataRule rule = myRulesCache.get(Pair.create(dataId, ruleType));
if (rule == null) return null;
return getDataFromRuleInner(rule, dataId, ruleType, alreadyComputedIds, provider);
return getDataFromRuleInner(rule, dataId, ruleType, provider);
}
private @Nullable Object getDataFromRuleInner(@NotNull GetDataRule rule,
@NotNull String dataId,
@NotNull GetDataRuleType ruleType,
@Nullable Set<String> alreadyComputedIds,
@NotNull DataProvider provider) {
int[] depth = ourGetDataLevel.get();
ThreadState state = ourGetDataLevel.get();
if (state.ids == null || state.depth == 0) state.ids = new HashSet<>();
state.depth++;
try {
depth[0]++;
Set<String> ids = alreadyComputedIds == null ? new HashSet<>() : alreadyComputedIds;
ids.add(dataId);
state.ids.add(dataId);
Object data = rule.getData(id -> {
Object o = getDataFromProviderAndRulesInner(id, ruleType, ids, provider);
Object o = getDataFromProviderAndRulesInner(id, ruleType, provider);
return o == CustomizedDataContext.EXPLICIT_NULL ? null : o;
});
return data == null ? null :
@@ -132,7 +135,8 @@ public class DataManagerImpl extends DataManager {
DataValidators.validOrNull(data, dataId, rule);
}
finally {
depth[0]--;
state.depth--;
state.ids.remove(dataId);
}
}
@@ -192,19 +196,20 @@ public class DataManagerImpl extends DataManager {
private static @Nullable GetDataRule getDataRuleInner(@NotNull String dataId, @NotNull GetDataRuleType ruleType) {
String uninjectedId = InjectedDataKeys.uninjectedId(dataId);
GetDataRule slowRule = ruleType == GetDataRuleType.PROVIDER &&
!PlatformCoreDataKeys.BGT_DATA_PROVIDER.is(dataId) ?
dataProvider -> getSlowData(dataId, dataProvider) : null;
boolean noSlowRule = ruleType != GetDataRuleType.PROVIDER || BGT_DATA_PROVIDER.is(dataId);
List<GetDataRule> rules1 = rulesForKey(dataId, ruleType);
List<GetDataRule> rules2 = uninjectedId == null ? null : rulesForKey(uninjectedId, ruleType);
if (rules1 == null && rules2 == null) return slowRule;
if (rules1 == null && rules2 == null && noSlowRule) return null;
return dataProvider -> {
Object data = slowRule == null ? null : slowRule.getData(dataProvider);
data = data != null ? data : rules1 == null ? null : getRulesData(dataId, rules1, dataProvider);
data = data != null ? data : rules2 == null ? null : getRulesData(dataId, rules2, id -> {
DataProvider bgtProvider = noSlowRule ? null : BGT_DATA_PROVIDER.getData(dataProvider);
DataProvider injectedProvider = uninjectedId == null ? null : id -> {
String injectedId = InjectedDataKeys.injectedId(id);
return injectedId != null ? dataProvider.getData(injectedId) : null;
});
};
Object data = getDataFromProviderInner(dataId, bgtProvider, dataProvider);
data = data != null ? data : uninjectedId == null ? null : getDataFromProviderInner(uninjectedId, bgtProvider, injectedProvider);
data = data != null ? data : rules1 == null ? null : getRulesData(dataId, rules1, dataProvider);
data = data != null ? data : rules2 == null ? null : getRulesData(uninjectedId, rules2, injectedProvider);
return data;
};
}
@@ -239,19 +244,17 @@ public class DataManagerImpl extends DataManager {
return null;
}
private static @Nullable Object getSlowData(@NotNull String dataId, @NotNull DataProvider dataProvider) {
DataProvider bgtProvider = PlatformCoreDataKeys.BGT_DATA_PROVIDER.getData(dataProvider);
if (bgtProvider != null) {
Object data = getDataFromProviderInner(dataId, bgtProvider);
if (data != null) return data;
private static @Nullable Object getDataFromProviderInner(@NotNull String dataId,
@Nullable DataProvider provider,
@Nullable DataProvider outerProvider) {
if (provider == null) {
return null;
}
return null;
}
private static @Nullable Object getDataFromProviderInner(@NotNull String dataId, @NotNull DataProvider provider) {
if (!(provider instanceof CompositeDataProvider)) {
else if (!(provider instanceof CompositeDataProvider composite)) {
try {
Object data = provider.getData(dataId);
Object data = provider instanceof ParametrizedDataProvider o
? (outerProvider == null ? null : o.getData(dataId, outerProvider))
: provider.getData(dataId);
if (data != null) {
return data == CustomizedDataContext.EXPLICIT_NULL ? data :
DataValidators.validOrNull(data, dataId, provider);
@@ -261,8 +264,8 @@ public class DataManagerImpl extends DataManager {
}
}
else {
for (DataProvider p : ((CompositeDataProvider)provider).getDataProviders()) {
Object data = getDataFromProviderInner(dataId, p);
for (DataProvider p : composite.getDataProviders()) {
Object data = getDataFromProviderInner(dataId, p, outerProvider);
if (data != null) return data;
}
}
@@ -272,7 +275,7 @@ public class DataManagerImpl extends DataManager {
@Override
public @NotNull DataContext getDataContext(Component component) {
ThreadingAssertions.assertEventDispatchThread();
if (ourGetDataLevel.get()[0] > 0) {
if (ourGetDataLevel.get().depth > 0) {
LOG.error("DataContext shall not be created and queried inside another getData() call.");
}
if (component instanceof DependentTransientComponent) {
@@ -379,4 +382,9 @@ public class DataManagerImpl extends DataManager {
return editor;
}
@ApiStatus.Internal
public interface ParametrizedDataProvider {
Object getData(@NotNull String dataId, @NotNull DataProvider dataProvider);
}
}

View File

@@ -10,7 +10,10 @@ import org.jetbrains.annotations.ApiStatus;
* <li><b>{@link GetDataRuleType#PROVIDER}</b> - a classic rule invoked on a single level data provider</li>
* <li><b>{@link GetDataRuleType#CONTEXT}</b> - a classic rule invoked on the full context data provider</li>
* </ul>
*
* @deprecated Use {@link com.intellij.openapi.actionSystem.DataSink#lazyValue} instead.
*/
@Deprecated
@ApiStatus.Internal
public enum GetDataRuleType {
/**

View File

@@ -11,7 +11,13 @@ import org.jetbrains.annotations.Nullable;
* Provides data for given {@link com.intellij.openapi.actionSystem.DataKey}.
* <p/>
* Must be registered with {@code key} value matching {@code DataKey#getName()}.
*
* @deprecated Use {@link com.intellij.openapi.actionSystem.UiDataRule} instead.
*
* @see com.intellij.openapi.actionSystem.DataSink#lazy
* @see com.intellij.openapi.actionSystem.DataSink#lazyValue
*/
@Deprecated
public interface GetDataRule {
ExtensionPointName<KeyedLazyInstance<GetDataRule>> EP_NAME = new ExtensionPointName<>("com.intellij.getDataRule");

View File

@@ -44,4 +44,7 @@ internal class TestDataSink : DataSink {
override fun <T : Any> lazyNull(key: DataKey<T>) {
set(key, null)
}
override fun <T : Any> lazyValue(key: DataKey<T>, data: (DataMap) -> T?) {
}
}

View File

@@ -523,6 +523,10 @@ private class MySink : DataSink {
})
}
override fun <T : Any> lazyValue(key: DataKey<T>, data: (DataMap) -> T?) {
set(PlatformCoreDataKeys.BGT_DATA_PROVIDER, MyLazyValue(key, data))
}
override fun uiDataSnapshot(provider: UiDataProvider) {
val prev = source
source = provider
@@ -566,12 +570,25 @@ private class MySink : DataSink {
private class MyLazy<T>(val key: DataKey<T>, val supplier: () -> T?) : DataProvider, DataValidators.SourceWrapper {
override fun getData(dataId: String): Any? {
return if (key.`is`(dataId)) supplier.invoke() else null
if (!key.`is`(dataId)) return null
return supplier.invoke()
}
override fun unwrapSource(): Any = supplier
override fun toString(): String = "Lazy(${key.name})"
}
private class MyLazyValue<T>(val key: DataKey<T>, val supplier: (DataMap) -> T?) : DataProvider, DataManagerImpl.ParametrizedDataProvider, DataValidators.SourceWrapper {
override fun getData(dataId: String, dataProvider: DataProvider): Any? {
if (!key.`is`(dataId)) return null
return supplier.invoke(object : DataMap {
@Suppress("UNCHECKED_CAST")
override fun <T : Any> get(key: DataKey<T>): T? = dataProvider.getData(key.name) as T?
})
}
override fun unwrapSource(): Any {
return supplier
}
override fun getData(dataId: String): Any? = null
override fun unwrapSource(): Any = supplier
override fun toString(): String = "LazyValue(${key.name})"
}
private fun hideEditor(component: Component?): Boolean {