diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java index 0f2d66fda7c1..e71ead4757d9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java @@ -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 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 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 { diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueMarkerProvider.java b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueMarkerProvider.java index 3b9497088dd9..4a2cb598b3db 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueMarkerProvider.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueMarkerProvider.java @@ -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.

@@ -54,12 +56,29 @@ public abstract class XValueMarkerProvider { return getMarker(value); } + /** + * Async version of the {@link #markValue(XValue)} method + * @return a promise with a marker for {@code value} + */ + @NotNull + public Promise 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 unmarkValueAsync(@NotNull V value, @NotNull M marker) { + unmarkValue(value, marker); + return Promises.resolvedPromise(); + } + public final Class getValueClass() { return myValueClass; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java index aa1a3ce9ca29..0d1f11d7627f 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java @@ -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 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 diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XValueMarkers.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XValueMarkers.java index 0cd4ed03567f..985c98b06e1c 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XValueMarkers.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XValueMarkers.java @@ -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 { private final XValueMarkerProvider myProvider; @@ -17,7 +22,7 @@ public final class XValueMarkers { private XValueMarkers(@NotNull XValueMarkerProvider provider) { myProvider = provider; - myMarkers = new HashMap<>(); + myMarkers = Collections.synchronizedMap(new HashMap<>()); } public static XValueMarkers createValueMarkers(@NotNull XValueMarkerProvider provider) { @@ -45,25 +50,52 @@ public final class XValueMarkers { 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 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 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 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(); } }