mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[python] PY-87445 Add pyproject.toml-based project model settings and configuration notification
Add PyProjectModelSettings with persistent state for pyproject.toml workspace configuration. Show a notification when pyproject.toml is detected, allowing users to enable the feature or dismiss it. Convert PythonContentEntriesConfigurable to Kotlin and guard the checkbox with the registry flag. # Conflicts: # community/python/python-pyproject/src/com/intellij/python/pyproject/model/internal/AutoImportstarter.kt GitOrigin-RevId: 4f07afe0ad1179f99c8a05f596379e83c7b299fc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
853d7e440d
commit
830d38cead
@@ -60,7 +60,8 @@
|
||||
bundle="messages.PyBundle"
|
||||
key="configurable.PythonContentEntriesConfigurable.display.name"
|
||||
provider="com.intellij.pycharm.community.ide.impl.configuration.PythonContentEntriesConfigurableProvider"
|
||||
groupWeight="15"/>
|
||||
groupWeight="15"
|
||||
nonDefaultProject="true"/>
|
||||
|
||||
<projectConfigurable groupId="project"
|
||||
id="com.intellij.pycharm.community.ide.impl.configuration.PyDependenciesConfigurable"
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// 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.pycharm.community.ide.impl.configuration;
|
||||
|
||||
import com.intellij.application.options.ModuleAwareProjectConfigurable;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.NonDefaultProjectConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.configuration.PlatformContentEntriesConfigurable;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
|
||||
|
||||
public class PythonContentEntriesConfigurable extends ModuleAwareProjectConfigurable implements NonDefaultProjectConfigurable {
|
||||
public PythonContentEntriesConfigurable(final Project project) {
|
||||
super(project, PyBundle.message("configurable.PythonContentEntriesConfigurable.display.name"), "reference.settingsdialog.project.structure");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Configurable createModuleConfigurable(Module module) {
|
||||
if (PlatformUtils.isPyCharmCommunity())
|
||||
return new PlatformContentEntriesConfigurable(module, JavaSourceRootType.SOURCE);
|
||||
return new PyContentEntriesModuleConfigurable(module);
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// 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.pycharm.community.ide.impl.configuration
|
||||
|
||||
import com.intellij.application.options.ModuleAwareProjectConfigurable
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.options.Configurable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ui.configuration.PlatformContentEntriesConfigurable
|
||||
import com.intellij.openapi.ui.DialogPanel
|
||||
import com.intellij.ui.dsl.builder.Align
|
||||
import com.intellij.ui.dsl.builder.bindSelected
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.intellij.python.pyproject.model.PyProjectModelSettings
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import javax.swing.JComponent
|
||||
|
||||
class PythonContentEntriesConfigurable(project: Project) : ModuleAwareProjectConfigurable<Configurable>(
|
||||
project,
|
||||
PyBundle.message("configurable.PythonContentEntriesConfigurable.display.name"),
|
||||
"reference.settingsdialog.project.structure"
|
||||
) {
|
||||
|
||||
private var pyprojectPanel: DialogPanel? = null
|
||||
|
||||
override fun createModuleConfigurable(module: Module): Configurable {
|
||||
if (PlatformUtils.isPyCharmCommunity()) {
|
||||
return PlatformContentEntriesConfigurable(module, JavaSourceRootType.SOURCE)
|
||||
}
|
||||
return PyContentEntriesModuleConfigurable(module)
|
||||
}
|
||||
|
||||
override fun createComponent(): JComponent? {
|
||||
if (!PyProjectModelSettings.isFeatureEnabled) {
|
||||
return super.createComponent()
|
||||
}
|
||||
|
||||
val settings = PyProjectModelSettings.getInstance(project)
|
||||
pyprojectPanel = panel {
|
||||
row {
|
||||
checkBox(PyBundle.message("python.pyproject.toml.based.project.model"))
|
||||
.bindSelected(settings::usePyprojectToml)
|
||||
.contextHelp(PyBundle.message("python.pyproject.toml.based.project.model.comment"))
|
||||
}
|
||||
super.createComponent()?.let { parentComponent ->
|
||||
row {
|
||||
cell(parentComponent).align(Align.FILL)
|
||||
}.resizableRow()
|
||||
}
|
||||
}
|
||||
|
||||
return pyprojectPanel
|
||||
}
|
||||
|
||||
override fun isModified(): Boolean {
|
||||
return (pyprojectPanel?.isModified() == true) || super.isModified()
|
||||
}
|
||||
|
||||
override fun apply() {
|
||||
pyprojectPanel?.apply()
|
||||
super.apply()
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
pyprojectPanel?.reset()
|
||||
super.reset()
|
||||
}
|
||||
}
|
||||
@@ -1382,6 +1382,8 @@ python.pyproject.install.package=Install package {0}
|
||||
python.pyproject.install.self.as.editable=Run 'pip install -e .'
|
||||
python.pyproject.install.self.as.editable.progress=Running 'pip install -e .'
|
||||
python.pyproject.install.self.error=Error Running 'pip install -e .'
|
||||
python.pyproject.toml.based.project.model=Use pyproject.toml-based project model
|
||||
python.pyproject.toml.based.project.model.comment=When enabled, the project structure is derived from pyproject.toml instead of manual configuration
|
||||
|
||||
# Python Packages toolwindow
|
||||
python.toolwindow.packages.installed.label=Installed
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<registryKey defaultValue="true" description="Load project structure from pyproject.toml" key="intellij.python.pyproject.model"
|
||||
restartRequired="true"/>
|
||||
<notificationGroup id="PyProject.toml" displayType="STICKY_BALLOON" isLogByDefault="true"
|
||||
bundle="messages.PyProjectTomlBundle" key="pyproject.notification.title"/>
|
||||
<backgroundPostStartupActivity implementation="com.intellij.python.pyproject.model.internal.platformBridge.PyProjectSyncActivity"/>
|
||||
<backgroundPostStartupActivity implementation="com.intellij.python.pyproject.model.internal.PyProjectModelStartupActivity"/>
|
||||
|
||||
<statistics.projectUsagesCollector implementation="com.intellij.python.pyproject.statistics.PythonTomlStatsUsagesCollector"/>
|
||||
</extensions>
|
||||
|
||||
@@ -2,3 +2,7 @@ intellij.python.pyproject.name=Generate project from pyproject.toml
|
||||
action.PyProjectTomlSyncAction.text=Sync Project with `pyproject.toml`
|
||||
action.PyProjectTomlSyncAction.description=Rebuild project model based on pyproject.toml
|
||||
intellij.python.pyproject.system.name=`pyproject.toml`-based Project
|
||||
pyproject.notification.title=Pyproject.toml workspace detected
|
||||
pyproject.notification.content=Automatically configure workspace structure and dependencies from pyproject.toml.\nMay override manual project settings. Back up your .idea directory if needed.
|
||||
pyproject.notification.configure=Configure
|
||||
pyproject.notification.dont.show.again=Don\u2019t show again
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.python.pyproject.model
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
@State(name = "PyProjectModelSettings", storages = [Storage("pyProjectModel.xml")])
|
||||
class PyProjectModelSettings : PersistentStateComponent<PyProjectModelSettings.State>, Disposable {
|
||||
override fun dispose() {}
|
||||
class State : BaseState() {
|
||||
var usePyprojectToml: Boolean by property(false)
|
||||
var showConfigurationNotification: Boolean by property(true)
|
||||
}
|
||||
|
||||
private var myState = State()
|
||||
|
||||
var onChanged: Runnable? = null // TODO trigger logic here
|
||||
|
||||
var usePyprojectToml: Boolean
|
||||
get() = isFeatureEnabled && myState.usePyprojectToml
|
||||
set(value) {
|
||||
if (myState.usePyprojectToml != value) {
|
||||
myState.usePyprojectToml = value
|
||||
onChanged?.run()
|
||||
}
|
||||
}
|
||||
|
||||
var showConfigurationNotification: Boolean
|
||||
get() = myState.showConfigurationNotification
|
||||
set(value) {
|
||||
myState.showConfigurationNotification = value
|
||||
}
|
||||
|
||||
override fun getState(): State = myState
|
||||
|
||||
override fun loadState(state: State) {
|
||||
myState = state
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): PyProjectModelSettings = project.service()
|
||||
|
||||
val isFeatureEnabled: Boolean get() = Registry.`is`("intellij.python.pyproject.model")
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -2,14 +2,12 @@ package com.intellij.python.pyproject.model.internal
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.python.pyproject.model.PyProjectModelSettings
|
||||
import com.intellij.python.pyproject.model.internal.autoImportBridge.PyProjectAutoImportService
|
||||
import com.intellij.python.pyproject.model.internal.platformBridge.startVenvExclusion
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
private val enabled: Boolean get() = Registry.`is`("intellij.python.pyproject.model")
|
||||
|
||||
/**
|
||||
* Starts autoimport process if [enabled] or simply "skips" to the next step: [notifyModelRebuilt].
|
||||
* This method usually called by [com.intellij.python.pyproject.model.internal.platformBridge.PyProjectSyncActivity] except for new projects.
|
||||
@@ -20,7 +18,7 @@ private val enabled: Boolean get() = Registry.`is`("intellij.python.pyproject.mo
|
||||
@ApiStatus.Internal
|
||||
suspend fun startAutoImportIfNeeded(project: Project) {
|
||||
startVenvExclusion(project)
|
||||
if (enabled) {
|
||||
if (PyProjectModelSettings.isFeatureEnabled) {
|
||||
project.service<PyProjectAutoImportService>().start()
|
||||
}
|
||||
else {
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.python.pyproject.model.internal
|
||||
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationAction
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.notification.impl.NotificationFullContent
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.python.pyproject.PY_PROJECT_TOML
|
||||
import com.intellij.python.pyproject.model.PyProjectModelSettings
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
private const val NOTIFICATION_GROUP_ID = "PyProject.toml"
|
||||
|
||||
internal class PyProjectModelStartupActivity : ProjectActivity {
|
||||
override suspend fun execute(project: Project) {
|
||||
if (!PyProjectModelSettings.isFeatureEnabled) return
|
||||
|
||||
val settings = PyProjectModelSettings.getInstance(project)
|
||||
if (!settings.showConfigurationNotification) return
|
||||
|
||||
val hasPyprojectToml = readAction {
|
||||
!project.isDisposed && FilenameIndex.getVirtualFilesByName(PY_PROJECT_TOML, GlobalSearchScope.projectScope(project)).isNotEmpty()
|
||||
}
|
||||
|
||||
if (hasPyprojectToml) {
|
||||
showNotification(project, settings)
|
||||
}
|
||||
else {
|
||||
listenForPyprojectToml(project, settings)
|
||||
}
|
||||
}
|
||||
|
||||
private fun listenForPyprojectToml(project: Project, settings: PyProjectModelSettings) {
|
||||
val disposable = Disposer.newDisposable("PyProjectModelStartupActivity")
|
||||
Disposer.register(settings, disposable)
|
||||
|
||||
project.messageBus.connect(disposable).subscribe(DumbService.DUMB_MODE, object : DumbService.DumbModeListener {
|
||||
override fun exitDumbMode() {
|
||||
if (!settings.showConfigurationNotification) {
|
||||
Disposer.dispose(disposable)
|
||||
return
|
||||
}
|
||||
|
||||
val hasAnyPyprojectToml = FilenameIndex.hasVirtualFileWithName(
|
||||
PY_PROJECT_TOML,
|
||||
true,
|
||||
GlobalSearchScope.projectScope(project),
|
||||
null
|
||||
)
|
||||
|
||||
if (hasAnyPyprojectToml) {
|
||||
Disposer.dispose(disposable)
|
||||
showNotification(project, settings)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun showNotification(project: Project, settings: PyProjectModelSettings) {
|
||||
FullContentNotification(
|
||||
NOTIFICATION_GROUP_ID,
|
||||
PyProjectTomlBundle.message("pyproject.notification.title"),
|
||||
PyProjectTomlBundle.message("pyproject.notification.content"),
|
||||
NotificationType.INFORMATION,
|
||||
)
|
||||
.setSuggestionType(true)
|
||||
.addAction(NotificationAction.createSimpleExpiring(PyProjectTomlBundle.message("pyproject.notification.configure")) {
|
||||
settings.usePyprojectToml = true
|
||||
settings.showConfigurationNotification = false
|
||||
})
|
||||
.addAction(NotificationAction.createSimpleExpiring(PyProjectTomlBundle.message("pyproject.notification.dont.show.again")) {
|
||||
settings.showConfigurationNotification = false
|
||||
})
|
||||
.notify(project)
|
||||
}
|
||||
|
||||
private class FullContentNotification(groupId: String, @Nls title: String, @Nls content: String, type: NotificationType)
|
||||
: Notification(groupId, title, content, type), NotificationFullContent
|
||||
Reference in New Issue
Block a user