@NotNull
  private FileAnnotation annotate(@NotNull VirtualFile file,
                                  @NotNull SvnRevisionNumber revisionNumber,
                                  @NotNull VcsRevisionNumber lastChangedRevision,
                                  @NotNull Throwable2Computable<String, VcsException, IOException> contentLoader) throws VcsException {
    if (file.isDirectory()) {
      throw new VcsException(message("exception.text.cannot.annotate.directory"));
    }
    final FileAnnotation[] annotation = new FileAnnotation[1];
    final VcsException[] exception = new VcsException[1];

    Runnable command = () -> {
      final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
      final File ioFile = virtualToIoFile(file).getAbsoluteFile();
      Info info = null;
      try {

        String contents = contentLoader.compute();
        final SvnFileAnnotation result = new SvnFileAnnotation(myVcs, file, contents, lastChangedRevision);

        info = myVcs.getInfo(ioFile);
        if (info == null) {
          exception[0] = new SvnBindException(message("error.file.is.not.under.version.control", ioFile));
          return;
        }
        Url url = info.getUrl();
        Revision endRevision = revisionNumber.getRevision();
        if (Revision.WORKING.equals(endRevision)) {
          endRevision = info.getRevision();
        }
        if (progress != null) {
          progress.setText(message("progress.text.computing.annotation", file.getName()));
        }

        // ignore mime type=true : IDEA-19562
        final AnnotationConsumer annotateHandler = createAnnotationHandler(progress, result);

        boolean calculateMergeinfo = myVcs.getSvnConfiguration().isShowMergeSourcesInAnnotate() && checkRepositoryVersion15(myVcs, url);
        final MySteppedLogGetter logGetter = new MySteppedLogGetter(
          myVcs, ioFile, progress,
          myVcs.getFactory(ioFile).createHistoryClient(), endRevision, result,
          url, calculateMergeinfo, file.getCharset());

        logGetter.go();
        final LinkedList<Revision> rp = logGetter.getRevisionPoints();

        // TODO: only 2 elements will be in rp and for loop will be executed only once - probably rewrite with Pair
        AnnotateClient annotateClient = myVcs.getFactory(ioFile).createAnnotateClient();
        for (int i = 0; i < rp.size() - 1; i++) {
          annotateClient.annotate(Target.on(ioFile), rp.get(i + 1), rp.get(i), calculateMergeinfo, getLogClientOptions(myVcs),
                                  annotateHandler);
        }

        if (rp.get(1).getNumber() > 0) {
          result.setFirstRevision(rp.get(1));
        }
        annotation[0] = result;
      }
      catch (IOException e) {
        exception[0] = new VcsException(e);
      }
      catch (SvnBindException e) {
        handleSvnException(ioFile, info, e, file, revisionNumber, annotation, exception);
      }
      catch (VcsException e) {
        exception[0] = e;
      }
    };
    if (ApplicationManager.getApplication().isDispatchThread()) {
      ProgressManager.getInstance()
        .runProcessWithProgressSynchronously(command, message("action.text.annotate"), false, myVcs.getProject());
    }
    else {
      command.run();
    }
    if (exception[0] != null) {
      throw new VcsException(exception[0]);
    }
    return annotation[0];
  }
@NotNull
  private FileAnnotation annotate(@NotNull VirtualFile file,
                                  @NotNull SvnRevisionNumber revisionNumber,
                                  @NotNull VcsRevisionNumber lastChangedRevision,
                                  @NotNull Throwable2Computable<String, VcsException, IOException> contentLoader)