mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
PY-50536 [NPW] Added New Python project wizard for IntelliJ
GitOrigin-RevId: ef728d8ec331182f18ee19948d96dd7e3c89291f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c724623599
commit
ca954ebf09
@@ -5,6 +5,7 @@
|
||||
<xi:include href="/META-INF/python-community-plugin-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<newProjectWizard.language implementation="com.jetbrains.python.newProject.PythonNewProjectWizard"/>
|
||||
<moduleType id="PYTHON_MODULE" implementationClass="com.jetbrains.python.module.PythonModuleType"/>
|
||||
<facetType implementation="com.jetbrains.python.facet.PythonFacetType"/>
|
||||
<framework.detector implementation="com.jetbrains.python.facet.PythonFacetType$PythonFrameworkDetector"/>
|
||||
|
||||
@@ -390,6 +390,8 @@ python.sdk.pipenv.pip.file.notification.content=Run <a href='#lock'>pipenv lock<
|
||||
python.sdk.pipenv.pip.file.notification.locking=Locking Pipfile
|
||||
python.sdk.pipenv.pip.file.notification.updating=Updating Pipenv environment
|
||||
python.sdk.pipenv.pip.file.watcher=Pipfile Watcher
|
||||
python.sdk.new.project.environment=Environment:
|
||||
python.sdk.new.project.environment.type=Environment type:
|
||||
|
||||
python.sdk.file.not.found=File {0} is not found
|
||||
python.sdk.cannot.execute=Cannot execute {0}
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.newProject
|
||||
|
||||
import com.intellij.ide.highlighter.ModuleFileType
|
||||
import com.intellij.ide.util.projectWizard.WizardContext
|
||||
import com.intellij.ide.wizard.*
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.observable.properties.GraphProperty
|
||||
import com.intellij.openapi.observable.properties.GraphPropertyImpl.Companion.graphProperty
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.PythonModuleTypeBase
|
||||
import com.jetbrains.python.newProject.steps.ProjectSpecificSettingsStep
|
||||
import com.jetbrains.python.newProject.steps.PyAddExistingSdkPanel
|
||||
import com.jetbrains.python.sdk.PySdkProvider
|
||||
import com.jetbrains.python.sdk.PySdkSettings
|
||||
import com.jetbrains.python.sdk.add.PyAddNewCondaEnvPanel
|
||||
import com.jetbrains.python.sdk.add.PyAddNewVirtualEnvPanel
|
||||
import com.jetbrains.python.sdk.add.PyAddSdkPanel
|
||||
import com.jetbrains.python.sdk.pythonSdk
|
||||
import kotlin.streams.toList
|
||||
|
||||
/**
|
||||
* A wizard for creating new pure-Python projects in IntelliJ.
|
||||
*
|
||||
* It suggests creating a new Python virtual environment for your new project to follow Python best practices.
|
||||
*/
|
||||
class PythonNewProjectWizard : LanguageNewProjectWizard {
|
||||
override val name: String = "Python"
|
||||
override fun createStep(parent: NewProjectWizardLanguageStep): NewProjectWizardStep = NewPythonProjectStep(parent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for sharing among the steps of the new Python project wizard.
|
||||
*/
|
||||
interface NewProjectWizardPythonData : NewProjectWizardBaseData {
|
||||
/**
|
||||
* A property for tracking changes in [pythonSdk].
|
||||
*/
|
||||
val pythonSdkProperty: GraphProperty<Sdk?>
|
||||
|
||||
/**
|
||||
* The Python SDK for the new Python project or module.
|
||||
*
|
||||
* During [NewProjectWizardStep.setupUI] it reflects the selected Python SDK (it may be `null` for a new environment or if there is no
|
||||
* Python installed on the machine). After [PythonSdkStep] gets or creates the actual SDK for the new project in its
|
||||
* [NewProjectWizardStep.setupProject], the attribute contains the actual SDK.
|
||||
*/
|
||||
var pythonSdk: Sdk?
|
||||
|
||||
/**
|
||||
* The Python module after it has been created during [NewProjectWizardStep.setupProject].
|
||||
*/
|
||||
val module: Module?
|
||||
}
|
||||
|
||||
/**
|
||||
* A new Python project wizard step that allows you to either create a new Python environment or select an existing Python interpreter.
|
||||
*
|
||||
* It works for both PyCharm (where the *.iml file resides in .idea/ directory and the SDK is set for the project) and other
|
||||
* IntelliJ-based IDEs (where the *.iml file resides in the module directory and the SDK is set for the module).
|
||||
*/
|
||||
class NewPythonProjectStep<P>(parent: P)
|
||||
: AbstractNewProjectWizardStep(parent),
|
||||
NewProjectWizardBaseData by parent,
|
||||
NewProjectWizardPythonData
|
||||
where P : NewProjectWizardStep, P : NewProjectWizardBaseData {
|
||||
|
||||
override val pythonSdkProperty: GraphProperty<Sdk?> = propertyGraph.graphProperty { null }
|
||||
override var pythonSdk: Sdk? by pythonSdkProperty
|
||||
override val module: Module?
|
||||
get() = intellijModule ?: context.project?.let { ModuleManager.getInstance(it).modules.firstOrNull() }
|
||||
|
||||
private var intellijModule: Module? = null
|
||||
private val sdkStep: PythonSdkStep<NewPythonProjectStep<P>> by lazy { PythonSdkStep(this) }
|
||||
|
||||
override fun setupUI(builder: Panel) {
|
||||
sdkStep.setupUI(builder)
|
||||
}
|
||||
|
||||
override fun setupProject(project: Project) {
|
||||
commitIntellijModule(project)
|
||||
sdkStep.setupProject(project)
|
||||
setupSdk(project)
|
||||
}
|
||||
|
||||
private fun commitIntellijModule(project: Project) {
|
||||
val moduleName = name
|
||||
val moduleBuilder = PythonModuleTypeBase.getInstance().createModuleBuilder().apply {
|
||||
name = moduleName
|
||||
contentEntryPath = projectPath.toString()
|
||||
moduleFilePath = projectPath.resolve(moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION).toString()
|
||||
}
|
||||
intellijModule = moduleBuilder.commit(project)?.firstOrNull()
|
||||
}
|
||||
|
||||
private fun setupSdk(project: Project) {
|
||||
var sdk = pythonSdk ?: return
|
||||
val existingSdk = ProjectJdkTable.getInstance().findJdk(sdk.name)
|
||||
if (existingSdk != null) {
|
||||
pythonSdk = existingSdk
|
||||
sdk = existingSdk
|
||||
}
|
||||
else {
|
||||
SdkConfigurationUtil.addSdk(sdk)
|
||||
}
|
||||
val module = intellijModule
|
||||
if (module != null) {
|
||||
module.pythonSdk = sdk
|
||||
}
|
||||
else {
|
||||
SdkConfigurationUtil.setDirectoryProjectSdk(project, sdk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A new Python project wizard step that allows you to get or create a Python SDK for your [projectPath].
|
||||
*
|
||||
* The resulting SDK is available as [pythonSdk]. The SDK may have not been saved to the project JDK table yet.
|
||||
*/
|
||||
class PythonSdkStep<P>(parent: P)
|
||||
: AbstractNewProjectWizardMultiStepBase(parent),
|
||||
NewProjectWizardPythonData by parent
|
||||
where P : NewProjectWizardStep, P : NewProjectWizardPythonData {
|
||||
|
||||
override val label: String = PyBundle.message("python.sdk.new.project.environment")
|
||||
|
||||
override val steps: Map<String, NewProjectWizardStep> by lazy {
|
||||
val existingSdkPanel = PyAddExistingSdkPanel(null, null, existingSdks(context), projectPath.toString(), null)
|
||||
mapOf(
|
||||
"New" to NewEnvironmentStep(this),
|
||||
// TODO: Handle remote project creation for remote SDKs
|
||||
"Existing" to PythonSdkPanelAdapterStep(this, existingSdkPanel),
|
||||
)
|
||||
}
|
||||
|
||||
override fun setupUI(builder: Panel) {
|
||||
super.setupUI(builder)
|
||||
step = if (PySdkSettings.instance.useNewEnvironmentForNewProject) "New" else "Existing"
|
||||
}
|
||||
|
||||
override fun setupProject(project: Project) {
|
||||
super.setupProject(project)
|
||||
PySdkSettings.instance.useNewEnvironmentForNewProject = step == "New"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A new Python project wizard step that allows you to create a new Python environment of various types.
|
||||
*
|
||||
* The following environment types are supported:
|
||||
*
|
||||
* * Virtualenv
|
||||
* * Conda
|
||||
* * Pipenv
|
||||
*
|
||||
* as well as other environments offered by third-party plugins via [PySdkProvider.createNewEnvironmentPanel].
|
||||
*
|
||||
* The suggested new environment path for some types of Python environments depends on the path of your new project.
|
||||
*/
|
||||
private class NewEnvironmentStep<P>(parent: P)
|
||||
: AbstractNewProjectWizardMultiStepBase(parent),
|
||||
NewProjectWizardPythonData by parent
|
||||
where P : NewProjectWizardStep, P : NewProjectWizardPythonData {
|
||||
|
||||
override val label: String = PyBundle.message("python.sdk.new.project.environment.type")
|
||||
|
||||
override val steps: Map<String, NewProjectWizardStep> by lazy {
|
||||
val sdks = existingSdks(context)
|
||||
val newProjectPath = projectPath.toString()
|
||||
val basePanels = listOf(
|
||||
PyAddNewVirtualEnvPanel(null, null, sdks, newProjectPath, context),
|
||||
PyAddNewCondaEnvPanel(null, null, sdks, newProjectPath),
|
||||
)
|
||||
val providedPanels = PySdkProvider.EP_NAME.extensions()
|
||||
.map { it.createNewEnvironmentPanel(null, null, sdks, newProjectPath, context) }
|
||||
.toList()
|
||||
val panels = basePanels + providedPanels
|
||||
panels
|
||||
.associateBy { it.envName }
|
||||
.mapValues { (_, v) -> PythonSdkPanelAdapterStep(this, v) }
|
||||
}
|
||||
|
||||
override fun setupUI(builder: Panel) {
|
||||
super.setupUI(builder)
|
||||
val preferred = PySdkSettings.instance.preferredEnvironmentType
|
||||
step = if (preferred != null && preferred in steps.keys) preferred else steps.keys.first()
|
||||
}
|
||||
|
||||
override fun setupProject(project: Project) {
|
||||
super.setupProject(project)
|
||||
PySdkSettings.instance.preferredEnvironmentType = step
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A new Python project wizard step that allows you to get or create a Python environment via its [PyAddSdkPanel].
|
||||
*/
|
||||
private class PythonSdkPanelAdapterStep<P>(parent: P, val panel: PyAddSdkPanel)
|
||||
: AbstractNewProjectWizardStep(parent),
|
||||
NewProjectWizardPythonData by parent
|
||||
where P : NewProjectWizardStep, P : NewProjectWizardPythonData {
|
||||
|
||||
val panelChangedProperty = propertyGraph.graphProperty {}
|
||||
var panelChanged by panelChangedProperty
|
||||
|
||||
override fun setupUI(builder: Panel) {
|
||||
with(builder) {
|
||||
row {
|
||||
cell(panel)
|
||||
.graphProperty(panelChangedProperty)
|
||||
.horizontalAlign(HorizontalAlign.FILL)
|
||||
.validationOnInput { panel.validateAll().firstOrNull() }
|
||||
.validationOnApply { panel.validateAll().firstOrNull() }
|
||||
}
|
||||
}
|
||||
panel.addChangeListener {
|
||||
panelChanged = Unit
|
||||
pythonSdk = panel.sdk
|
||||
}
|
||||
nameProperty.afterChange { updateNewProjectPath() }
|
||||
pathProperty.afterChange { updateNewProjectPath() }
|
||||
}
|
||||
|
||||
override fun setupProject(project: Project) {
|
||||
pythonSdk = panel.getOrCreateSdk()
|
||||
}
|
||||
|
||||
private fun updateNewProjectPath() {
|
||||
panel.newProjectPath = projectPath.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of already configured Python SDKs.
|
||||
*/
|
||||
private fun existingSdks(context: WizardContext): List<Sdk> {
|
||||
val sdksModel = ProjectSdksModel().apply {
|
||||
reset(context.project)
|
||||
Disposer.register(context.disposable, Disposable {
|
||||
disposeUIResources()
|
||||
})
|
||||
}
|
||||
return ProjectSpecificSettingsStep.getValidPythonSdks(sdksModel.sdks.toList())
|
||||
}
|
||||
@@ -2,13 +2,16 @@
|
||||
package com.jetbrains.python.sdk.configuration
|
||||
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.UserDataHolderBase
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
@@ -36,7 +39,13 @@ object PyProjectVirtualEnvConfiguration {
|
||||
val task = object : Task.WithResult<String, ExecutionException>(project, PySdkBundle.message("python.creating.venv.title"), false) {
|
||||
override fun compute(indicator: ProgressIndicator): String {
|
||||
indicator.isIndeterminate = true
|
||||
val packageManager = PyPackageManager.getInstance(installedSdk)
|
||||
val sdk = if (installedSdk is Disposable && Disposer.isDisposed(installedSdk)) {
|
||||
ProjectJdkTable.getInstance().findJdk(installedSdk.name)!!
|
||||
}
|
||||
else {
|
||||
installedSdk
|
||||
}
|
||||
val packageManager = PyPackageManager.getInstance(sdk)
|
||||
return packageManager.createVirtualEnv(venvRoot, inheritSitePackages)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user