PY-84031: Support non-project indexed files for import quick fixes

Space-RevId: e9b97177394ca59bf933dcfae8f94473f48e3701

GitOrigin-RevId: ba2d11489d2212876e6a56d3b0367cfc8e375ce1
This commit is contained in:
Ilia Zakoulov
2026-02-03 23:35:47 +00:00
committed by intellij-monorepo-bot
parent 6e2d9cc90b
commit 22a80d6f35
4 changed files with 24 additions and 5 deletions
@@ -101,6 +101,13 @@ class PyExternalFilesIndexService(private val project: Project, private val coro
}
}
fun findSdkForExternallyIndexedFile(file: VirtualFile): Sdk? {
if (!isFileAddedToNonProjectIndex(file)) {
return null
}
return ProjectRootManager.getInstance(project).getProjectSdk()
}
fun isFileAddedToNonProjectIndex(file: VirtualFile): Boolean {
val virtualFileManager = project.workspaceModel.getVirtualFileUrlManager()
val storage = project.workspaceModel.currentSnapshot
@@ -4,7 +4,9 @@ package com.jetbrains.python.inspections.quickfix
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.python.externalIndex.PyExternalFilesIndexService
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PyPsiPackageUtil.moduleToPackageName
import com.jetbrains.python.packaging.management.ui.PythonPackageManagerUI
@@ -16,7 +18,9 @@ class InstallAllPackagesQuickFix(private val packageNames: List<String>) : Local
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement ?: return
val sdk = PythonSdkUtil.findPythonSdk(element) ?: return
val sdk = PythonSdkUtil.findPythonSdk(element)
?: project.service<PyExternalFilesIndexService>().findSdkForExternallyIndexedFile(descriptor.psiElement.containingFile.virtualFile)
?: return
val normalizedPackageNames = packageNames.map { moduleToPackageName(it) }
@@ -5,7 +5,9 @@ import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.python.externalIndex.PyExternalFilesIndexService
import com.jetbrains.python.PyBundle
import com.jetbrains.python.packaging.management.ui.PythonPackageManagerUI
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
@@ -18,7 +20,9 @@ internal open class InstallPackageQuickFix(open val packageName: String) : Local
override fun getFamilyName(): @Nls String = PyBundle.message("python.unresolved.reference.inspection.install.package", packageName)
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val sdk = PythonSdkUtil.findPythonSdk(descriptor.psiElement) ?: return
val sdk = PythonSdkUtil.findPythonSdk(descriptor.psiElement)
?: project.service<PyExternalFilesIndexService>().findSdkForExternallyIndexedFile(descriptor.psiElement.containingFile.virtualFile)
?: return
PyPackageCoroutine.launch(project) {
PythonPackageManagerUI.forSdk(project, sdk).installWithConfirmation(listOf(packageName)) ?: return@launch
onSuccess(descriptor)
@@ -10,6 +10,7 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.options.OptPane
import com.intellij.ide.projectView.actions.MarkRootsManager
import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.openapi.components.service
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
@@ -26,6 +27,7 @@ import com.intellij.psi.search.FilenameIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.QualifiedName
import com.intellij.python.externalIndex.PyExternalFilesIndexService
import com.intellij.python.pyproject.model.api.isPyProjectTomlBased
import com.intellij.util.containers.ContainerUtil
import com.jetbrains.python.PyNames
@@ -105,7 +107,7 @@ class PyUnresolvedReferencesInspection : PyUnresolvedReferencesInspectionBase()
// Don't suggest installing "name" for `import pkg.name` or `from pkg.name import foo`.
if (node !is PyReferenceExpression || node.isQualified) {
return emptyList()
}
}
val qname = QualifiedName.fromDottedString(refName)
val components = qname.components
@@ -114,14 +116,16 @@ class PyUnresolvedReferencesInspection : PyUnresolvedReferencesInspectionBase()
}
val packageName = PyPsiPackageUtil.moduleToPackageName(components[0])
val project = node.project
val module = ModuleUtilCore.findModuleForPsiElement(node)
val sdk = PythonSdkUtil.findPythonSdk(module)
if (module == null || sdk == null || !PyPackageUtil.packageManagementEnabled(sdk, false, true)) {
?: project.service<PyExternalFilesIndexService>().findSdkForExternallyIndexedFile(node.containingFile.virtualFile)
if (sdk == null || !PyPackageUtil.packageManagementEnabled(sdk, false, true)) {
return emptyList()
}
val packageManager = PythonPackageManager.forSdk(module.project, sdk)
val packageManager = PythonPackageManager.forSdk(project, sdk)
val shouldBeSuggest = !sdk.isReadOnly && packageManager.isNotInstalledAndCanBeInstalled(packageName)
if (!shouldBeSuggest)