22package com.intellij.platform.ide.impl.wsl.ijent.nio
33
44import com.intellij.platform.core.nio.fs.BasicFileAttributesHolder2
5+ import com.intellij.platform.core.nio.fs.MultiRoutingFsPath
56import com.intellij.platform.eel.provider.utils.EelPathUtils.getActualPath
7+ import com.intellij.platform.eel.provider.utils.impl.ijentToLocal
8+ import com.intellij.platform.eel.provider.utils.impl.localToIjent
69import com.intellij.platform.ide.impl.wsl.WSL_PREFIXES
10+ import com.intellij.platform.ijent.community.impl.nio.IjentNioPath
711import com.intellij.platform.ijent.community.impl.nio.fs.IjentNioPosixFileAttributesWithDosAdapter
812import java.net.URI
913import java.nio.file.LinkOption
1014import java.nio.file.Path
1115import java.nio.file.WatchEvent
1216import java.nio.file.WatchKey
1317import java.nio.file.WatchService
18+ import kotlin.io.path.pathString
1419
1520internal class IjentWslNioPath (
1621 private val fileSystem : IjentWslNioFileSystem ,
@@ -23,7 +28,12 @@ internal class IjentWslNioPath(
2328 cachedAttributes : IjentNioPosixFileAttributesWithDosAdapter ? ,
2429) : Path, BasicFileAttributesHolder2.Impl(cachedAttributes) {
2530 init {
26- require(presentablePath !is IjentWslNioPath ) { " IjentWslNioPath should be a wrapper over other instances of path, namely WindowsPath or IjentNioPath" }
31+ // `MultiRoutingFsPath` is rejected as well: it may delegate to an `IjentWslNioPath`, and such nesting silently breaks
32+ // `equals`, `hashCode` and every call that passes `presentablePath` to the original (Windows) file system provider.
33+ require(presentablePath !is IjentWslNioPath && presentablePath !is MultiRoutingFsPath ) {
34+ " IjentWslNioPath should be a wrapper over other instances of path, namely WindowsPath or IjentNioPath," +
35+ " but got ${presentablePath.javaClass.name} : $presentablePath "
36+ }
2737 }
2838
2939 val actualPath: Path = getActualPath(presentablePath)
@@ -50,13 +60,18 @@ internal class IjentWslNioPath(
5060
5161 override fun normalize (): IjentWslNioPath = presentablePath.normalize().toIjentWslPath()
5262
53- override fun resolve (other : Path ): IjentWslNioPath = presentablePath.resolve(other.toOriginalPath()).toIjentWslPath()
63+ override fun resolve (other : Path ): IjentWslNioPath {
64+ val otherPath = other.toSameFlavourAsPresentablePath()
65+ // `Path.resolve` returns `other` as is when it is absolute, but the result still has to be a path of this file system.
66+ return if (otherPath.isAbsolute) otherPath.toIjentWslPath()
67+ else presentablePath.resolve(otherPath).toIjentWslPath()
68+ }
5469
5570 override fun relativize (other : Path ): IjentWslNioPath {
5671 if (isAbsolute != other.isAbsolute) {
5772 throw IllegalArgumentException (" Tried to relativize a relative and an absolute path: `$this ` and `$other `." + " Check for possible confusion." + " Maybe some code up the call stack tried to use a path from the Linux machine as a WSL path for Windows." )
5873 }
59- return presentablePath.relativize(other.toOriginalPath ()).toIjentWslPath()
74+ return presentablePath.relativize(other.toSameFlavourAsPresentablePath ()).toIjentWslPath()
6075 }
6176
6277 override fun toUri (): URI = presentablePath.toUri()
@@ -83,10 +98,7 @@ internal class IjentWslNioPath(
8398 else {
8499 ijentNioPath.toRealPath(* options)
85100 }
86- val originalPath = fileSystem.provider().toOriginalPath(
87- path = ijentNioRealPath,
88- notation = root.toString().removePrefix(" \\\\ " ).substringBefore(' \\ ' ),
89- )
101+ val originalPath = fileSystem.provider().toOriginalPath(path = ijentNioRealPath, notation = presentableNotation)
90102 return originalPath.toIjentWslPath()
91103 }
92104
@@ -97,10 +109,49 @@ internal class IjentWslNioPath(
97109
98110 override fun compareTo (other : Path ): Int = presentablePath.compareTo(other.toOriginalPath())
99111
100- private fun Path.toIjentWslPath (): IjentWslNioPath = IjentWslNioPath (this @IjentWslNioPath.fileSystem, this , null )
112+ private fun Path.toIjentWslPath (): IjentWslNioPath =
113+ this as ? IjentWslNioPath ? : IjentWslNioPath (this @IjentWslNioPath.fileSystem, this , null )
114+
115+ private fun Path.toOriginalPath (): Path = when (this ) {
116+ is IjentWslNioPath -> this .presentablePath.toOriginalPath()
117+ // A path of the routing file system may delegate to a path of this very file system, so it is not a foreign path.
118+ is MultiRoutingFsPath -> this .initialDelegate.toOriginalPath()
119+ else -> this
120+ }
101121
102- private fun Path.toOriginalPath (): Path = if (this is IjentWslNioPath ) this .presentablePath
103- else this
122+ /* *
123+ * Returns [this] converted to the same kind of path as [presentablePath] (i.e. `WindowsPath` or [IjentNioPath]),
124+ * so that both can be used together in a single [Path] operation.
125+ * Special chars like `:` are mapped in the direction that [presentablePath] requires, see [ijentToLocal] and [localToIjent].
126+ *
127+ * This is the same trick as [MultiRoutingFsPath.toSameTypeAsDelegate]:
128+ * it is always the *argument* that is brought to the flavour of the receiver, never the other way round.
129+ */
130+ private fun Path.toSameFlavourAsPresentablePath (): Path {
131+ val originalPath = toOriginalPath()
132+ return when {
133+ presentablePath.javaClass == originalPath.javaClass -> originalPath
134+ // An absolute IJent path has no `\\wsl$\distro\` prefix, and that prefix must use the same notation as this path.
135+ originalPath is IjentNioPath && originalPath.isAbsolute ->
136+ this @IjentWslNioPath.fileSystem.provider().toOriginalPath(originalPath, presentableNotation)
137+
138+ originalPath is IjentNioPath ->
139+ presentablePath.fileSystem.getPath(ijentToLocal(originalPath.pathString))
140+
141+ presentablePath is IjentNioPath ->
142+ presentablePath.fileSystem.getPath(localToIjent(originalPath.pathString.replace(' \\ ' , ' /' )))
143+
144+ else ->
145+ presentablePath.fileSystem.getPath(originalPath.pathString)
146+ }
147+ }
148+
149+ /* *
150+ * `wsl$` or `wsl.localhost`: the notation used by this path.
151+ * These two must never be mixed within one path, see [com.intellij.platform.eel.provider.asNioPath].
152+ */
153+ private val presentableNotation: String
154+ get() = fileSystem.provider().notationFromRoot(presentablePath.root?.toString() ? : " " )
104155
105156 override fun toString (): String = presentablePath.toString()
106157
0 commit comments