From d26001dea9df2e744a9326ee0be743997c952394 Mon Sep 17 00:00:00 2001 From: Ilia Zakoulov Date: Mon, 18 Aug 2025 14:36:20 +0200 Subject: [PATCH] PY-83395: Display a notification that will allow to revert marking directory as source root GitOrigin-RevId: 295c25fca066163fe449742e174ec8a052f8992b --- .../intellij.python.community.impl.xml | 3 ++ .../messages/PyBundle.properties | 1 + .../resources/messages/PyPsiBundle.properties | 5 +++ .../PyMarkDirectoryAsSourceRootQuickFix.kt | 41 +++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index bc85ff87ab05..8ad59600a122 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -620,6 +620,9 @@ key="python.sdk.installation.notification.group"/> + + diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index e1a21b16504e..4c21a893ce41 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -1755,6 +1755,7 @@ pycharm.free.mode.upgrade.body=Unlock the full potential of your IDE! Boost prod pycharm.free.mode.upgrade.button=Purchase Subscription python.packaging.sync.packages=Syncing project dependencies python.sdk.read.only=Python SDK ''{0}'' is read-only and operation cannot be performed. +python.source.root.detection.notification.group=Python source root detection read.only.python.sdk.system.wide.read.only.message=System Python packages are read-only. Select a different interpreter to modify packages. tracecontext.detecting.poetry.executable=Detecting Poetry Executable tracecontext.detecting.pip.executable=Detecting Pip Executable diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index 01bd40553cc2..40c556f741b4 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -1371,3 +1371,8 @@ INSP.unnecessary.cast.message=Unnecessary cast; type is already ''{0}'' # Quick fixes for PyUnnecessaryCastInspection QFIX.remove.cast.call=Remove 'cast' call + +# Quick fix: mark directory as source root +QFIX.add.source.root.notification.text=''{0}'' has been marked as source root. +QFIX.add.source.root.notification.ok=Ok +QFIX.add.source.root.notification.revert=Revert diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyMarkDirectoryAsSourceRootQuickFix.kt b/python/src/com/jetbrains/python/inspections/quickfix/PyMarkDirectoryAsSourceRootQuickFix.kt index 268a10f449f3..05dffce1f17e 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyMarkDirectoryAsSourceRootQuickFix.kt +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyMarkDirectoryAsSourceRootQuickFix.kt @@ -7,6 +7,12 @@ import com.intellij.codeInspection.util.IntentionFamilyName import com.intellij.codeInspection.util.IntentionName import com.intellij.icons.AllIcons import com.intellij.ide.projectView.actions.MarkRootsManager +import com.intellij.notification.Notification +import com.intellij.notification.NotificationAction +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.openapi.project.guessProjectDir @@ -51,6 +57,31 @@ internal class PyMarkDirectoryAsSourceRootQuickFix( override fun applyFix(project: Project, descriptor: ProblemDescriptor) { sourceRoot.markAsSourceRoot(module) findCache(context)?.clearCache() + showNotification() + } + + private fun showNotification() { + val message = PyPsiBundle.message("QFIX.add.source.root.notification.text", getPathName()) + val group = NotificationGroupManager.getInstance().getNotificationGroup("Python source root detection") + val notification = group.createNotification(message, NotificationType.INFORMATION) + + // Ok action just closes the notification + notification.addAction(NotificationAction.createSimpleExpiring( + PyPsiBundle.message("QFIX.add.source.root.notification.ok") + ) { /* no-op, expiring */ }) + + // Revert action removes the just added source root + notification.addAction(object : NotificationAction(PyPsiBundle.message("QFIX.add.source.root.notification.revert")) { + override fun actionPerformed(e: AnActionEvent, notification: Notification) { + ApplicationManager.getApplication().runWriteAction { + sourceRoot.unmarkAsSourceRoot(module) + } + findCache(context)?.clearCache() + notification.expire() + } + }) + + notification.notify(project) } private fun VirtualFile.markAsSourceRoot(module: Module) { @@ -59,4 +90,14 @@ internal class PyMarkDirectoryAsSourceRootQuickFix( entry.addSourceFolder(this, JavaSourceRootType.SOURCE) model.commit() } + + private fun VirtualFile.unmarkAsSourceRoot(module: Module) { + val model = ModuleRootManager.getInstance(module).modifiableModel + val entry = MarkRootsManager.findContentEntry(model, this) ?: return + val toRemove = entry.sourceFolders.firstOrNull { it.file == this || it.url == this.url } + if (toRemove != null) { + entry.removeSourceFolder(toRemove) + } + model.commit() + } }