@Nullable("null if the file is not subject for indexing (a directory, invalid, etc.)")
  public UnindexedFileStatus getFileStatus(@NotNull VirtualFile file) {
    ProgressManager.checkCanceled(); // give a chance to suspend indexing
    if (!file.isValid() || !(file instanceof VirtualFileWithId)) {
      return null;
    }
    // snapshot at the beginning: if file changes while being processed, we can detect this on the following scanning
    FileIndexingStamp indexingStamp = indexingRequest.getFileIndexingStamp(file);
    FileIndexesValuesApplier.ApplicationMode applicationMode = FileBasedIndexImpl.getContentIndependentIndexesApplicationMode();

    if (TRUST_INDEXING_FLAG) {
      if (IndexingFlag.isFileIndexed(file, indexingStamp)) {
        myFilterHandler.addFileId(myProject, FileBasedIndex.getFileId(file));
        return new UnindexedFileStatusBuilder(applicationMode).build();
      }
    }

    Supplier<@NotNull Boolean> checker = CachedFileType.getFileTypeChangeChecker();
    FileType cachedFileType = file.getFileType();
    return ReadAction.compute(() -> {
      if (myProject.isDisposed() || !file.isValid()) {
        return null;
      }
      FileType fileType = checker.get() ? cachedFileType : null;

      UnindexedFileStatusBuilder fileStatusBuilder = new UnindexedFileStatusBuilder(applicationMode);

      IndexedFileImpl indexedFile = new IndexedFileImpl(file, fileType, myProject);
      int inputId = FileBasedIndex.getFileId(file);
      myFilterHandler.addFileId(myProject, inputId);

      if (IndexingFlag.isFileIndexed(file, indexingStamp)) {
        boolean wasInvalidated = false;
        List<ID<?, ?>> ids = IndexingStamp.getNontrivialFileIndexedStates(inputId);
        for (FileBasedIndexInfrastructureExtension.FileIndexingStatusProcessor processor : myStateProcessors) {
          for (ID<?, ?> id : ids) {
            if (myFileBasedIndex.needsFileContentLoading(id)) {
              long nowTime = System.nanoTime();
              try {
                if (!processor.processUpToDateFile(indexedFile, inputId, id)) {
                  wasInvalidated = true;
                }
              }
              finally {
                fileStatusBuilder.timeProcessingUpToDateFiles += (System.nanoTime() - nowTime);
              }
            }
          }
        }
        if (!wasInvalidated) {
          IndexingStamp.flushCache(inputId);
          return fileStatusBuilder.build();
        }
      }

      FileTypeManagerEx ex = FileTypeManagerEx.getInstanceEx();
      if (!(ex instanceof FileTypeManagerImpl)) {
        return fileStatusBuilder.build();
      }
      Ref<Runnable> finalization = new Ref<>();
      ((FileTypeManagerImpl)ex).freezeFileTypeTemporarilyWithProvidedValueIn(file, fileType, () -> {
        boolean isDirectory = file.isDirectory();
        FileIndexingState fileTypeIndexState = null;
        boolean shouldCheckContentIndexes;
        if (!isDirectory && !myFileBasedIndex.isTooLarge(file)) {
          if ((fileTypeIndexState = myFileBasedIndex.getIndexingState(indexedFile, myFileTypeIndex, indexingStamp)) == FileIndexingState.OUT_DATED) {
            if (FileBasedIndexEx.doTraceIndexUpdates()) {
              LOG.info("Scheduling full indexing of " + indexedFile.getFileName() + " because file type index is outdated");
            }
            myFileBasedIndex.dropNontrivialIndexedStates(inputId);
            fileStatusBuilder.shouldIndex = true;
            shouldCheckContentIndexes = false;
          }
          else {
            shouldCheckContentIndexes = true;
          }
        }
        else {
          shouldCheckContentIndexes = false;
        }
        boolean fileTypeIndexAlreadyUpToData = fileTypeIndexState != null && !fileTypeIndexState.updateRequired();
        Set<ID<?, ?>> appliedIndexes = myFileBasedIndex.getAppliedIndexes(inputId);
        List<ID<?, ?>> requiredIndexes = myFileBasedIndex.getRequiredIndexes(indexedFile);

        // TODO - this non-cancelable section is just a precaution, it should be removed once we are sure
        //        the process is cancelled only when expected
        Cancellation.computeInNonCancelableSection(() -> {
          for (ID<?, ?> indexId : requiredIndexes) {
            appliedIndexes.remove(indexId);

            boolean needsFileContentLoading = myFileBasedIndex.needsFileContentLoading(indexId);
            // this is the same: (shouldCheckContentIndexes && needsFileContentLoading) || !needsFileContentLoading
            boolean shouldCheckAgainstSingleIndex = !needsFileContentLoading || shouldCheckContentIndexes;

            // if FileTypeIndex already checked, no need to check it twice
            if (shouldCheckAgainstSingleIndex && !(FileTypeIndex.NAME.equals(indexId) && fileTypeIndexAlreadyUpToData)) {
              long contentlessStartTime = needsFileContentLoading ? -1 : System.nanoTime(); // measure contentless indexes only
              try {
                applyOrScheduleRequiredIndex(indexId, fileStatusBuilder, indexedFile, inputId, indexingStamp);
              }
              finally {
                if (contentlessStartTime >= 0) {
                  fileStatusBuilder.timeUpdatingContentLessIndexes += (System.nanoTime() - contentlessStartTime);
                }
              }
            }
          }

          // remove unneeded data from indexes
          for (ID<?, ?> indexId : appliedIndexes) {
            removeIndexedValue(indexedFile, inputId, indexId, fileStatusBuilder);
          }

          if (!fileStatusBuilder.hasAppliersOrRemovers()) {
            finishGettingStatus(file, indexedFile, inputId, fileStatusBuilder, indexingStamp);
            finalization.set(EmptyRunnable.getInstance());
          }
          else {
            finalization.set(() -> {
              long applyingStart = System.nanoTime();
              try {
                fileStatusBuilder.removers.forEach(SingleIndexValueRemover::remove);
                fileStatusBuilder.appliers.forEach(SingleIndexValueApplier::apply);
              }
              finally {
                fileStatusBuilder.timeUpdatingContentLessIndexes += (System.nanoTime() - applyingStart);
              }
              finishGettingStatus(file, indexedFile, inputId, fileStatusBuilder, indexingStamp);
            });
          }
          return null;
        });
      });

      finalization.get().run();

      fileStatusBuilder.explain(indexedFile, explanationLogger);
      return fileStatusBuilder.build();
    });
  }
@Nullable("null if the file is not subject for indexing (a directory, invalid, etc.)")
  public UnindexedFileStatus getFileStatus(@NotNull VirtualFile file)