From dd1be69e5833043d2868d90271e37faa32c6ccf2 Mon Sep 17 00:00:00 2001 From: Mihail Buryakov Date: Tue, 10 Feb 2026 12:09:15 +0200 Subject: [PATCH] [ijent] IJPL-232220 additional fixes for path manipulation GitOrigin-RevId: b8104ca8253150da2e155e960f223549d4c8e608 --- .../eel/provider/utils/WindowsPathUtils.kt | 10 ++-- .../fs/IjentEphemeralRootAwareFileSystem.kt | 11 +++- .../intellij/openapi/vfs/impl/local/util.kt | 56 ++++++++++--------- .../execution/eel/WindowsPathUtilsTest.kt | 2 +- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/platform/eel-provider/src/com/intellij/platform/eel/provider/utils/WindowsPathUtils.kt b/platform/eel-provider/src/com/intellij/platform/eel/provider/utils/WindowsPathUtils.kt index cf397829cb85..d520522156c8 100644 --- a/platform/eel-provider/src/com/intellij/platform/eel/provider/utils/WindowsPathUtils.kt +++ b/platform/eel-provider/src/com/intellij/platform/eel/provider/utils/WindowsPathUtils.kt @@ -40,10 +40,10 @@ object WindowsPathUtils { } /** - * Performs backward conversion. + * Performs backward conversion. Accepts and returns system independent slashes. * - * `@/C/Users` -> `C:\Users` - * `server.share/dir/` -> `\\server.share\dir\` + * `@/C/Users` -> `C:/Users` + * `server.share/dir/` -> `//server.share/dir/` * */ fun rootRelativeToEelPath(relativePath: String): String { @@ -51,12 +51,12 @@ object WindowsPathUtils { "${relativePath[2]}:${relativePath.drop(3)}" } else { - "\\\\$relativePath" + "//$relativePath" } } /** - * Performs backward conversion. + * Performs backward conversion. Returns a pair of root and remaining path, both ready for constructing EelPath. * * `@/C/Users` -> `C:`, `Users` * `server.share/dir/tmp` -> `\\server.share\dir`, `tmp` diff --git a/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/fs/IjentEphemeralRootAwareFileSystem.kt b/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/fs/IjentEphemeralRootAwareFileSystem.kt index d326fd540c5a..56a2216ab59f 100644 --- a/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/fs/IjentEphemeralRootAwareFileSystem.kt +++ b/platform/ijent/impl/src/com/intellij/platform/ijent/community/impl/nio/fs/IjentEphemeralRootAwareFileSystem.kt @@ -13,6 +13,7 @@ import com.intellij.platform.eel.EelOsFamily import com.intellij.platform.eel.provider.getEelDescriptor import com.intellij.platform.eel.provider.utils.EelPathUtils import com.intellij.platform.eel.provider.utils.WindowsPathUtils +import com.intellij.platform.ijent.community.impl.nio.AbsoluteIjentNioPath import com.intellij.platform.ijent.community.impl.nio.IjentNioPath import com.intellij.util.text.nullize import org.jetbrains.annotations.ApiStatus @@ -185,7 +186,15 @@ class IjentEphemeralRootAwarePath( override fun toString(): String { return if (isAbsolute) { - rootPath.resolve(originalPath.pathString.removePrefix("/").replace("\\", fileSystem.separator)).toString() + when (originalPath.fileSystem.ijentFs.descriptor.osFamily) { + EelOsFamily.Posix -> { + rootPath.resolve(originalPath.pathString.removePrefix("/").replace("\\", fileSystem.separator)).pathString + } + EelOsFamily.Windows -> { + WindowsPathUtils.resolveEelPathOntoRoot(rootPath, (originalPath as AbsoluteIjentNioPath).eelPath).pathString + } + } + } else { originalPath.toString() diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/util.kt b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/util.kt index ac5bd110bc50..893b65f54e12 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/util.kt +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/util.kt @@ -16,7 +16,9 @@ import com.intellij.platform.eel.channels.EelDelicateApi import com.intellij.platform.eel.fs.EelFileInfo import com.intellij.platform.eel.fs.EelFileSystemApi import com.intellij.platform.eel.fs.EelFileSystemPosixApi +import com.intellij.platform.eel.fs.EelFileSystemWindowsApi import com.intellij.platform.eel.fs.EelPosixFileInfo +import com.intellij.platform.eel.fs.EelWindowsFileInfo import com.intellij.platform.eel.fs.listDirectoryWithAttrs import com.intellij.platform.eel.fs.readFile import com.intellij.platform.eel.fs.stat @@ -167,17 +169,25 @@ internal fun readAttributesUsingEel(nioPath: Path): FileAttributes { return FileAttributes.fromNio(directAccessNioPath, nioAttributes) } return fsBlocking { - when (val eelFsApi = eelPath.descriptor.toEelApi().fs) { - is EelFileSystemPosixApi -> { - val fileInfo = eelFsApi.stat(eelPath).eelIt().getOrThrowFileSystemException() - fileInfo.toVfs(fileInfo.isWritable(eelFsApi)) - } - else -> TODO() - } + val eelFsApi = eelPath.descriptor.toEelApi().fs + val fileInfo = eelFsApi.stat(eelPath).eelIt().getOrThrowFileSystemException() + toVfs(fileInfo, eelFsApi) } } } +private fun toVfs(eelFileInfo: EelFileInfo, eelFsApi: EelFileSystemApi): FileAttributes { + return when { + eelFsApi is EelFileSystemPosixApi && eelFileInfo is EelPosixFileInfo -> { + eelFileInfo.toVfs(eelFileInfo.isWritable(eelFsApi)) + } + eelFsApi is EelFileSystemWindowsApi && eelFileInfo is EelWindowsFileInfo -> { + eelFileInfo.toVfs(!eelFileInfo.permissions.isReadOnly) + } + else -> error("EelFileInfo ${eelFileInfo} does not belong to EelFileSystemApi ${eelFsApi}") + } +} + internal fun listWithAttributesUsingEel( dir: VirtualFile, filter: Set?, @@ -205,10 +215,10 @@ internal fun listWithAttributesUsingEel( //We must return a 'normal' (=case-sensitive) map from this method, see BatchingFileSystem.listWithAttributes() contract: val childrenWithAttributes = CollectionFactory.createFilePathMap(expectedSize, /*caseSensitive: */true) - visitDirectory(eelPath, filter) { file: EelPath, attributes: EelPosixFileInfo, eelFsApi: EelFileSystemPosixApi -> + visitDirectory(eelPath, filter) { file: EelPath, attributes: EelFileInfo, eelFsApi: EelFileSystemApi -> try { //val attributes = amendAttributes(file, fromNio(file, attributes)) - childrenWithAttributes[file.fileName] = attributes.toVfs(attributes.isWritable(eelFsApi)) + childrenWithAttributes[file.fileName] = toVfs(attributes, eelFsApi) } catch (e: Exception) { LOG.debug(e) @@ -237,28 +247,24 @@ internal fun listWithAttributesUsingEel( private fun visitDirectory( directory: EelPath, filter: Set?, - consumer: (EelPath, EelPosixFileInfo, EelFileSystemPosixApi) -> Boolean, + consumer: (EelPath, EelFileInfo, EelFileSystemApi) -> Boolean, ) { if (filter != null && filter.isEmpty()) { return //nothing to read } fsBlocking { - return@fsBlocking when (val eelFsApi = directory.descriptor.toEelApi().fs) { - is EelFileSystemPosixApi -> { - val directoryList = - eelFsApi.listDirectoryWithAttrs(directory).symlinkPolicy(EelFileSystemApi.SymlinkPolicy.RESOLVE_AND_FOLLOW).eelIt() - .getOrThrowFileSystemException() - for ((childName, childStat) in directoryList) { - val childIjentPath = directory.getChild(childName) - if (filter != null && !filter.contains(childIjentPath.fileName)) { - continue - } - if (!consumer(childIjentPath, childStat, eelFsApi)) { - break - } - } + val eelFsApi = directory.descriptor.toEelApi().fs + val directoryList = + eelFsApi.listDirectoryWithAttrs(directory).symlinkPolicy(EelFileSystemApi.SymlinkPolicy.RESOLVE_AND_FOLLOW).eelIt() + .getOrThrowFileSystemException() + for ((childName, childStat) in directoryList) { + val childIjentPath = directory.getChild(childName) + if (filter != null && !filter.contains(childIjentPath.fileName)) { + continue + } + if (!consumer(childIjentPath, childStat, eelFsApi)) { + break } - else -> TODO() } } } diff --git a/platform/platform-tests/testSrc/com/intellij/execution/eel/WindowsPathUtilsTest.kt b/platform/platform-tests/testSrc/com/intellij/execution/eel/WindowsPathUtilsTest.kt index 8409a43f46d0..3fcad943f0c4 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/eel/WindowsPathUtilsTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/execution/eel/WindowsPathUtilsTest.kt @@ -58,7 +58,7 @@ class WindowsPathUtilsTest { server.share/dir/tmp/nested/file, \\server.share\dir, tmp/nested/file""" ) fun `test rootRelativeToEelPath with Path parameter`(relativePath: String, expectedRoot: String, expectedSubpath: String) { - assertEquals("$expectedRoot${if (expectedSubpath.isEmpty()) "" else "\\$expectedSubpath"}".replace("\\", File.separator), WindowsPathUtils.rootRelativeToEelPath(relativePath.replace("/", File.separator)).replace("\\", File.separator)) + assertEquals("$expectedRoot${if (expectedSubpath.isEmpty()) "" else "/$expectedSubpath"}".replace("\\", "/"), WindowsPathUtils.rootRelativeToEelPath(relativePath)) assertEquals(expectedRoot to Path(expectedSubpath), WindowsPathUtils.rootRelativeToEelPath(Path(relativePath.replace("/", File.separator)))) } }