mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
IJPL-194952 Revert "[wsl-eel] IJPL-186144: Get rid of IjentWslNioFsToggler"
This reverts commit dae3a216 (cherry picked from commit b7ba1c026d1bcbf8d51f57a64646ce8d3a1db483) IJ-MR-167682 GitOrigin-RevId: ea15fa242203ade09290ad23e02f767ca03ab72a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b0537e456c
commit
70908d094a
@@ -7,9 +7,10 @@ import com.intellij.platform.eel.EelDescriptor
|
||||
import com.intellij.platform.eel.provider.EelNioBridgeService
|
||||
import com.intellij.util.containers.forEachGuaranteed
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.job
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import java.io.Closeable
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
@@ -20,19 +21,13 @@ import kotlin.io.path.pathString
|
||||
|
||||
@ApiStatus.Internal
|
||||
@VisibleForTesting
|
||||
class EelNioBridgeServiceImpl(coroutineScope: CoroutineScope) : EelNioBridgeService {
|
||||
class EelNioBridgeServiceImpl(private val coroutineScope: CoroutineScope) : EelNioBridgeService {
|
||||
private val multiRoutingFileSystemProvider = FileSystems.getDefault().provider()
|
||||
|
||||
private val rootRegistry = ConcurrentHashMap<EelDescriptor, MutableSet<Path>>()
|
||||
private val fsRegistry = ConcurrentHashMap<String, FileSystem>()
|
||||
private val idRegistry = ConcurrentHashMap<EelDescriptor, String>()
|
||||
|
||||
init {
|
||||
coroutineScope.coroutineContext.job.invokeOnCompletion {
|
||||
idRegistry.keys().asSequence().forEach { unregister(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun tryGetEelDescriptor(nioPath: Path): EelDescriptor? {
|
||||
return rootRegistry.entries.asSequence()
|
||||
.flatMap { (descriptor, paths) -> paths.map { path -> descriptor to path } }
|
||||
@@ -93,4 +88,36 @@ class EelNioBridgeServiceImpl(coroutineScope: CoroutineScope) : EelNioBridgeServ
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun temporarilyResetState(): Closeable {
|
||||
val oldRootRegistry = HashMap(rootRegistry)
|
||||
val oldFsRegistry = HashMap(fsRegistry)
|
||||
val oldIdRegistry = HashMap(idRegistry)
|
||||
|
||||
for (descriptor in rootRegistry.keys.toList()) {
|
||||
val roots = rootRegistry.remove(descriptor)!!
|
||||
|
||||
for (localRoot in roots) {
|
||||
fsRegistry.compute(localRoot.toString()) { _, existingFileSystem ->
|
||||
MultiRoutingFileSystemProvider.computeBackend(multiRoutingFileSystemProvider, localRoot.toString(), false, false) { underlyingProvider, actualFs ->
|
||||
require(existingFileSystem == actualFs)
|
||||
null
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
idRegistry.clear()
|
||||
|
||||
return Closeable {
|
||||
rootRegistry.putAll(oldRootRegistry)
|
||||
fsRegistry.putAll(oldFsRegistry)
|
||||
idRegistry.putAll(oldIdRegistry)
|
||||
|
||||
for ((localRootString, fs) in fsRegistry) {
|
||||
MultiRoutingFileSystemProvider.computeBackend(multiRoutingFileSystemProvider, localRootString, false, false) { _, _ -> fs }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,5 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl.wsl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.execution" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,24 +1,17 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:JvmName("WslIjentTestUtil")
|
||||
|
||||
package com.intellij.ijent.testFramework.wsl
|
||||
|
||||
import com.intellij.execution.wsl.WSLDistribution
|
||||
import com.intellij.execution.wsl.WslIjentManager
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.extensions.BaseExtensionPointName
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.eel.impl.provider.EelNioBridgeServiceImpl
|
||||
import com.intellij.platform.eel.provider.EelInitialization
|
||||
import com.intellij.platform.eel.provider.EelNioBridgeService
|
||||
import com.intellij.platform.eel.provider.EelProvider
|
||||
import com.intellij.platform.ide.impl.wsl.ProductionWslIjentManager
|
||||
import com.intellij.platform.ide.impl.wsl.WslEelProvider
|
||||
import com.intellij.platform.ide.impl.wsl.ijent.nio.toggle.IjentWslNioFsToggler
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.testFramework.registerExtension
|
||||
import com.intellij.testFramework.replaceService
|
||||
import com.intellij.util.asDisposable
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.concurrent.CancellationException
|
||||
|
||||
@@ -30,17 +23,14 @@ fun replaceProductionWslIjentManager(newServiceScope: CoroutineScope) {
|
||||
replaceService(WslIjentManager::class.java, ::ProductionWslIjentManager, newServiceScope)
|
||||
}
|
||||
|
||||
suspend fun replaceWslServicesAndRunWslEelInitialization(newServiceScope: CoroutineScope, wsl: WSLDistribution) {
|
||||
replaceProductionWslIjentManager(newServiceScope)
|
||||
replaceService(EelNioBridgeService::class.java, ::EelNioBridgeServiceImpl, newServiceScope)
|
||||
replaceExtension(newServiceScope, EelProvider.EP_NAME, WslEelProvider(newServiceScope))
|
||||
EelInitialization.runEelInitialization(wsl.getUNCRootPath().toString())
|
||||
fun replaceIjentWslNioFsToggler(newServiceScope: CoroutineScope) {
|
||||
replaceService(IjentWslNioFsToggler::class.java, ::IjentWslNioFsToggler, newServiceScope)
|
||||
}
|
||||
|
||||
private fun <T : Any> replaceExtension(scope: CoroutineScope, name: BaseExtensionPointName<*>, instance: T) {
|
||||
ApplicationManager.getApplication().apply {
|
||||
extensionArea.getExtensionPoint<T>(name.name).unregisterExtension(instance.javaClass)
|
||||
registerExtension(name, instance, scope.asDisposable())
|
||||
fun temporarilyResetEelNioBridge(serviceScope: CoroutineScope) {
|
||||
val guard = (EelNioBridgeService.getInstanceSync() as EelNioBridgeServiceImpl).temporarilyResetState()
|
||||
serviceScope.coroutineContext.job.invokeOnCompletion {
|
||||
guard.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -310,14 +310,17 @@ internal object SystemHealthMonitor {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkEelVmOptions() {
|
||||
// TODO: remove this check
|
||||
private suspend fun checkEelVmOptions() {
|
||||
if (!WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()) return
|
||||
|
||||
val changedOptions = MultiRoutingFileSystemVmOptionsSetter.ensureInVmOptions()
|
||||
when {
|
||||
changedOptions.isEmpty() -> Unit
|
||||
|
||||
changedOptions.isEmpty() -> {
|
||||
// Since IjentWslNioFsToggler was moved from the core to the module, it can't be accessed here anymore.
|
||||
// And probably, it's not required anymore.
|
||||
// IjentWslNioFsToggler.instanceAsync().enableForAllWslDistributions()
|
||||
}
|
||||
|
||||
PluginManagerCore.isRunningFromSources() || AppMode.isDevServer() -> {
|
||||
logger<MultiRoutingFileSystemVmOptionsSetter>().warn(
|
||||
changedOptions.joinToString(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<eelProvider implementation="com.intellij.platform.ide.impl.wsl.WslEelProvider" os="windows"/>
|
||||
<eelProvider implementation="com.intellij.platform.ide.impl.wsl.ijent.nio.toggle.IjentWslNioFsToggler$WslEelProvider" os="windows"/>
|
||||
<applicationService serviceInterface="com.intellij.execution.wsl.WslIjentManager"
|
||||
serviceImplementation="com.intellij.platform.ide.impl.wsl.ProductionWslIjentManager"/>
|
||||
</extensions>
|
||||
|
||||
@@ -1,171 +1 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.impl.wsl
|
||||
|
||||
import com.intellij.execution.eel.MultiRoutingFileSystemUtils
|
||||
import com.intellij.execution.ijent.nio.IjentEphemeralRootAwareFileSystemProvider
|
||||
import com.intellij.execution.wsl.*
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.eel.EelDescriptor
|
||||
import com.intellij.platform.eel.EelOsFamily
|
||||
import com.intellij.platform.eel.provider.EelNioBridgeService
|
||||
import com.intellij.platform.eel.provider.EelProvider
|
||||
import com.intellij.platform.eel.provider.LocalEelDescriptor
|
||||
import com.intellij.platform.ide.impl.wsl.ijent.nio.IjentWslNioFileSystemProvider
|
||||
import com.intellij.platform.ijent.IjentPosixApi
|
||||
import com.intellij.platform.ijent.community.impl.IjentFailSafeFileSystemPosixApi
|
||||
import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider
|
||||
import com.intellij.platform.ijent.community.impl.nio.telemetry.TracingFileSystemProvider
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.forEachGuaranteed
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.job
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystemAlreadyExistsException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.spi.FileSystemProvider
|
||||
import kotlin.io.path.Path
|
||||
|
||||
private val WSLDistribution.roots: Set<String>
|
||||
get() {
|
||||
val localRoots = mutableSetOf(getWindowsPath("/"))
|
||||
localRoots.single().let {
|
||||
localRoots += it.replace("wsl.localhost", "wsl$")
|
||||
localRoots += it.replace("wsl$", "wsl.localhost")
|
||||
}
|
||||
return localRoots
|
||||
}
|
||||
|
||||
private suspend fun WSLDistribution.getIjent(): IjentPosixApi {
|
||||
return WslIjentManager.instanceAsync().getIjentApi(this, null, false)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
class WslEelProvider(private val coroutineScope: CoroutineScope) : EelProvider {
|
||||
private val providersCache = ContainerUtil.createConcurrentWeakMap<String, FileSystemProvider>()
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<WslEelProvider>()
|
||||
}
|
||||
|
||||
override suspend fun tryInitialize(path: String) {
|
||||
if (!serviceAsync<WslIjentAvailabilityService>().useIjentForWslNioFileSystem()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!MultiRoutingFileSystemUtils.isMultiRoutingFsEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!WslPath.isWslUncPath(path)) {
|
||||
return
|
||||
}
|
||||
|
||||
val allWslDistributions = serviceAsync<WslDistributionManager>().installedDistributions
|
||||
|
||||
val path = Path.of(path)
|
||||
val service = serviceAsync<EelNioBridgeService>()
|
||||
val descriptor = service.tryGetEelDescriptor(path)
|
||||
|
||||
if (descriptor != null && descriptor !== LocalEelDescriptor) {
|
||||
check(descriptor is WslEelDescriptor)
|
||||
return
|
||||
}
|
||||
|
||||
for (distro in allWslDistributions) {
|
||||
val matches =
|
||||
try {
|
||||
distro.getWslPath(path) != null
|
||||
}
|
||||
catch (_: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
if (matches) {
|
||||
service.registerNioWslFs(distro)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun EelNioBridgeService.registerNioWslFs(distro: WSLDistribution) {
|
||||
val descriptor = distro.getIjent().descriptor as WslEelDescriptor
|
||||
val ijentFsProvider = TracingFileSystemProvider(IjentNioFileSystemProvider.getInstance())
|
||||
val ijentUri = URI("ijent", "wsl", "/${distro.id}", null, null)
|
||||
|
||||
try {
|
||||
val ijentFs = IjentFailSafeFileSystemPosixApi(coroutineScope) { distro.getIjent() }
|
||||
val fs = ijentFsProvider.newFileSystem(ijentUri, IjentNioFileSystemProvider.newFileSystemMap(ijentFs))
|
||||
|
||||
coroutineScope.coroutineContext.job.invokeOnCompletion {
|
||||
fs?.close()
|
||||
}
|
||||
}
|
||||
catch (_: FileSystemAlreadyExistsException) {
|
||||
// Nothing.
|
||||
}
|
||||
|
||||
descriptor.distribution.roots.forEachGuaranteed { localRoot ->
|
||||
register(localRoot, descriptor, descriptor.distribution.id, false, false) { underlyingProvider, _ ->
|
||||
val key = if (Registry.`is`("wsl.use.new.filesystem")) localRoot else distro.id
|
||||
|
||||
val fileSystemProvider = providersCache.computeIfAbsent(key) {
|
||||
if (Registry.`is`("wsl.use.new.filesystem")) {
|
||||
IjentEphemeralRootAwareFileSystemProvider(
|
||||
root = Path(localRoot),
|
||||
ijentFsProvider = ijentFsProvider,
|
||||
originalFsProvider = TracingFileSystemProvider(underlyingProvider),
|
||||
// FIXME: is this behavior really correct?
|
||||
//
|
||||
// It is known that `originalFs.rootDirectories` always returns all WSL drives.
|
||||
// Also, it is known that `ijentFs.rootDirectories` returns a single WSL drive,
|
||||
// which is already mentioned in `originalFs.rootDirectories`.
|
||||
//
|
||||
// `ijentFs` is usually represented by `IjentFailSafeFileSystemPosixApi`,
|
||||
// which launches IJent and the corresponding WSL containers lazily.
|
||||
//
|
||||
// This function avoids fetching root directories directly from IJent.
|
||||
// This way, various UI file trees don't start all WSL containers during loading the file system root.
|
||||
useRootDirectoriesFromOriginalFs = true,
|
||||
)
|
||||
}
|
||||
else {
|
||||
IjentWslNioFileSystemProvider(
|
||||
wslDistribution = distro,
|
||||
ijentFsProvider = ijentFsProvider,
|
||||
originalFsProvider = TracingFileSystemProvider(underlyingProvider),
|
||||
)
|
||||
}
|
||||
}
|
||||
val fileSystem = if (fileSystemProvider is IjentEphemeralRootAwareFileSystemProvider) {
|
||||
fileSystemProvider.getFileSystem(ijentUri)
|
||||
}
|
||||
else {
|
||||
fileSystemProvider.getFileSystem(distro.getUNCRootPath().toUri())
|
||||
}
|
||||
LOG.info("Switching $distro to IJent WSL nio.FS: $fileSystem")
|
||||
fileSystem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WslEelDescriptor(val distribution: WSLDistribution) : EelDescriptor {
|
||||
override val osFamily: EelOsFamily = EelOsFamily.Posix
|
||||
|
||||
override val userReadableDescription: @NonNls String = "WSL: ${distribution.presentableName}"
|
||||
|
||||
override suspend fun toEelApi(): EelApi {
|
||||
return distribution.getIjent()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is WslEelDescriptor && other.distribution.id == distribution.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return distribution.id.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.eel.EelDescriptor
|
||||
import com.intellij.platform.ide.impl.wsl.ijent.nio.toggle.WslEelDescriptor
|
||||
import com.intellij.platform.ijent.spi.IjentConnectionStrategy
|
||||
import com.intellij.platform.ijent.spi.IjentDeployingOverShellProcessStrategy
|
||||
import com.intellij.util.io.computeDetached
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.impl.wsl.ijent.nio.toggle
|
||||
|
||||
import com.intellij.execution.wsl.WSLDistribution
|
||||
import com.intellij.execution.wsl.WslDistributionManager
|
||||
import com.intellij.execution.wsl.WslIjentManager
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.runBlockingMaybeCancellable
|
||||
import com.intellij.platform.eel.provider.EelNioBridgeService
|
||||
import com.intellij.platform.ide.impl.wsl.ijent.nio.IjentWslNioFileSystemProvider
|
||||
import com.intellij.platform.ijent.IjentPosixApi
|
||||
import com.intellij.platform.ijent.community.impl.IjentFailSafeFileSystemPosixApi
|
||||
import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider
|
||||
import com.intellij.platform.ijent.community.impl.nio.telemetry.TracingFileSystemProvider
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.forEachGuaranteed
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.job
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.FileSystemAlreadyExistsException
|
||||
import java.nio.file.spi.FileSystemProvider
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
private suspend fun WSLDistribution.getIjent(): IjentPosixApi {
|
||||
return WslIjentManager.instanceAsync().getIjentApi(this, null, false)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@VisibleForTesting
|
||||
class IjentWslNioFsToggleStrategy(
|
||||
private val coroutineScope: CoroutineScope,
|
||||
) {
|
||||
internal val enabledInDistros: MutableMap<WSLDistribution, WslEelDescriptor> = ConcurrentHashMap()
|
||||
|
||||
private val providersCache = ContainerUtil.createConcurrentWeakMap<String, IjentWslNioFileSystemProvider>()
|
||||
|
||||
init {
|
||||
coroutineScope.coroutineContext.job.invokeOnCompletion {
|
||||
unregisterAll()
|
||||
}
|
||||
}
|
||||
|
||||
fun enableForAllWslDistributions() {
|
||||
val listener = BiConsumer<Set<WSLDistribution>, Set<WSLDistribution>> { old, new ->
|
||||
// TODO The code is race prone. Frequent creations and deletions of WSL containers may break the state.
|
||||
for (distro in new - old) {
|
||||
handleWslDistributionAddition(distro)
|
||||
}
|
||||
for (distro in old - new) {
|
||||
handleWslDistributionDeletion(distro)
|
||||
}
|
||||
}
|
||||
|
||||
val wslDistributionManager = WslDistributionManager.getInstance()
|
||||
wslDistributionManager.addWslDistributionsChangeListener(listener)
|
||||
coroutineScope.coroutineContext.job.invokeOnCompletion {
|
||||
wslDistributionManager.removeWslDistributionsChangeListener(listener)
|
||||
}
|
||||
|
||||
for (distro in wslDistributionManager.installedDistributions) {
|
||||
handleWslDistributionAddition(distro)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleWslDistributionAddition(distro: WSLDistribution) {
|
||||
switchToIjentFs(distro)
|
||||
}
|
||||
|
||||
private fun handleWslDistributionDeletion(distro: WSLDistribution) {
|
||||
val descriptor = enabledInDistros.remove(distro)
|
||||
|
||||
if (descriptor != null) {
|
||||
recomputeEel(descriptor) { _, actualFs ->
|
||||
actualFs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun switchToIjentFs(distro: WSLDistribution) {
|
||||
val ijentFsProvider = TracingFileSystemProvider(IjentNioFileSystemProvider.getInstance())
|
||||
val descriptor = runBlockingMaybeCancellable { distro.getIjent() }.descriptor as WslEelDescriptor
|
||||
|
||||
enabledInDistros[distro] = descriptor
|
||||
|
||||
try {
|
||||
val ijentFs = IjentFailSafeFileSystemPosixApi(coroutineScope) { distro.getIjent() }
|
||||
ijentFsProvider.newFileSystem(
|
||||
URI("ijent", "wsl", "/${distro.id}", null, null),
|
||||
IjentNioFileSystemProvider.newFileSystemMap(ijentFs),
|
||||
)
|
||||
}
|
||||
catch (_: FileSystemAlreadyExistsException) {
|
||||
// Nothing.
|
||||
}
|
||||
|
||||
recomputeEel(descriptor) { underlyingProvider, _ ->
|
||||
val fileSystemProvider = providersCache.computeIfAbsent(distro.id) {
|
||||
IjentWslNioFileSystemProvider(
|
||||
wslDistribution = distro,
|
||||
ijentFsProvider = ijentFsProvider,
|
||||
originalFsProvider = TracingFileSystemProvider(underlyingProvider),
|
||||
)
|
||||
}
|
||||
val fileSystem = fileSystemProvider.getFileSystem(distro.getUNCRootPath().toUri())
|
||||
LOG.info("Switching $distro to IJent WSL nio.FS: $fileSystem")
|
||||
fileSystem
|
||||
}
|
||||
}
|
||||
|
||||
fun switchToTracingWsl9pFs(descriptor: WslEelDescriptor) {
|
||||
recomputeEel(descriptor) { underlyingProvider, previousFs ->
|
||||
LOG.info("Switching $descriptor to the original file system but with tracing")
|
||||
|
||||
try {
|
||||
previousFs?.close()
|
||||
}
|
||||
catch (_: UnsupportedOperationException) {
|
||||
// It is expected that the default file system always throws that exception on calling close(),
|
||||
// but for the sake of following contracts, this method is nonetheless called.
|
||||
}
|
||||
TracingFileSystemProvider(underlyingProvider).getLocalFileSystem()
|
||||
}
|
||||
}
|
||||
|
||||
fun unregisterAll() {
|
||||
val service = EelNioBridgeService.getInstanceSync()
|
||||
|
||||
enabledInDistros.entries.forEachGuaranteed { (_, descriptor) ->
|
||||
service.unregister(descriptor)
|
||||
}
|
||||
|
||||
enabledInDistros.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun FileSystemProvider.getLocalFileSystem(): FileSystem = getFileSystem(URI.create("file:/"))
|
||||
|
||||
private val LOG = logger<IjentWslNioFsToggleStrategy>()
|
||||
|
||||
private val WSLDistribution.roots: Set<String>
|
||||
get() {
|
||||
val localRoots = mutableSetOf(getWindowsPath("/"))
|
||||
localRoots.single().let {
|
||||
localRoots += it.replace("wsl.localhost", "wsl$")
|
||||
localRoots += it.replace("wsl$", "wsl.localhost")
|
||||
}
|
||||
return localRoots
|
||||
}
|
||||
|
||||
private fun recomputeEel(
|
||||
descriptor: WslEelDescriptor,
|
||||
action: (underlyingProvider: FileSystemProvider, previousFs: FileSystem?) -> FileSystem?,
|
||||
) {
|
||||
val service = EelNioBridgeService.getInstanceSync()
|
||||
|
||||
descriptor.distribution.roots.forEachGuaranteed { localRoot ->
|
||||
service.register(localRoot, descriptor, descriptor.distribution.id, false, false, action)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.impl.wsl.ijent.nio.toggle
|
||||
|
||||
import com.intellij.diagnostic.VMOptions
|
||||
import com.intellij.execution.wsl.*
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
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.eel.EelApi
|
||||
import com.intellij.platform.eel.EelDescriptor
|
||||
import com.intellij.platform.eel.EelOsFamily
|
||||
import com.intellij.platform.eel.provider.EelProvider
|
||||
import com.intellij.platform.ide.impl.wsl.ijent.nio.toggle.IjentWslNioFsToggler.WslEelProvider
|
||||
import kotlinx.coroutines.*
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import java.io.BufferedReader
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.bufferedReader
|
||||
|
||||
/**
|
||||
* This service, along with listeners inside it, enables and disables access to WSL drives through IJent.
|
||||
*/
|
||||
@Internal
|
||||
@Service
|
||||
@VisibleForTesting
|
||||
class IjentWslNioFsToggler(private val coroutineScope: CoroutineScope) {
|
||||
companion object {
|
||||
suspend fun instanceAsync(): IjentWslNioFsToggler = serviceAsync()
|
||||
fun instance(): IjentWslNioFsToggler = service()
|
||||
}
|
||||
|
||||
val isAvailable: Boolean get() = strategy != null
|
||||
|
||||
fun enableForAllWslDistributions() {
|
||||
logErrorIfNotWindows()
|
||||
strategy?.enableForAllWslDistributions()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun switchToIjentFs(distro: WSLDistribution) {
|
||||
logErrorIfNotWindows()
|
||||
strategy ?: error("Not available")
|
||||
strategy.switchToIjentFs(distro)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun switchToTracingWsl9pFs(descriptor: WslEelDescriptor) {
|
||||
logErrorIfNotWindows()
|
||||
strategy ?: error("Not available")
|
||||
strategy.switchToTracingWsl9pFs(descriptor)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun unregisterAll() {
|
||||
logErrorIfNotWindows()
|
||||
strategy ?: error("Not available")
|
||||
strategy.unregisterAll()
|
||||
}
|
||||
|
||||
private fun logErrorIfNotWindows() {
|
||||
if (!SystemInfo.isWindows) {
|
||||
thisLogger().error("${javaClass.name} should be requested only on Windows")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Move to ijent.impl?
|
||||
internal class WslEelProvider : EelProvider {
|
||||
|
||||
suspend fun getApiByDistribution(distro: WSLDistribution): EelApi {
|
||||
val enabledDistros = serviceAsync<IjentWslNioFsToggler>().strategy?.enabledInDistros
|
||||
if (enabledDistros == null || distro !in enabledDistros) {
|
||||
throw IllegalStateException("IJent is not enabled in $distro")
|
||||
}
|
||||
return WslIjentManager.getInstance().getIjentApi(distro, null, rootUser = false)
|
||||
}
|
||||
|
||||
override suspend fun tryInitialize(path: String) = tryInitializeEelOnWsl(path)
|
||||
}
|
||||
|
||||
private val strategy = run {
|
||||
val defaultProvider = FileSystems.getDefault().provider()
|
||||
when {
|
||||
!WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem() -> null
|
||||
|
||||
defaultProvider.javaClass.name == MultiRoutingFileSystemProvider::class.java.name -> {
|
||||
IjentWslNioFsToggleStrategy(coroutineScope)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val vmOptions = runCatching {
|
||||
VMOptions.getUserOptionsFile()?.bufferedReader()?.use<BufferedReader, String> { it.readText() }
|
||||
?: "<null>"
|
||||
}.getOrElse<String, String> { err -> err.stackTraceToString() }
|
||||
|
||||
val systemProperties = runCatching {
|
||||
System.getProperties().entries.joinToString("\n") { (k, v) -> "$k=$v" }
|
||||
}.getOrElse<String, String> { err -> err.stackTraceToString() }
|
||||
|
||||
val message = "The default filesystem ${FileSystems.getDefault()} is not ${MultiRoutingFileSystemProvider::class.java}"
|
||||
|
||||
logger<IjentWslNioFsToggler>().warn("$message\nVM Options:\n$vmOptions\nSystem properties:\n$systemProperties")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun tryInitializeEelOnWsl(path: String) {
|
||||
if (!WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!WslPath.isWslUncPath(path)) {
|
||||
return
|
||||
}
|
||||
|
||||
val ijentWslNioFsToggler = IjentWslNioFsToggler.instanceAsync()
|
||||
|
||||
coroutineScope {
|
||||
launch {
|
||||
ijentWslNioFsToggler.enableForAllWslDistributions()
|
||||
}
|
||||
|
||||
val allWslDistributions = async(Dispatchers.IO) {
|
||||
serviceAsync<WslDistributionManager>().installedDistributions
|
||||
}
|
||||
|
||||
val path = Path.of(path)
|
||||
|
||||
for (distro in allWslDistributions.await()) {
|
||||
val matches =
|
||||
try {
|
||||
distro.getWslPath(path) != null
|
||||
}
|
||||
catch (_: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
if (matches) {
|
||||
launch {
|
||||
serviceAsync<WslIjentManager>().getIjentApi(distro, null, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class WslEelDescriptor(val distribution: WSLDistribution) : EelDescriptor {
|
||||
override val osFamily: EelOsFamily = EelOsFamily.Posix
|
||||
|
||||
override val userReadableDescription: @NonNls String = "WSL: ${distribution.presentableName}"
|
||||
|
||||
override suspend fun toEelApi(): EelApi {
|
||||
return WslEelProvider().getApiByDistribution(distribution)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is WslEelDescriptor && other.distribution.id == distribution.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return distribution.id.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.platform.ide.impl.wsl;
|
||||
package com.intellij.platform.ide.impl.wsl.ijent.nio.toggle;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -2,12 +2,10 @@
|
||||
package com.intellij.execution.eel
|
||||
|
||||
import com.intellij.diagnostic.VMOptions
|
||||
import com.intellij.execution.wsl.WslIjentAvailabilityService
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.platform.core.nio.fs.MultiRoutingFileSystem
|
||||
import com.intellij.platform.core.nio.fs.MultiRoutingFileSystemProvider
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.io.BufferedReader
|
||||
import java.nio.file.FileSystems
|
||||
import kotlin.io.path.bufferedReader
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.execution.wsl
|
||||
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
|
||||
Reference in New Issue
Block a user