mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 04:51:24 +07:00
[python] PY-79486: (WIP): Improve UI towards the new design:
1. Show module paths 2. Show icons 3. Make window 65% wide GitOrigin-RevId: 31c1e0e8e7f25bb9d387d9fc6165f7170626d3f0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6326cb84cc
commit
249c8ce892
@@ -2,15 +2,24 @@
|
||||
|
||||
package com.intellij.python.pyproject.model.internal
|
||||
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleEntity
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleEntityBuilder
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleId
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.EntityType
|
||||
import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntityBuilder
|
||||
import com.intellij.platform.workspace.storage.annotations.Parent
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
|
||||
import com.intellij.python.common.tools.ToolId
|
||||
|
||||
@GeneratedCodeApiVersion(3)
|
||||
internal interface PyProjectTomlWorkspaceEntityBuilder : WorkspaceEntityBuilder<PyProjectTomlWorkspaceEntity> {
|
||||
interface PyProjectTomlWorkspaceEntityBuilder : WorkspaceEntityBuilder<PyProjectTomlWorkspaceEntity> {
|
||||
override var entitySource: EntitySource
|
||||
var participatedTools: Map<ToolId, ModuleId?>
|
||||
var dirWithToml: VirtualFileUrl
|
||||
var module: ModuleEntityBuilder
|
||||
}
|
||||
|
||||
@@ -18,30 +27,33 @@ internal object PyProjectTomlWorkspaceEntityType : EntityType<PyProjectTomlWorks
|
||||
override val entityClass: Class<PyProjectTomlWorkspaceEntity> get() = PyProjectTomlWorkspaceEntity::class.java
|
||||
operator fun invoke(
|
||||
participatedTools: Map<ToolId, ModuleId?>,
|
||||
dirWithToml: VirtualFileUrl,
|
||||
entitySource: EntitySource,
|
||||
init: (PyProjectTomlWorkspaceEntityBuilder.() -> Unit)? = null,
|
||||
): PyProjectTomlWorkspaceEntityBuilder {
|
||||
val builder = builder()
|
||||
builder.participatedTools = participatedTools
|
||||
builder.dirWithToml = dirWithToml
|
||||
builder.entitySource = entitySource
|
||||
init?.invoke(builder)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
|
||||
internal fun MutableEntityStorage.modifyPyProjectTomlWorkspaceEntity(
|
||||
fun MutableEntityStorage.modifyPyProjectTomlWorkspaceEntity(
|
||||
entity: PyProjectTomlWorkspaceEntity,
|
||||
modification: PyProjectTomlWorkspaceEntityBuilder.() -> Unit,
|
||||
): PyProjectTomlWorkspaceEntity = modifyEntity(PyProjectTomlWorkspaceEntityBuilder::class.java, entity, modification)
|
||||
|
||||
internal var ModuleEntityBuilder.pyProjectTomlEntity: PyProjectTomlWorkspaceEntityBuilder?
|
||||
var ModuleEntityBuilder.pyProjectTomlEntity: PyProjectTomlWorkspaceEntityBuilder?
|
||||
by WorkspaceEntity.extensionBuilder(PyProjectTomlWorkspaceEntity::class.java)
|
||||
|
||||
|
||||
@JvmOverloads
|
||||
@JvmName("createPyProjectTomlWorkspaceEntity")
|
||||
internal fun PyProjectTomlWorkspaceEntity(
|
||||
fun PyProjectTomlWorkspaceEntity(
|
||||
participatedTools: Map<ToolId, ModuleId?>,
|
||||
dirWithToml: VirtualFileUrl,
|
||||
entitySource: EntitySource,
|
||||
init: (PyProjectTomlWorkspaceEntityBuilder.() -> Unit)? = null,
|
||||
): PyProjectTomlWorkspaceEntityBuilder = PyProjectTomlWorkspaceEntityType(participatedTools, entitySource, init)
|
||||
): PyProjectTomlWorkspaceEntityBuilder = PyProjectTomlWorkspaceEntityType(participatedTools, dirWithToml, entitySource, init)
|
||||
|
||||
@@ -54,6 +54,11 @@ internal object MetadataStorageImpl : MetadataStorageBase() {
|
||||
valueType = primitiveTypeStringNotNullable, withDefault = false)),
|
||||
supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId")))),
|
||||
primitive = primitiveTypeMapNotNullable), withDefault = false),
|
||||
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "dirWithToml",
|
||||
valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false,
|
||||
typeMetadata = FinalClassMetadata.KnownClass(
|
||||
fqName = "com.intellij.platform.workspace.storage.url.VirtualFileUrl")),
|
||||
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",
|
||||
@@ -70,7 +75,7 @@ internal object MetadataStorageImpl : MetadataStorageBase() {
|
||||
}
|
||||
|
||||
override fun initializeMetadataHash() {
|
||||
addMetadataHash(typeFqn = "com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity", metadataHash = 1939139269)
|
||||
addMetadataHash(typeFqn = "com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity", metadataHash = 1371919551)
|
||||
addMetadataHash(typeFqn = "com.intellij.python.common.tools.ToolId", metadataHash = -1193602517)
|
||||
addMetadataHash(typeFqn = "com.intellij.platform.workspace.jps.entities.ModuleId", metadataHash = -575206713)
|
||||
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.EntitySource", metadataHash = -1282078904)
|
||||
|
||||
@@ -3,7 +3,15 @@ package com.intellij.python.pyproject.model.internal.impl
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleEntity
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleEntityBuilder
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleId
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.ConnectionId
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion
|
||||
import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntityBuilder
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi
|
||||
import com.intellij.platform.workspace.storage.annotations.Parent
|
||||
import com.intellij.platform.workspace.storage.impl.EntityLink
|
||||
import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
@@ -14,6 +22,7 @@ import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInst
|
||||
import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi
|
||||
import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation
|
||||
import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
|
||||
import com.intellij.python.common.tools.ToolId
|
||||
import com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntity
|
||||
import com.intellij.python.pyproject.model.internal.PyProjectTomlWorkspaceEntityBuilder
|
||||
@@ -40,6 +49,12 @@ internal class PyProjectTomlWorkspaceEntityImpl(private val dataSource: PyProjec
|
||||
readField("participatedTools")
|
||||
return dataSource.participatedTools
|
||||
}
|
||||
override val dirWithToml: VirtualFileUrl
|
||||
get() {
|
||||
readField("dirWithToml")
|
||||
return dataSource.dirWithToml
|
||||
}
|
||||
|
||||
override val module: ModuleEntity
|
||||
get() = snapshot.extractOneToOneParent(MODULE_CONNECTION_ID, this)!!
|
||||
|
||||
@@ -76,6 +91,7 @@ internal class PyProjectTomlWorkspaceEntityImpl(private val dataSource: PyProjec
|
||||
// Builder may switch to snapshot at any moment and lock entity data to modification
|
||||
this.currentEntityData = null
|
||||
|
||||
index(this, "dirWithToml", this.dirWithToml)
|
||||
// Process linked entities that are connected without a builder
|
||||
processLinkedEntities(builder)
|
||||
checkInitialization() // TODO uncomment and check failed tests
|
||||
@@ -89,6 +105,9 @@ internal class PyProjectTomlWorkspaceEntityImpl(private val dataSource: PyProjec
|
||||
if (!getEntityData().isParticipatedToolsInitialized()) {
|
||||
error("Field PyProjectTomlWorkspaceEntity#participatedTools should be initialized")
|
||||
}
|
||||
if (!getEntityData().isDirWithTomlInitialized()) {
|
||||
error("Field PyProjectTomlWorkspaceEntity#dirWithToml should be initialized")
|
||||
}
|
||||
if (_diff != null) {
|
||||
if (_diff.extractOneToOneParent<WorkspaceEntityBase>(MODULE_CONNECTION_ID, this) == null) {
|
||||
error("Field PyProjectTomlWorkspaceEntity#module should be initialized")
|
||||
@@ -110,6 +129,7 @@ internal class PyProjectTomlWorkspaceEntityImpl(private val dataSource: PyProjec
|
||||
dataSource as PyProjectTomlWorkspaceEntity
|
||||
if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource
|
||||
if (this.participatedTools != dataSource.participatedTools) this.participatedTools = dataSource.participatedTools.toMutableMap()
|
||||
if (this.dirWithToml != dataSource.dirWithToml) this.dirWithToml = dataSource.dirWithToml
|
||||
updateChildToParentReferences(parents)
|
||||
}
|
||||
|
||||
@@ -131,6 +151,16 @@ internal class PyProjectTomlWorkspaceEntityImpl(private val dataSource: PyProjec
|
||||
changedProperty.add("participatedTools")
|
||||
}
|
||||
|
||||
override var dirWithToml: VirtualFileUrl
|
||||
get() = getEntityData().dirWithToml
|
||||
set(value) {
|
||||
checkModificationAllowed()
|
||||
getEntityData(true).dirWithToml = value
|
||||
changedProperty.add("dirWithToml")
|
||||
val _diff = diff
|
||||
if (_diff != null) index(this, "dirWithToml", value)
|
||||
}
|
||||
|
||||
override var module: ModuleEntityBuilder
|
||||
get() {
|
||||
val _diff = diff
|
||||
@@ -174,8 +204,10 @@ internal class PyProjectTomlWorkspaceEntityImpl(private val dataSource: PyProjec
|
||||
@OptIn(WorkspaceEntityInternalApi::class)
|
||||
internal class PyProjectTomlWorkspaceEntityData : WorkspaceEntityData<PyProjectTomlWorkspaceEntity>() {
|
||||
lateinit var participatedTools: Map<ToolId, ModuleId?>
|
||||
lateinit var dirWithToml: VirtualFileUrl
|
||||
|
||||
internal fun isParticipatedToolsInitialized(): Boolean = ::participatedTools.isInitialized
|
||||
internal fun isDirWithTomlInitialized(): Boolean = ::dirWithToml.isInitialized
|
||||
|
||||
override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntityBuilder<PyProjectTomlWorkspaceEntity> {
|
||||
val modifiable = PyProjectTomlWorkspaceEntityImpl.Builder(null)
|
||||
@@ -205,7 +237,7 @@ internal class PyProjectTomlWorkspaceEntityData : WorkspaceEntityData<PyProjectT
|
||||
}
|
||||
|
||||
override fun createDetachedEntity(parents: List<WorkspaceEntityBuilder<*>>): WorkspaceEntityBuilder<*> {
|
||||
return PyProjectTomlWorkspaceEntity(participatedTools, entitySource) {
|
||||
return PyProjectTomlWorkspaceEntity(participatedTools, dirWithToml, entitySource) {
|
||||
parents.filterIsInstance<ModuleEntityBuilder>().singleOrNull()?.let { this.module = it }
|
||||
}
|
||||
}
|
||||
@@ -224,6 +256,7 @@ internal class PyProjectTomlWorkspaceEntityData : WorkspaceEntityData<PyProjectT
|
||||
|
||||
if (this.entitySource != other.entitySource) return false
|
||||
if (this.participatedTools != other.participatedTools) return false
|
||||
if (this.dirWithToml != other.dirWithToml) return false
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -234,18 +267,21 @@ internal class PyProjectTomlWorkspaceEntityData : WorkspaceEntityData<PyProjectT
|
||||
other as PyProjectTomlWorkspaceEntityData
|
||||
|
||||
if (this.participatedTools != other.participatedTools) return false
|
||||
if (this.dirWithToml != other.dirWithToml) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = entitySource.hashCode()
|
||||
result = 31 * result + participatedTools.hashCode()
|
||||
result = 31 * result + dirWithToml.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun hashCodeIgnoringEntitySource(): Int {
|
||||
var result = javaClass.hashCode()
|
||||
result = 31 * result + participatedTools.hashCode()
|
||||
result = 31 * result + dirWithToml.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.intellij.python.pyproject.model.api
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.python.common.tools.ToolId
|
||||
import com.intellij.python.pyproject.model.internal.suggestSdkImpl
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
|
||||
|
||||
sealed interface SuggestedSdk {
|
||||
@@ -12,9 +13,10 @@ sealed interface SuggestedSdk {
|
||||
data class SameAs(val parentModule: Module, val accordingTo: ToolId) : SuggestedSdk
|
||||
|
||||
/**
|
||||
* Standalone module. When possible, use one of [preferTools] to configure it
|
||||
* Standalone module. When possible, use one of [preferTools] to configure it.
|
||||
* Module's toml file is in [moduleDir]
|
||||
*/
|
||||
data class PyProjectIndependent(val preferTools: Set<ToolId>) : SuggestedSdk
|
||||
data class PyProjectIndependent(val preferTools: Set<ToolId>, val moduleDir: Directory) : SuggestedSdk
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.intellij.platform.workspace.jps.entities.ModuleEntity
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleId
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.annotations.Parent
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
|
||||
import com.intellij.python.common.tools.ToolId
|
||||
|
||||
interface PyProjectTomlWorkspaceEntity : WorkspaceEntity {
|
||||
@@ -11,6 +12,8 @@ interface PyProjectTomlWorkspaceEntity : WorkspaceEntity {
|
||||
// [tool, probablyWorkspaceRoot?]. If root is null -> tool didn't implement workspace, just participated in this entry creation
|
||||
val participatedTools: Map<ToolId, ModuleId?>
|
||||
|
||||
val dirWithToml: VirtualFileUrl
|
||||
|
||||
@Parent
|
||||
val module: ModuleEntity
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.intellij.python.pyproject.model.internal
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.platform.backend.workspace.virtualFile
|
||||
import com.intellij.platform.backend.workspace.workspaceModel
|
||||
import com.intellij.python.pyproject.model.api.SuggestedSdk
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.findModule
|
||||
@@ -21,7 +22,11 @@ internal suspend fun suggestSdkImpl(module: Module): SuggestedSdk? = withContext
|
||||
}
|
||||
else {
|
||||
val tools = entity.participatedTools.keys
|
||||
SuggestedSdk.PyProjectIndependent(preferTools = tools)
|
||||
val dirWithToml = entity.dirWithToml
|
||||
val dirWithTomlPath = (dirWithToml.virtualFile
|
||||
?: error("Can't find dir for $dirWithToml . Directory might already be deleted. Try to restart IDE")
|
||||
).toNioPath()
|
||||
SuggestedSdk.PyProjectIndependent(preferTools = tools, moduleDir = dirWithTomlPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager
|
||||
import com.intellij.python.common.tools.ToolId
|
||||
import com.intellij.python.pyproject.PyProjectToml
|
||||
import com.intellij.python.pyproject.model.api.ModelRebuiltListener
|
||||
import com.intellij.python.pyproject.model.spi.*
|
||||
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.venvReader.Directory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -144,7 +147,8 @@ private suspend fun generatePyProjectTomlEntries(files: Map<Path, PyProjectToml>
|
||||
val namesByDir = entries.associate { Pair(it.root, it.name) }
|
||||
val allNames = entriesByName.keys
|
||||
for (tool in Tool.EP.extensionList) {
|
||||
val (dependencies, workspaceMembers) = tool.getProjectStructure(entriesByName, namesByDir) ?: getProjectStructureDefault(entriesByName, namesByDir)
|
||||
val (dependencies, workspaceMembers) = tool.getProjectStructure(entriesByName, namesByDir)
|
||||
?: getProjectStructureDefault(entriesByName, namesByDir)
|
||||
for ((name, deps) in dependencies) {
|
||||
val orphanNames = deps - allNames
|
||||
assert(orphanNames.isEmpty()) { "Tool $tool retuned wrong project names ${orphanNames.joinToString(", ")}" }
|
||||
@@ -206,7 +210,7 @@ private suspend fun createEntityStorage(
|
||||
}
|
||||
}
|
||||
|
||||
pyProjectTomlEntity = PyProjectTomlWorkspaceEntity(participatedTools = participatedTools, entitySource)
|
||||
pyProjectTomlEntity = PyProjectTomlWorkspaceEntity(participatedTools = participatedTools, pyProject.tomlFile.parent.toVirtualFileUrl(fileUrlManager), entitySource)
|
||||
exModuleOptions = ExternalSystemModuleOptionsEntity(entitySource) {
|
||||
externalSystem = PYTHON_SOURCE_ROOT_TYPE.name
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ jvm_library(
|
||||
"//platform/workspace/jps",
|
||||
"//platform/workspace/storage",
|
||||
"@lib//:kotlinx-collections-immutable",
|
||||
"//platform/eel-provider",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.python.sdkConfigurator.backend` end
|
||||
@@ -52,5 +52,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.workspace.jps" />
|
||||
<orderEntry type="module" module-name="intellij.platform.workspace.storage" />
|
||||
<orderEntry type="library" name="kotlinx-collections-immutable" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel.provider" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -4,6 +4,7 @@ import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessModuleDir
|
||||
import com.intellij.openapi.project.modules
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
@@ -18,12 +19,14 @@ import com.intellij.python.sdkConfigurator.backend.impl.ModulesSdkConfigurator.C
|
||||
import com.intellij.python.sdkConfigurator.backend.impl.ModulesSdkConfigurator.Companion.popModulesSDKConfigurator
|
||||
import com.intellij.python.sdkConfigurator.common.impl.ModuleDTO
|
||||
import com.intellij.python.sdkConfigurator.common.impl.ModuleName
|
||||
import com.jetbrains.python.PathShorter
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.sdk.configuration.CreateSdkInfo
|
||||
import com.jetbrains.python.sdk.configuration.CreateSdkInfoWithTool
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension
|
||||
import com.jetbrains.python.sdk.getOrCreateAdditionalData
|
||||
import com.jetbrains.python.sdk.setAssociationToPath
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
@@ -44,6 +47,7 @@ import kotlinx.coroutines.withContext
|
||||
internal class ModulesSdkConfigurator private constructor(
|
||||
private val project: Project,
|
||||
private val modules: Map<ModuleName, ModuleCreateInfo>,
|
||||
private val pathShorter: PathShorter,
|
||||
) {
|
||||
|
||||
val modulesDTO: List<ModuleDTO>
|
||||
@@ -69,22 +73,24 @@ internal class ModulesSdkConfigurator private constructor(
|
||||
is CreateSdkInfo.ExistingEnv -> r.pythonInfo.languageLevel.toPythonVersion()
|
||||
is CreateSdkInfo.WillCreateEnv -> null
|
||||
}
|
||||
ModuleDTO(moduleName, createInfo.toolId.id, version, children[moduleName]!!.toList().sorted().toPersistentList())
|
||||
ModuleDTO(moduleName,
|
||||
path = createInfo.moduleDir?.let { pathShorter.toString(it) },
|
||||
createdByTool = createInfo.toolId.id,
|
||||
existingPyVersion = version,
|
||||
childModules = children[moduleName]!!.toList().sorted().toPersistentList())
|
||||
}
|
||||
is ModuleCreateInfo.SameAs -> null
|
||||
}
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
.toList()
|
||||
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create instance and save in [project]
|
||||
*/
|
||||
suspend fun create(project: Project): ModulesSdkConfigurator = ModulesSdkConfigurator(project, getModulesWithoutSDKCreateInfo(project)).also {
|
||||
suspend fun create(project: Project): ModulesSdkConfigurator = ModulesSdkConfigurator(project, getModulesWithoutSDKCreateInfo(project), PathShorter.create(project)).also {
|
||||
project.putUserData(key, it)
|
||||
}
|
||||
|
||||
@@ -120,7 +126,7 @@ internal class ModulesSdkConfigurator private constructor(
|
||||
private val logger = fileLogger()
|
||||
|
||||
private sealed interface ModuleCreateInfo {
|
||||
data class CreateSdkInfoWrapper(val createSdkInfo: CreateSdkInfo, val toolId: ToolId) : ModuleCreateInfo
|
||||
data class CreateSdkInfoWrapper(val createSdkInfo: CreateSdkInfo, val toolId: ToolId, val moduleDir: Directory?) : ModuleCreateInfo
|
||||
data class SameAs(val parentModuleName: ModuleName) : ModuleCreateInfo
|
||||
}
|
||||
|
||||
@@ -132,7 +138,7 @@ internal class ModulesSdkConfigurator private constructor(
|
||||
tools.firstNotNullOfOrNull { tool ->
|
||||
val createInfo = (tool.asPyProjectTomlSdkConfigurationExtension()?.createSdkWithoutPyProjectTomlChecks(module)
|
||||
?: tool.checkEnvironmentAndPrepareSdkCreator(module)) ?: return@firstNotNullOfOrNull null
|
||||
CreateSdkInfoWithTool(createInfo, tool.toolId).asDTO()
|
||||
CreateSdkInfoWithTool(createInfo, tool.toolId).asDTO(r.moduleDir)
|
||||
}
|
||||
}
|
||||
is SuggestedSdk.SameAs -> {
|
||||
@@ -140,10 +146,10 @@ internal class ModulesSdkConfigurator private constructor(
|
||||
}
|
||||
null -> null
|
||||
} // No tools or not pyproject.toml at all? Use EP as a fallback
|
||||
?: PyProjectSdkConfigurationExtension.findAllSortedForModule(module).firstOrNull()?.let { CreateSdkInfoWithTool(it.createSdkInfo, it.toolId).asDTO() }
|
||||
?: PyProjectSdkConfigurationExtension.findAllSortedForModule(module).firstOrNull()?.let { CreateSdkInfoWithTool(it.createSdkInfo, it.toolId).asDTO(module.guessModuleDir()?.toNioPath()) }
|
||||
|
||||
|
||||
private fun CreateSdkInfoWithTool.asDTO(): ModuleCreateInfo = ModuleCreateInfo.CreateSdkInfoWrapper(createSdkInfo, toolId)
|
||||
private fun CreateSdkInfoWithTool.asDTO(moduleDir: Directory?): ModuleCreateInfo = ModuleCreateInfo.CreateSdkInfoWrapper(createSdkInfo, toolId, moduleDir)
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,14 +6,16 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
typealias ModuleName = @NlsSafe String
|
||||
typealias ToolIdDTO = @NlsSafe String // value classes aren't serializable by default
|
||||
|
||||
typealias ModulePath = @NlsSafe String? // Path can't be sent to the other OS
|
||||
|
||||
/**
|
||||
* Module and how do we create it
|
||||
* Module and how do we create it.
|
||||
* [ModulePath] is a string to be shown to user (might be null if module is not pyproject.toml based)
|
||||
*/
|
||||
@Serializable
|
||||
data class ModuleDTO(
|
||||
val name: ModuleName,
|
||||
val path: ModulePath,
|
||||
val createdByTool: ToolIdDTO,
|
||||
val existingPyVersion: @NlsSafe String?,
|
||||
val childModules: ImmutableList<ModuleName>,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
python.sdk.configurator.frontend.choose.modules.title=Configure Modules Environment
|
||||
python.sdk.configurator.frontend.choose.modules.text=Pick the ones you want to use to run code and set up environment for them
|
||||
python.sdk.configurator.frontend.choose.modules.project.structure=Project Structure:
|
||||
python.sdk.configurator.frontend.choose.modules.environment=Environment
|
||||
python.sdk.configurator.frontend.choose.modules.project.structure=Modules:
|
||||
python.sdk.configurator.frontend.choose.modules.environment=Environments
|
||||
python.sdk.configurator.frontend.choose.modules.workspace.existing=Use existing version {0}
|
||||
python.sdk.configurator.frontend.choose.modules.new=Create new environment
|
||||
python.sdk.configurator.frontend.choose.modules.configure=Configure
|
||||
|
||||
@@ -6,15 +6,17 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.python.sdkConfigurator.common.impl.ModuleDTO
|
||||
import com.intellij.python.sdkConfigurator.common.impl.ModuleName
|
||||
import com.intellij.python.sdkConfigurator.common.impl.ToolIdDTO
|
||||
import com.intellij.python.sdkConfigurator.frontend.ModulesViewModel
|
||||
import com.intellij.python.sdkConfigurator.frontend.PySdkConfiguratorFrontendBundle
|
||||
import com.intellij.python.sdkConfigurator.frontend.PySdkConfiguratorFrontendBundle.message
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.jewel.bridge.icon.fromPlatformIcon
|
||||
import org.jetbrains.jewel.foundation.Stroke
|
||||
import org.jetbrains.jewel.foundation.modifier.border
|
||||
import org.jetbrains.jewel.foundation.theme.JewelTheme
|
||||
@@ -22,30 +24,37 @@ import org.jetbrains.jewel.ui.Orientation
|
||||
import org.jetbrains.jewel.ui.component.*
|
||||
import org.jetbrains.jewel.ui.component.styling.LocalCheckboxStyle
|
||||
import org.jetbrains.jewel.ui.icon.IconKey
|
||||
import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
|
||||
/**
|
||||
* List of modules from [viewModel]
|
||||
* List of modules from [viewModel]. Screen sizes are in physical pixels
|
||||
*/
|
||||
@OptIn(FlowPreview::class)
|
||||
@Composable
|
||||
internal fun ModuleList(
|
||||
screenWidthPx: Int,
|
||||
screenHeightPx: Int,
|
||||
viewModel: ModulesViewModel,
|
||||
// UI labels
|
||||
topLabel: @Nls String,
|
||||
projectStructureLabel: @Nls String,
|
||||
environmentLabel: @Nls String,
|
||||
) {
|
||||
LaunchedEffect(viewModel) {
|
||||
viewModel.processFilterUpdates()
|
||||
}
|
||||
val space = 5.dp
|
||||
VerticallyScrollableContainer {
|
||||
val checkboxArrangement = Arrangement.spacedBy(space)
|
||||
Column(Modifier.width(IntrinsicSize.Max), verticalArrangement = checkboxArrangement) {
|
||||
Text(text = topLabel, Modifier.padding(end = space, bottom = space))
|
||||
val topLabel = remember { message("python.sdk.configurator.frontend.choose.modules.text") }
|
||||
val projectStructureLabel = remember { message("python.sdk.configurator.frontend.choose.modules.project.structure") }
|
||||
val environmentLabel = remember { message("python.sdk.configurator.frontend.choose.modules.environment") }
|
||||
|
||||
Column(Modifier.padding(space).border(Stroke.Alignment.Outside, 1.dp, JewelTheme.globalColors.borders.normal).padding(space).fillMaxWidth(), verticalArrangement = checkboxArrangement) {
|
||||
Row(Modifier.height(IntrinsicSize.Min).padding(start = space, bottom = space, end = space), horizontalArrangement = checkboxArrangement) {
|
||||
val (width, height) = with(LocalDensity.current) {
|
||||
// width: 50% of screen, height: 65% of the screen (according to Lena)
|
||||
Pair(screenWidthPx.toDp() * 0.5f, screenHeightPx.toDp() * 0.65f)
|
||||
}
|
||||
val border = Modifier.border(Stroke.Alignment.Outside, 1.dp, JewelTheme.globalColors.borders.normal)
|
||||
val space = 5.dp
|
||||
VerticallyScrollableContainer(Modifier.padding(space).then(border).size(width = width, height = height)) {
|
||||
val checkboxArrangement = Arrangement.spacedBy(space)
|
||||
Column(Modifier.fillMaxSize(), verticalArrangement = checkboxArrangement) {
|
||||
Text(text = topLabel, Modifier.padding(space))
|
||||
|
||||
Column(Modifier.fillMaxSize(), verticalArrangement = checkboxArrangement) {
|
||||
Row(Modifier.fillMaxSize().then(border).padding(space), horizontalArrangement = checkboxArrangement) {
|
||||
ModuleRow(
|
||||
left = { modifier ->
|
||||
Row(modifier) {
|
||||
@@ -56,7 +65,7 @@ internal fun ModuleList(
|
||||
},
|
||||
right = { modifier ->
|
||||
Text(environmentLabel, modifier = modifier)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
for (module in viewModel.filteredModules) {
|
||||
@@ -81,9 +90,10 @@ private fun Module(
|
||||
checkBoxArrangement: Arrangement.HorizontalOrVertical,
|
||||
) {
|
||||
var subModuleOpened by remember { mutableStateOf(false) }
|
||||
val newText = remember { PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.new") }
|
||||
val newText = remember { message("python.sdk.configurator.frontend.choose.modules.new") }
|
||||
val moduleName = module.name
|
||||
val checkBoxWidth = Modifier.padding(start = LocalCheckboxStyle.current.metrics.checkboxSize.width)
|
||||
val moduleIcon = remember { IntelliJIconKey.fromPlatformIcon(AllIcons.Nodes.Module) }
|
||||
|
||||
Row(verticalAlignment = Alignment.Top, horizontalArrangement = checkBoxArrangement) {
|
||||
ModuleRow(
|
||||
@@ -97,13 +107,19 @@ private fun Module(
|
||||
else Modifier)
|
||||
Column(verticalArrangement = checkBoxArrangement) {
|
||||
CheckboxRow(
|
||||
moduleName,
|
||||
softWrap = false,
|
||||
maxLines = 1,
|
||||
checked = checked,
|
||||
onCheckedChange = {
|
||||
onCheck(moduleName)
|
||||
},
|
||||
content = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||
Icon(moduleIcon, module.path)
|
||||
Text(moduleName, softWrap = false, maxLines = 1)
|
||||
module.path?.let { path ->
|
||||
InfoText(path, softWrap = false, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
if (subModuleOpened) {
|
||||
for (childModule in module.childModules) {
|
||||
@@ -124,8 +140,8 @@ private fun Module(
|
||||
}
|
||||
},
|
||||
right = { modifier ->
|
||||
Row(modifier) {
|
||||
val text = module.existingPyVersion?.let { PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.workspace.existing", it) }
|
||||
Row(modifier, horizontalArrangement = checkBoxArrangement) {
|
||||
val text = module.existingPyVersion?.let { message("python.sdk.configurator.frontend.choose.modules.workspace.existing", it) }
|
||||
?: newText
|
||||
val icon = icons[module.createdByTool]
|
||||
if (icon != null) {
|
||||
@@ -143,4 +159,4 @@ private fun RowScope.ModuleRow(left: @Composable (modifier: Modifier) -> Unit, r
|
||||
left(modifier)
|
||||
Divider(Orientation.Vertical, color = JewelTheme.globalColors.borders.normal, thickness = 1.dp, modifier = Modifier.fillMaxHeight())
|
||||
right(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,13 @@ import kotlin.time.Duration.Companion.milliseconds
|
||||
* While composable is displayed. call [processFilterUpdates]
|
||||
*/
|
||||
internal class ModulesViewModel(modulesDTO: ModulesDTO) {
|
||||
// To be called when "ok" button should be enabled
|
||||
@Volatile
|
||||
var okButtonEnabledListener: ((enabled: Boolean) -> Unit)? = null
|
||||
set(value) {
|
||||
field = value
|
||||
callEnabledButtonListener() // Set value as soon as set
|
||||
}
|
||||
val icons: ImmutableMap<ToolIdDTO, IconKey> = persistentMapOf(*modulesDTO.modules
|
||||
.mapNotNull { module ->
|
||||
val toolId = module.createdByTool
|
||||
@@ -60,6 +67,13 @@ internal class ModulesViewModel(modulesDTO: ModulesDTO) {
|
||||
else {
|
||||
checkedModules.removeAll(toChange)
|
||||
}
|
||||
callEnabledButtonListener()
|
||||
}
|
||||
|
||||
private fun callEnabledButtonListener() {
|
||||
this.okButtonEnabledListener?.let { listener ->
|
||||
listener(checkedModules.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
|
||||
@@ -12,6 +12,7 @@ import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.jewel.bridge.compose
|
||||
import org.jetbrains.jewel.foundation.ExperimentalJewelApi
|
||||
import org.jetbrains.jewel.foundation.enableNewSwingCompositing
|
||||
import java.awt.GraphicsEnvironment
|
||||
import javax.swing.JComponent
|
||||
|
||||
internal suspend fun askUser(project: Project, modules: ModulesDTO, onResult: (Set<ModuleName>) -> Unit) {
|
||||
@@ -31,17 +32,28 @@ private class MyDialog(project: Project, private val viewModel: ModulesViewModel
|
||||
init {
|
||||
title = message("python.sdk.configurator.frontend.choose.modules.title")
|
||||
isResizable = false
|
||||
setOKButtonText(message("python.sdk.configurator.frontend.choose.modules.configure"))
|
||||
init()
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
viewModel.okButtonEnabledListener = null
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalJewelApi::class)
|
||||
override fun createCenterPanel(): JComponent {
|
||||
enableNewSwingCompositing()
|
||||
return compose(focusOnClickInside = true, content = {
|
||||
ModuleList(viewModel,
|
||||
topLabel = message("python.sdk.configurator.frontend.choose.modules.text"),
|
||||
projectStructureLabel = message("python.sdk.configurator.frontend.choose.modules.project.structure"),
|
||||
environmentLabel = message("python.sdk.configurator.frontend.choose.modules.environment"))
|
||||
})
|
||||
viewModel.okButtonEnabledListener = { enabled ->
|
||||
isOKActionEnabled = enabled // To enable/disable "OK" button
|
||||
}
|
||||
val screenSize = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice.displayMode
|
||||
return compose(focusOnClickInside = true) {
|
||||
ModuleList(
|
||||
screenWidthPx = screenSize.width,
|
||||
screenHeightPx = screenSize.height,
|
||||
viewModel = viewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user