Add async API to XValueMarkerProvider

GitOrigin-RevId: 4a0372dd6f98de71f2ea3dbe8a1b6ebb6849e2fa
This commit is contained in:
Vyacheslav Moklev
2022-02-18 18:31:06 +00:00
committed by intellij-monorepo-bot
parent 6ab258333a
commit 2da966fb75
4 changed files with 105 additions and 40 deletions
@@ -32,6 +32,7 @@ import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.ui.JBColor;
import com.intellij.util.ObjectUtils;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.xdebugger.frame.XValueModifier;
import com.intellij.xdebugger.frame.XValueNode;
@@ -41,6 +42,8 @@ import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
import javax.swing.*;
import java.util.Collections;
@@ -575,30 +578,32 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
});
}
return ReadAction.nonBlocking(() -> {
PsiElement res = null;
try {
res = getDescriptorEvaluation(context);
}
catch (NeedMarkException e) {
XValueMarkers<?, ?> markers = DebuggerUtilsImpl.getValueMarkers(context.getDebugProcess());
if (markers != null) {
ValueMarkup existing = markers.getMarkup(value);
String name;
if (existing != null) {
name = existing.getText();
}
else {
name = e.getMarkName();
markers.markValue(value, new ValueMarkup(name, new JBColor(0, 0), null));
}
res = JavaPsiFacade.getElementFactory(myProject)
.createExpressionFromText(name + CodeFragmentFactoryContextWrapper.DEBUG_LABEL_SUFFIX,
PositionUtil.getContextElement(context));
Promise<PsiElement> res;
try {
res = Promises.resolvedPromise(getDescriptorEvaluation(context));
}
catch (NeedMarkException e) {
XValueMarkers<?, ?> markers = DebuggerUtilsImpl.getValueMarkers(context.getDebugProcess());
if (markers != null) {
ValueMarkup existing = markers.getMarkup(value);
String markName;
Promise<Object> promise;
if (existing != null) {
markName = existing.getText();
promise = Promises.resolvedPromise();
}
else {
markName = e.getMarkName();
promise = markers.markValueAsync(value, new ValueMarkup(markName, new JBColor(0, 0), null));
}
res = promise.then(__ -> ReadAction.nonBlocking(() -> JavaPsiFacade.getElementFactory(myProject)
.createExpressionFromText(markName + CodeFragmentFactoryContextWrapper.DEBUG_LABEL_SUFFIX,
PositionUtil.getContextElement(context))).executeSynchronously());
} else {
res = Promises.resolvedPromise(null);
}
return CompletableFuture.completedFuture(res);
}).executeSynchronously();
}
return Promises.asCompletableFuture(res);
}
protected static class NeedMarkException extends EvaluateException {
@@ -16,6 +16,8 @@
package com.intellij.xdebugger.frame;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
/**
* Provides implementation of 'Mark Object' feature. <p>
@@ -54,12 +56,29 @@ public abstract class XValueMarkerProvider<V extends XValue, M> {
return getMarker(value);
}
/**
* Async version of the {@link #markValue(XValue)} method
* @return a promise with a marker for {@code value}
*/
@NotNull
public Promise<M> markValueAsync(@NotNull V value) {
return Promises.resolvedPromise(markValue(value));
}
/**
* This method is called when 'Unmark Object' action is invoked.
*/
public void unmarkValue(@NotNull V value, @NotNull M marker) {
}
/**
* Async version of the {@link #unmarkValue(XValue, Object)} method
*/
public Promise<Object> unmarkValueAsync(@NotNull V value, @NotNull M marker) {
unmarkValue(value, marker);
return Promises.resolvedPromise();
}
public final Class<V> getValueClass() {
return myValueClass;
}
@@ -4,6 +4,7 @@ package com.intellij.xdebugger.impl.actions.handlers;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.ui.ComponentUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.impl.XDebugSessionImpl;
@@ -17,6 +18,7 @@ import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.Promise;
import javax.swing.*;
import java.awt.*;
@@ -38,8 +40,9 @@ public class XMarkObjectActionHandler extends MarkObjectActionHandler {
XDebuggerTreeState treeState = XDebuggerTreeState.saveState(node.getTree());
ValueMarkup existing = markers.getMarkup(value);
Promise<Object> markPromise;
if (existing != null) {
markers.unmarkValue(value);
markPromise = markers.unmarkValueAsync(value);
}
else {
Component component = event.getData(CONTEXT_COMPONENT);
@@ -52,13 +55,19 @@ public class XMarkObjectActionHandler extends MarkObjectActionHandler {
dialog.show();
ValueMarkup markup = dialog.getConfiguredMarkup();
if (dialog.isOK() && markup != null) {
markers.markValue(value, markup);
markPromise = markers.markValueAsync(value, markup);
} else {
return;
}
}
if (detachedView) {
node.getTree().rebuildAndRestore(treeState);
}
session.rebuildViews();
markPromise.onSuccess(__ ->
UIUtil.invokeLaterIfNeeded(() -> {
if (detachedView) {
node.getTree().rebuildAndRestore(treeState);
}
session.rebuildViews();
})
);
}
@Override
@@ -6,10 +6,15 @@ import com.intellij.xdebugger.frame.XValueMarkerProvider;
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public final class XValueMarkers<V extends XValue, M> {
private final XValueMarkerProvider<V, M> myProvider;
@@ -17,7 +22,7 @@ public final class XValueMarkers<V extends XValue, M> {
private XValueMarkers(@NotNull XValueMarkerProvider<V, M> provider) {
myProvider = provider;
myMarkers = new HashMap<>();
myMarkers = Collections.synchronizedMap(new HashMap<>());
}
public static <V extends XValue, M> XValueMarkers<V, M> createValueMarkers(@NotNull XValueMarkerProvider<V, M> provider) {
@@ -45,25 +50,52 @@ public final class XValueMarkers<V extends XValue, M> {
return myProvider.canMark(valueClass.cast(value));
}
@Deprecated
public void markValue(@NotNull XValue value, @NotNull ValueMarkup markup) {
// remove the existing label if any
myMarkers.entrySet().stream()
.filter(entry -> markup.getText().equals(entry.getValue().getText()))
.findFirst()
.ifPresent(entry -> myMarkers.remove(entry.getKey()));
//noinspection unchecked
M m = myProvider.markValue((V)value);
myMarkers.put(m, markup);
try {
markValueAsync(value, markup).blockingGet(5, TimeUnit.SECONDS);
}
catch (TimeoutException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public Promise<Object> markValueAsync(@NotNull XValue value, @NotNull ValueMarkup markup) {
synchronized (myMarkers) {
// remove the existing label if any
myMarkers.entrySet().stream()
.filter(entry -> markup.getText().equals(entry.getValue().getText()))
.findFirst()
.ifPresent(entry -> myMarkers.remove(entry.getKey()));
}
//noinspection unchecked
Promise<M> promise = myProvider.markValueAsync((V)value);
return promise.then(m -> {
myMarkers.put(m, markup);
return null;
});
}
@Deprecated
public void unmarkValue(@NotNull XValue value) {
try {
unmarkValueAsync(value).blockingGet(5, TimeUnit.SECONDS);
}
catch (TimeoutException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public Promise<Object> unmarkValueAsync(@NotNull XValue value) {
//noinspection unchecked
final V v = (V)value;
M m = myProvider.getMarker(v);
if (m != null) {
myProvider.unmarkValue(v, m);
myMarkers.remove(m);
return myProvider.unmarkValueAsync(v, m)
.onSuccess(__ -> myMarkers.remove(m));
} else {
return Promises.resolvedPromise();
}
}