diff --git a/platform/core-nio-fs/src/com/intellij/platform/core/nio/fs/BasicFileAttributesHolder2.java b/platform/core-nio-fs/src/com/intellij/platform/core/nio/fs/BasicFileAttributesHolder2.java index 13273c63c8d7..e97ed8628668 100644 --- a/platform/core-nio-fs/src/com/intellij/platform/core/nio/fs/BasicFileAttributesHolder2.java +++ b/platform/core-nio-fs/src/com/intellij/platform/core/nio/fs/BasicFileAttributesHolder2.java @@ -6,10 +6,14 @@ import org.jetbrains.annotations.Nullable; import sun.nio.fs.BasicFileAttributesHolder; import java.lang.ref.WeakReference; +import java.nio.file.DirectoryStream; +import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; /** * This interface allows not adding `--add-exports` to modules where some class implements {@link BasicFileAttributesHolder}. + * + * Also, this interface defines some hacks and helper methods related to {@link BasicFileAttributesHolder}. */ public interface BasicFileAttributesHolder2 extends BasicFileAttributesHolder { /** @@ -47,4 +51,30 @@ public interface BasicFileAttributesHolder2 extends BasicFileAttributesHolder { myCachedAttributes.clear(); } } + + static @Nullable BasicFileAttributes getAttributesFromHolder(@NotNull Path path) { + if (path instanceof BasicFileAttributesHolder bafh) { + return bafh.get(); + } + return null; + } + + /** + * A marker interface for {@link java.nio.file.spi.FileSystemProvider#newDirectoryStream} + * that advises the file system to fetch file attributes and fill {@link BasicFileAttributesHolder}, if it's supported. + *

+ * Unlike many other methods in {@code FileSystemProvider}, {@code newDirectoryStream} doesn't accept a set of options. + * The easiest way to provide additional information to the method is through the filter argument. + *

+ * A file system provider should support this feature explicitly. + * This interface has no effect on default filesystems from JDK: + * + */ + @FunctionalInterface + interface FetchAttributesFilter extends DirectoryStream.Filter { + FetchAttributesFilter ACCEPT_ALL = path -> true; + } } diff --git a/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/IjentNioFileSystemProvider.kt b/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/IjentNioFileSystemProvider.kt index cfe95c5e8db8..b13e49c839cc 100644 --- a/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/IjentNioFileSystemProvider.kt +++ b/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/IjentNioFileSystemProvider.kt @@ -2,6 +2,7 @@ package com.intellij.platform.ijent.community.impl.nio import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.platform.core.nio.fs.BasicFileAttributesHolder2.FetchAttributesFilter import com.intellij.platform.ijent.community.impl.IjentFsResultImpl import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider.Companion.newFileSystemMap import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider.UnixFilePermissionBranch.* @@ -180,23 +181,34 @@ class IjentNioFileSystemProvider : FileSystemProvider() { val nioFs = dir.nioFs return fsBlocking { - val children = nioFs.ijentFs - .listDirectoryWithAttrs(ensurePathIsAbsolute(dir.ijentPath), IjentFileSystemApi.SymlinkPolicy.DO_NOT_RESOLVE) - .getOrThrowFileSystemException() - - val nioPathList = children.asSequence() - .map { (childName, childStat) -> - val childIjentPath = dir.ijentPath.getChild(childName).getOrThrow() - val childAttrs = when (childStat) { - is IjentPosixFileInfo -> IjentNioPosixFileAttributes(childStat) - is IjentWindowsFileInfo -> TODO() - } - IjentNioPath(childIjentPath, nioFs, childAttrs) + val notFilteredPaths = + if (pathFilter is FetchAttributesFilter) { + nioFs.ijentFs + .listDirectoryWithAttrs(ensurePathIsAbsolute(dir.ijentPath), IjentFileSystemApi.SymlinkPolicy.DO_NOT_RESOLVE) + .getOrThrowFileSystemException() + .asSequence() + .map { (childName, childStat) -> + val childIjentPath = dir.ijentPath.getChild(childName).getOrThrow() + val childAttrs = when (childStat) { + is IjentPosixFileInfo -> IjentNioPosixFileAttributes(childStat) + is IjentWindowsFileInfo -> TODO() + } + IjentNioPath(childIjentPath, nioFs, childAttrs) + } } - .filter { nioPath -> - pathFilter?.accept(nioPath) != false + else { + nioFs.ijentFs + .listDirectory(ensurePathIsAbsolute(dir.ijentPath)) + .getOrThrowFileSystemException() + .asSequence() + .map { childName -> + val childIjentPath = dir.ijentPath.getChild(childName).getOrThrow() + IjentNioPath(childIjentPath, nioFs, null) + } } - .toMutableList() + val nioPathList = notFilteredPaths.filterTo(mutableListOf()) { nioPath -> + pathFilter?.accept(nioPath) != false + } object : DirectoryStream { // The compiler doesn't (didn't?) allow to relax types here. diff --git a/platform/platform-impl/src/com/intellij/execution/wsl/ijent/nio/IjentWslNioFileSystemProvider.kt b/platform/platform-impl/src/com/intellij/execution/wsl/ijent/nio/IjentWslNioFileSystemProvider.kt index 8ab53ec95546..00bb9e263624 100644 --- a/platform/platform-impl/src/com/intellij/execution/wsl/ijent/nio/IjentWslNioFileSystemProvider.kt +++ b/platform/platform-impl/src/com/intellij/execution/wsl/ijent/nio/IjentWslNioFileSystemProvider.kt @@ -6,6 +6,8 @@ import com.intellij.execution.wsl.WslPath import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.util.io.CaseSensitivityAttribute import com.intellij.openapi.util.io.FileAttributes +import com.intellij.platform.core.nio.fs.BasicFileAttributesHolder2 +import com.intellij.platform.core.nio.fs.BasicFileAttributesHolder2.FetchAttributesFilter import com.intellij.platform.core.nio.fs.RoutingAwareFileSystemProvider import com.intellij.platform.ijent.IjentPosixInfo import com.intellij.platform.ijent.community.impl.nio.IjentNioPath @@ -315,11 +317,9 @@ class IjentWslNioFileSystemProvider( lastDirectory = lastDirectory.parent } - // TODO Add BasicFileAttributesHolder, it gives a huge speed up. - //val stat = - // source.asSafely()?.get() - // ?: source.readAttributes(LinkOption.NOFOLLOW_LINKS) - val stat = source.readAttributes(LinkOption.NOFOLLOW_LINKS) + val stat = + BasicFileAttributesHolder2.getAttributesFromHolder(source) + ?: source.readAttributes(LinkOption.NOFOLLOW_LINKS) // WindowsPath doesn't support resolve() from paths of different class. val target = source.relativeTo(sourceRoot).fold(targetRoot) { parent, file -> @@ -337,7 +337,7 @@ class IjentWslNioFileSystemProvider( throw err } } - Files.newDirectoryStream(source).use { children -> + source.fileSystem.provider().newDirectoryStream(source, FetchAttributesFilter.ACCEPT_ALL).use { children -> sourceStack.addAll(children.toList().asReversed()) } } diff --git a/platform/platform-impl/src/com/intellij/util/io/PlatformNioHelper.java b/platform/platform-impl/src/com/intellij/util/io/PlatformNioHelper.java index 152cba2c0769..3ad07b8f24dd 100644 --- a/platform/platform-impl/src/com/intellij/util/io/PlatformNioHelper.java +++ b/platform/platform-impl/src/com/intellij/util/io/PlatformNioHelper.java @@ -2,6 +2,7 @@ package com.intellij.util.io; import com.intellij.openapi.util.io.NioFiles; +import com.intellij.platform.core.nio.fs.BasicFileAttributesHolder2.FetchAttributesFilter; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,7 +41,7 @@ public final class PlatformNioHelper { @SuppressWarnings({"UnnecessaryFullyQualifiedName", "InstanceofIncompatibleInterface"}) public static void visitDirectory(@NotNull Path directory, @Nullable Set filter, @NotNull BiPredicate> consumer) throws IOException, SecurityException { - try (var dirStream = Files.newDirectoryStream(directory)) { + try (var dirStream = directory.getFileSystem().provider().newDirectoryStream(directory, FetchAttributesFilter.ACCEPT_ALL)) { for (var path : dirStream) { if (filter != null && !filter.contains(path.getFileName().toString())) { continue;