IJPL-156626 IJent: remove id from IjentApi

It's now possible to create an Ijent NIO filesystem with any URL.

Also, it becomes easier to create wrappers for Ijent*Api. Before the change, a wrapper couldn't have implemented `id` fairly: on the first hand, a wrapper must have its own id and shouldn't return the id of its delegate, on the other hand, a wrapper couldn't create and register its own id.

GitOrigin-RevId: b3f48e87c6b1fb9dae8d4487834ac542f3e4874d
This commit is contained in:
Vladimir Lagunov
2024-08-02 11:11:45 +02:00
committed by intellij-monorepo-bot
parent 11d706dddf
commit b461a07bd9
17 changed files with 188 additions and 185 deletions

View File

@@ -18,7 +18,7 @@ import com.intellij.platform.ide.progress.TaskCancellation
import com.intellij.platform.ide.progress.withModalProgress
import com.intellij.platform.ijent.IjentExecApi
import com.intellij.platform.ijent.IjentMissingBinary
import com.intellij.platform.ijent.community.impl.nio.asNioFileSystem
import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider
import com.intellij.platform.ijent.deploy
import com.intellij.platform.ijent.executeProcess
import com.intellij.platform.ijent.spi.IjentDeployingStrategy
@@ -26,6 +26,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.consumeEach
import org.jetbrains.annotations.ApiStatus.Internal
import java.io.ByteArrayOutputStream
import java.net.URI
import kotlin.io.path.isDirectory
/**
@@ -86,9 +87,15 @@ abstract class AbstractIjentVerificationAction : DumbAwareAction() {
}
launch(Dispatchers.IO) {
val nioFs = ijent.fs.asNioFileSystem()
val path = "/etc"
val isDir = nioFs.getPath(path).isDirectory()
val isDir =
IjentNioFileSystemProvider.getInstance()
.newFileSystem(
URI("ijent://some-random-string"),
IjentNioFileSystemProvider.newFileSystemMap(ijent.fs),
).use { nioFs ->
nioFs.getPath(path).isDirectory()
}
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
Messages.showInfoMessage("$path is directory: $isDir", title)
}

View File

@@ -10,7 +10,7 @@ internal class IjentNioFileStore(
private val ijentFsApi: IjentFileSystemApi,
) : FileStore() {
override fun name(): String =
ijentFsApi.id.toString()
ijentFsApi.toString()
// TODO: uncomment appropriate part of the test com.intellij.platform.ijent.functional.fs.UnixLikeFileSystemTest.test file store

View File

@@ -3,6 +3,8 @@ package com.intellij.platform.ijent.community.impl.nio
import com.intellij.platform.ijent.fs.*
import kotlinx.coroutines.isActive
import org.jetbrains.annotations.ApiStatus
import java.net.URI
import java.nio.file.FileStore
import java.nio.file.FileSystem
import java.nio.file.PathMatcher
@@ -11,15 +13,20 @@ import java.nio.file.attribute.UserPrincipalLookupService
class IjentNioFileSystem internal constructor(
private val fsProvider: IjentNioFileSystemProvider,
internal val ijentFs: IjentFileSystemApi,
private val onClose: () -> Unit,
internal val uri: URI,
) : FileSystem() {
override fun close() {
onClose()
fsProvider.close(uri)
}
override fun provider(): IjentNioFileSystemProvider = fsProvider
val ijentFs: IjentFileSystemApi
@ApiStatus.Internal
get() =
fsProvider.ijentFsApi(uri)
?: throw java.nio.file.FileSystemException("`$uri` was removed from IJent FS providers")
override fun isOpen(): Boolean =
ijentFs.coroutineScope.isActive

View File

@@ -2,16 +2,15 @@
package com.intellij.platform.ijent.community.impl.nio
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.platform.ijent.IjentId
import com.intellij.platform.ijent.IjentSessionRegistry
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.*
import com.intellij.platform.ijent.fs.*
import com.intellij.platform.ijent.fs.IjentFileInfo.Type.*
import com.intellij.platform.ijent.fs.IjentFileSystemPosixApi.CreateDirectoryException
import com.intellij.platform.ijent.fs.IjentPosixFileInfo.Type.Symlink
import com.intellij.util.text.nullize
import com.sun.nio.file.ExtendedCopyOption
import kotlinx.coroutines.job
import java.io.IOException
import java.net.URI
import java.nio.channels.AsynchronousFileChannel
@@ -23,11 +22,19 @@ import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileAttribute
import java.nio.file.attribute.FileAttributeView
import java.nio.file.spi.FileSystemProvider
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
/**
* This filesystem connects to particular IJent instances.
*
* A new filesystem can be created with [newFileSystem] and [newFileSystemMap].
* The URL must have the scheme "ijent", and the unique identifier of a filesystem
* is represented with an authority and a path.
*
* For accessing WSL use [com.intellij.execution.wsl.ijent.nio.IjentWslNioFileSystemProvider].
*/
class IjentNioFileSystemProvider : FileSystemProvider() {
companion object {
@JvmStatic
@@ -35,56 +42,91 @@ class IjentNioFileSystemProvider : FileSystemProvider() {
installedProviders()
.filterIsInstance<IjentNioFileSystemProvider>()
.single()
@JvmStatic
fun newFileSystemMap(ijentFs: IjentFileSystemApi): MutableMap<String, *> =
mutableMapOf(KEY_IJENT_FS to ijentFs)
private const val SCHEME = "ijent"
private const val KEY_IJENT_FS = "ijentFs"
}
private val registeredFileSystems = ConcurrentHashMap<IjentId, IjentNioFileSystem>()
@JvmInline
internal value class CriticalSection<D : Any>(val hidden: D) {
inline operator fun <T> invoke(body: D.() -> T): T =
synchronized(hidden) {
hidden.body()
}
}
override fun getScheme(): String = "ijent"
private val criticalSection = CriticalSection(object {
val authorityRegistry: MutableMap<URI, IjentFileSystemApi> = hashMapOf()
})
override fun newFileSystem(uri: URI, env: MutableMap<String, *>?): IjentNioFileSystem {
override fun getScheme(): String = SCHEME
override fun newFileSystem(uri: URI, env: MutableMap<String, *>): IjentNioFileSystem {
@Suppress("NAME_SHADOWING") val uri = uri.normalize()
typicalUriChecks(uri)
if (!uri.path.isNullOrEmpty()) {
TODO("Filesystems with non-empty paths are not supported yet.")
val ijentFs =
try {
env[KEY_IJENT_FS] as IjentFileSystemApi
}
catch (err: Exception) {
throw when (err) {
is NullPointerException, is ClassCastException ->
IllegalArgumentException("Invalid map. `IjentNioFileSystemProvider.newFileSystemMap` should be used for map creation.")
else ->
err
}
}
val uriParts = getUriParts(uri)
criticalSection {
for (uriPart in uriParts) {
if (uriPart in authorityRegistry) {
throw FileSystemAlreadyExistsException(
"`$uri` can't be registered because there's an already registered as IJent FS provider `$uriPart`"
)
}
}
authorityRegistry[uri] = ijentFs
}
val ijentId = IjentId(uri.host)
val ijentApi = IjentSessionRegistry.instance().ijents[ijentId]
require(ijentApi != null) {
"$ijentApi is not registered in ${IjentSessionRegistry::class.java.simpleName}"
}
val fs = IjentNioFileSystem(this, ijentApi.fs, onClose = { registeredFileSystems.remove(ijentId) })
if (registeredFileSystems.putIfAbsent(ijentId, fs) != null) {
throw FileSystemAlreadyExistsException("A filesystem for $ijentId is already registered")
}
fs.ijentFs.coroutineScope.coroutineContext.job.invokeOnCompletion {
registeredFileSystems.remove(ijentId)
}
return fs
return IjentNioFileSystem(this, uri)
}
override fun newFileSystem(path: Path, env: MutableMap<String, *>?): IjentNioFileSystem =
private fun getUriParts(uri: URI): Collection<URI> = uri.path.asSequence()
.mapIndexedNotNull { index, c -> if (c == '/') index else null }
.map { URI(uri.scheme, uri.authority, uri.path.substring(0, it), null, null) }
.plus(sequenceOf(uri))
.toList()
.asReversed()
override fun newFileSystem(path: Path, env: MutableMap<String, *>): IjentNioFileSystem =
newFileSystem(path.toUri(), env)
override fun getFileSystem(uri: URI): IjentNioFileSystem {
typicalUriChecks(uri)
return registeredFileSystems[IjentId(uri.host)] ?: throw FileSystemNotFoundException()
val uriParts = getUriParts(uri.normalize())
val matchingUri = criticalSection {
uriParts.firstOrNull { it in authorityRegistry }
}
if (matchingUri != null) {
return IjentNioFileSystem(this, matchingUri)
}
else {
throw FileSystemNotFoundException("`$uri` is not registered as IJent FS provider")
}
}
override fun getPath(uri: URI): IjentNioPath =
getFileSystem(uri).run {
getPath(
when (ijentFs) {
is IjentFileSystemPosixApi -> uri.path
is IjentFileSystemWindowsApi -> uri.path.trimStart('/')
}
)
}
override fun getPath(uri: URI): IjentNioPath {
val nioFs = getFileSystem(uri)
val relativeUri = nioFs.uri.relativize(uri)
return nioFs.getPath(
when (nioFs.ijentFs) {
is IjentFileSystemPosixApi -> relativeUri.path.nullize() ?: "/"
is IjentFileSystemWindowsApi -> relativeUri.path.trimStart('/') // TODO Check that uri.path contains the drive letter.
}
)
}
override fun newByteChannel(path: Path, options: Set<OpenOption>, vararg attrs: FileAttribute<*>): SeekableByteChannel =
newFileChannel(path, options, *attrs)
@@ -93,7 +135,7 @@ class IjentNioFileSystemProvider : FileSystemProvider() {
ensureIjentNioPath(path)
require(path.ijentPath is IjentPath.Absolute)
// TODO Handle options and attrs
val fs = registeredFileSystems[path.ijentId] ?: throw FileSystemNotFoundException()
val fs = path.nioFs
require(!(READ in options && APPEND in options)) { "READ + APPEND not allowed" }
require(!(APPEND in options && TRUNCATE_EXISTING in options)) { "APPEND + TRUNCATE_EXISTING not allowed" }
@@ -340,10 +382,10 @@ class IjentNioFileSystemProvider : FileSystemProvider() {
override fun <A : BasicFileAttributes> readAttributes(path: Path, type: Class<A>, vararg options: LinkOption): A {
val fs = ensureIjentNioPath(path).nioFs
val result = when (fs.ijentFs) {
val result = when (val ijentFs = fs.ijentFs) {
is IjentFileSystemPosixApi ->
IjentNioPosixFileAttributes(fsBlocking {
statPosix(path.ijentPath, fs.ijentFs, LinkOption.NOFOLLOW_LINKS in options)
statPosix(path.ijentPath, ijentFs, LinkOption.NOFOLLOW_LINKS in options)
})
is IjentFileSystemWindowsApi -> TODO()
@@ -390,6 +432,17 @@ class IjentNioFileSystemProvider : FileSystemProvider() {
TODO("Not yet implemented")
}
internal fun close(uri: URI) {
criticalSection {
authorityRegistry.remove(uri)
}
}
internal fun ijentFsApi(uri: URI): IjentFileSystemApi? =
criticalSection {
authorityRegistry[uri]
}
@OptIn(ExperimentalContracts::class)
private fun ensureIjentNioPath(path: Path): IjentNioPath {
contract {
@@ -416,11 +469,10 @@ class IjentNioFileSystemProvider : FileSystemProvider() {
}
private fun typicalUriChecks(uri: URI) {
require(uri.host.isNotEmpty())
require(uri.authority.isNotEmpty())
require(uri.scheme == scheme)
require(uri.userInfo.isNullOrEmpty())
require(uri.query.isNullOrEmpty())
require(uri.fragment.isNullOrEmpty())
require(uri.scheme == scheme) { "${uri.scheme} != $scheme" }
require(uri.query.isNullOrEmpty()) { uri.query }
require(uri.fragment.isNullOrEmpty()) { uri.fragment }
}
}

View File

@@ -12,28 +12,6 @@ import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.startCoroutine
/**
* Returns an adapter from [IjentFileSystemApi] to [java.nio.file.FileSystem]. The adapter is automatically registered in advance,
* also it is automatically closed when it is needed.
*
* The function is idempotent and thread-safe.
*/
fun IjentFileSystemApi.asNioFileSystem(): FileSystem {
val nioFsProvider = IjentNioFileSystemProvider.getInstance()
val uri = id.uri
return try {
nioFsProvider.getFileSystem(uri)
}
catch (ignored: FileSystemNotFoundException) {
try {
nioFsProvider.newFileSystem(uri, mutableMapOf<String, Any>())
}
catch (ignored: FileSystemAlreadyExistsException) {
nioFsProvider.getFileSystem(uri)
}
}
}
@Throws(FileSystemException::class)
internal fun <T, E : IjentFsError> IjentFsResult<T, E>.getOrThrowFileSystemException(): T =
when (this) {

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.ijent.community.impl.nio
import com.intellij.platform.ijent.IjentId
import com.intellij.platform.ijent.fs.*
import java.net.URI
import java.nio.file.*
@@ -12,22 +11,20 @@ import java.nio.file.*
* How to get a path:
* ```kotlin
* val ijent: IjentApi = getIjentFromSomewhere()
* val path: Path = ijentFs.asNioFileSystem().getPath("/usr/bin/cowsay")
* val path: Path = ijent.fs.asNioFileSystem().getPath("/usr/bin/cowsay")
* ```
*/
class IjentNioPath internal constructor(
val ijentPath: IjentPath,
internal val nioFs: IjentNioFileSystem,
) : Path {
val ijentId: IjentId get() = nioFs.ijentFs.id
private val isWindows
get() = when (nioFs.ijentFs) {
is IjentFileSystemPosixApi -> false
is IjentFileSystemWindowsApi -> true
}
override fun getFileSystem(): FileSystem = nioFs
override fun getFileSystem(): IjentNioFileSystem = nioFs
override fun isAbsolute(): Boolean =
when (ijentPath) {
@@ -106,12 +103,7 @@ class IjentNioPath internal constructor(
override fun toUri(): URI =
when (ijentPath) {
is IjentPath.Absolute ->
URI(
"ijent",
ijentId.id,
ijentPath.toString(),
null,
)
nioFs.uri.resolve(ijentPath.toString())
is IjentPath.Relative ->
throw InvalidPathException(toString(), "Can't create a URL from a relative path") // TODO Really no way?

View File

@@ -13,8 +13,6 @@ import com.intellij.platform.ijent.fs.IjentFileSystemWindowsApi
* Usually, [com.intellij.platform.ijent.deploy] creates instances of [IjentApi].
*/
sealed interface IjentApi : AutoCloseable {
val id: IjentId
val platform: IjentPlatform
/**

View File

@@ -1,20 +1,13 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.ijent
import java.net.URI
/** [id] must be unique across all other instances registered in [IjentSessionRegistry]. */
data class IjentId(val id: String) {
val uri = URI("ijent", null, id, -1, null, null, null)
init {
try {
check(uri.authority == id)
}
catch (err: Exception) {
throw IllegalArgumentException("IjentId must be URI-serializable, but this value isn't: $id", err)
}
}
/** [IjentSessionRegistry] creates instances of this class. */
class IjentId internal constructor(val id: String) {
override fun toString(): String = "IjentId($id)"
override fun equals(other: Any?): Boolean =
other is IjentId && other.id == id
override fun hashCode(): Int =
id.hashCode()
}

View File

@@ -15,8 +15,6 @@ import kotlin.contracts.contract
/** This service should know about all running IJents. */
@Service(Service.Level.APP)
class IjentSessionRegistry {
val ijents: Map<IjentId, IjentApi> get() = ijentsInternal
private val counter = AtomicLong()
private val ijentsInternal = ConcurrentHashMap<IjentId, IjentApi>()

View File

@@ -9,11 +9,6 @@ import java.nio.ByteBuffer
// TODO Integrate case-(in)sensitiveness into the interface.
sealed interface IjentFileSystemApi {
/**
* The same [IjentId] as in the corresponding [com.intellij.platform.ijent.IjentApi].
*/
val id: IjentId
/**
* The same [CoroutineScope] as in the corresponding [com.intellij.platform.ijent.IjentApi].
*/

View File

@@ -51,7 +51,11 @@ class IjentSessionMediator private constructor(val scope: CoroutineScope, val pr
var expectedErrorCode = ExpectedErrorCode.NO
companion object {
/** See the docs of [IjentSessionMediator] */
/**
* See the docs of [IjentSessionMediator].
*
* [ijentId] is used only for logging.
*/
@OptIn(DelicateCoroutinesApi::class)
fun create(process: Process, ijentId: IjentId): IjentSessionMediator {
val lastStderrMessages = MutableSharedFlow<String?>(

View File

@@ -2,12 +2,7 @@
package com.intellij.platform.ijent.spi
import com.intellij.openapi.components.serviceAsync
import com.intellij.platform.ijent.IjentApi
import com.intellij.platform.ijent.IjentId
import com.intellij.platform.ijent.IjentPlatform
import com.intellij.platform.ijent.IjentPosixApi
import com.intellij.platform.ijent.IjentSessionRegistry
import com.intellij.platform.ijent.IjentWindowsApi
import com.intellij.platform.ijent.*
/**
* Given that there is some IJent process launched, this extension gets handles to stdin+stdout of the process and returns
@@ -18,7 +13,6 @@ interface IjentSessionProvider {
* Supposed to be used inside [IjentSessionRegistry.register].
*/
suspend fun connect(
ijentId: IjentId,
platform: IjentPlatform,
mediator: IjentSessionMediator
): IjentApi
@@ -44,7 +38,7 @@ sealed class IjentStartupError : RuntimeException {
}
internal class DefaultIjentSessionProvider : IjentSessionProvider {
override suspend fun connect(ijentId: IjentId, platform: IjentPlatform, mediator: IjentSessionMediator): IjentApi {
override suspend fun connect(platform: IjentPlatform, mediator: IjentSessionMediator): IjentApi {
throw IjentStartupError.MissingImplPlugin()
}
}
@@ -60,7 +54,7 @@ suspend fun connectToRunningIjent(ijentName: String, platform: IjentPlatform, pr
IjentSessionRegistry.instanceAsync().register(ijentName) { ijentId ->
val mediator = IjentSessionMediator.create(process, ijentId)
mediator.expectedErrorCode = IjentSessionMediator.ExpectedErrorCode.ZERO
IjentSessionProvider.instanceAsync().connect(ijentId, platform, mediator)
IjentSessionProvider.instanceAsync().connect(platform, mediator)
}
/** A specialized overload of [connectToRunningIjent] */

View File

@@ -12,7 +12,7 @@ internal class IjentWslNioFileSystem(
private val ijentFs: FileSystem,
private val originalFs: FileSystem,
) : FileSystem() {
override fun toString(): String = """${javaClass.simpleName}(ijentId=${provider.ijentId}, wslLocalRoot=${provider.wslLocalRoot})"""
override fun toString(): String = """${javaClass.simpleName}($provider)"""
override fun close() {
ijentFs.close()

View File

@@ -1,10 +1,11 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.wsl.ijent.nio
import com.intellij.execution.wsl.WSLDistribution
import com.intellij.openapi.util.io.CaseSensitivityAttribute
import com.intellij.openapi.util.io.FileAttributes
import com.intellij.platform.core.nio.fs.RoutingAwareFileSystemProvider
import com.intellij.platform.ijent.*
import com.intellij.platform.ijent.IjentPosixInfo
import com.intellij.platform.ijent.community.impl.nio.IjentNioPath
import com.intellij.platform.ijent.community.impl.nio.IjentPosixGroupPrincipal
import com.intellij.platform.ijent.community.impl.nio.IjentPosixUserPrincipal
@@ -30,23 +31,22 @@ import kotlin.io.path.name
*
* Also, this wrapper delegates calls to the default file system [originalFsProvider]
* for the methods not implemented in [ijentFsProvider] yet.
*
* Normally, there's no need to create this filesystem manually because [com.intellij.execution.wsl.ijent.nio.toggle.IjentWslNioFsToggler]
* does this job automatically.
* It should be used even for manual creation of filesystems.
* Nevertheless, in case when this filesystem should be accessed directly,
* an instance of [IjentWslNioFileSystem] can be obtained with a URL like "ijent://wsl/distribution-name".
*/
internal class IjentWslNioFileSystemProvider(
internal val ijentId: IjentId,
internal val wslLocalRoot: Path,
wslDistribution: WSLDistribution,
private val ijentFsProvider: FileSystemProvider,
internal val originalFsProvider: FileSystemProvider,
) : FileSystemProvider(), RoutingAwareFileSystemProvider {
init {
require(wslLocalRoot.isAbsolute)
}
private val ijentFsUri: URI = URI("ijent", "wsl", "/${wslDistribution.id}", null, null)
private val wslLocalRoot: Path = originalFsProvider.getFileSystem(URI("file:/")).getPath(wslDistribution.getWindowsPath("/"))
private val ijentUserInfo: IjentInfo.User by lazy {
val api = IjentSessionRegistry.instance().ijents[ijentId] ?: error("The session $ijentId was unregistered")
api.info.user
}
override fun toString(): String = """${javaClass.simpleName}(ijentId=$ijentId, wslLocalRoot=$wslLocalRoot)"""
override fun toString(): String = """${javaClass.simpleName}(${wslLocalRoot})"""
override fun canHandleRouting(): Boolean = true
@@ -54,32 +54,20 @@ internal class IjentWslNioFileSystemProvider(
if (this is IjentNioPath)
this
else
fold(ijentFsProvider.getPath(ijentId.uri.resolve("/")) as IjentNioPath, IjentNioPath::resolve)
private fun Path.toDefaultPath(): Path =
if (this is IjentNioPath)
wslLocalRoot.resolve(this)
else
this
fold(ijentFsProvider.getPath(ijentFsUri) as IjentNioPath, IjentNioPath::resolve)
override fun getScheme(): String =
originalFsProvider.scheme
override fun newFileSystem(path: Path, env: MutableMap<String, *>?): IjentWslNioFileSystem {
val ijentNioPath = path.toIjentPath()
require(ijentNioPath.toUri() == ijentId.uri) { "${ijentNioPath.toUri()} != ${ijentId.uri}" }
return IjentWslNioFileSystem(
provider = this,
ijentFs = ijentFsProvider.newFileSystem(ijentNioPath, env),
originalFs = originalFsProvider.newFileSystem(Path.of("."), env),
)
}
override fun newFileSystem(path: Path, env: MutableMap<String, *>?): IjentWslNioFileSystem =
getFileSystem(path.toUri())
override fun getFileSystem(uri: URI): IjentWslNioFileSystem {
require(uri == ijentId.uri) { "$uri != ${ijentId.uri}" }
require(uri.scheme == scheme) { "Wrong scheme in `$uri` (expected `$scheme`)" }
val wslId = wslIdFromPath(originalFsProvider.getPath(uri))
return IjentWslNioFileSystem(
provider = this,
ijentFs = ijentFsProvider.getFileSystem(uri),
ijentFs = ijentFsProvider.getFileSystem(URI("ijent", "wsl", "/$wslId", null, null)),
originalFsProvider.getFileSystem(URI("file:/"))
)
}
@@ -87,6 +75,13 @@ internal class IjentWslNioFileSystemProvider(
override fun newFileSystem(uri: URI, env: MutableMap<String, *>?): IjentWslNioFileSystem =
getFileSystem(uri)
private fun wslIdFromPath(path: Path): String {
val root = path.toAbsolutePath().root.toString()
require(root.startsWith("""\\wsl""")) { "`$path` doesn't look like a file on WSL" }
val wslId = root.removePrefix("""\\wsl""").substringAfter('\\').trimEnd('\\')
return wslId
}
override fun checkAccess(path: Path, vararg modes: AccessMode): Unit =
ijentFsProvider.checkAccess(path.toIjentPath(), *modes)
@@ -187,27 +182,19 @@ internal class IjentWslNioFileSystemProvider(
// the function always assumes that the returned object is DosFileAttributes on Windows,
// and that's always true with the default WindowsFileSystemProvider.
val actualType = when (ijentUserInfo) {
is IjentPosixInfo.User ->
if (DosFileAttributes::class.java.isAssignableFrom(type)) PosixFileAttributes::class.java
else type
val actualType =
if (DosFileAttributes::class.java.isAssignableFrom(type)) PosixFileAttributes::class.java
else type
is IjentWindowsInfo.User -> TODO()
}
val actualAttrs = ijentFsProvider.readAttributes(path.toIjentPath(), actualType, *options)
val resultAttrs = when (actualAttrs) {
is DosFileAttributes -> actualAttrs
val ijentNioPath = path.toIjentPath()
val resultAttrs = when (val actualAttrs = ijentFsProvider.readAttributes(ijentNioPath, actualType, *options)) {
is DosFileAttributes -> actualAttrs // TODO How can it be possible? It's certainly known that the remote OS is GNU/Linux.
is PosixFileAttributes ->
when (val ijentUserInfo = ijentUserInfo) {
is IjentPosixInfo.User ->
IjentNioPosixFileAttributesWithDosAdapter(ijentUserInfo, actualAttrs, path.name.startsWith("."))
is IjentWindowsInfo.User ->
actualAttrs
}
IjentNioPosixFileAttributesWithDosAdapter(
ijentNioPath.fileSystem.ijentFs.user as IjentPosixInfo.User,
actualAttrs, path.name.startsWith("."),
)
else -> actualAttrs
}

View File

@@ -7,7 +7,6 @@ import com.intellij.execution.wsl.WslIjentManager
import com.intellij.execution.wsl.ijent.nio.IjentWslNioFileSystem
import com.intellij.execution.wsl.ijent.nio.IjentWslNioFileSystemProvider
import com.intellij.platform.core.nio.fs.MultiRoutingFileSystemProvider
import com.intellij.platform.ijent.IjentId
import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider
import com.intellij.platform.ijent.community.impl.nio.telemetry.TracingFileSystemProvider
import com.intellij.util.containers.forEachGuaranteed
@@ -67,8 +66,7 @@ class IjentWslNioFsToggleStrategy(
}
private suspend fun handleWslDistributionAddition(distro: WSLDistribution) {
val ijentApi = WslIjentManager.instanceAsync().getIjentApi(distro, null, false)
switchToIjentFs(distro, ijentApi.id)
switchToIjentFs(distro)
}
private fun handleWslDistributionDeletion(distro: WSLDistribution) {
@@ -78,10 +76,14 @@ class IjentWslNioFsToggleStrategy(
}
}
fun switchToIjentFs(distro: WSLDistribution, ijentId: IjentId) {
suspend fun switchToIjentFs(distro: WSLDistribution) {
val ijentFsProvider = TracingFileSystemProvider(IjentNioFileSystemProvider.getInstance())
try {
ijentFsProvider.newFileSystem(ijentId.uri, null)
val ijentFs = WslIjentManager.instanceAsync().getIjentApi(distro, null, false).fs
ijentFsProvider.newFileSystem(
URI("ijent", "wsl", "/${distro.id}", null, null),
IjentNioFileSystemProvider.newFileSystemMap(ijentFs),
)
}
catch (_: FileSystemAlreadyExistsException) {
// Nothing.
@@ -93,11 +95,10 @@ class IjentWslNioFsToggleStrategy(
}
else {
IjentWslNioFileSystemProvider(
ijentId = ijentId,
wslLocalRoot = underlyingProvider.getLocalFileSystem().getPath(distro.getWindowsPath("/")),
wslDistribution = distro,
ijentFsProvider = ijentFsProvider,
originalFsProvider = TracingFileSystemProvider(underlyingProvider),
).getFileSystem(ijentId.uri)
).getFileSystem(distro.getUNCRootPath().toUri())
}
}
}

View File

@@ -10,7 +10,6 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.util.SystemInfo
import com.intellij.platform.core.nio.fs.MultiRoutingFileSystemProvider
import com.intellij.platform.ijent.IjentId
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.annotations.ApiStatus.Internal
import org.jetbrains.annotations.TestOnly
@@ -43,9 +42,9 @@ class IjentWslNioFsToggler(private val coroutineScope: CoroutineScope) {
}
@TestOnly
fun switchToIjentFs(distro: WSLDistribution, ijentId: IjentId) {
suspend fun switchToIjentFs(distro: WSLDistribution) {
strategy ?: error("Not available")
strategy.switchToIjentFs(distro, ijentId)
strategy.switchToIjentFs(distro)
}
@TestOnly

View File

@@ -504,8 +504,6 @@ class WSLDistributionTest {
enum class WslTestStrategy { Legacy, Ijent }
private class MockIjentApi(private val adapter: GeneralCommandLine, val rootUser: Boolean) : IjentPosixApi {
override val id: IjentId get() = throw UnsupportedOperationException()
override val platform: IjentPlatform get() = throw UnsupportedOperationException()
override val isRunning: Boolean get() = true