@ApiStatus.Internal
  public void processEventsImpl(@NotNull List<? extends CompoundVFileEvent> events, boolean excludeAsyncListeners) {
    app.assertWriteAccessAllowed();

    int startIndex = 0;
    int cappedInitialSize = Math.min(events.size(), INNER_ARRAYS_THRESHOLD);
    List<Runnable> applyActions = new ArrayList<>(cappedInitialSize);
    // even in the unlikely case when case-insensitive maps falsely detect conflicts of case-sensitive paths,
    // the worst outcome will be one extra event batch, which is acceptable
    MostlySingularMultiMap<String, VFileEvent> files = new MostlySingularMultiMap<>(CollectionFactory.createFilePathMap(cappedInitialSize));
    Set<String> middleDirs = CollectionFactory.createFilePathSet(cappedInitialSize);

    List<VFileEvent> validated = new ArrayList<>(cappedInitialSize);
    BulkFileListener publisher = getPublisher();
    Map<VirtualDirectoryImpl, Object> toCreate = new LinkedHashMap<>();
    Set<VFileEvent> toIgnore = new ReferenceOpenHashSet<>(); // VFileEvent overrides equals(), hence identity-based
    Set<VirtualFile> toDelete = CollectionFactory.createSmallMemoryFootprintSet();
    while (startIndex != events.size()) {
      PingProgress.interactWithEdtProgress();

      applyActions.clear();
      files.clear();
      middleDirs.clear();
      validated.clear();
      toCreate.clear();
      toIgnore.clear();
      toDelete.clear();
      startIndex = groupAndValidate(events, startIndex, applyActions, validated, files, middleDirs, toCreate, toIgnore, toDelete,
                                    excludeAsyncListeners);

      if (!validated.isEmpty()) {
        applyMultipleEvents(publisher, applyActions, validated, excludeAsyncListeners);
      }
    }
  }
@ApiStatus.Internal
  public void processEventsImpl(@NotNull List<? extends CompoundVFileEvent> events, boolean excludeAsyncListeners)