mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
IJ-CR-149809 [ijent] IJPL-161967: Initialize Eel right before Workspace model
Since the creation of WSM involves access to project files, we must ensure that Eel is up at this point. Also fixes IJPL-171263, IJPL-171949, IJPL-172074 (cherry picked from commit 986812336cdbca288deeb5124f51a959182f53a4) GitOrigin-RevId: c30d7a1673b630bd386e9a42914a3a4f8f6f5f7e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b803acd45f
commit
17cf754719
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.eel.impl.local.LocalPosixEelApiImpl
|
||||
import com.intellij.platform.eel.impl.local.LocalWindowsEelApiImpl
|
||||
import com.intellij.platform.util.coroutines.forEachConcurrent
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -31,11 +32,23 @@ suspend fun Path.getEelApi(): EelApi {
|
||||
return eels.firstOrNull() ?: if (SystemInfo.isWindows) LocalWindowsEelApiImpl() else LocalPosixEelApiImpl()
|
||||
}
|
||||
|
||||
object EelInitialization {
|
||||
suspend fun runEelInitialization(project: Project) {
|
||||
val eels = EP_NAME.extensionList
|
||||
eels.forEachConcurrent { eelProvider ->
|
||||
eelProvider.tryInitialize(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Path.getEelApiBlocking() = runBlockingMaybeCancellable { getEelApi() }
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface EelProvider {
|
||||
suspend fun getEelApi(path: Path): EelApi?
|
||||
|
||||
suspend fun tryInitialize(project: Project)
|
||||
}
|
||||
|
||||
private val EP_NAME = ExtensionPointName<EelProvider>("com.intellij.eelProvider")
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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.eel
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.impl.ProjectServiceContainerInitializedListener
|
||||
import com.intellij.platform.diagnostic.telemetry.impl.span
|
||||
import com.intellij.platform.eel.provider.EelInitialization
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
/**
|
||||
* During the process of project initialization, the IDE interacts with the file system where the project is located on.
|
||||
* It happens, for example, during the loading of Workspace Model cache.
|
||||
* Before the first such interaction, we must ensure that the file system is accessible to the IDE.
|
||||
* Since Eel is responsible for it, it must run *very* early.
|
||||
*
|
||||
* On the other hand, we should not access non-local environments excessively. The process of initialization may require IO requests
|
||||
* which could severely hinder the performance of IDE startup.
|
||||
* It means that the suitable way to initialize Eel is right before the initialization of a project (when we can decide if we should access the environment),
|
||||
* and not earlier.
|
||||
*/
|
||||
@Internal
|
||||
class EelProjectServiceInitializer : ProjectServiceContainerInitializedListener {
|
||||
override suspend fun execute(project: Project, workspaceIndexReady: () -> Unit) {
|
||||
span("eel initialization") {
|
||||
EelInitialization.runEelInitialization(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// 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.toggle
|
||||
|
||||
import com.intellij.execution.wsl.WSLDistribution
|
||||
import com.intellij.execution.wsl.WslDistributionManager
|
||||
import com.intellij.execution.wsl.WslIjentAvailabilityService
|
||||
import com.intellij.execution.wsl.WslIjentManager
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.modules
|
||||
import com.intellij.openapi.project.rootManager
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Starts the IJent if a project on WSL is opened.
|
||||
*
|
||||
* At the moment of writing this string,
|
||||
* this class was just an optimization handler that speeds up sometimes the first request to the IJent.
|
||||
* It was not necessary for running the IDE.
|
||||
*
|
||||
* See also [IjentWslFileSystemApplicationActivity].
|
||||
*/
|
||||
internal class IjentInProjectStarter : ProjectActivity {
|
||||
override suspend fun execute(project: Project): Unit = coroutineScope {
|
||||
if (!WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
val ijentWslNioFsToggler = IjentWslNioFsToggler.instanceAsync()
|
||||
if (!ijentWslNioFsToggler.isAvailable) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
val allWslDistributions = async(Dispatchers.IO) {
|
||||
serviceAsync<WslDistributionManager>().installedDistributions
|
||||
}
|
||||
|
||||
val relatedWslDistributions = hashSetOf<WSLDistribution>()
|
||||
for (module in project.modules) {
|
||||
for (contentRoot in module.rootManager.contentRoots) {
|
||||
val path =
|
||||
try {
|
||||
contentRoot.toNioPath()
|
||||
}
|
||||
catch (_: UnsupportedOperationException) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
for (distro in allWslDistributions.await()) {
|
||||
val matches =
|
||||
try {
|
||||
distro.getWslPath(path) != null
|
||||
}
|
||||
catch (_: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
if (matches) {
|
||||
relatedWslDistributions += distro
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (distro in relatedWslDistributions) {
|
||||
launch {
|
||||
serviceAsync<WslIjentManager>().getIjentApi(distro, project, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// 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.toggle
|
||||
|
||||
import com.intellij.execution.wsl.WslIjentAvailabilityService
|
||||
import com.intellij.ide.ApplicationActivity
|
||||
|
||||
/**
|
||||
* This activity registers IJent file systems as early as possible.
|
||||
*
|
||||
* It's important to register them before opening projects, because:
|
||||
* * The project itself may be located on an IJent filesystem.
|
||||
* * There could have been initialization races during loading the project.
|
||||
*
|
||||
* See also [IjentInProjectStarter].
|
||||
*/
|
||||
class IjentWslFileSystemApplicationActivity : ApplicationActivity {
|
||||
override suspend fun execute() {
|
||||
if (!WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()) {
|
||||
return
|
||||
}
|
||||
|
||||
val ijentWslNioFsToggler = IjentWslNioFsToggler.instanceAsync()
|
||||
if (!ijentWslNioFsToggler.isAvailable) {
|
||||
return
|
||||
}
|
||||
|
||||
ijentWslNioFsToggler.enableForAllWslDistributions()
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@ package com.intellij.execution.wsl.ijent.nio.toggle
|
||||
import com.intellij.diagnostic.VMOptions
|
||||
import com.intellij.execution.eel.EelApiWithPathsMapping
|
||||
import com.intellij.execution.wsl.WSLDistribution
|
||||
import com.intellij.execution.wsl.WslDistributionManager
|
||||
import com.intellij.execution.wsl.WslIjentAvailabilityService
|
||||
import com.intellij.execution.wsl.WslIjentManager
|
||||
import com.intellij.execution.wsl.WslPath
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
@@ -13,11 +15,16 @@ import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.diagnostic.Attachment
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.project.Project
|
||||
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.provider.EelProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
@@ -86,6 +93,15 @@ class IjentWslNioFsToggler(private val coroutineScope: CoroutineScope) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the IJent if a project on WSL is opened.
|
||||
*
|
||||
* At the moment of writing this string,
|
||||
* this class was just an optimization handler that speeds up sometimes the first request to the IJent.
|
||||
* It was not necessary for running the IDE.
|
||||
*/
|
||||
override suspend fun tryInitialize(project: Project) = tryInitializeEelOnWsl(project)
|
||||
}
|
||||
|
||||
private val strategy = run {
|
||||
@@ -123,4 +139,50 @@ class IjentWslNioFsToggler(private val coroutineScope: CoroutineScope) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun tryInitializeEelOnWsl(project: Project) = coroutineScope {
|
||||
if (project.isDefault) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
if (!WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
val ijentWslNioFsToggler = IjentWslNioFsToggler.instanceAsync()
|
||||
if (!ijentWslNioFsToggler.isAvailable) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
val projectFile = project.projectFilePath
|
||||
check(projectFile != null) { "Impossible: project is not default, but it does not have project file" }
|
||||
val path = Path.of(projectFile)
|
||||
|
||||
if (!WslPath.isWslUncPath(path.toString())) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
launch {
|
||||
ijentWslNioFsToggler.enableForAllWslDistributions()
|
||||
}
|
||||
|
||||
val allWslDistributions = async(Dispatchers.IO) {
|
||||
serviceAsync<WslDistributionManager>().installedDistributions
|
||||
}
|
||||
|
||||
for (distro in allWslDistributions.await()) {
|
||||
val matches =
|
||||
try {
|
||||
distro.getWslPath(path) != null
|
||||
}
|
||||
catch (_: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
if (matches) {
|
||||
launch {
|
||||
serviceAsync<WslIjentManager>().getIjentApi(distro, project, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<projectServiceContainerInitializedListener
|
||||
id="moduleBridgeLoaderService"
|
||||
implementation="com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleBridgeLoaderService" order="first"/>
|
||||
implementation="com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleBridgeLoaderService"/>
|
||||
<projectService serviceInterface="com.intellij.openapi.module.AutomaticModuleUnloader"
|
||||
serviceImplementation="com.intellij.openapi.module.impl.AutomaticModuleUnloaderImpl" overrides="true"/>
|
||||
<projectService serviceInterface="com.intellij.openapi.roots.ProjectRootManager"
|
||||
|
||||
@@ -131,7 +131,10 @@
|
||||
<applicationService serviceInterface="com.intellij.openapi.keymap.KeymapManager"
|
||||
serviceImplementation="com.intellij.openapi.keymap.impl.KeymapManagerImpl"/>
|
||||
|
||||
<eelProvider implementation="com.intellij.execution.wsl.ijent.nio.toggle.IjentWslNioFsToggler$WslEelProvider"/>
|
||||
<eelProvider implementation="com.intellij.execution.wsl.ijent.nio.toggle.IjentWslNioFsToggler$WslEelProvider" os="windows"/>
|
||||
|
||||
<projectServiceContainerInitializedListener implementation="com.intellij.execution.eel.EelProjectServiceInitializer"
|
||||
order="before moduleBridgeLoaderService"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.openapi.project.ProjectManager"
|
||||
serviceImplementation="com.intellij.openapi.project.impl.ProjectManagerImpl"
|
||||
@@ -831,7 +834,8 @@
|
||||
|
||||
<projectService serviceInterface="com.intellij.execution.RunManager"
|
||||
serviceImplementation="com.intellij.execution.impl.RunManagerImpl"/>
|
||||
<projectServiceContainerInitializedListener implementation="com.intellij.execution.impl.ProjectRunConfigurationInitializer"/>
|
||||
<projectServiceContainerInitializedListener implementation="com.intellij.execution.impl.ProjectRunConfigurationInitializer"
|
||||
order="after moduleBridgeLoaderService"/>
|
||||
<projectFileScanner implementation="com.intellij.execution.impl.RunConfigurationInArbitraryFileScanner"/>
|
||||
<vfs.asyncListener implementation="com.intellij.execution.impl.RCInArbitraryFileListener"/>
|
||||
<editorNotificationProvider implementation="com.intellij.execution.impl.RunConfigEditorNotificationProvider"/>
|
||||
@@ -1842,10 +1846,9 @@
|
||||
<applicationService serviceImplementation="com.intellij.openapi.wm.impl.customFrameDecorations.frameButtons.LinuxIconThemeConfiguration" headlessImplementation=""
|
||||
preload="notHeadless" os="linux"/>
|
||||
|
||||
<postStartupActivity implementation="com.intellij.execution.wsl.ijent.nio.toggle.IjentInProjectStarter"
|
||||
os="windows"/>
|
||||
<applicationActivity implementation="com.intellij.execution.wsl.ijent.nio.toggle.IjentWslFileSystemApplicationActivity"
|
||||
os="windows"/>
|
||||
<projectServiceInitializer.essential
|
||||
implementation="com.intellij.execution.eel.EelProjectServiceInitializer"
|
||||
order="before moduleBridgeLoaderService"/>
|
||||
<applicationActivity implementation="com.intellij.ide.SwingTooltipManagerCustomizer"/>
|
||||
<registryKey key="rhizome.progress"
|
||||
defaultValue="false"
|
||||
|
||||
Reference in New Issue
Block a user