PY-82800 Requirements: Add floting toolbar for requirements.txt for venv interpreters

Signed-off-by: Nikita.Ashihmin <nikita.ashihmin@jetbrains.com>

GitOrigin-RevId: b9fb948d7f12fce1f60e0990e47657f9b17e94f6
This commit is contained in:
Nikita.Ashihmin
2025-07-22 19:05:39 +00:00
committed by intellij-monorepo-bot
parent be41ec6c3f
commit c35bacc607
5 changed files with 82 additions and 1 deletions
@@ -1090,6 +1090,15 @@
<separator/>
<action id="PipSetDefaultRequirementsAction"
class="com.jetbrains.python.packaging.pip.actions.PipSetDefaultRequirementsAction"
icon="com.intellij.icons.AllIcons.General.GreenCheckmark"/>
<action id="PipUpdateEnvAction"
class="com.jetbrains.python.packaging.pip.actions.PipUpdateEnvAction"
icon="com.intellij.icons.AllIcons.Actions.Refresh"/>
<separator/>
<add-to-group group-id="EditorContextBarMenu" anchor="first"/>
</group>
</actions>
@@ -1604,6 +1604,9 @@ action.CondaUpdateEnvAction.text=Conda Update from "environment.yml"
python.sdk.intention.family.name.sync.project=Sync project
python.sdk.sync.project.text=Syncing project
action.PipSetDefaultRequirementsAction.text=Set This Requirements File as Default for SDK
action.PipUpdateEnvAction.text=Install Packages from This File
python.survey.user.job.notification.group=PyCharm Job Survey
python.survey.user.job.notification.title=Feedback In IDE
python.survey.user.job.notification.content=Tell us about your Python development experience. Just one question.
@@ -12,6 +12,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.util.NlsContexts.ProgressTitle
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.python.pyproject.PY_PROJECT_TOML
import com.jetbrains.python.errorProcessing.ErrorSink
import com.jetbrains.python.errorProcessing.PyResult
@@ -56,7 +57,7 @@ abstract class PythonPackageManagerAction<T : PythonPackageManager, V> : DumbAwa
override fun update(e: AnActionEvent) {
val virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE)
val isWatchedFile = virtualFile?.name?.let { fileNamesPattern.matches(it) } ?: false
val isWatchedFile = isWatchedFile(virtualFile)
val manager = if (isWatchedFile) getManager(e) else null
with(e.presentation) {
@@ -65,6 +66,8 @@ abstract class PythonPackageManagerAction<T : PythonPackageManager, V> : DumbAwa
}
}
protected open fun isWatchedFile(virtualFile: VirtualFile?): Boolean = virtualFile?.name?.let { fileNamesPattern.matches(it) } ?: false
/**
* This action saves the current document on fs because tools are command line tools, and they need actual files to be up to date
* Handles errors via [errorSink]
@@ -7,6 +7,7 @@ import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.execution.process.ProcessOutput
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.python.community.helpersLocator.PythonHelpersLocator.Companion.findPathInHelpers
import com.jetbrains.python.errorProcessing.PyResult
import com.jetbrains.python.packaging.PyPackageUtil
@@ -62,6 +63,13 @@ open class PipPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageMa
}
}
suspend fun syncRequirementsTxt(requirementsFile: VirtualFile): PyResult<Unit> {
engine.syncRequirementsTxt(requirementsFile).getOr {
return it
}
return reloadPackages().mapSuccess { }
}
override suspend fun updatePackageCommand(
vararg specifications: PythonRepositoryPackageSpecification,
): PyResult<Unit> = engine.updatePackageCommand(*specifications)
@@ -0,0 +1,58 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.packaging.pip.actions
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.vfs.VirtualFile
import com.jetbrains.python.errorProcessing.PyResult
import com.jetbrains.python.packaging.management.PythonPackageManagerAction
import com.jetbrains.python.packaging.management.getPythonPackageManager
import com.jetbrains.python.packaging.pip.PipPythonPackageManager
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementTxtSdkUtils
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementsTxtManager
import com.jetbrains.python.requirements.RequirementsFileType
internal sealed class PipPackageManagerAction : PythonPackageManagerAction<PipPythonPackageManager, String>() {
override fun isWatchedFile(virtualFile: VirtualFile?): Boolean {
return virtualFile?.fileType is RequirementsFileType
}
override fun getManager(e: AnActionEvent): PipPythonPackageManager? = e.getPythonPackageManager()
}
internal class PipSetDefaultRequirementsAction() : PipPackageManagerAction() {
override suspend fun execute(e: AnActionEvent, manager: PipPythonPackageManager): PyResult<Unit> {
val envFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return PyResult.success(Unit)
val project = e.project ?: return PyResult.success(Unit)
val newFilePath = envFile.toNioPath()
val requirementsTxtManager = PythonRequirementsTxtManager.getInstance(project, manager.sdk)
requirementsTxtManager.getDependenciesFile()
PythonRequirementTxtSdkUtils.saveRequirementsTxtPath(project, manager.sdk, newFilePath)
return PyResult.success(Unit)
}
override fun update(e: AnActionEvent) {
super.update(e)
if (!e.presentation.isEnabledAndVisible)
return
val manager = e.getPythonPackageManager<PipPythonPackageManager>() ?: return
val savedFile = PythonRequirementTxtSdkUtils.findRequirementsTxt(manager.sdk)
val currentFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return
if (savedFile == currentFile) {
e.presentation.isEnabledAndVisible = false
}
}
}
internal class PipUpdateEnvAction() : PipPackageManagerAction() {
override suspend fun execute(e: AnActionEvent, manager: PipPythonPackageManager): PyResult<Unit> {
val currentFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return PyResult.success(Unit)
return manager.syncRequirementsTxt(currentFile)
}
}