mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-79486: Introduce sdkConfigurator module.
This module listens for topic that model was just rebuilt from `pyproject.toml` and does its best to configure SDK. It will support UI to make this process configurable GitOrigin-RevId: c752c94385814b5fc4413316c0d9d4cc2adb41e4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ed0c01f602
commit
5a4843d743
Generated
+1
@@ -1145,6 +1145,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-pyproject/intellij.python.pyproject.iml" filepath="$PROJECT_DIR$/python/python-pyproject/intellij.python.pyproject.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-sdk/intellij.python.sdk.iml" filepath="$PROJECT_DIR$/python/python-sdk/intellij.python.sdk.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-sdk-ui/intellij.python.sdk.ui.iml" filepath="$PROJECT_DIR$/python/python-sdk-ui/intellij.python.sdk.ui.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-sdk-configurator/intellij.python.sdkConfigurator.iml" filepath="$PROJECT_DIR$/python/python-sdk-configurator/intellij.python.sdkConfigurator.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-syntax/intellij.python.syntax.iml" filepath="$PROJECT_DIR$/python/python-syntax/intellij.python.syntax.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-syntax-core/intellij.python.syntax.core.iml" filepath="$PROJECT_DIR$/python/python-syntax-core/intellij.python.syntax.core.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/python-terminal/intellij.python.terminal.iml" filepath="$PROJECT_DIR$/python/python-terminal/intellij.python.terminal.iml" />
|
||||
|
||||
@@ -1348,6 +1348,7 @@ python/python-psi-impl
|
||||
python/python-pyproject
|
||||
python/python-restructuredtext
|
||||
python/python-sdk
|
||||
python/python-sdk-configurator
|
||||
python/python-sdk-ui
|
||||
python/python-syntax
|
||||
python/python-syntax-core
|
||||
|
||||
+3
-4
@@ -15,9 +15,11 @@ import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBund
|
||||
import com.intellij.python.pyproject.PyProjectToml
|
||||
import com.intellij.python.pyproject.model.internal.projectModelEnabled
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.poetry.findPoetryLock
|
||||
import com.jetbrains.python.poetry.getPyProjectTomlForPoetry
|
||||
import com.jetbrains.python.projectModel.poetry.POETRY_TOOL_ID
|
||||
import com.jetbrains.python.sdk.PythonSdkType
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
@@ -33,12 +35,9 @@ import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
@ApiStatus.Internal
|
||||
class PyPoetrySdkConfiguration : PyProjectSdkConfigurationExtension {
|
||||
init {
|
||||
if (projectModelEnabled) throw ExtensionNotApplicableException.create()
|
||||
}
|
||||
override val toolId: ToolId = POETRY_TOOL_ID
|
||||
|
||||
companion object {
|
||||
private val LOGGER = Logger.getInstance(PyPoetrySdkConfiguration::class.java)
|
||||
|
||||
+13
-7
@@ -4,17 +4,19 @@ package com.intellij.pycharm.community.ide.impl.configuration
|
||||
import com.intellij.codeInspection.util.IntentionName
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.openapi.extensions.ExtensionNotApplicableException
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.readText
|
||||
import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle
|
||||
import com.intellij.python.pyproject.PyProjectToml
|
||||
import com.intellij.python.pyproject.model.internal.projectModelEnabled
|
||||
import com.intellij.python.pyproject.model.api.SuggestedSdk
|
||||
import com.intellij.python.pyproject.model.api.suggestSdk
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.getOrLogException
|
||||
import com.jetbrains.python.onSuccess
|
||||
import com.jetbrains.python.projectModel.uv.UV_TOOL_ID
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension
|
||||
@@ -33,10 +35,7 @@ private val logger = fileLogger()
|
||||
|
||||
@ApiStatus.Internal
|
||||
class PyUvSdkConfiguration : PyProjectSdkConfigurationExtension {
|
||||
|
||||
init {
|
||||
if (projectModelEnabled) throw ExtensionNotApplicableException.create()
|
||||
}
|
||||
override val toolId: ToolId = UV_TOOL_ID
|
||||
|
||||
override suspend fun getIntention(module: Module): @IntentionName String? {
|
||||
val tomlFile = PyProjectToml.findFile(module) ?: return null
|
||||
@@ -66,7 +65,14 @@ class PyUvSdkConfiguration : PyProjectSdkConfigurationExtension {
|
||||
override fun supportsHeadlessModel(): Boolean = true
|
||||
|
||||
private suspend fun createUv(module: Module): PyResult<Sdk> {
|
||||
val sdkAssociatedModule: Module = module
|
||||
val sdkAssociatedModule =
|
||||
when (val r = module.suggestSdk()) {
|
||||
// Workspace suggested by uv
|
||||
is SuggestedSdk.SameAs -> if (r.accordingTo == toolId) r.parentModule else null
|
||||
null, is SuggestedSdk.PyProjectIndependent -> null
|
||||
} ?: module
|
||||
|
||||
|
||||
val workingDir: Path? = tryResolvePath(sdkAssociatedModule.basePath)
|
||||
if (workingDir == null) {
|
||||
return PyResult.failure(MessageError("Can't determine working dir for the module"))
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
/**
|
||||
* Each tool must have unique id i.e: `uv`
|
||||
*/
|
||||
@JvmInline
|
||||
@ApiStatus.Internal
|
||||
value class ToolId(val id: @NonNls String)
|
||||
@@ -142,6 +142,9 @@
|
||||
files:
|
||||
- name: $MAVEN_REPOSITORY$/io/github/z4kn4fein/semver-jvm/2/semver-jvm-2.jar
|
||||
reason: <- intellij.python.sdk.ui
|
||||
- name: lib/modules/intellij.python.sdkConfigurator.jar
|
||||
contentModules:
|
||||
- name: intellij.python.sdkConfigurator
|
||||
- name: lib/modules/intellij.python.syntax.core.jar
|
||||
contentModules:
|
||||
- name: intellij.python.syntax.core
|
||||
|
||||
@@ -33,6 +33,7 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
|
||||
<module name="intellij.python.sdk" loading="required"/>
|
||||
<module name="intellij.python.sdk.ui" loading="required"/>
|
||||
<module name="intellij.python.pyproject" loading="required"/>
|
||||
<module name="intellij.python.sdkConfigurator" loading="required"/>
|
||||
<module name="intellij.python.community.impl.poetry" loading="required"/>
|
||||
<module name="intellij.python.community.core.impl" loading="required"/>
|
||||
<module name="intellij.python.community.helpersLocator" loading="required"/>
|
||||
|
||||
@@ -36,6 +36,8 @@ jvm_library(
|
||||
"//platform/external-system-impl:externalSystem-impl",
|
||||
"//platform/platform-impl:ide-impl",
|
||||
"//platform/workspace/jps",
|
||||
"//platform/projectModel-impl",
|
||||
"//platform/projectModel-api:projectModel",
|
||||
],
|
||||
exports = ["@lib//:tuweni-toml"]
|
||||
)
|
||||
@@ -67,6 +69,8 @@ jvm_library(
|
||||
"//platform/external-system-impl:externalSystem-impl",
|
||||
"//platform/platform-impl:ide-impl",
|
||||
"//platform/workspace/jps",
|
||||
"//platform/projectModel-impl",
|
||||
"//platform/projectModel-api:projectModel",
|
||||
],
|
||||
exports = ["@lib//:tuweni-toml"]
|
||||
)
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ internal object MetadataStorageImpl: MetadataStorageBase() {
|
||||
addMetadata(typeMetadata)
|
||||
|
||||
typeMetadata = EntityMetadata(fqName = "com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity", entityDataFqName = "com.intellij.python.pyproject.model.internal.impl.PyProjectTomlWorkspaceEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false),
|
||||
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "participatedTools", valueType = ValueTypeMetadata.ParameterizedType(generics = listOf(ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.python.pyproject.model.spi.ToolId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "id", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf())),
|
||||
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "participatedTools", valueType = ValueTypeMetadata.ParameterizedType(generics = listOf(ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.jetbrains.python.ToolId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "id", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf())),
|
||||
ValueTypeMetadata.SimpleType.CustomType(isNullable = true, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.platform.workspace.jps.entities.ModuleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false),
|
||||
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId")))), primitive = primitiveTypeMapNotNullable), withDefault = false),
|
||||
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "module", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_ONE, entityFqName = "com.intellij.platform.workspace.jps.entities.ModuleEntity", isChild = false, isNullable = false), withDefault = false)), extProperties = listOf(ExtPropertyMetadata(isComputable = false, isOpen = false, name = "pyProjectTomlEntity", receiverFqn = "com.intellij.platform.workspace.jps.entities.ModuleEntity", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_ONE, entityFqName = "com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity", isChild = true, isNullable = false), withDefault = false)), isAbstract = false)
|
||||
@@ -32,8 +32,8 @@ OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name =
|
||||
}
|
||||
|
||||
override fun initializeMetadataHash() {
|
||||
addMetadataHash(typeFqn = "com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity", metadataHash = 775488913)
|
||||
addMetadataHash(typeFqn = "com.intellij.python.pyproject.model.spi.ToolId", metadataHash = 2027768588)
|
||||
addMetadataHash(typeFqn = "com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity", metadataHash = 120308767)
|
||||
addMetadataHash(typeFqn = "com.jetbrains.python.ToolId", metadataHash = 434494956)
|
||||
addMetadataHash(typeFqn = "com.intellij.platform.workspace.jps.entities.ModuleId", metadataHash = -575206713)
|
||||
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.EntitySource", metadataHash = -1282078904)
|
||||
addMetadataHash(typeFqn = "com.intellij.python.pyproject.model.internal.PyProjectTomlEntitySource", metadataHash = -1054650782)
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ import com.intellij.platform.workspace.storage.impl.EntityLink
|
||||
import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData
|
||||
import com.intellij.platform.workspace.storage.impl.containers.toMutableWorkspaceSet
|
||||
import com.intellij.platform.workspace.storage.impl.extractOneToOneParent
|
||||
import com.intellij.platform.workspace.storage.impl.updateOneToOneParentOfChild
|
||||
import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation
|
||||
@@ -16,7 +15,7 @@ import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInst
|
||||
import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation
|
||||
import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata
|
||||
import com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity
|
||||
import com.intellij.python.pyproject.model.spi.ToolId
|
||||
import com.jetbrains.python.ToolId
|
||||
|
||||
@GeneratedCodeApiVersion(3)
|
||||
@GeneratedCodeImplVersion(7)
|
||||
|
||||
@@ -29,5 +29,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.externalSystem.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.workspace.jps" />
|
||||
<orderEntry type="module" module-name="intellij.platform.projectModel.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.projectModel" />
|
||||
</component>
|
||||
</module>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.intellij.python.pyproject.model.api
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
fun interface ModelRebuiltListener {
|
||||
/**
|
||||
* Topic called right after project was rebuilt from pyproject.toml
|
||||
*/
|
||||
fun modelRebuilt(project: Project)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* API to get info about modules built from pyproject.toml
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.python.pyproject.model.api;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.intellij.python.pyproject.model.api
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.python.pyproject.model.internal.suggestSdkImpl
|
||||
import com.jetbrains.python.ToolId
|
||||
|
||||
|
||||
sealed interface SuggestedSdk {
|
||||
/**
|
||||
* Part of workspace with [parentModule] as a root
|
||||
*/
|
||||
data class SameAs(val parentModule: Module, val accordingTo: ToolId) : SuggestedSdk
|
||||
|
||||
/**
|
||||
* Standalone module. When possible, use one of [preferTools] to configure it
|
||||
*/
|
||||
data class PyProjectIndependent(val preferTools: Set<ToolId>) : SuggestedSdk
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Suggests how to configure SDK for a certain module.
|
||||
* `null` means this module is not `pyproject.toml` based
|
||||
*/
|
||||
suspend fun Module.suggestSdk(): SuggestedSdk? = suggestSdkImpl(this)
|
||||
+1
-1
@@ -4,7 +4,7 @@ import com.intellij.platform.workspace.jps.entities.ModuleEntity
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleId
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.annotations.Parent
|
||||
import com.intellij.python.pyproject.model.spi.ToolId
|
||||
import com.jetbrains.python.ToolId
|
||||
|
||||
internal interface PyProjectTomlWorkspaceEntity : WorkspaceEntity {
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.intellij.python.pyproject.model.internal
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.platform.backend.workspace.workspaceModel
|
||||
import com.intellij.python.pyproject.model.api.SuggestedSdk
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.findModule
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.findModuleEntity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal suspend fun suggestSdkImpl(module: Module): SuggestedSdk? = withContext(Dispatchers.Default) {
|
||||
val entity = module.findModuleEntity()?.pyProjectTomlEntity ?: return@withContext null
|
||||
val storage = module.project.workspaceModel.currentSnapshot
|
||||
val toolWithWorkspace = entity.participatedTools.firstNotNullOfOrNull { (tool, moduleId) ->
|
||||
val module = moduleId?.resolve(storage)?.findModule(storage)
|
||||
if (module != null) Pair(tool, module) else null
|
||||
}
|
||||
if (toolWithWorkspace != null) {
|
||||
val (tool, module) = toolWithWorkspace
|
||||
SuggestedSdk.SameAs(module, tool)
|
||||
}
|
||||
else {
|
||||
val tools = entity.participatedTools.keys
|
||||
SuggestedSdk.PyProjectIndependent(preferTools = tools)
|
||||
}
|
||||
}
|
||||
|
||||
+80
-28
@@ -4,6 +4,10 @@ import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectId
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTracker
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemRefreshStatus
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.modules
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.util.removeUserData
|
||||
@@ -16,11 +20,20 @@ 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
|
||||
import com.intellij.python.pyproject.PyProjectToml
|
||||
import com.intellij.python.pyproject.model.spi.*
|
||||
import com.intellij.python.pyproject.model.api.ModelRebuiltListener
|
||||
import com.intellij.python.pyproject.model.spi.ProjectName
|
||||
import com.intellij.python.pyproject.model.spi.PyProjectTomlProject
|
||||
import com.intellij.python.pyproject.model.spi.Tool
|
||||
import com.intellij.python.pyproject.model.spi.WorkspaceName
|
||||
import com.intellij.util.messages.Topic
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
@@ -31,6 +44,12 @@ internal fun isProjectLinked(project: Project): Boolean =
|
||||
project.workspaceModel.currentSnapshot.entitiesBySource(sourceFilter = { it is PyProjectTomlEntitySource }).any()
|
||||
|
||||
internal suspend fun unlinkProject(project: Project, externalProjectPath: String) {
|
||||
changeWorkspaceMutex.withLock {
|
||||
unlinkProjectImpl(project, externalProjectPath)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unlinkProjectImpl(project: Project, externalProjectPath: String) {
|
||||
val tracker = ExternalSystemProjectTracker.getInstance(project)
|
||||
project.getUserData(PY_PROJECT_TOML_KEY)?.let { oldProjectId ->
|
||||
tracker.remove(oldProjectId)
|
||||
@@ -45,31 +64,53 @@ internal suspend fun unlinkProject(project: Project, externalProjectPath: String
|
||||
}
|
||||
|
||||
internal suspend fun linkProject(project: Project, projectModelRoot: Path) {
|
||||
val externalProjectPath = projectModelRoot.pathString
|
||||
val (files, excludeDirs) = walkFileSystem(projectModelRoot)
|
||||
val entries = generatePyProjectTomlEntries(files, excludeDirs)
|
||||
unlinkProject(project, externalProjectPath)
|
||||
changeWorkspaceMutex.withLock {
|
||||
val externalProjectPath = projectModelRoot.pathString
|
||||
val (files, excludeDirs) = walkFileSystem(projectModelRoot)
|
||||
val entries = generatePyProjectTomlEntries(files, excludeDirs)
|
||||
if (entries.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val sdks = project.modules.associate { Pair(it.name, ModuleRootManager.getInstance(it).sdk?.name) }
|
||||
project.workspaceModel.currentSnapshot.entities(ModuleEntity::class.java)
|
||||
unlinkProjectImpl(project, externalProjectPath)
|
||||
|
||||
val tracker = ExternalSystemProjectTracker.getInstance(project)
|
||||
val projectAware = PyExternalSystemProjectAware(ExternalSystemProjectId(SYSTEM_ID, externalProjectPath), files.map { it.key.pathString }.toSet(), project, projectModelRoot)
|
||||
tracker.register(projectAware)
|
||||
tracker.activate(projectAware.projectId)
|
||||
project.putUserData(PY_PROJECT_TOML_KEY, projectAware.projectId)
|
||||
val storage = createEntityStorage(project, entries, project.workspaceModel.getVirtualFileUrlManager())
|
||||
val listener = project.messageBus.syncPublisher(PROJECT_AWARE_TOPIC)
|
||||
listener.onProjectReloadStart()
|
||||
try {
|
||||
project.workspaceModel.update(PyProjectTomlBundle.message("action.PyProjectTomlSyncAction.description")) { mutableStorage -> // Fake module entity is added by default if nothing was discovered
|
||||
removeFakeModuleEntity(mutableStorage)
|
||||
mutableStorage.replaceBySource({ it is PyProjectTomlEntitySource }, storage)
|
||||
|
||||
val tracker = ExternalSystemProjectTracker.getInstance(project)
|
||||
val projectAware = PyExternalSystemProjectAware(ExternalSystemProjectId(SYSTEM_ID, externalProjectPath), files.map { it.key.pathString }.toSet(), project, projectModelRoot)
|
||||
tracker.register(projectAware)
|
||||
tracker.activate(projectAware.projectId)
|
||||
project.putUserData(PY_PROJECT_TOML_KEY, projectAware.projectId)
|
||||
val storage = createEntityStorage(project, entries, project.workspaceModel.getVirtualFileUrlManager())
|
||||
val listener = project.messageBus.syncPublisher(PROJECT_AWARE_TOPIC)
|
||||
listener.onProjectReloadStart()
|
||||
try {
|
||||
project.workspaceModel.update(PyProjectTomlBundle.message("action.PyProjectTomlSyncAction.description")) { mutableStorage -> // Fake module entity is added by default if nothing was discovered
|
||||
removeFakeModuleEntity(mutableStorage)
|
||||
mutableStorage.replaceBySource({ it is PyProjectTomlEntitySource }, storage)
|
||||
}
|
||||
|
||||
// Restore SDK assoc
|
||||
for (module in project.modules) {
|
||||
if (ModuleRootManager.getInstance(module).sdk == null) {
|
||||
val sdkName = sdks[module.name] ?: continue
|
||||
ProjectJdkTable.getInstance().findJdk(sdkName)?.let { sdk ->
|
||||
ModuleRootModificationUtil.setModuleSdk(module, sdk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
listener.onProjectReloadFinish(ExternalSystemRefreshStatus.FAILURE)
|
||||
throw e
|
||||
}
|
||||
listener.onProjectReloadFinish(ExternalSystemRefreshStatus.SUCCESS)
|
||||
project.messageBus.syncPublisher(PROJECT_LINKER_AWARE_TOPIC).onProjectLinked(externalProjectPath)
|
||||
|
||||
withContext(Dispatchers.Default) {
|
||||
project.messageBus.syncPublisher(MODEL_REBUILD).modelRebuilt(project)
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
listener.onProjectReloadFinish(ExternalSystemRefreshStatus.FAILURE)
|
||||
throw e
|
||||
}
|
||||
listener.onProjectReloadFinish(ExternalSystemRefreshStatus.SUCCESS)
|
||||
project.messageBus.syncPublisher(PROJECT_LINKER_AWARE_TOPIC).onProjectLinked(externalProjectPath)
|
||||
}
|
||||
|
||||
private suspend fun generatePyProjectTomlEntries(files: Map<Path, PyProjectToml>, allExcludeDirs: Set<Directory>): Set<PyProjectTomlBasedEntryImpl> = withContext(Dispatchers.Default) {
|
||||
@@ -88,7 +129,7 @@ private suspend fun generatePyProjectTomlEntries(files: Map<Path, PyProjectToml>
|
||||
}
|
||||
val projectName = ProjectName(projectNameAsString ?: "${root.name}@${tomlFile.hashCode()}")
|
||||
val sourceRootsAndTools = Tool.EP.extensionList.flatMap { tool -> tool.getSrcRoots(toml.toml, root).map { Pair(tool, it) } }.toSet()
|
||||
val sourceRoots = sourceRootsAndTools.map { it.second }.toSet()
|
||||
val sourceRoots = sourceRootsAndTools.map { it.second }.toSet() + findSrc(root)
|
||||
participatedTools.addAll(sourceRootsAndTools.map { it.first.id })
|
||||
val excludedDirs = allExcludeDirs.filter { it.startsWith(root) }
|
||||
val relationsWithTools: List<PyProjectTomlToolRelation> = participatedTools.map { PyProjectTomlToolRelation.SimpleRelation(it) }
|
||||
@@ -108,9 +149,9 @@ private suspend fun generatePyProjectTomlEntries(files: Map<Path, PyProjectToml>
|
||||
if (deps.isNotEmpty()) {
|
||||
entity.relationsWithTools.add(PyProjectTomlToolRelation.SimpleRelation(tool.id))
|
||||
}
|
||||
workspaceMembers[entity.name]?.let { workspace ->
|
||||
entity.relationsWithTools.add(PyProjectTomlToolRelation.WorkspaceMember(tool.id, workspace))
|
||||
}
|
||||
}
|
||||
for ((member, workspace) in workspaceMembers) {
|
||||
entriesByName[member]!!.relationsWithTools.add(PyProjectTomlToolRelation.WorkspaceMember(tool.id, workspace))
|
||||
}
|
||||
}
|
||||
return@withContext entries.toSet()
|
||||
@@ -223,4 +264,15 @@ private sealed interface PyProjectTomlToolRelation {
|
||||
* This module is a part of workspace, and the root is [workspace]
|
||||
*/
|
||||
data class WorkspaceMember(override val toolId: ToolId, val workspace: WorkspaceName) : PyProjectTomlToolRelation
|
||||
}
|
||||
}
|
||||
|
||||
@Topic.ProjectLevel
|
||||
private val MODEL_REBUILD: Topic<ModelRebuiltListener> = Topic(ModelRebuiltListener::class.java, Topic.BroadcastDirection.NONE)
|
||||
|
||||
private val changeWorkspaceMutex = Mutex()
|
||||
|
||||
private suspend fun findSrc(root: Directory): Set<Directory> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val src = root.resolve("src")
|
||||
if (src.exists()) setOf(src) else emptySet()
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.intellij.python.pyproject.model.spi
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
|
||||
@@ -11,6 +13,7 @@ interface Tool {
|
||||
}
|
||||
|
||||
val id: ToolId
|
||||
val ui: PyToolUIInfo?
|
||||
|
||||
/**
|
||||
* Tools that support old (tool-specific) naming should return it here
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.intellij.python.pyproject.model.spi
|
||||
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
/**
|
||||
* Each tool must have unique id i.e: `uv`
|
||||
*/
|
||||
@JvmInline
|
||||
value class ToolId(val id: @NonNls String)
|
||||
@@ -0,0 +1,28 @@
|
||||
### auto-generated section `build intellij.python.sdkConfigurator` start
|
||||
load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
|
||||
|
||||
resourcegroup(
|
||||
name = "sdkConfigurator_resources",
|
||||
srcs = glob(["resources/**/*"]),
|
||||
strip_prefix = "resources"
|
||||
)
|
||||
|
||||
jvm_library(
|
||||
name = "sdkConfigurator",
|
||||
module_name = "intellij.python.sdkConfigurator",
|
||||
visibility = ["//visibility:public"],
|
||||
srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True),
|
||||
resources = [":sdkConfigurator_resources"],
|
||||
deps = [
|
||||
"@lib//:kotlin-stdlib",
|
||||
"//python/python-pyproject:pyproject",
|
||||
"//platform/core-api:core",
|
||||
"@lib//:jetbrains-annotations",
|
||||
"//platform/platform-api:ide",
|
||||
"//python/python-sdk:sdk",
|
||||
"//python/openapi:community",
|
||||
"//platform/projectModel-api:projectModel",
|
||||
"//platform/util/progress",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.python.sdkConfigurator` end
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.python.sdkConfigurator" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.python.pyproject" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
<orderEntry type="library" name="jetbrains-annotations" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide" />
|
||||
<orderEntry type="module" module-name="intellij.python.sdk" />
|
||||
<orderEntry type="module" module-name="intellij.python.community" />
|
||||
<orderEntry type="module" module-name="intellij.platform.projectModel" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.progress" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,11 @@
|
||||
<idea-plugin>
|
||||
<dependencies>
|
||||
<module name="intellij.python.pyproject"/>
|
||||
<module name="intellij.python.sdk"/>
|
||||
<module name="intellij.python.community"/>
|
||||
</dependencies>
|
||||
<projectListeners>
|
||||
<listener class="com.intellij.python.sdkConfigurator.platformBridge.ModelRebuiltListenerImpl"
|
||||
topic="com.intellij.python.pyproject.model.api.ModelRebuiltListener"/>
|
||||
</projectListeners>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1 @@
|
||||
intellij.python.sdk.configuring.module=Configuring SDK for module {0}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.python.sdkConfigurator;
|
||||
|
||||
import com.intellij.DynamicBundle;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
final class PySdkConfiguratorBundle extends DynamicBundle {
|
||||
public static final @NonNls String BUNDLE = "messages.PySdkConfiguratorBundle";
|
||||
public static final PySdkConfiguratorBundle INSTANCE = new PySdkConfiguratorBundle();
|
||||
|
||||
private PySdkConfiguratorBundle() { super(BUNDLE); }
|
||||
|
||||
public static @NotNull @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
|
||||
return INSTANCE.getMessage(key, params);
|
||||
}
|
||||
|
||||
public static @NotNull Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
|
||||
return INSTANCE.getLazyMessage(key, params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.intellij.python.sdkConfigurator
|
||||
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.modules
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.intellij.python.pyproject.model.api.SuggestedSdk
|
||||
import com.intellij.python.pyproject.model.api.suggestSdk
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension
|
||||
import com.jetbrains.python.sdk.getOrCreateAdditionalData
|
||||
import com.jetbrains.python.sdk.setAssociationToPath
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// TODO: Add UI to ask user to choose from
|
||||
|
||||
/**
|
||||
* Configures SDK for modules without SDK in automatic manner trying to fix as many modules as possible.
|
||||
* Errors are logged.
|
||||
*/
|
||||
internal suspend fun configureSdkAutomatically(project: Project) {
|
||||
withContext(Dispatchers.Default) {
|
||||
val modules = project.modules.filter { ModuleRootManager.getInstance(it).sdk == null }
|
||||
if (modules.isEmpty()) {
|
||||
// All modules have SDK
|
||||
return@withContext
|
||||
}
|
||||
val configurators = PyProjectSdkConfigurationExtension.EP_NAME.extensionList
|
||||
val configuratorsByTool = configurators
|
||||
.mapNotNull { extension -> extension.toolId?.let { Pair(it, extension) } }
|
||||
.toMap()
|
||||
|
||||
assert(configurators.isNotEmpty()) { "PyCharm can't work without any SDK configurator" }
|
||||
|
||||
val tomlBasedConfigurators = configurators.filter { it.toolId != null }
|
||||
val legacyConfigurators = configurators.filter { it.toolId == null }
|
||||
val allSortedConfigurators = tomlBasedConfigurators + legacyConfigurators
|
||||
|
||||
val modulesWithSameSdk = mutableMapOf<Module, Module>()
|
||||
for (module in modules) {
|
||||
// TODO: Run in parallel
|
||||
withBackgroundProgress(project, PySdkConfiguratorBundle.message("intellij.python.sdk.configuring.module", module.name)) {
|
||||
when (val r = module.suggestSdk()) {
|
||||
null -> {
|
||||
// Not a pyproject.toml: try all configurators
|
||||
configureSdkForModule(module, allSortedConfigurators, checkForIntention = true)
|
||||
}
|
||||
is SuggestedSdk.PyProjectIndependent -> {
|
||||
val preferredConfigurators = r.preferTools.mapNotNull { configuratorsByTool[it] }
|
||||
if (!configureSdkForModule(module, preferredConfigurators, checkForIntention = false)) {
|
||||
// For pyproject.toml based -- use pyproject.toml only configs
|
||||
configureSdkForModule(module, tomlBasedConfigurators - preferredConfigurators.toSet(), checkForIntention = true)
|
||||
}
|
||||
}
|
||||
is SuggestedSdk.SameAs -> {
|
||||
modulesWithSameSdk[module] = r.parentModule
|
||||
}
|
||||
}
|
||||
// Link workspace members with their workspace
|
||||
val reportedBrokenModules = mutableSetOf<Module>()
|
||||
for ((module, parentModule) in modulesWithSameSdk) {
|
||||
val parentSdk = ModuleRootManager.getInstance(parentModule).sdk
|
||||
if (parentSdk != null) {
|
||||
ModuleRootModificationUtil.setModuleSdk(module, parentSdk)
|
||||
// This SDK is shared, no need to associate it
|
||||
// TODO: Support association with multiple modules
|
||||
if (parentSdk.getOrCreateAdditionalData().associatedModulePath != null) {
|
||||
parentSdk.setAssociationToPath(null)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (parentModule != reportedBrokenModules) {
|
||||
logger.warn("No sdk for workspace root ${parentModule}, all children will have no SDKs")
|
||||
}
|
||||
reportedBrokenModules.add(parentModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun configureSdkForModule(module: Module, configurators: List<PyProjectSdkConfigurationExtension>, checkForIntention: Boolean): Boolean {
|
||||
for (extension in configurators) {
|
||||
if (checkForIntention && extension.getIntention(module) == null) {
|
||||
logger.info("${extension.javaClass} skipped for ${module.name}")
|
||||
continue
|
||||
}
|
||||
val created = when (val r = extension.createAndAddSdkForInspection(module)) {
|
||||
is Result.Failure -> {
|
||||
logger.warn("can't create SDK for ${module.name}: ${r.error.message}")
|
||||
false
|
||||
}
|
||||
is Result.Success -> r.result?.also { sdk ->
|
||||
ModuleRootModificationUtil.setModuleSdk(module, sdk)
|
||||
} != null
|
||||
}
|
||||
if (created) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private val logger = fileLogger()
|
||||
@@ -0,0 +1,4 @@
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.python.sdkConfigurator;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.intellij.python.sdkConfigurator.platformBridge
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.Service.Level
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.python.pyproject.model.api.ModelRebuiltListener
|
||||
import com.intellij.python.sdkConfigurator.configureSdkAutomatically
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
internal class ModelRebuiltListenerImpl : ModelRebuiltListener {
|
||||
private val mutex = Mutex()
|
||||
override fun modelRebuilt(project: Project) {
|
||||
project.service<MyService>().scope.launch(Dispatchers.Default) {
|
||||
mutex.withLock {
|
||||
configureSdkAutomatically(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Level.PROJECT)
|
||||
private class MyService(val scope: CoroutineScope)
|
||||
@@ -0,0 +1,4 @@
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.python.sdkConfigurator.platformBridge;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
+8
@@ -7,8 +7,10 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.progress.runBlockingMaybeCancellable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
|
||||
/**
|
||||
* Used on directory opening with an attempt to configure suitable Python interpreter
|
||||
@@ -19,6 +21,10 @@ import org.jetbrains.annotations.ApiStatus
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
interface PyProjectSdkConfigurationExtension {
|
||||
/**
|
||||
* Every tool (poetry, uv) has id
|
||||
*/
|
||||
val toolId: ToolId? get() = null
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@@ -37,6 +43,7 @@ interface PyProjectSdkConfigurationExtension {
|
||||
*
|
||||
* Rule of thumb is to explicitly ask a user if sdk creation is desired and allowed.
|
||||
*/
|
||||
@CheckReturnValue
|
||||
suspend fun createAndAddSdkForConfigurator(module: Module): PyResult<Sdk?>
|
||||
|
||||
/**
|
||||
@@ -45,6 +52,7 @@ interface PyProjectSdkConfigurationExtension {
|
||||
*
|
||||
* You're free here to create sdk immediately, without any user permission since quick fix is explicitly clicked.
|
||||
*/
|
||||
@CheckReturnValue
|
||||
suspend fun createAndAddSdkForInspection(module: Module): PyResult<Sdk?>
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -8,8 +8,8 @@ import com.intellij.platform.eel.provider.localEel
|
||||
import com.intellij.python.community.impl.venv.createVenv
|
||||
import com.intellij.python.community.services.shared.LanguageLevelWithUiComparator
|
||||
import com.intellij.python.community.services.shared.PythonWithUi
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.intellij.python.community.services.shared.VanillaPythonWithLanguageLevel
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
|
||||
@@ -5,20 +5,27 @@ import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.python.pyproject.PyProjectToml
|
||||
import com.intellij.python.pyproject.model.spi.*
|
||||
import com.intellij.python.sdk.ui.icons.PythonSdkUIIcons
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.toPath
|
||||
|
||||
@ApiStatus.Internal
|
||||
val POETRY_TOOL_ID: ToolId = ToolId("poetry")
|
||||
internal class PoetryTool : Tool {
|
||||
|
||||
override val id: ToolId = ToolId("poetry")
|
||||
override val id: ToolId = POETRY_TOOL_ID
|
||||
override val ui: PyToolUIInfo = PyToolUIInfo("Poetry", PythonSdkUIIcons.Tools.Poetry)
|
||||
|
||||
override suspend fun getSrcRoots(toml: TomlTable, projectRoot: Directory): Set<Directory> = emptySet()
|
||||
|
||||
|
||||
@@ -7,30 +7,31 @@ import com.intellij.openapi.util.getPathMatcher
|
||||
import com.intellij.python.pyproject.PY_PROJECT_TOML_BUILD_SYSTEM
|
||||
import com.intellij.python.pyproject.model.spi.*
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.ToolId
|
||||
import com.jetbrains.python.sdk.uv.UV_UI_INFO
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.tuweni.toml.TomlArray
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.PathMatcher
|
||||
import kotlin.io.path.relativeTo
|
||||
|
||||
@ApiStatus.Internal
|
||||
val UV_TOOL_ID: ToolId = ToolId("uv")
|
||||
internal class UvTool : Tool {
|
||||
|
||||
override val id: ToolId = ToolId("uv")
|
||||
override val id: ToolId = UV_TOOL_ID
|
||||
|
||||
override val ui: PyToolUIInfo = UV_UI_INFO
|
||||
|
||||
override suspend fun getProjectName(projectToml: TomlTable): @NlsSafe String? = null
|
||||
|
||||
override suspend fun getSrcRoots(toml: TomlTable, projectRoot: Directory): Set<Directory> = withContext(Dispatchers.Default) {
|
||||
if (toml.getString("${PY_PROJECT_TOML_BUILD_SYSTEM}.build-backend") == "uv_build") {
|
||||
setOf(projectRoot.resolve("src"))
|
||||
}
|
||||
else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
override suspend fun getSrcRoots(toml: TomlTable, projectRoot: Directory): Set<Directory> = emptySet()
|
||||
|
||||
override suspend fun getProjectStructure(entries: Map<ProjectName, PyProjectTomlProject>, rootIndex: Map<Directory, ProjectName>): ProjectStructureInfo = withContext(Dispatchers.Default) {
|
||||
val workspaces = entries.mapNotNull { (name, entry) ->
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.jetbrains.python.sdk.uv
|
||||
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.eel.provider.localEel
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.intellij.python.community.services.systemPython.SystemPythonProvider
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
@@ -24,5 +24,7 @@ internal class UvSystemPythonProvider : SystemPythonProvider {
|
||||
return uv.listUvPythons()
|
||||
}
|
||||
|
||||
override val uiCustomization: PyToolUIInfo = PyToolUIInfo("uv", PythonIcons.UV)
|
||||
}
|
||||
override val uiCustomization: PyToolUIInfo = UV_UI_INFO
|
||||
}
|
||||
|
||||
internal val UV_UI_INFO = PyToolUIInfo("uv", PythonIcons.UV)
|
||||
|
||||
Reference in New Issue
Block a user