diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index d58980868414..398164522c2a 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -123,6 +123,11 @@ topic="com.intellij.openapi.fileEditor.FileEditorManagerListener"/> + + + + - if (type in moduleSdksByTypes) { - if (index != 0) group.addSeparator() - group.addAll(moduleSdksByTypes - .getValue(type) - .filter { - targetModuleSitsOn == null || - targetModuleSitsOn.codeCouldProbablyBeRunWithConfig(it.targetAdditionalData?.targetEnvironmentConfiguration) - } - .mapNotNull { model.findSdk(it) } - .map { SwitchToSdkAction(it, currentSdk) }) + if (index != 0) group.addSeparator() + val sdksByType = moduleSdksByTypes[type]?.distinctBy { + it.sdkAdditionalData?.javaClass to it.homePath + } ?: return@forEachIndexed + + val uniqueSdks = if (type == PyRenderedSdkType.REMOTE) { + sdksByType.filter { + targetModuleSitsOn == null || + targetModuleSitsOn.codeCouldProbablyBeRunWithConfig(it.targetAdditionalData?.targetEnvironmentConfiguration) + } } + else { + sdksByType.distinctBy { it.sdkAdditionalData?.javaClass to it.homePath } + } + + val actions = uniqueSdks.map { SwitchToSdkAction(it, currentSdk) } + group.addAll(actions) } if (moduleSdksByTypes.isNotEmpty()) group.addSeparator() val addNewInterpreterPopupGroup = DefaultActionGroup(PyBundle.message("python.sdk.action.add.new.interpreter.text"), true) - addNewInterpreterPopupGroup.addAll(collectAddInterpreterActions(ModuleOrProject.ModuleAndProject(module)) { sdk -> - SlowOperations.knownIssue("PY-76167").use { - switchToSdk(module, sdk, currentSdk) - } - }) + addNewInterpreterPopupGroup.addAll(collectAddInterpreterActions(ModuleOrProject.ModuleAndProject(module)) { }) ActionManager.getInstance().getAction("Python.NewInterpreter.Extra")?.let { addNewInterpreterPopupGroup.add(it) } @@ -123,7 +124,7 @@ class PySdkPopupFactory(val module: Module) { presentation.icon = icon(sdk) } - override fun actionPerformed(e: AnActionEvent) = switchToSdk(module, sdk, currentSdk) + override fun actionPerformed(e: AnActionEvent) = run { module.pythonSdk = sdk } } private inner class InterpreterSettingsAction : DumbAwareAction(PyBundle.messagePointer("python.sdk.popup.interpreter.settings")) { diff --git a/python/src/com/jetbrains/python/sdk/PyTransferredSdkRoots.kt b/python/src/com/jetbrains/python/sdk/PyTransferredSdkRoots.kt index 1a1b098b07ac..33434b2a7520 100644 --- a/python/src/com/jetbrains/python/sdk/PyTransferredSdkRoots.kt +++ b/python/src/com/jetbrains/python/sdk/PyTransferredSdkRoots.kt @@ -172,6 +172,32 @@ private fun updateRootsForModulesWithInheritedSdk(project: Project, sdk: Sdk?, a } } +/** + * Handles transferred roots and virtualenv exclusion when a module's Python SDK changes. + * Registered as a listener for [PySdkListener.TOPIC]. + * + * Note: cannot use the public [removeTransferredRoots] for `prevSdk` because its guard + * `module.pythonSdk == sdk` fails — by the time the listener fires, the SDK is already changed. + */ +internal class PySdkTransferredRootsListener : PySdkListener { + override fun moduleSdkUpdated(module: Module, prevSdk: Sdk?, newSdk: Sdk?) { + if (prevSdk != null) { + val oldPaths = getPathsToTransfer(prevSdk) + if (oldPaths.isNotEmpty()) { + runInEdt { + val rootsToRemove = TransferredRootsDetector(module.project).detect(module, oldPaths, oldPaths) + removeTransferredRoots(module, rootsToRemove) + } + } + } + + if (newSdk != null) { + transferRoots(module, newSdk) + module.excludeInnerVirtualEnv(newSdk) + } + } +} + private object ModuleRootAndDepOps { val LOG = thisLogger() } diff --git a/python/src/com/jetbrains/python/sdk/evolution/advancedActions.kt b/python/src/com/jetbrains/python/sdk/evolution/advancedActions.kt index 95ecc53c9519..29035260998e 100644 --- a/python/src/com/jetbrains/python/sdk/evolution/advancedActions.kt +++ b/python/src/com/jetbrains/python/sdk/evolution/advancedActions.kt @@ -7,12 +7,9 @@ import com.intellij.python.sdk.ui.evolution.ui.EvoSelectSdkProvider import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeLazyNodeElement import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeLeafElement import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeSection -import com.intellij.util.SlowOperations import com.jetbrains.python.Result import com.jetbrains.python.sdk.ModuleOrProject import com.jetbrains.python.sdk.collectAddInterpreterActions -import com.jetbrains.python.sdk.pythonSdk -import com.jetbrains.python.sdk.switchToSdk internal class AdvancedSelectSdkProvider() : EvoSelectSdkProvider { @@ -21,11 +18,7 @@ internal class AdvancedSelectSdkProvider() : EvoSelectSdkProvider { icon = AllIcons.Toolwindows.ToolWindowInternal ) { - val baseIdeActions = collectAddInterpreterActions(ModuleOrProject.ModuleAndProject(evoModuleSdk.module)) { sdk -> - SlowOperations.knownIssue("PY-76167").use { - switchToSdk(evoModuleSdk.module, sdk, evoModuleSdk.module.pythonSdk) - } - } + val baseIdeActions = collectAddInterpreterActions(ModuleOrProject.ModuleAndProject(evoModuleSdk.module)) { } val section = EvoTreeSection( label = null,