diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java index 5581cfe7927e..4cc754e75ed0 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java @@ -25,6 +25,7 @@ import com.intellij.ide.IdeBundle; import com.intellij.ide.highlighter.ModuleFileType; import com.intellij.ide.impl.FlattenModulesToggleAction; import com.intellij.ide.projectView.impl.ModuleGroup; +import com.intellij.ide.projectView.impl.ModuleGroupingImplementation; import com.intellij.ide.projectView.impl.ModuleGroupingTreeHelper; import com.intellij.ide.util.projectWizard.ModuleBuilder; import com.intellij.ide.util.projectWizard.NamePathComponent; @@ -221,8 +222,10 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple private void createProjectNodes() { ModuleGrouper moduleGrouper = getModuleGrouper(); - ModuleGroupingTreeHelper helper = ModuleGroupingTreeHelper.forEmptyTree(!myHideModuleGroups && !myFlattenModules, - moduleGrouper, ModuleStructureConfigurable::createModuleGroupNode, this::createModuleNode, getNodeComparator()); + ModuleGroupingTreeHelper helper = ModuleGroupingTreeHelper.forEmptyTree(!myHideModuleGroups && !myFlattenModules, + ModuleGroupingTreeHelper.createDefaultGrouping(moduleGrouper), + ModuleStructureConfigurable::createModuleGroupNode, + m -> createModuleNode(m, moduleGrouper), getNodeComparator()); helper.createModuleNodes(Arrays.asList(myModuleManager.getModules()), myRoot, getTreeModel()); if (containsSecondLevelNodes(myRoot)) { myTree.setShowsRootHandles(true); @@ -290,7 +293,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple LOG.assertTrue(node != null, "Module " + module.getName() + " is not in project."); nodes.add(Pair.create(node, module)); } - ModuleGroupingTreeHelper helper = createGroupingHelper(); + ModuleGroupingTreeHelper helper = createGroupingHelper(); helper.moveModuleNodesToProperGroup(nodes, myRoot, getTreeModel(), myTree); return true; } @@ -300,11 +303,14 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple } @NotNull - private ModuleGroupingTreeHelper createGroupingHelper() { + private ModuleGroupingTreeHelper createGroupingHelper() { + ModuleGrouper grouper = getModuleGrouper(); + ModuleGroupingImplementation grouping = ModuleGroupingTreeHelper.createDefaultGrouping(grouper); return ModuleGroupingTreeHelper.forTree(myRoot, (node) -> node instanceof ModuleGroupNode ? ((ModuleGroupNode)node).getModuleGroup() : null, (node) -> node instanceof ModuleNode ? ((ModuleNode)node).getModule() : null, - !myHideModuleGroups && !myFlattenModules, getModuleGrouper(), ModuleStructureConfigurable::createModuleGroupNode, - this::createModuleNode, getNodeComparator()); + !myHideModuleGroups && !myFlattenModules, + grouping, ModuleStructureConfigurable::createModuleGroupNode, + module -> createModuleNode(module, grouper), getNodeComparator()); } @Override @@ -656,7 +662,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple boolean autoScrollWasEnabled = myAutoScrollEnabled; try { myAutoScrollEnabled = false; - ModuleGroupingTreeHelper helper = createGroupingHelper(); + ModuleGroupingTreeHelper helper = createGroupingHelper(); MyNode newNode = helper.moveModuleNodeToProperGroup(this, getModule(), myRoot, treeModel, myTree); treeModel.reload(newNode); } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ModuleGroupingTreeHelper.kt b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ModuleGroupingTreeHelper.kt index c04eed353ced..116835a0062d 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ModuleGroupingTreeHelper.kt +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ModuleGroupingTreeHelper.kt @@ -42,33 +42,34 @@ import javax.swing.tree.MutableTreeNode import javax.swing.tree.TreeNode /** - * Provides methods to build trees where nodes are grouped by modules (and optionally by module groups). + * Provides methods to build trees where nodes are grouped by modules (and optionally by module groups). Type parameter M specified class + * of modules (may be [Module] if real modules are shown, or [com.intellij.openapi.module.ModuleDescription] if loaded and unloaded modules are shown. * * @author nik */ -class ModuleGroupingTreeHelper private constructor( +class ModuleGroupingTreeHelper private constructor( private val groupingEnabled: Boolean, - private val grouper: ModuleGrouper, + private val grouping: ModuleGroupingImplementation, private val moduleGroupNodeFactory: (ModuleGroup) -> N, - private val moduleNodeFactory: (Module, ModuleGrouper) -> N, + private val moduleNodeFactory: (M) -> N, private val nodeComparator: Comparator ) { private val nodeForGroup = HashMap() - private val nodeData = HashMap() + private val nodeData = HashMap>() companion object { @JvmStatic - fun forEmptyTree(groupingEnabled: Boolean, grouper: ModuleGrouper, - moduleGroupNodeFactory: (ModuleGroup) -> N, moduleNodeFactory: (Module, ModuleGrouper) -> N, - nodeComparator: Comparator) = - ModuleGroupingTreeHelper(groupingEnabled, grouper, moduleGroupNodeFactory, moduleNodeFactory, nodeComparator) + fun forEmptyTree(groupingEnabled: Boolean, grouping: ModuleGroupingImplementation, + moduleGroupNodeFactory: (ModuleGroup) -> N, moduleNodeFactory: (M) -> N, + nodeComparator: Comparator) = + ModuleGroupingTreeHelper(groupingEnabled, grouping, moduleGroupNodeFactory, moduleNodeFactory, nodeComparator) @JvmStatic - fun forTree(rootNode: N, moduleGroupByNode: (N) -> ModuleGroup?, moduleByNode: (N) -> Module?, - groupingEnabled: Boolean, grouper: ModuleGrouper, - moduleGroupNodeFactory: (ModuleGroup) -> N, moduleNodeFactory: (Module, ModuleGrouper) -> N, - nodeComparator: Comparator): ModuleGroupingTreeHelper { - val helper = ModuleGroupingTreeHelper(groupingEnabled, grouper, moduleGroupNodeFactory, moduleNodeFactory, nodeComparator) + fun forTree(rootNode: N, moduleGroupByNode: (N) -> ModuleGroup?, moduleByNode: (N) -> M?, + groupingEnabled: Boolean, grouping: ModuleGroupingImplementation, + moduleGroupNodeFactory: (ModuleGroup) -> N, moduleNodeFactory: (M) -> N, + nodeComparator: Comparator): ModuleGroupingTreeHelper { + val helper = ModuleGroupingTreeHelper(groupingEnabled, grouping, moduleGroupNodeFactory, moduleNodeFactory, nodeComparator) TreeUtil.traverse(rootNode) { node -> @Suppress("UNCHECKED_CAST") val group = moduleGroupByNode(node as N) @@ -83,19 +84,25 @@ class ModuleGroupingTreeHelper private constructor( } return helper } + + @JvmStatic + fun createDefaultGrouping(grouper: ModuleGrouper) = object : ModuleGroupingImplementation { + override fun getGroupPath(m: Module) = grouper.getGroupPath(m) + override fun getModuleAsGroupPath(m: Module) = grouper.getModuleAsGroupPath(m) + } } - fun createModuleNodes(modules: Collection, rootNode: N, model: DefaultTreeModel): List { + fun createModuleNodes(modules: Collection, rootNode: N, model: DefaultTreeModel): List { val nodes = modules.map { createModuleNode(it, rootNode, model) } TreeUtil.sortRecursively(rootNode, nodeComparator) model.nodeStructureChanged(rootNode) return nodes } - private fun createModuleNode(module: Module, rootNode: N, model: DefaultTreeModel): N { - val group = ModuleGroup(grouper.getGroupPath(module)) + private fun createModuleNode(module: M, rootNode: N, model: DefaultTreeModel): N { + val group = ModuleGroup(grouping.getGroupPath(module)) val parentNode = getOrCreateNodeForModuleGroup(group, rootNode, model, true) - val moduleNode = moduleNodeFactory(module, grouper) + val moduleNode = moduleNodeFactory(module) insertModuleNode(moduleNode, parentNode, module, model, true) return moduleNode } @@ -103,7 +110,7 @@ class ModuleGroupingTreeHelper private constructor( /** * If [bulkOperation] is true, no events will be fired and new node will be added into arbitrary place in the children list */ - private fun insertModuleNode(moduleNode: N, parentNode: N, module: Module, model: DefaultTreeModel, bulkOperation: Boolean) { + private fun insertModuleNode(moduleNode: N, parentNode: N, module: M, model: DefaultTreeModel, bulkOperation: Boolean) { val moduleAsGroup = moduleAsGroup(module) if (moduleAsGroup != null) { val oldModuleGroupNode = nodeForGroup[moduleAsGroup] @@ -122,7 +129,7 @@ class ModuleGroupingTreeHelper private constructor( insertNode(moduleNode, parentNode, model, bulkOperation) } - private fun moduleAsGroup(module: Module) = grouper.getModuleAsGroupPath(module)?.let(::ModuleGroup) + private fun moduleAsGroup(module: M) = grouping.getModuleAsGroupPath(module)?.let(::ModuleGroup) private fun moveChildren(fromNode: N, toNode: N, model: DefaultTreeModel) { val children = TreeUtil.listChildren(fromNode) @@ -150,7 +157,7 @@ class ModuleGroupingTreeHelper private constructor( node = moduleGroupNodeFactory(current) insertNode(node, parentNode, model, bulkOperation) nodeForGroup[current] = node - nodeData[node] = ModuleTreeNodeData(null,group) + nodeData[node] = ModuleTreeNodeData(null,group) } parentNode = node } @@ -174,14 +181,14 @@ class ModuleGroupingTreeHelper private constructor( createModuleNodes(modules, rootNode, model) } - fun moveModuleNodesToProperGroup(nodes: List>, rootNode: N, model: DefaultTreeModel, tree: JTree) { + fun moveModuleNodesToProperGroup(nodes: List>, rootNode: N, model: DefaultTreeModel, tree: JTree) { nodes.forEach { moveModuleNodeToProperGroup(it.first, it.second, rootNode, model, tree) } } - fun moveModuleNodeToProperGroup(node: N, module: Module, rootNode: N, model: DefaultTreeModel, tree: JTree): N { - val actualGroup = ModuleGroup(grouper.getGroupPath(module)) + fun moveModuleNodeToProperGroup(node: N, module: M, rootNode: N, model: DefaultTreeModel, tree: JTree): N { + val actualGroup = ModuleGroup(grouping.getGroupPath(module)) val parent = node.parent val nodeAsGroup = nodeData[node]?.group val expectedParent = if (groupingEnabled && !actualGroup.groupPathList.isEmpty()) nodeForGroup[actualGroup] else rootNode @@ -205,7 +212,7 @@ class ModuleGroupingTreeHelper private constructor( removeEmptySyntheticModuleGroupNodes(parent, model) val newParent = getOrCreateNodeForModuleGroup(actualGroup, rootNode, model, false) - val newNode = moduleNodeFactory(module, grouper) + val newNode = moduleNodeFactory(module) insertModuleNode(newNode, newParent, module, model, false) if (wasSelected) { @@ -220,6 +227,7 @@ class ModuleGroupingTreeHelper private constructor( while (parent is MutableTreeNode && parent in nodeData && nodeData[parent]?.module == null && parent.childCount == 0) { val grandParent = parent.parent model.removeNodeFromParent(parent) + @Suppress("UNCHECKED_CAST") removeNode(parent as N) parent = grandParent } @@ -242,4 +250,9 @@ class ModuleGroupingTreeHelper private constructor( fun getGroupByNodeMap() = nodeData.mapValues { it.value.group }.filterValues { it != null } } -private class ModuleTreeNodeData(val module: Module?, val group: ModuleGroup?) \ No newline at end of file +private class ModuleTreeNodeData(val module: M?, val group: ModuleGroup?) + +interface ModuleGroupingImplementation { + fun getGroupPath(m: M): List + fun getModuleAsGroupPath(m: M): List? +} \ No newline at end of file diff --git a/platform/lang-impl/testSources/com/intellij/ide/projectView/ModuleGroupingTreeHelperTest.kt b/platform/lang-impl/testSources/com/intellij/ide/projectView/ModuleGroupingTreeHelperTest.kt index c6317194b0a4..ce166050efed 100644 --- a/platform/lang-impl/testSources/com/intellij/ide/projectView/ModuleGroupingTreeHelperTest.kt +++ b/platform/lang-impl/testSources/com/intellij/ide/projectView/ModuleGroupingTreeHelperTest.kt @@ -16,14 +16,11 @@ package com.intellij.ide.projectView import com.intellij.ide.projectView.impl.ModuleGroup +import com.intellij.ide.projectView.impl.ModuleGroupingImplementation import com.intellij.ide.projectView.impl.ModuleGroupingTreeHelper -import com.intellij.module.getQualifiedNameModuleGrouper -import com.intellij.module.renameModule -import com.intellij.openapi.module.Module -import com.intellij.openapi.module.ModuleGrouper import com.intellij.openapi.util.Pair -import com.intellij.testFramework.PlatformTestCase import com.intellij.testFramework.PlatformTestUtil +import com.intellij.testFramework.UsefulTestCase import com.intellij.ui.treeStructure.Tree import com.intellij.util.ui.tree.TreeUtil import junit.framework.TestCase @@ -34,7 +31,7 @@ import javax.swing.tree.DefaultTreeModel /** * @author nik */ -class ModuleGroupingTreeHelperTest: PlatformTestCase() { +class ModuleGroupingTreeHelperTest: UsefulTestCase() { private lateinit var tree: Tree private lateinit var root: MockModuleTreeNode private lateinit var model: DefaultTreeModel @@ -128,7 +125,7 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { main util""") val node = nodes.find { it.second.name == "main" }!! - renameModule(node.second, "a.main") + node.second.name = "a.main" moveModuleNodeToProperGroupAndCheckResult(node, """ -root -a @@ -143,7 +140,7 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { -a a.main""") val node = nodes.find { it.second.name == "a.main" }!! - renameModule(node.second, "main") + node.second.name = "main" moveModuleNodeToProperGroupAndCheckResult(node, """ -root a @@ -153,7 +150,7 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { fun `test move module node to parent module`() { val nodes = createHelper().createModuleNodes("a", "main") val node = nodes.find { it.second.name == "main" }!! - renameModule(node.second, "a.main") + node.second.name = "a.main" moveModuleNodeToProperGroupAndCheckResult(node, """ -root -a @@ -163,7 +160,7 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { fun `test module node become parent module`() { val nodes = createHelper().createModuleNodes("b", "a.main") val node = nodes.find { it.second.name == "b" }!! - renameModule(node.second, "a") + node.second.name = "a" moveModuleNodeToProperGroupAndCheckResult(node, """ -root -a @@ -173,7 +170,7 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { fun `test parent module become ordinary module`() { val nodes = createHelper().createModuleNodes("a", "a.main") val node = nodes.find { it.second.name == "a" }!! - renameModule(node.second, "b") + node.second.name = "b" moveModuleNodeToProperGroupAndCheckResult(node, """ -root -a @@ -189,7 +186,7 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { } } - private fun moveModuleNodeToProperGroupAndCheckResult(node: Pair, + private fun moveModuleNodeToProperGroupAndCheckResult(node: Pair, expected: String) { val helper = createHelperFromTree() helper.checkConsistency() @@ -198,14 +195,14 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { helper.checkConsistency() } - private fun ModuleGroupingTreeHelper.moveAllModuleNodesAndCheckResult(expected: String) { + private fun ModuleGroupingTreeHelper.moveAllModuleNodesAndCheckResult(expected: String) { checkConsistency() moveAllModuleNodesToProperGroups(root, model) assertTreeEqual(expected) checkConsistency() } - private fun ModuleGroupingTreeHelper.createModuleNodes(vararg names: String): List> { + private fun ModuleGroupingTreeHelper.createModuleNodes(vararg names: String): List> { val modules = createModules(*names) val nodes = createModuleNodes(modules, root, model) return nodes.map { Pair(it, (it as MockModuleNode).module)} @@ -216,23 +213,21 @@ class ModuleGroupingTreeHelperTest: PlatformTestCase() { PlatformTestUtil.assertTreeEqual(tree, expected.trimIndent() + "\n") } - private fun createHelper(enableGrouping: Boolean = true): ModuleGroupingTreeHelper { - val grouper = getQualifiedNameModuleGrouper(myProject) - return ModuleGroupingTreeHelper.forEmptyTree(enableGrouping, grouper, ::MockModuleGroupNode, ::MockModuleNode, nodeComparator) + private fun createHelper(enableGrouping: Boolean = true): ModuleGroupingTreeHelper { + return ModuleGroupingTreeHelper.forEmptyTree(enableGrouping, mockModuleGrouping, ::MockModuleGroupNode, ::MockModuleNode, nodeComparator) } - private fun createHelperFromTree(enableGrouping: Boolean = true): ModuleGroupingTreeHelper { - val grouper = getQualifiedNameModuleGrouper(myProject) + private fun createHelperFromTree(enableGrouping: Boolean = true): ModuleGroupingTreeHelper { return ModuleGroupingTreeHelper.forTree(root, { it.moduleGroup }, { (it as? MockModuleNode)?.module }, - enableGrouping, grouper, ::MockModuleGroupNode, ::MockModuleNode, nodeComparator) + enableGrouping, mockModuleGrouping, ::MockModuleGroupNode, ::MockModuleNode, nodeComparator) } - private fun createModules(vararg names: String) = names.map { createModule(it) } + private fun createModules(vararg names: String) = names.map { MockModule(it) } - private fun ModuleGroupingTreeHelper.checkConsistency() { + private fun ModuleGroupingTreeHelper.checkConsistency() { val expectedNodeForGroup = HashMap(getNodeForGroupMap()) val expectedGroupByNode = HashMap(getGroupByNodeMap()) - val expectedModuleByNode = HashMap(getModuleByNodeMap()) + val expectedModuleByNode = HashMap(getModuleByNodeMap()) TreeUtil.traverse(root, { o -> val node = o as MockModuleTreeNode if (node == root) return@traverse true @@ -259,8 +254,14 @@ private open class MockModuleTreeNode(userObject: Any, val text: String = userOb override fun toString() = text } -private class MockModuleNode(val module: Module, grouper: ModuleGrouper): MockModuleTreeNode(module, module.name) { - override val moduleGroup = grouper.getModuleAsGroupPath(module)?.let(::ModuleGroup) +private val mockModuleGrouping = object : ModuleGroupingImplementation { + override fun getGroupPath(m: MockModule) = m.name.split('.').dropLast(1) + override fun getModuleAsGroupPath(m: MockModule) = m.name.split('.') +} +private class MockModule(var name: String) + +private class MockModuleNode(val module: MockModule): MockModuleTreeNode(module, module.name) { + override val moduleGroup = ModuleGroup(mockModuleGrouping.getModuleAsGroupPath(module)!!) } private class MockModuleGroupNode(override val moduleGroup: ModuleGroup): MockModuleTreeNode(moduleGroup) \ No newline at end of file