@NotNull
  public CompletableFuture<NodeRenderer> getAutoRendererAsync(@Nullable Type type) {
    DebuggerManagerThreadImpl.assertIsManagerThread();
    // in case evaluation is not possible, force default renderer
    if (!isEvaluationPossible()) {
      return CompletableFuture.completedFuture(getDefaultRenderer(type));
    }

    try {
      Object renderer = myNodeRenderersMap.get(type);
      if (renderer instanceof NodeRenderer) {
        return CompletableFuture.completedFuture((NodeRenderer)renderer);
      }
      else if (renderer instanceof CompletableFuture) {
        //noinspection unchecked
        CompletableFuture<NodeRenderer> future = (CompletableFuture<NodeRenderer>)renderer;
        LOG.assertTrue(!future.isDone(), "Completed future for " + type);
        return future;
      }
      List<NodeRenderer> enabledRenderers = ContainerUtil.filter(myRenderers, NodeRenderer::isEnabled);
      CompletableFuture<NodeRenderer> res = DebuggerUtilsImpl.getFirstApplicableRenderer(enabledRenderers, type)
        .handle((r, throwable) -> {
          // sometimes we may be not in DebuggerManagerThread here, see EA-433577 for more details
          //DebuggerManagerThreadImpl.assertIsManagerThread();
          if (r == null || throwable != null) {
            r = getDefaultRenderer(type); // do not cache the fallback renderer
            myNodeRenderersMap.remove(type);
          }
          else {
            // TODO: may add a new (possibly incorrect) value after the cleanup in reloadRenderers
            myNodeRenderersMap.put(type, r);
          }
          return r;
        });
      if (!res.isDone()) {
        myNodeRenderersMap.putIfAbsent(type, res);
      }
      return res;
    }
    catch (ClassNotPreparedException e) {
      LOG.info(e);
      // use default, but do not cache
      return CompletableFuture.completedFuture(getDefaultRenderer(type));
    }
  }
@NotNull
  public CompletableFuture<NodeRenderer> getAutoRendererAsync(@Nullable Type type)