@Deprecated
  public static @NotNull CompletableFuture<?> scheduleWithWriteActionPriority(@NotNull ProgressIndicator progressIndicator,
                                                                              @NotNull Executor executor,
                                                                              @NotNull ReadTask readTask) {
    // invoke later even if on EDT
    // to avoid tasks eagerly restarting immediately, allocating many pooled threads
    // which get cancelled too soon when the next write action arrives in the same EDT batch
    // (can happen when processing multiple VFS events or writing multiple files on save)

    CompletableFuture<?> future = new CompletableFuture<>();
    Application application = ApplicationManager.getApplication();
    application.invokeLater(() -> {
      if (application.isDisposed() || progressIndicator.isCanceled() || future.isCancelled()) {
        future.complete(null);
        return;
      }
      Disposable listenerDisposable = Disposer.newDisposable();
      ApplicationListener listener = new ApplicationListener() {
        @Override
        public void beforeWriteActionStart(@NotNull Object action) {
          if (!progressIndicator.isCanceled()) {
            progressIndicator.cancel();
            readTask.onCanceled(progressIndicator);
          }
        }
      };
      application.addApplicationListener(listener, listenerDisposable);
      future.whenComplete((__, ___) -> Disposer.dispose(listenerDisposable));
      try {
        executor.execute(ClientId.decorateRunnable(new Runnable() {
          @Override
          public void run() {
            ReadTask.Continuation continuation;
            try {
              continuation = runUnderProgress(progressIndicator, readTask);
            }
            catch (Throwable e) {
              future.completeExceptionally(e);
              throw e;
            }
            if (continuation == null) {
              future.complete(null);
            }
            else if (!future.isCancelled()) {
              application.invokeLater(new Runnable() {
                @Override
                public void run() {
                  if (future.isCancelled()) return;

                  Disposer.dispose(listenerDisposable); // remove listener early to prevent firing it during continuation execution
                  try {
                    if (!progressIndicator.isCanceled()) {
                      continuation.getAction().run();
                    }
                  }
                  finally {
                    future.complete(null);
                  }
                }

                @Override
                public String toString() {
                  return "continuation of " + readTask;
                }
              }, continuation.getModalityState());
            }
          }

          @Override
          public String toString() {
            return readTask.toString();
          }
        }));
      }
      catch (Throwable e) {
        future.completeExceptionally(e);
        throw e;
      }
    }, ModalityState.any()); // 'any' to tolerate immediate modality changes (e.g. https://youtrack.jetbrains.com/issue/IDEA-135180)
    return future;
  }
@Deprecated
  public static @NotNull CompletableFuture<?> scheduleWithWriteActionPriority(@NotNull ProgressIndicator progressIndicator,
                                                                              @NotNull Executor executor,
                                                                              @NotNull ReadTask readTask)