mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[python] support multi-module projects on Python Packages toolwindow (PY-54800)
(cherry picked from commit b525b804dadf9432314295ff9eed5571c97c02c7) IJ-MR-102434 GitOrigin-RevId: ced58522f8bae1bc551df8737d120e2ce2545838
This commit is contained in:
committed by
intellij-monorepo-bot
parent
51255eec58
commit
f52c2e22d0
@@ -23,6 +23,8 @@
|
||||
<listener
|
||||
class="com.jetbrains.python.inspections.PyInterpreterInspection$Visitor$CacheCleaner"
|
||||
topic="com.intellij.openapi.projectRoots.ProjectJdkTable$Listener"/>
|
||||
<listener class="com.jetbrains.python.packaging.toolwindow.PyPackagesToolWindowModuleAttachListener"
|
||||
topic="com.intellij.platform.ModuleAttachListener"/>
|
||||
</projectListeners>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.toolwindow
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.platform.ModuleAttachListener
|
||||
import java.nio.file.Path
|
||||
|
||||
class PyPackagesToolWindowModuleAttachListener : ModuleAttachListener {
|
||||
override fun afterAttach(module: Module, primaryModule: Module?, imlFile: Path, tasks: MutableList<suspend () -> Unit>) {
|
||||
tasks.add {
|
||||
module.project.service<PyPackagingToolWindowService>().moduleAttached()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,13 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.modules
|
||||
import com.intellij.openapi.ui.SimpleToolWindowPanel
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||
@@ -38,6 +44,8 @@ import com.jetbrains.python.packaging.PyPackageUtil
|
||||
import com.jetbrains.python.packaging.common.PythonLocalPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonVcsPackageSpecification
|
||||
import com.jetbrains.python.sdk.pythonSdk
|
||||
import icons.PythonIcons
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -45,11 +53,13 @@ import kotlinx.coroutines.withContext
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.Dimension
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.event.ActionEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.event.DocumentEvent
|
||||
import javax.swing.event.ListSelectionListener
|
||||
|
||||
class PyPackagingToolWindowPanel(private val project: Project, toolWindow: ToolWindow) : SimpleToolWindowPanel(false, true), Disposable {
|
||||
private val packagingScope = ApplicationManager.getApplication().coroutineScope.childScope(Dispatchers.Default)
|
||||
@@ -80,7 +90,7 @@ class PyPackagingToolWindowPanel(private val project: Project, toolWindow: ToolW
|
||||
// layout
|
||||
private var mainPanel: JPanel? = null
|
||||
private var splitter: OnePixelSplitter? = null
|
||||
private val leftPanel: JScrollPane
|
||||
private var leftPanel: JComponent
|
||||
private val rightPanel: JComponent
|
||||
|
||||
internal var contentVisible: Boolean
|
||||
@@ -163,8 +173,8 @@ class PyPackagingToolWindowPanel(private val project: Project, toolWindow: ToolW
|
||||
}
|
||||
|
||||
tablesView = PyPackagingTablesView(project, packageListPanel, this)
|
||||
leftPanel = ScrollPaneFactory.createScrollPane(packageListPanel, true)
|
||||
|
||||
leftPanel = createLeftPanel(service)
|
||||
rightPanel = borderPanel {
|
||||
add(borderPanel {
|
||||
border = SideBorder(JBColor.GRAY, SideBorder.BOTTOM)
|
||||
@@ -274,6 +284,61 @@ class PyPackagingToolWindowPanel(private val project: Project, toolWindow: ToolW
|
||||
setContent(mainPanel!!)
|
||||
}
|
||||
|
||||
private fun createLeftPanel(service: PyPackagingToolWindowService): JComponent {
|
||||
if (project.modules.size == 1) return ScrollPaneFactory.createScrollPane(packageListPanel, true)
|
||||
|
||||
val left = JPanel(BorderLayout()).apply {
|
||||
border = BorderFactory.createEmptyBorder()
|
||||
}
|
||||
|
||||
val modulePanel = JPanel(FlowLayout(FlowLayout.LEFT, 0, 0)).apply {
|
||||
border = SideBorder(NamedColorUtil.getBoundsColor(), SideBorder.RIGHT)
|
||||
maximumSize = Dimension(80, maximumSize.height)
|
||||
minimumSize = Dimension(50, minimumSize.height)
|
||||
}
|
||||
|
||||
val moduleList = JBList(ModuleManager.getInstance(project).modules.toList().sortedBy { it.name }).apply {
|
||||
selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||
border = JBUI.Borders.empty()
|
||||
|
||||
val itemRenderer = JBLabel("", PythonIcons.Python.PythonClosed, JLabel.LEFT).apply {
|
||||
border = JBUI.Borders.empty(0, 10)
|
||||
}
|
||||
cellRenderer = ListCellRenderer { _, value, _, _, _ -> itemRenderer.text = value.name; itemRenderer }
|
||||
|
||||
addListSelectionListener(ListSelectionListener { e ->
|
||||
if (e.valueIsAdjusting) return@ListSelectionListener
|
||||
val selectedModule = this@apply.selectedValue
|
||||
val sdk = selectedModule.pythonSdk ?: return@ListSelectionListener
|
||||
packagingScope.launch {
|
||||
service.initForSdk(sdk)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val fileListener = object : FileEditorManagerListener {
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
if (project.modules.size > 1) {
|
||||
val newFile = event.newFile ?: return
|
||||
val module = ModuleUtilCore.findModuleForFile(newFile, project)
|
||||
packagingScope.launch(Dispatchers.IO) {
|
||||
val index = (moduleList.model as DefaultListModel<Module>).indexOf(module)
|
||||
moduleList.selectionModel.setSelectionInterval(index, index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
service.project.messageBus
|
||||
.connect(service)
|
||||
.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, fileListener)
|
||||
|
||||
modulePanel.add(moduleList)
|
||||
left.add(ScrollPaneFactory.createScrollPane(modulePanel, true), BorderLayout.WEST)
|
||||
left.add(ScrollPaneFactory.createScrollPane(packageListPanel, true), BorderLayout.CENTER)
|
||||
|
||||
return left
|
||||
}
|
||||
|
||||
private fun showInstallFromVcsDialog(service: PyPackagingToolWindowService): PythonVcsPackageSpecification? {
|
||||
var editable = false
|
||||
var link = ""
|
||||
@@ -487,6 +552,15 @@ class PyPackagingToolWindowPanel(private val project: Project, toolWindow: ToolW
|
||||
packagingScope.cancel()
|
||||
}
|
||||
|
||||
internal suspend fun recreateModulePanel() {
|
||||
val newPanel = createLeftPanel(project.service<PyPackagingToolWindowService>())
|
||||
withContext(Dispatchers.Main) {
|
||||
leftPanel = newPanel
|
||||
splitter?.firstComponent = leftPanel
|
||||
splitter?.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val HORIZONTAL_SPLITTER_KEY = "Python.PackagingToolWindow.Horizontal"
|
||||
private const val VERTICAL_SPLITTER_KEY = "Python.PackagingToolWindow.Vertical"
|
||||
|
||||
@@ -11,6 +11,9 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.options.ex.SingleConfigurableEditor
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
@@ -27,8 +30,8 @@ import com.jetbrains.python.PythonHelper
|
||||
import com.jetbrains.python.PythonHelpersLocator
|
||||
import com.jetbrains.python.packaging.*
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.PythonPackageManagementListener
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.packagesByRepository
|
||||
import com.jetbrains.python.packaging.repository.*
|
||||
@@ -37,18 +40,18 @@ import com.jetbrains.python.run.applyHelperPackageToPythonPath
|
||||
import com.jetbrains.python.run.buildTargetedCommandLine
|
||||
import com.jetbrains.python.run.prepareHelperScriptExecution
|
||||
import com.jetbrains.python.sdk.PythonSdkType
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil
|
||||
import com.jetbrains.python.sdk.pythonSdk
|
||||
import com.jetbrains.python.sdk.sdkFlavor
|
||||
import com.jetbrains.python.statistics.modules
|
||||
import kotlinx.coroutines.*
|
||||
import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil
|
||||
import org.jetbrains.annotations.Nls
|
||||
import kotlin.Comparator
|
||||
|
||||
@Service
|
||||
class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
|
||||
private lateinit var toolWindowPanel: PyPackagingToolWindowPanel
|
||||
private var toolWindowPanel: PyPackagingToolWindowPanel? = null
|
||||
lateinit var manager: PythonPackageManager
|
||||
private var installedPackages: List<InstalledPackage> = emptyList()
|
||||
internal var currentSdk: Sdk? = null
|
||||
@@ -87,7 +90,7 @@ class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
|
||||
if (isActive) {
|
||||
withContext(Dispatchers.Main) {
|
||||
toolWindowPanel.showSearchResult(installed, packagesFromRepos + invalidRepositories)
|
||||
toolWindowPanel?.showSearchResult(installed, packagesFromRepos + invalidRepositories)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +101,7 @@ class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
PyPackagesViewData(repository, shownPackages, moreItems = packages.size - PACKAGES_LIMIT)
|
||||
}.toList()
|
||||
|
||||
toolWindowPanel.resetSearch(installedPackages, packagesByRepository + invalidRepositories)
|
||||
toolWindowPanel?.resetSearch(installedPackages, packagesByRepository + invalidRepositories)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +115,7 @@ class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
if (result.isSuccess) showPackagingNotification(message("python.packaging.notification.deleted", selectedPackage.name))
|
||||
}
|
||||
|
||||
private suspend fun initForSdk(sdk: Sdk?) {
|
||||
internal suspend fun initForSdk(sdk: Sdk?) {
|
||||
val previousSdk = currentSdk
|
||||
currentSdk = sdk
|
||||
if (currentSdk != null) {
|
||||
@@ -125,9 +128,9 @@ class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
toolWindowPanel.contentVisible = currentSdk != null
|
||||
toolWindowPanel?.contentVisible = currentSdk != null
|
||||
if (currentSdk == null || currentSdk != previousSdk) {
|
||||
toolWindowPanel.setEmpty()
|
||||
toolWindowPanel?.setEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,6 +154,17 @@ class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, object : FileEditorManagerListener {
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
val newFile = event.newFile ?: return
|
||||
val module = ModuleUtilCore.findModuleForFile(newFile, project)
|
||||
val sdk = PythonSdkUtil.findPythonSdk(module) ?: return
|
||||
serviceScope.launch(Dispatchers.IO) {
|
||||
initForSdk(sdk)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -317,6 +331,10 @@ class PyPackagingToolWindowService(val project: Project) : Disposable {
|
||||
.toList()
|
||||
}
|
||||
|
||||
internal suspend fun moduleAttached() {
|
||||
toolWindowPanel?.recreateModulePanel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PACKAGES_LIMIT = 50
|
||||
private fun createNameComparator(query: String, url: String): Comparator<String> {
|
||||
|
||||
Reference in New Issue
Block a user