@Override
  public void computeSourcePosition(@NotNull final XNavigatable navigatable) {
    if (navigatable instanceof XInlineSourcePosition
        && !(navigatable instanceof XNearestSourcePosition)
        && !(myValueDescriptor instanceof ThisDescriptorImpl
            || myValueDescriptor instanceof LocalVariableDescriptor)) {
      return;
    }
    myEvaluationContext
        .getManagerThread()
        .schedule(
            new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
              @Override
              public Priority getPriority() {
                if (navigatable instanceof XInlineSourcePosition) {
                  return Priority.LOW;
                }
                return Priority.NORMAL;
              }

              @Override
              protected void commandCancelled() {
                navigatable.setSourcePosition(null);
              }

              @Override
              public void contextAction() throws Exception {
                ApplicationManager.getApplication()
                    .runReadAction(
                        new Runnable() {
                          @Override
                          public void run() {
                            final boolean nearest = navigatable instanceof XNearestSourcePosition;
                            SourcePosition position =
                                SourcePositionProvider.getSourcePosition(
                                    myValueDescriptor, getProject(), getDebuggerContext(), nearest);
                            if (position != null) {
                              navigatable.setSourcePosition(
                                  DebuggerUtilsEx.toXSourcePosition(position));
                            }
                          }
                        });
              }
            });
  }
  private void computeSourcePosition(
      @NotNull final XNavigatable navigatable, final boolean inline) {
    myEvaluationContext
        .getManagerThread()
        .schedule(
            new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
              @Override
              public Priority getPriority() {
                return inline ? Priority.LOWEST : Priority.NORMAL;
              }

              @Override
              protected void commandCancelled() {
                navigatable.setSourcePosition(null);
              }

              @Override
              public void contextAction() throws Exception {
                ApplicationManager.getApplication()
                    .runReadAction(
                        new Runnable() {
                          @Override
                          public void run() {
                            SourcePosition position =
                                SourcePositionProvider.getSourcePosition(
                                    myValueDescriptor, getProject(), getDebuggerContext(), false);
                            if (position != null) {
                              navigatable.setSourcePosition(
                                  DebuggerUtilsEx.toXSourcePosition(position));
                            }
                            if (inline) {
                              position =
                                  SourcePositionProvider.getSourcePosition(
                                      myValueDescriptor, getProject(), getDebuggerContext(), true);
                              if (position != null) {
                                navigatable.setSourcePosition(
                                    DebuggerUtilsEx.toXSourcePosition(position));
                              }
                            }
                          }
                        });
              }
            });
  }
  protected static boolean scheduleCommand(
      EvaluationContextImpl evaluationContext,
      @NotNull final XCompositeNode node,
      final SuspendContextCommandImpl command) {
    evaluationContext
        .getManagerThread()
        .schedule(
            new SuspendContextCommandImpl(command.getSuspendContext()) {
              @Override
              public void contextAction() throws Exception {
                command.contextAction();
              }

              @Override
              protected void commandCancelled() {
                node.setErrorMessage(DebuggerBundle.message("error.context.has.changed"));
              }
            });
    return true;
  }
  @Nullable
  @Override
  public String getEvaluationExpression() {
    if (evaluationExpression == null) {
      // TODO: change API to allow to calculate it asynchronously
      myEvaluationContext
          .getManagerThread()
          .invokeAndWait(
              new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
                @Override
                public Priority getPriority() {
                  return Priority.HIGH;
                }

                @Override
                public void contextAction() throws Exception {
                  evaluationExpression =
                      ApplicationManager.getApplication()
                          .runReadAction(
                              new Computable<String>() {
                                @Override
                                public String compute() {
                                  try {
                                    PsiExpression psiExpression =
                                        getDescriptor()
                                            .getTreeEvaluation(
                                                JavaValue.this, getDebuggerContext());
                                    if (psiExpression != null) {
                                      return new TextWithImportsImpl(psiExpression).getText();
                                    }
                                  } catch (EvaluateException e) {
                                    LOG.info(e);
                                  }
                                  return null;
                                }
                              });
                }
              });
    }
    return evaluationExpression;
  }
    @Override
    public void startEvaluation(@NotNull final XFullValueEvaluationCallback callback) {
      if (callback.isObsolete()) return;
      myEvaluationContext
          .getManagerThread()
          .schedule(
              new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
                @Override
                public Priority getPriority() {
                  return Priority.NORMAL;
                }

                @Override
                protected void commandCancelled() {
                  callback.errorOccurred(DebuggerBundle.message("error.context.has.changed"));
                }

                @Override
                public void contextAction() throws Exception {
                  if (callback.isObsolete()) return;
                  evaluate(callback);
                }
              });
    }
  @NotNull
  @Override
  public Promise<XExpression> calculateEvaluationExpression() {
    if (evaluationExpression != null) {
      return Promise.resolve(evaluationExpression);
    } else {
      final AsyncPromise<XExpression> res = new AsyncPromise<XExpression>();
      myEvaluationContext
          .getManagerThread()
          .schedule(
              new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
                @Override
                public Priority getPriority() {
                  return Priority.HIGH;
                }

                @Override
                public void contextAction() throws Exception {
                  evaluationExpression =
                      ApplicationManager.getApplication()
                          .runReadAction(
                              new Computable<XExpression>() {
                                @Override
                                public XExpression compute() {
                                  try {
                                    PsiElement psiExpression =
                                        getDescriptor()
                                            .getTreeEvaluation(
                                                JavaValue.this, getDebuggerContext());
                                    if (psiExpression != null) {
                                      XExpression res =
                                          TextWithImportsImpl.toXExpression(
                                              new TextWithImportsImpl(psiExpression));
                                      // add runtime imports if any
                                      Set<String> imports =
                                          psiExpression.getUserData(
                                              DebuggerTreeNodeExpression.ADDITIONAL_IMPORTS_KEY);
                                      if (imports != null && res != null) {
                                        if (res.getCustomInfo() != null) {
                                          imports.add(res.getCustomInfo());
                                        }
                                        res =
                                            new XExpressionImpl(
                                                res.getExpression(),
                                                res.getLanguage(),
                                                StringUtil.join(imports, ","),
                                                res.getMode());
                                      }
                                      return res;
                                    }
                                  } catch (EvaluateException e) {
                                    LOG.info(e);
                                  }
                                  return null;
                                }
                              });
                  res.setResult(evaluationExpression);
                }
              });
      return res;
    }
  }
  @Override
  public void computePresentation(@NotNull final XValueNode node, @NotNull XValuePlace place) {
    final SuspendContextImpl suspendContext = myEvaluationContext.getSuspendContext();
    myEvaluationContext
        .getManagerThread()
        .schedule(
            new SuspendContextCommandImpl(suspendContext) {
              @Override
              public Priority getPriority() {
                return Priority.NORMAL;
              }

              @Override
              protected void commandCancelled() {
                node.setPresentation(
                    null,
                    new XErrorValuePresentation(
                        DebuggerBundle.message("error.context.has.changed")),
                    false);
              }

              @Override
              public void contextAction() throws Exception {
                if (!myContextSet) {
                  myValueDescriptor.setContext(myEvaluationContext);
                }
                myValueDescriptor.updateRepresentation(
                    myEvaluationContext,
                    new DescriptorLabelListener() {
                      @Override
                      public void labelChanged() {
                        Icon nodeIcon = DebuggerTreeRenderer.getValueIcon(myValueDescriptor);
                        final String value = getValueString();
                        XValuePresentation presentation;
                        @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
                        EvaluateException exception = myValueDescriptor.getEvaluateException();
                        presentation =
                            new JavaValuePresentation(
                                value,
                                myValueDescriptor.getIdLabel(),
                                exception != null ? exception.getMessage() : null,
                                myValueDescriptor);

                        if (myValueDescriptor.getLastRenderer()
                            instanceof FullValueEvaluatorProvider) {
                          XFullValueEvaluator evaluator =
                              ((FullValueEvaluatorProvider) myValueDescriptor.getLastRenderer())
                                  .getFullValueEvaluator(myEvaluationContext, myValueDescriptor);
                          if (evaluator != null) {
                            node.setFullValueEvaluator(evaluator);
                          }
                        } else if (value.length() > XValueNode.MAX_VALUE_LENGTH) {
                          node.setFullValueEvaluator(
                              new JavaFullValueEvaluator(myEvaluationContext) {
                                @Override
                                public void evaluate(
                                    @NotNull final XFullValueEvaluationCallback callback) {
                                  final ValueDescriptorImpl fullValueDescriptor =
                                      myValueDescriptor.getFullValueDescriptor();
                                  fullValueDescriptor.updateRepresentation(
                                      myEvaluationContext,
                                      new DescriptorLabelListener() {
                                        @Override
                                        public void labelChanged() {
                                          callback.evaluated(fullValueDescriptor.getValueText());
                                        }
                                      });
                                }
                              });
                        }
                        node.setPresentation(
                            nodeIcon, presentation, myValueDescriptor.isExpandable());
                      }
                    });
              }
            });
  }