From d8eb93a9ed393147bd5a6cd750c88eea9c23cf4b Mon Sep 17 00:00:00 2001 From: Andras Kovi Date: Wed, 25 Jun 2025 12:09:06 +0200 Subject: [PATCH 1/2] Add support for showing python version in tree view Resolves: #10 --- .gitignore | 2 + .../github/pyvenvmanage/VenvIconProvider.kt | 38 -------------- .../VenvProjectViewNodeDecorator.kt | 25 ++++++++++ .../com/github/pyvenvmanage/VenvUtils.kt | 50 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 6 +-- 5 files changed, 80 insertions(+), 41 deletions(-) delete mode 100644 src/main/kotlin/com/github/pyvenvmanage/VenvIconProvider.kt create mode 100644 src/main/kotlin/com/github/pyvenvmanage/VenvProjectViewNodeDecorator.kt create mode 100644 src/main/kotlin/com/github/pyvenvmanage/VenvUtils.kt diff --git a/.gitignore b/.gitignore index bbbd37d..70a030f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .idea .intellijPlatform build +.DS_Store +.kotlin diff --git a/src/main/kotlin/com/github/pyvenvmanage/VenvIconProvider.kt b/src/main/kotlin/com/github/pyvenvmanage/VenvIconProvider.kt deleted file mode 100644 index a23cf33..0000000 --- a/src/main/kotlin/com/github/pyvenvmanage/VenvIconProvider.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.pyvenvmanage - -import javax.swing.Icon - -import com.intellij.ide.IconLayerProvider -import com.intellij.ide.IconProvider -import com.intellij.openapi.util.Iconable -import com.intellij.psi.PsiDirectory -import com.intellij.psi.PsiElement - -import com.jetbrains.python.icons.PythonIcons.Python.Virtualenv -import com.jetbrains.python.sdk.PythonSdkUtil - -class VenvIconProvider : - IconProvider(), - IconLayerProvider { - override fun getLayerDescription(): String = "Python virtual environment" - - override fun getIcon( - element: PsiElement, - flags: Int, - ): Icon? = determineIcon(element) - - override fun getLayerIcon( - element: Iconable, - isLocked: Boolean, - ): Icon? = determineIcon(element) - - private fun determineIcon(element: Any): Icon? { - if (element is PsiDirectory) { - val venvRootPath = element.virtualFile.path - if (PythonSdkUtil.getPythonExecutable(venvRootPath) != null) { - return Virtualenv - } - } - return null - } -} diff --git a/src/main/kotlin/com/github/pyvenvmanage/VenvProjectViewNodeDecorator.kt b/src/main/kotlin/com/github/pyvenvmanage/VenvProjectViewNodeDecorator.kt new file mode 100644 index 0000000..9d67d5e --- /dev/null +++ b/src/main/kotlin/com/github/pyvenvmanage/VenvProjectViewNodeDecorator.kt @@ -0,0 +1,25 @@ +package com.github.pyvenvmanage + +import com.intellij.ide.projectView.PresentationData +import com.intellij.ide.projectView.ProjectViewNode +import com.intellij.ide.projectView.ProjectViewNodeDecorator +import com.intellij.ui.SimpleTextAttributes + +import com.jetbrains.python.icons.PythonIcons.Python.Virtualenv + +class VenvProjectViewNodeDecorator : ProjectViewNodeDecorator { + override fun decorate( + node: ProjectViewNode<*>, + data: PresentationData, + ) { + val pyVenvCfgPath = VenvUtils.getPyVenvCfg(node.getVirtualFile()) + if (pyVenvCfgPath != null) { + val pythonVersion = VenvUtils.getPythonVersionFromPyVenv(pyvenvCfgPath = pyVenvCfgPath) + val fileName: String? = data.getPresentableText() + data.clearText() + data.addText(fileName, SimpleTextAttributes.REGULAR_ATTRIBUTES) + data.addText(" [" + pythonVersion + "]", SimpleTextAttributes.GRAY_ATTRIBUTES) + data.setIcon(Virtualenv) + } + } +} diff --git a/src/main/kotlin/com/github/pyvenvmanage/VenvUtils.kt b/src/main/kotlin/com/github/pyvenvmanage/VenvUtils.kt new file mode 100644 index 0000000..f4be217 --- /dev/null +++ b/src/main/kotlin/com/github/pyvenvmanage/VenvUtils.kt @@ -0,0 +1,50 @@ +package com.github.pyvenvmanage + +import java.io.IOException +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Path +import java.util.Properties + +import com.intellij.openapi.vfs.VirtualFile + +import com.jetbrains.python.sdk.PythonSdkUtil + +object VenvUtils { + @JvmStatic + fun getPyVenvCfg(file: VirtualFile?): Path? { + if (file != null && file.isDirectory()) { + val venvRootPath = file.getPath() + val pythonExecutable = PythonSdkUtil.getPythonExecutable(venvRootPath) + if (pythonExecutable != null) { + val pyvenvFile = file.findChild("pyvenv.cfg") + if (pyvenvFile != null) { + return Path.of(pyvenvFile.getPath()) + } + } + } + return null + } + + @JvmStatic + fun getPythonVersionFromPyVenv(pyvenvCfgPath: Path): String { + val unknownVersion = "unknown" + + val props = Properties() + + try { + Files.newBufferedReader(pyvenvCfgPath, StandardCharsets.UTF_8).use { reader -> + props.load(reader) + } + } catch (e: IOException) { + return unknownVersion // file could not be read + } + + val version = props.getProperty("version_info") + if (version != null) { + return version.trim { it <= ' ' } + } + + return unknownVersion + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 1b164cc..cf5f4e7 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -7,9 +7,9 @@ com.intellij.modules.platform - - - + + From 68ac589a63886e035cbc215cae3d648f8ee306e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Wed, 25 Jun 2025 07:54:48 -0700 Subject: [PATCH 2/2] Update .gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 70a030f..55f0c72 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .gradle .idea .intellijPlatform -build -.DS_Store .kotlin +build