mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
'Load/Unload Modules' dialog: suggest to load/unload dependencies to avoid compilation errors
If a loaded module depends on an unloaded module it may lead to compilation problems, so the dialog suggests to unloaded all dependent modules when a module is unloaded and load all dependencies when a module is loaded. (IDEA-27471)
This commit is contained in:
+66
-8
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.openapi.roots.ui.configuration
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.projectView.impl.ModuleGroup
|
||||
import com.intellij.ide.projectView.impl.ModuleGroupingImplementation
|
||||
@@ -24,6 +25,7 @@ import com.intellij.openapi.module.impl.LoadedModuleDescriptionImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectBundle
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.VerticalFlowLayout
|
||||
import com.intellij.openapi.util.text.NaturalComparator
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
@@ -32,6 +34,7 @@ import com.intellij.ui.TreeSpeedSearch
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBScrollPane
|
||||
import com.intellij.ui.treeStructure.Tree
|
||||
import com.intellij.util.graph.*
|
||||
import com.intellij.util.ui.GridBag
|
||||
import com.intellij.util.ui.tree.TreeUtil
|
||||
import com.intellij.xml.util.XmlStringUtil
|
||||
@@ -55,6 +58,8 @@ class ConfigureUnloadedModulesDialog(private val project: Project, selectedModul
|
||||
private val unloadedModulesTree = ModuleDescriptionsTree(project)
|
||||
private val moduleDescriptions = ModuleManager.getInstance(project).allModuleDescriptions.associateBy { it.name }
|
||||
private val statusLabel = JBLabel()
|
||||
/** graph contains an edge a -> b if b depends on a */
|
||||
private val dependentsGraph by lazy { buildGraph() }
|
||||
|
||||
init {
|
||||
title = ProjectBundle.message("module.load.unload.dialog.title")
|
||||
@@ -64,6 +69,18 @@ class ConfigureUnloadedModulesDialog(private val project: Project, selectedModul
|
||||
init()
|
||||
}
|
||||
|
||||
private fun buildGraph(): Graph<ModuleDescription> {
|
||||
return GraphGenerator.generate(CachingSemiGraph.cache(object: InboundSemiGraph<ModuleDescription> {
|
||||
override fun getNodes(): Collection<ModuleDescription> {
|
||||
return moduleDescriptions.values
|
||||
}
|
||||
|
||||
override fun getIn(node: ModuleDescription): Iterator<ModuleDescription> {
|
||||
return node.dependencyModuleNames.asIterable().mapNotNull { moduleDescriptions[it] }.iterator()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent? {
|
||||
val buttonsPanel = JPanel(VerticalFlowLayout())
|
||||
val moveToUnloadedButton = JButton(ProjectBundle.message("module.unload.button.text"))
|
||||
@@ -71,10 +88,22 @@ class ConfigureUnloadedModulesDialog(private val project: Project, selectedModul
|
||||
val moveAllToUnloadedButton = JButton(ProjectBundle.message("module.unload.all.button.text"))
|
||||
val moveAllToLoadedButton = JButton(ProjectBundle.message("module.load.all.button.text"))
|
||||
moveToUnloadedButton.addActionListener {
|
||||
moveSelectedNodes(loadedModulesTree, unloadedModulesTree)
|
||||
val modulesToMove = includeMissingModules(loadedModulesTree.selectedModules, unloadedModulesTree.getAllModules(),
|
||||
dependentsGraph,
|
||||
ProjectBundle.message("module.unload.dependents.dialog.title"),
|
||||
{ selectedSize, additionalSize, additionalFirst -> ProjectBundle.message("module.unload.dependents.dialog.text", selectedSize, additionalSize, additionalFirst)},
|
||||
ProjectBundle.message("module.unload.with.dependents.button.text"),
|
||||
ProjectBundle.message("module.unload.without.dependents.button.text"))
|
||||
moveModules(modulesToMove, loadedModulesTree, unloadedModulesTree)
|
||||
}
|
||||
moveToLoadedButton.addActionListener {
|
||||
moveSelectedNodes(unloadedModulesTree, loadedModulesTree)
|
||||
val modulesToMove = includeMissingModules(unloadedModulesTree.selectedModules, loadedModulesTree.getAllModules(),
|
||||
GraphAlgorithms.getInstance().invertEdgeDirections(dependentsGraph),
|
||||
ProjectBundle.message("module.load.dependencies.dialog.title"),
|
||||
{ selectedSize, additionalSize, additionalFirst -> ProjectBundle.message("module.load.dependencies.dialog.text", selectedSize, additionalSize, additionalFirst)},
|
||||
ProjectBundle.message("module.load.with.dependencies.button.text"),
|
||||
ProjectBundle.message("module.load.without.dependencies.button.text"))
|
||||
moveModules(modulesToMove, unloadedModulesTree, loadedModulesTree)
|
||||
}
|
||||
moveAllToUnloadedButton.addActionListener {
|
||||
moveAllNodes(loadedModulesTree, unloadedModulesTree)
|
||||
@@ -104,6 +133,35 @@ class ConfigureUnloadedModulesDialog(private val project: Project, selectedModul
|
||||
return mainPanel
|
||||
}
|
||||
|
||||
private fun includeMissingModules(selected: List<ModuleDescription>, availableTargetModules: List<ModuleDescription>,
|
||||
dependenciesGraph: Graph<ModuleDescription>,
|
||||
dialogTitle: String, dialogMessage: (Int, Int, String) -> String, yesButtonText: String,
|
||||
noButtonText: String): Collection<ModuleDescription> {
|
||||
val additional = computeDependenciesToMove(selected, availableTargetModules, dependenciesGraph)
|
||||
if (additional.isNotEmpty()) {
|
||||
val answer = Messages.showYesNoCancelDialog(project, dialogMessage(selected.size, additional.size, additional.first().name),
|
||||
dialogTitle, yesButtonText, noButtonText, CommonBundle.getCancelButtonText(), null)
|
||||
if (answer == Messages.YES) {
|
||||
return selected + additional
|
||||
}
|
||||
if (answer == Messages.CANCEL) {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
|
||||
private fun computeDependenciesToMove(modulesToMove: Collection<ModuleDescription>, availableModules: Collection<ModuleDescription>,
|
||||
graph: Graph<ModuleDescription>): Set<ModuleDescription> {
|
||||
val result = LinkedHashSet<ModuleDescription>()
|
||||
for (module in modulesToMove) {
|
||||
GraphAlgorithms.getInstance().collectOutsRecursively(graph, module, result)
|
||||
}
|
||||
result.removeAll(modulesToMove)
|
||||
result.removeAll(availableModules)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun moveAllNodes(from: ModuleDescriptionsTree, to: ModuleDescriptionsTree) {
|
||||
from.removeAllNodes()
|
||||
to.fillTree(moduleDescriptions.values)
|
||||
@@ -112,11 +170,11 @@ class ConfigureUnloadedModulesDialog(private val project: Project, selectedModul
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveSelectedNodes(from: ModuleDescriptionsTree, to: ModuleDescriptionsTree) {
|
||||
val selected = from.selectedModules
|
||||
private fun moveModules(modulesToMove: Collection<ModuleDescription>, from: ModuleDescriptionsTree, to: ModuleDescriptionsTree) {
|
||||
if (modulesToMove.isEmpty()) return
|
||||
val oldSelectedRow = from.tree.selectionModel.leadSelectionRow
|
||||
from.removeModules(selected)
|
||||
val modules = to.addModules(selected)
|
||||
from.removeModules(modulesToMove)
|
||||
val modules = to.addModules(modulesToMove)
|
||||
modules.firstOrNull()?.let { TreeUtil.selectNode(to.tree, it)}
|
||||
IdeFocusManager.getInstance(project).requestFocus(from.tree, true).doWhenDone {
|
||||
from.tree.selectionModel.selectionPath = from.tree.getPathForRow(oldSelectedRow.coerceAtMost(from.tree.rowCount-1))
|
||||
@@ -165,11 +223,11 @@ private class ModuleDescriptionsTree(project: Project) {
|
||||
tree.expandPath(TreePath(root))
|
||||
}
|
||||
|
||||
fun addModules(modules: List<ModuleDescription>): List<ModuleDescriptionTreeNode> {
|
||||
fun addModules(modules: Collection<ModuleDescription>): List<ModuleDescriptionTreeNode> {
|
||||
return modules.map { helper.createModuleNode(it, root, model) }
|
||||
}
|
||||
|
||||
fun removeModules(modules: List<ModuleDescription>) {
|
||||
fun removeModules(modules: Collection<ModuleDescription>) {
|
||||
val names = modules.mapTo(HashSet<String>()) { it.name }
|
||||
val toRemove = findNodes { it.moduleDescription.name in names }
|
||||
for (node in toRemove) {
|
||||
|
||||
@@ -133,6 +133,16 @@ module.unloaded.label.text=Unloaded modules
|
||||
module.unloaded.explanation=Content of unloaded modules is effectively excluded from this copy of the project: 'Find' actions won't search \
|
||||
for results in these modules, refactoring actions won't update usages in them, their sources won't be compiled, etc. Use this on the parts of \
|
||||
the project you are currently not working on to make IDE work faster and exclude uninteresting elements from UI.
|
||||
module.load.dependencies.dialog.text=The selected {0, choice, 1#module depends|2#modules depend} on {1, choice, 1#module ''''{2}'''' which is|2#{1} modules which are} \
|
||||
currently unloaded. If a loaded module depends on an unloaded module it may lead to compilations errors. What do you want?
|
||||
module.load.dependencies.dialog.title=Load Required Modules
|
||||
module.load.with.dependencies.button.text=Load All Required Modules
|
||||
module.load.without.dependencies.button.text=Load Selected Modules Only
|
||||
module.unload.dependents.dialog.text={1, choice, 1#Module ''''{2}'''' depends|2#{1} modules depend} on the selected {0, choice, 1#module|2#modules}. \
|
||||
If the selected {0, choice, 1#module|2#modules} become unloaded it may cause compilations errors in the dependent modules. What do you want?
|
||||
module.unload.dependents.dialog.title=Unload Dependent Modules
|
||||
module.unload.with.dependents.button.text=Unload All Dependent Modules
|
||||
module.unload.without.dependents.button.text=Unload Selected Modules Only
|
||||
|
||||
button.text.attach.files=Attach &Files or Directories...
|
||||
library.attach.files.action=Attach Files or Directories
|
||||
|
||||
Reference in New Issue
Block a user