private ChildInfo @Nullable [] scanChildren(Path root, List<Path> excluded, NewVirtualFile currentDir) {
    // the stack contains a list of children found so far in the current directory
    Stack<List<ChildInfo>> stack = new Stack<>();
    int nameId = myPersistencePeer.getNameId("");
    ChildInfo fakeRoot = new ChildInfoImpl(nameId, null, null, null);
    stack.push(new SmartList<>(fakeRoot));
    FileVisitor<Path> visitor = new SimpleFileVisitor<>() {
      private int checkCanceledCount;

      @Override
      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        if (!dir.equals(root)) {
          visitFile(dir, attrs);
        }
        if (SystemInfoRt.isWindows && attrs.isOther()) {
          return FileVisitResult.SKIP_SUBTREE;  // bypassing NTFS reparse points
        }
        // on average, this "excluded" array is small for any particular root, so linear search it is.
        if (excluded.contains(dir)) {
          // skipping excluded roots (record its attributes nevertheless), even if we have content roots beneath
          // stop optimization right here - it's too much pain to track all these nested content/excluded/content otherwise
          return FileVisitResult.SKIP_SUBTREE;
        }
        stack.push(new ArrayList<>());
        return FileVisitResult.CONTINUE;
      }

      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        if ((++checkCanceledCount & 0xf) == 0) {
          checkCancelled(currentDir);
        }
        FileAttributes attributes = FileAttributes.fromNio(file, attrs);
        String symLinkTarget = attrs.isSymbolicLink() ? FileUtilRt.toSystemIndependentName(file.toRealPath().toString()) : null;
        int nameId = myPersistencePeer.getNameId(file.getFileName().toString());
        ChildInfo info = new ChildInfoImpl(nameId, attributes, null, symLinkTarget);
        stack.peek().add(info);
        return FileVisitResult.CONTINUE;
      }

      @Override
      public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        List<ChildInfo> childInfos = stack.pop();
        List<ChildInfo> parentInfos = stack.peek();
        // store children back
        ChildInfo parentInfo = ContainerUtil.getLastItem(parentInfos);
        ChildInfo[] children = childInfos.toArray(ChildInfo.EMPTY_ARRAY);
        ChildInfo newInfo = ((ChildInfoImpl)parentInfo).withChildren(children);
        parentInfos.set(parentInfos.size() - 1, newInfo);
        return FileVisitResult.CONTINUE;
      }

      @Override
      public FileVisitResult visitFileFailed(Path file, IOException exc) {
        return FileVisitResult.CONTINUE;  // ignoring exceptions from short-living temp files
      }
    };

    try {
      Files.walkFileTree(root, visitor);
    }
    catch (IOException e) {
      LOG.warn(e);
      return null;  // tell the client we didn't find any children, abandon the optimization altogether
    }

    return stack.pop().get(0).getChildren();
  }
private ChildInfo @Nullable [] scanChildren(Path root, List<Path> excluded, NewVirtualFile currentDir)