private List<Pair<File, Integer>> processRecursively(File dir, BitSet enabledDetectors, Set<? super File> parentDirectories) {
    List<Pair<File, Integer>> parentsToSkip = new SmartList<>();

    if (myTypeManager.isFileIgnored(dir.getName())) {
      return parentsToSkip;
    }
    if (myProgressIndicator != null) {
      if (myProgressIndicator.isCanceled()) {
        return parentsToSkip;
      }
      @NlsSafe final String path = dir.getPath();
      myProgressIndicator.setText2(path);
    }

    if (FileSystemUtil.isSymLink(dir)) {
      try {
        if (parentDirectories.contains(dir.getCanonicalFile())) {
          return parentsToSkip;
        }
      }
      catch (IOException ignored) {
      }
    }

    try {
      parentDirectories.add(dir);
      File[] children = dir.listFiles();

      if (children == null) {
        children = ArrayUtilRt.EMPTY_FILE_ARRAY;
      }

      BitSet enabledForChildren = enabledDetectors;
      for (int i = 0, detectorsLength = myDetectors.length; i < detectorsLength; i++) {
        if (!enabledDetectors.get(i)) continue;

        final ProjectStructureDetector.DirectoryProcessingResult result = myDetectors[i].detectRoots(dir, children, myBaseDir, myDetectedRoots[i]);

        if (!result.isProcessChildren()) {
          if (enabledForChildren == enabledDetectors) {
            enabledForChildren = new BitSet();
            enabledForChildren.or(enabledDetectors);
          }
          enabledForChildren.set(i, false);
        }

        final File parentToSkip = result.getParentToSkip();
        if (parentToSkip != null && !FileUtil.filesEqual(parentToSkip, dir)) {
          parentsToSkip.add(Pair.create(parentToSkip, i));
        }
      }

      if (!enabledForChildren.isEmpty()) {
        for (File child : children) {
          if (child.isDirectory() && !FrameworkDetectionProcessor.SKIPPED_DIRECTORIES.contains(child.getName())) {
            final List<Pair<File, Integer>> toSkip = processRecursively(child, enabledForChildren, parentDirectories);
            if (!toSkip.isEmpty()) {
              if (enabledForChildren == enabledDetectors) {
                enabledForChildren = new BitSet();
                enabledForChildren.or(enabledDetectors);
              }
              for (Pair<File, Integer> pair : toSkip) {
                enabledForChildren.set(pair.getSecond(), false);
                if (!FileUtil.filesEqual(pair.getFirst(), dir)) {
                  parentsToSkip.add(pair);
                }
              }
              if (enabledForChildren.isEmpty()) {
                break;
              }
            }
          }
        }
      }
      return parentsToSkip;
    }
    finally {
      parentDirectories.remove(dir);
    }
  }
private List<Pair<File, Integer>> processRecursively(File dir, BitSet enabledDetectors, Set<? super File> parentDirectories)