mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-87021: (Part 1, TBC): Postpone project rebuild until project generation finished and transfer run config. settings
1. Project rebuild process disposes modules and kills `PyV3` project generation (i.e. Django). We now skip autoimport for newly generated projects, and PyV3 calls `startAutoImportIfNeeded` after project generation. 2. EP introduced to update run configurations when module gets renamed. GitOrigin-RevId: 55183da9c1f9d1e96e837e970254443fbaec6b5e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c0a421aa98
commit
3da2fb2642
@@ -32,6 +32,7 @@
|
||||
<resource-bundle>messages.PyBundle</resource-bundle>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<python.pyproject.model.moduleDataTransfer implementation="com.jetbrains.python.run.PyRunConfigTransfer"/>
|
||||
<localInspection language="TOML" enabledByDefault="true"
|
||||
implementationClass="com.jetbrains.python.sdk.poetry.PoetryPackageVersionsInspection"
|
||||
key="INSP.poetry.package.versions.display.name" bundle="messages.PyBundle" groupKey="INSP.GROUP.python"
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
<resource-bundle>messages.PyProjectTomlBundle</resource-bundle>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<registryKey defaultValue="true" description="Load project structure from pyproject.toml" key="intellij.python.pyproject.model" restartRequired="true"/>
|
||||
<registryKey defaultValue="true" description="Load project structure from pyproject.toml" key="intellij.python.pyproject.model"
|
||||
restartRequired="true"/>
|
||||
<postStartupActivity implementation="com.intellij.python.pyproject.model.internal.platformBridge.PyProjectSyncActivity"/>
|
||||
|
||||
<statistics.projectUsagesCollector implementation="com.intellij.python.pyproject.statistics.PythonTomlStatsUsagesCollector"/>
|
||||
@@ -17,6 +18,8 @@
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.python.pyproject.model.tool" interface="com.intellij.python.pyproject.model.spi.Tool"
|
||||
dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.python.pyproject.model.moduleDataTransfer"
|
||||
interface="com.intellij.python.pyproject.model.spi.PyModuleDataTransfer" dynamic="true"/>
|
||||
</extensionPoints>
|
||||
|
||||
</idea-plugin>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.intellij.python.pyproject.model.internal
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.python.pyproject.model.internal.autoImportBridge.PyProjectAutoImportService
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
private val enabled: Boolean get() = Registry.`is`("intellij.python.pyproject.model")
|
||||
|
||||
/**
|
||||
* Starts autoimport process if [enabled] or simply "skips" to the next step: [notifyModelRebuilt].
|
||||
* This method usually called by [com.intellij.python.pyproject.model.internal.platformBridge.PyProjectSyncActivity] except for new projects.
|
||||
* In this case, it is postponed till project generation (see usages).
|
||||
*
|
||||
* This method can only be called once (see [PyProjectAutoImportService.start])
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
suspend fun startAutoImportIfNeeded(project: Project) {
|
||||
if (enabled) {
|
||||
project.service<PyProjectAutoImportService>().start()
|
||||
}
|
||||
else {
|
||||
// User disabled "pyproject.toml -> module" convertion (aka project model rebuilding), but we still need to notify listener,
|
||||
// so they configure SDK
|
||||
notifyModelRebuilt(project)
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -19,7 +19,11 @@ internal class PyProjectAutoImportService(private val project: Project) : Dispos
|
||||
private var projectId: ExternalSystemProjectId? = null
|
||||
|
||||
|
||||
suspend fun start() {
|
||||
/**
|
||||
* Starts auto-import (`builds project module on any pyproject.toml` change). To be called only once!
|
||||
*/
|
||||
internal suspend fun start() {
|
||||
assert(projectId == null) { "Already started, do not call second time" }
|
||||
val tracker = getTracker()
|
||||
val projectAware = PyExternalSystemProjectAware.create(project)
|
||||
val projectId = projectAware.projectId
|
||||
|
||||
+7
-13
@@ -1,25 +1,19 @@
|
||||
package com.intellij.python.pyproject.model.internal.platformBridge
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.python.pyproject.model.internal.autoImportBridge.PyProjectAutoImportService
|
||||
import com.intellij.python.pyproject.model.internal.notifyModelRebuilt
|
||||
import com.intellij.platform.PlatformProjectOpenProcessor
|
||||
import com.intellij.python.pyproject.model.internal.startAutoImportIfNeeded
|
||||
|
||||
internal class PyProjectSyncActivity : ProjectActivity {
|
||||
private val enabled: Boolean get() = Registry.`is`("intellij.python.pyproject.model")
|
||||
|
||||
override suspend fun execute(project: Project) {
|
||||
if (project.isDefault) return // Service doesn't support default project
|
||||
|
||||
if (enabled) {
|
||||
project.service<PyProjectAutoImportService>().start()
|
||||
}
|
||||
else {
|
||||
// User disabled "pyproject.toml -> module" convertion (aka project model rebuilding), but we still need to notify listener,
|
||||
// so they configure SDK
|
||||
notifyModelRebuilt(project)
|
||||
// For newly created projects, lots of files are generated in background by so-called `PyV3` framework.
|
||||
// This is done in sync. manner, so we can't rebuild project until they finish, and we let them call startAutoImportIfNeeded.
|
||||
if (!PlatformProjectOpenProcessor.isNewlyCreatedProject(project)) {
|
||||
startAutoImportIfNeeded(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+74
-41
@@ -4,10 +4,30 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.platform.backend.workspace.workspaceModel
|
||||
import com.intellij.platform.workspace.jps.entities.*
|
||||
import com.intellij.platform.workspace.jps.entities.ContentRootEntity
|
||||
import com.intellij.platform.workspace.jps.entities.DependencyScope
|
||||
import com.intellij.platform.workspace.jps.entities.ExcludeUrlEntity
|
||||
import com.intellij.platform.workspace.jps.entities.ExternalSystemModuleOptionsEntity
|
||||
import com.intellij.platform.workspace.jps.entities.FacetEntityBuilder
|
||||
import com.intellij.platform.workspace.jps.entities.InheritedSdkDependency
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryDependency
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleDependency
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleEntity
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleId
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleSourceDependency
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleTypeId
|
||||
import com.intellij.platform.workspace.jps.entities.SdkDependency
|
||||
import com.intellij.platform.workspace.jps.entities.SdkId
|
||||
import com.intellij.platform.workspace.jps.entities.SourceRootEntity
|
||||
import com.intellij.platform.workspace.jps.entities.SourceRootTypeId
|
||||
import com.intellij.platform.workspace.jps.entities.exModuleOptions
|
||||
import com.intellij.platform.workspace.jps.entities.modifyContentRootEntity
|
||||
import com.intellij.platform.workspace.jps.entities.modifyModuleEntity
|
||||
import com.intellij.platform.workspace.jps.entities.sdkId
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.ImmutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.createEntityTreeCopy
|
||||
import com.intellij.platform.workspace.storage.impl.url.toVirtualFileUrl
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager
|
||||
@@ -17,6 +37,7 @@ import com.intellij.python.pyproject.model.internal.PyProjectTomlBundle
|
||||
import com.intellij.python.pyproject.model.internal.pyProjectToml.FSWalkInfoWithToml
|
||||
import com.intellij.python.pyproject.model.internal.pyProjectToml.getPEP621Deps
|
||||
import com.intellij.python.pyproject.model.spi.ProjectName
|
||||
import com.intellij.python.pyproject.model.spi.PyModuleDataTransfer
|
||||
import com.intellij.python.pyproject.model.spi.PyProjectTomlProject
|
||||
import com.intellij.python.pyproject.model.spi.Tool
|
||||
import com.intellij.python.pyproject.model.spi.WorkspaceName
|
||||
@@ -49,9 +70,13 @@ internal suspend fun rebuildProjectModel(project: Project, files: FSWalkInfoWith
|
||||
}
|
||||
val newStorage = createEntityStorage(entries, project.workspaceModel.getVirtualFileUrlManager())
|
||||
|
||||
val transfers = PyModuleDataTransfer.EP.extensionList.map { it.beforeRename(project) }
|
||||
val workspaceModel = project.workspaceModel
|
||||
val oldToNewModuleNames = object {
|
||||
lateinit var value: Map<String, String>
|
||||
}
|
||||
workspaceModel.update(PyProjectTomlBundle.message("action.PyProjectTomlSyncAction.description")) { currentStorage -> // Fake module entity is added by default if nothing was discovered
|
||||
relocateUserDefinedModuleSdk(currentStorage) {
|
||||
oldToNewModuleNames.value = relocateUserDefinedModuleSdk(currentStorage) {
|
||||
removeFakeModuleAndConflictingEntities(currentStorage, newStorage.entities(ModuleEntity::class.java))
|
||||
currentStorage.replaceBySource({ it is PyProjectTomlEntitySource }, newStorage)
|
||||
|
||||
@@ -62,6 +87,10 @@ internal suspend fun rebuildProjectModel(project: Project, files: FSWalkInfoWith
|
||||
currentStorage.excludeRoot(excludedRoot, modules)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (transfer in transfers) {
|
||||
transfer.modulesRenamed(oldToNewModuleNames.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,53 +114,45 @@ private fun MutableEntityStorage.excludeRoot(rootToExclude: VirtualFileUrl, modu
|
||||
*
|
||||
* For each module in [storage] stores `sdkId` and `moduleId`, then calls [transfer] and sets `sdkId` for modules with the same id
|
||||
*/
|
||||
internal fun relocateUserDefinedModuleSdk(storage: MutableEntityStorage, transfer: () -> Unit) {
|
||||
internal fun relocateUserDefinedModuleSdk(storage: MutableEntityStorage, transfer: () -> Unit): Map<String, String> {
|
||||
val oldToNewName = mutableMapOf<String, String>()
|
||||
|
||||
// Store SDK
|
||||
val pyModules = storage.entities(ModuleEntity::class.java).filter { it.isPythonModule }.toList()
|
||||
// Module might be renamed, so we store its path as a last resort
|
||||
val tomlDirToSdkId = mutableMapOf<VirtualFileUrl, SdkId>()
|
||||
val moduleIdToSdkId = pyModules
|
||||
.mapNotNull { moduleEntity ->
|
||||
val sdkId = moduleEntity.sdkId
|
||||
// Module has no SDK, but might have a facet
|
||||
?: moduleEntity.facets.asSequence()
|
||||
.filter { it.entitySource is PyProjectTomlEntitySource }
|
||||
.mapNotNull { storage.findFacet(it) }
|
||||
.map { it.configuration }
|
||||
.filterIsInstance<PythonFacetSettings>()
|
||||
.mapNotNull { storage.findSdkEntity(it.sdk) }
|
||||
.map { it.symbolicId }
|
||||
.firstOrNull()
|
||||
if (sdkId == null) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
val tomlDir = moduleEntity.pyProjectTomlEntity?.dirWithToml
|
||||
if (tomlDir != null) {
|
||||
tomlDirToSdkId[tomlDir] = sdkId
|
||||
}
|
||||
Pair(moduleEntity.symbolicId, sdkId)
|
||||
}.toMap()
|
||||
val moduleToSdkAndFacets = pyModules.map { moduleEntity ->
|
||||
val facets = moduleEntity.facets
|
||||
val sdkId = moduleEntity.sdkId
|
||||
// Module has no SDK, but might have a facet
|
||||
?: facets.asSequence()
|
||||
.filter { it.entitySource is PyProjectTomlEntitySource }
|
||||
.mapNotNull { storage.findFacet(it) }
|
||||
.map { it.configuration }
|
||||
.filterIsInstance<PythonFacetSettings>()
|
||||
.mapNotNull { storage.findSdkEntity(it.sdk) }
|
||||
.map { it.symbolicId }
|
||||
.firstOrNull()
|
||||
Pair(ModuleAnchor(moduleEntity),
|
||||
Triple(moduleEntity.facets.map { it.createEntityTreeCopy() as FacetEntityBuilder }, sdkId, moduleEntity.name))
|
||||
}
|
||||
|
||||
transfer()
|
||||
|
||||
// Restore SDKs
|
||||
for ((moduleId, sdkId) in moduleIdToSdkId.entries) {
|
||||
val moduleEntity = storage.resolve(moduleId)
|
||||
if (moduleEntity != null) {
|
||||
storage.modifyModuleEntity(moduleEntity) {
|
||||
this.sdkId = sdkId
|
||||
for (newEntity in storage.entities(ModuleEntity::class.java)) {
|
||||
val newEntityAnchor = ModuleAnchor(newEntity)
|
||||
val (facetsToSet, sdkIdToSet, oldName) = moduleToSdkAndFacets.firstOrNull { it.first.sameAs(newEntityAnchor) }?.second ?: continue
|
||||
storage.modifyModuleEntity(newEntity) {
|
||||
if (this.sdkId == null) {
|
||||
this.sdkId = sdkIdToSet
|
||||
}
|
||||
for (facetEntityBuilder in facetsToSet) {
|
||||
facetEntityBuilder.module = this@modifyModuleEntity
|
||||
facetEntityBuilder.moduleId = newEntity.symbolicId
|
||||
this@modifyModuleEntity.facets += facetEntityBuilder
|
||||
}
|
||||
}
|
||||
oldToNewName[oldName] = newEntity.name
|
||||
}
|
||||
val pyModulesNoSdk = storage.entities(ModuleEntity::class.java).filter { it.isPythonModule && it.sdkId == null }
|
||||
for (moduleEntity in pyModulesNoSdk) {
|
||||
val tomlDir = moduleEntity.pyProjectTomlEntity?.dirWithToml ?: continue
|
||||
val sdkId = tomlDirToSdkId[tomlDir] ?: continue
|
||||
storage.modifyModuleEntity(moduleEntity) {
|
||||
this.sdkId = sdkId
|
||||
}
|
||||
}
|
||||
return oldToNewName
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,4 +378,16 @@ private val ModuleEntity.sdkId: SdkId?
|
||||
}
|
||||
}
|
||||
|
||||
private val ModuleEntity.isPythonModule: Boolean get() = entitySource is PyProjectTomlEntitySource && type == PYTHON_MODULE_ID
|
||||
private val ModuleEntity.isPythonModule: Boolean get() = entitySource is PyProjectTomlEntitySource || type == PYTHON_MODULE_ID
|
||||
|
||||
private class ModuleAnchor(moduleEntity: ModuleEntity) {
|
||||
private val symbolicId = moduleEntity.symbolicId
|
||||
private val dirWithToml = moduleEntity.pyProjectTomlEntity?.dirWithToml
|
||||
private val theOnlyContentRoot = moduleEntity.contentRoots.let { if (it.size == 1) it[0] else null }
|
||||
|
||||
fun sameAs(o: ModuleAnchor): Boolean =
|
||||
symbolicId == o.symbolicId ||
|
||||
symbolicId.name.equals(o.symbolicId.name, ignoreCase = true) ||
|
||||
(dirWithToml != null && dirWithToml == o.dirWithToml) ||
|
||||
(theOnlyContentRoot != null && theOnlyContentRoot.url == o.theOnlyContentRoot?.url)
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.intellij.python.pyproject.model.spi
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* Modules can be renamed. To update external things (like run configs), implement this EP.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun interface PyModuleDataTransfer {
|
||||
companion object {
|
||||
internal val EP = ExtensionPointName.create<PyModuleDataTransfer>("com.intellij.python.pyproject.model.moduleDataTransfer")
|
||||
}
|
||||
|
||||
/**
|
||||
* Called *before* all modules in [project] are renamed
|
||||
*/
|
||||
suspend fun beforeRename(project: Project): AfterRename
|
||||
}
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun interface AfterRename {
|
||||
/**
|
||||
* Modules rename, and [oldToNewModuleNames] are new names.
|
||||
*/
|
||||
suspend fun modulesRenamed(oldToNewModuleNames: Map<String, String>)
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.intellij.python.junit5Tests.unit.pyproject
|
||||
|
||||
import com.intellij.ide.impl.OpenProjectTask
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.python.pyproject.model.internal.autoImportBridge.PyProjectAutoImportService
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.projectFixture
|
||||
import kotlinx.coroutines.delay
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@TestApplication
|
||||
internal class PyProjectSyncActivityTest {
|
||||
private val projectFixture =
|
||||
projectFixture(openProjectTask = OpenProjectTask().copy(isProjectCreatedWithWizard = true), openAfterCreation = true)
|
||||
|
||||
|
||||
@Test
|
||||
fun testNoAutoRebuildForWizardBasedProject(): Unit = timeoutRunBlocking {
|
||||
delay(1.seconds)
|
||||
projectFixture.get().service<PyProjectAutoImportService>().start()
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.platform.DirectoryProjectGenerator
|
||||
import com.intellij.platform.ProjectGeneratorPeer
|
||||
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.intellij.python.pyproject.model.internal.startAutoImportIfNeeded
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.Result
|
||||
@@ -70,39 +71,49 @@ abstract class PyV3ProjectBaseGenerator<TYPE_SPECIFIC_SETTINGS : PyV3ProjectType
|
||||
override fun generateProject(project: Project, baseDir: VirtualFile, settings: PyV3BaseProjectSettings, module: Module) {
|
||||
val coroutineScope = project.service<MyService>().coroutineScope
|
||||
coroutineScope.launch {
|
||||
val (sdk, interpreterStatistics) = settings.generateAndGetSdk(module, baseDir, supportsNotEmptyModuleStructure).getOr {
|
||||
withContext(Dispatchers.EDT) {
|
||||
uiServices.errorSink.emit(it.error, project)
|
||||
}
|
||||
return@launch // Since we failed to generate a project, we do not need to go any further
|
||||
}
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
edtWriteAction {
|
||||
VirtualFileManager.getInstance().syncRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
val pythonVersion = withContext(Dispatchers.IO) { sdk.version }
|
||||
logPythonNewProjectGenerated(interpreterStatistics,
|
||||
pythonVersion,
|
||||
this@PyV3ProjectBaseGenerator,
|
||||
emptyList())
|
||||
|
||||
// The project view must be expanded (PY-75909), but it can't be unless it contains some files.
|
||||
// Either base settings (which create venv) might generate some or type-specific settings (like Django) may.
|
||||
// So we expand it right after SDK generation, but if there are no files yet, we do it again after project generation
|
||||
uiServices.expandProjectTreeView(project)
|
||||
withBackgroundProgress(project, PyBundle.message("python.project.model.progress.title.generating"), cancellable = true) {
|
||||
typeSpecificSettings.generateProject(module, baseDir, sdk).onFailure {
|
||||
uiServices.errorSink.emit(it, project)
|
||||
}
|
||||
refreshPaths(project, sdk)
|
||||
}
|
||||
uiServices.expandProjectTreeView(project)
|
||||
generateProjectImpl(settings, module, baseDir)
|
||||
startAutoImportIfNeeded(project)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun generateProjectImpl(
|
||||
settings: PyV3BaseProjectSettings,
|
||||
module: Module,
|
||||
baseDir: VirtualFile,
|
||||
) {
|
||||
val project = module.project
|
||||
val (sdk, interpreterStatistics) = settings.generateAndGetSdk(module, baseDir, supportsNotEmptyModuleStructure).getOr {
|
||||
withContext(Dispatchers.EDT) {
|
||||
uiServices.errorSink.emit(it.error, project)
|
||||
}
|
||||
return // Since we failed to generate a project, we do not need to go any further
|
||||
}
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
edtWriteAction {
|
||||
VirtualFileManager.getInstance().syncRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
val pythonVersion = withContext(Dispatchers.IO) { sdk.version }
|
||||
logPythonNewProjectGenerated(interpreterStatistics,
|
||||
pythonVersion,
|
||||
this@PyV3ProjectBaseGenerator,
|
||||
emptyList())
|
||||
|
||||
// The project view must be expanded (PY-75909), but it can't be unless it contains some files.
|
||||
// Either base settings (which create venv) might generate some or type-specific settings (like Django) may.
|
||||
// So we expand it right after SDK generation, but if there are no files yet, we do it again after project generation
|
||||
uiServices.expandProjectTreeView(project)
|
||||
withBackgroundProgress(project, PyBundle.message("python.project.model.progress.title.generating"), cancellable = true) {
|
||||
typeSpecificSettings.generateProject(module, baseDir, sdk).onFailure {
|
||||
uiServices.errorSink.emit(it, project)
|
||||
}
|
||||
refreshPaths(project, sdk)
|
||||
}
|
||||
uiServices.expandProjectTreeView(project)
|
||||
}
|
||||
|
||||
|
||||
override fun createPeer(): ProjectGeneratorPeer<PyV3BaseProjectSettings> =
|
||||
PyV3GeneratorPeer(baseSettings, typeSpecificUI?.let { Pair(it, typeSpecificSettings) }, uiServices)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.run
|
||||
|
||||
import com.intellij.execution.RunManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.python.pyproject.model.spi.AfterRename
|
||||
import com.intellij.python.pyproject.model.spi.PyModuleDataTransfer
|
||||
|
||||
// Run configurations store module name that can be changed when user changes pyproject.toml, we change them as well
|
||||
internal class PyRunConfigTransfer : PyModuleDataTransfer {
|
||||
override suspend fun beforeRename(project: Project): AfterRename {
|
||||
val manager = RunManager.getInstance(project)
|
||||
val moduleNameToConfig =
|
||||
manager.allConfigurationsList.filterIsInstance<AbstractPythonRunConfiguration<*>>().associateBy { it.moduleName }
|
||||
return { oldToNameName ->
|
||||
for ((oldModuleName, config) in moduleNameToConfig) {
|
||||
val newModuleName = oldToNameName[oldModuleName] ?: continue
|
||||
config.moduleName = newModuleName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user