project configuration: automatically group modules accordingly to qualified names (IDEA-166061)

Initial implementation: if 'project.qualified.module.names' registry option is enabled and the project has no groups defined, modules are automatically grouped accordingly to their names.
This commit is contained in:
nik
2017-01-18 09:03:38 +03:00
parent 62f0f06b02
commit 1d2d4bc17f
26 changed files with 481 additions and 209 deletions
@@ -16,7 +16,6 @@
package com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.openapi.util.Comparing;
import com.intellij.packaging.elements.PackagingElement;
import com.intellij.packaging.ui.ArtifactEditorContext;
import com.intellij.packaging.ui.PackagingSourceItem;
@@ -26,7 +25,6 @@ import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.PlatformIcons;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -35,20 +33,20 @@ import java.util.List;
*/
public class ModuleGroupItem extends PackagingSourceItem {
private final String myGroupName;
private final String[] myPath;
private final List<String> myPath;
public ModuleGroupItem(String[] path) {
public ModuleGroupItem(@NotNull List<String> path) {
super(false);
myGroupName = path[path.length - 1];
myGroupName = path.get(path.size() - 1);
myPath = path;
}
public boolean equals(Object obj) {
return obj instanceof ModuleGroupItem && Comparing.equal(myPath, ((ModuleGroupItem)obj).myPath);
return obj instanceof ModuleGroupItem && myPath.equals(((ModuleGroupItem)obj).myPath);
}
public int hashCode() {
return Arrays.hashCode(myPath);
return myPath.hashCode();
}
@Override
@@ -62,7 +60,7 @@ public class ModuleGroupItem extends PackagingSourceItem {
return Collections.emptyList();
}
public String[] getPath() {
public List<String> getPath() {
return myPath;
}
@@ -16,6 +16,7 @@
package com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.roots.DependencyScope;
@@ -120,14 +121,14 @@ public class ModuleSourceItemGroup extends PackagingSourceItem {
@Override
public String getPresentableName() {
return myModule.getName();
return ModuleGrouper.Companion.instanceFor(myContext.getProject(), myContext.getModifiableModuleModel()).getPresentableName(myModule);
}
@Override
public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
SimpleTextAttributes commentAttributes) {
presentationData.setIcon(ModuleType.get(myModule).getIcon());
presentationData.addText(myModule.getName(), mainAttributes);
presentationData.addText(getPresentableName(), mainAttributes);
}
@Override
@@ -15,23 +15,22 @@
*/
package com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
import com.intellij.packaging.impl.elements.FileCopyPackagingElement;
import com.intellij.packaging.impl.elements.ProductionModuleOutputElementType;
import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
import com.intellij.packaging.impl.elements.PackagingElementFactoryImpl;
import com.intellij.packaging.impl.elements.ProductionModuleOutputElementType;
import com.intellij.packaging.ui.ArtifactEditorContext;
import com.intellij.packaging.ui.PackagingSourceItem;
import com.intellij.packaging.ui.PackagingSourceItemsProvider;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -46,7 +45,7 @@ public class ModulesAndLibrariesSourceItemsProvider extends PackagingSourceItems
public Collection<? extends PackagingSourceItem> getSourceItems(@NotNull ArtifactEditorContext editorContext, @NotNull Artifact artifact,
PackagingSourceItem parent) {
if (parent == null) {
return createModuleItems(editorContext, ArrayUtil.EMPTY_STRING_ARRAY);
return createModuleItems(editorContext, Collections.emptyList());
}
else if (parent instanceof ModuleGroupItem) {
return createModuleItems(editorContext, ((ModuleGroupItem)parent).getPath());
@@ -85,25 +84,21 @@ public class ModulesAndLibrariesSourceItemsProvider extends PackagingSourceItems
}
@NotNull
private static Collection<? extends PackagingSourceItem> createModuleItems(@NotNull ArtifactEditorContext editorContext, @NotNull String[] groupPath) {
final Module[] modules = editorContext.getModulesProvider().getModules();
private static Collection<? extends PackagingSourceItem> createModuleItems(@NotNull ArtifactEditorContext editorContext, @NotNull List<String> groupPath) {
final List<PackagingSourceItem> items = new ArrayList<>();
ModuleGrouper grouper = ModuleGrouper.Companion.instanceFor(editorContext.getProject(), editorContext.getModifiableModuleModel());
Set<String> groups = new HashSet<>();
for (Module module : modules) {
String[] path = ModuleManager.getInstance(editorContext.getProject()).getModuleGroupPath(module);
if (path == null) {
path = ArrayUtil.EMPTY_STRING_ARRAY;
}
for (Module module : grouper.getAllModules()) {
List<String> path = grouper.getGroupPath(module);
if (Comparing.equal(path, groupPath)) {
items.add(new ModuleSourceItemGroup(module));
}
else if (ArrayUtil.startsWith(path, groupPath)) {
groups.add(path[groupPath.length]);
else if (ContainerUtil.startsWith(path, groupPath)) {
groups.add(path.get(groupPath.size()));
}
}
for (String group : groups) {
items.add(0, new ModuleGroupItem(ArrayUtil.append(groupPath, group)));
items.add(0, new ModuleGroupItem(ContainerUtil.append(groupPath, group)));
}
return items;
}
@@ -25,6 +25,7 @@ import com.intellij.ide.IdeBundle;
import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.ide.projectView.impl.ModuleGroup;
import com.intellij.ide.projectView.impl.ModuleGroupUtil;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.ide.util.projectWizard.ModuleBuilder;
import com.intellij.ide.util.projectWizard.NamePathComponent;
import com.intellij.ide.util.projectWizard.ProjectWizardUtil;
@@ -68,10 +69,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.*;
import java.awt.*;
import java.util.*;
import java.util.List;
@@ -222,24 +220,22 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
final Module[] modules = myModuleManager.getModules();
for (final Module module : modules) {
ModuleConfigurable configurable = new ModuleConfigurable(myContext.myModulesConfigurator, module, TREE_UPDATER);
final MyNode moduleNode = new MyNode(configurable);
ModuleGrouper moduleGrouper = getModuleGrouper();
final MyNode moduleNode = new ModuleNode(configurable, moduleGrouper);
boolean nodesAdded = myFacetEditorFacade.addFacetsNodes(module, moduleNode);
nodesAdded |= addNodesFromExtensions(module, moduleNode);
if (nodesAdded) {
myTree.setShowsRootHandles(true);
}
final String[] groupPath = myHideModuleGroups ? null : myContext.myModulesConfigurator.getModuleModel().getModuleGroupPath(module);
if (groupPath == null || groupPath.length == 0){
final List<String> groupPath = myHideModuleGroups ? Collections.emptyList() : moduleGrouper.getGroupPath(module);
if (groupPath.isEmpty()) {
myRoot.add(moduleNode);
}
else {
final MyNode moduleGroupNode = ModuleGroupUtil
.buildModuleGroupPath(new ModuleGroup(groupPath), myRoot, moduleGroup2NodeMap,
parentChildRelation -> parentChildRelation.getParent().add(parentChildRelation.getChild()),
moduleGroup -> {
final NamedConfigurable moduleGroupConfigurable = createModuleGroupConfigurable(moduleGroup);
return new MyNode(moduleGroupConfigurable, true);
});
ModuleStructureConfigurable::createModuleGroupNode);
moduleGroupNode.add(moduleNode);
}
}
@@ -256,6 +252,15 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
//myProjectNode.add(myLevel2Nodes.get(LibraryTablesRegistrar.PROJECT_LEVEL));
}
@NotNull
private static MyNode createModuleGroupNode(ModuleGroup moduleGroup) {
final NamedConfigurable moduleGroupConfigurable = new TextConfigurable<>(moduleGroup, moduleGroup.toString(),
ProjectBundle.message("module.group.banner.text", moduleGroup.toString()),
ProjectBundle.message("project.roots.module.groups.text"),
PlatformIcons.CLOSED_MODULE_GROUP_ICON);
return new ModuleGroupNode(moduleGroupConfigurable, moduleGroup);
}
private void addRootNodesFromExtensions(final MyNode root, final Project project) {
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
extension.addRootNodes(root, project, TREE_UPDATER);
@@ -281,17 +286,13 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
nodes[i ++] = node;
}
for (final MyNode moduleNode : nodes) {
final String[] groupPath = myHideModuleGroups
? null
: group != null ? group.getGroupPath() : null;
if (groupPath == null || groupPath.length == 0){
List<String> groupPath = myHideModuleGroups
? Collections.emptyList()
: group != null ? group.getGroupPathList() : Collections.emptyList();
if (groupPath.isEmpty()) {
myRoot.add(moduleNode);
} else {
final MyNode moduleGroupNode = ModuleGroupUtil
.updateModuleGroupPath(new ModuleGroup(groupPath), myRoot, group1 -> findNodeByObject(myRoot, group1), parentChildRelation -> parentChildRelation.getParent().add(parentChildRelation.getChild()), moduleGroup -> {
final NamedConfigurable moduleGroupConfigurable = createModuleGroupConfigurable(moduleGroup);
return new MyNode(moduleGroupConfigurable, true);
});
final MyNode moduleGroupNode = getOrCreateModuleGroupNode(groupPath, null);
moduleGroupNode.add(moduleNode);
}
Module module = (Module)moduleNode.getConfigurable().getEditableObject();
@@ -302,6 +303,18 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
return true;
}
private MyNode getOrCreateModuleGroupNode(List<String> groupPath, @Nullable DefaultTreeModel treeModel) {
return ModuleGroupUtil.updateModuleGroupPath(new ModuleGroup(groupPath), myRoot, group1 -> findNodeByObject(myRoot, group1),
parentChildRelation -> {
MyNode parent = parentChildRelation.getParent();
parent.add(parentChildRelation.getChild());
if (treeModel != null) {
treeModel.nodesWereInserted(parent, new int[] {parent.getChildCount() - 1});
}
},
ModuleStructureConfigurable::createModuleGroupNode);
}
@Override
protected Comparator<MyNode> getNodeComparator() {
List<Comparator<MyNode>> comparators = ContainerUtil
@@ -456,6 +469,10 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
return r != null ? result.doWhenDone(r) : result;
}
private ModuleGrouper getModuleGrouper() {
return ModuleGrouper.Companion.instanceFor(myProject, myContext.myModulesConfigurator.getModuleModel());
}
public static ModuleStructureConfigurable getInstance(final Project project) {
return ServiceManager.getService(project, ModuleStructureConfigurable.class);
@@ -534,7 +551,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
}
private void addModuleNode(final Module module) {
final MyNode node = new MyNode(new ModuleConfigurable(myContext.myModulesConfigurator, module, TREE_UPDATER));
final MyNode node = new ModuleNode(new ModuleConfigurable(myContext.myModulesConfigurator, module, TREE_UPDATER), getModuleGrouper());
final TreePath selectionPath = myTree.getSelectionPath();
MyNode parent = null;
if (selectionPath != null) {
@@ -548,7 +565,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
final String[] groupPath = modifiableModuleModel.getModuleGroupPath((Module)o);
if (groupPath != null) {
modifiableModuleModel.setModuleGroupPath(module, groupPath);
parent = findNodeByObject(myRoot, new ModuleGroup(groupPath));
parent = findNodeByObject(myRoot, new ModuleGroup(Arrays.asList(groupPath)));
}
}
}
@@ -595,13 +612,6 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
return myContext;
}
private static TextConfigurable<ModuleGroup> createModuleGroupConfigurable(final ModuleGroup moduleGroup) {
return new TextConfigurable<>(moduleGroup, moduleGroup.toString(),
ProjectBundle.message("module.group.banner.text", moduleGroup.toString()),
ProjectBundle.message("project.roots.module.groups.text"),
PlatformIcons.CLOSED_MODULE_GROUP_ICON);
}
private static boolean canBeCopiedByExtension(final NamedConfigurable configurable) {
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
if (extension.canBeCopied(configurable)) {
@@ -617,6 +627,69 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
}
}
private class ModuleNode extends MyNode {
private final ModuleGrouper myGrouper;
public ModuleNode(@NotNull ModuleConfigurable configurable, ModuleGrouper grouper) {
super(configurable);
myGrouper = grouper;
}
@NotNull
@Override
public String getDisplayName() {
return myGrouper.getPresentableName(getModule());
}
private Module getModule() {
return ((ModuleConfigurable)getConfigurable()).getModule();
}
@Override
protected void reloadNode(DefaultTreeModel treeModel) {
TreeNode parent = getParent();
if (parent instanceof ModuleGroupNode) {
List<String> actualPath = ((ModuleGroupNode)parent).myModuleGroup.getGroupPathList();
List<String> expectedPath = myGrouper.getGroupPath(getModule());
if (!actualPath.equals(expectedPath)) {
TreePath path = myTree.getSelectionPath();
boolean wasSelected = path != null && this.equals(path.getLastPathComponent());
boolean autoScrollWasEnabled = myAutoScrollEnabled;
try {
myAutoScrollEnabled = false;
MyNode newParent = getOrCreateModuleGroupNode(expectedPath, treeModel);
treeModel.removeNodeFromParent(this);
while (parent instanceof ModuleGroupNode && parent.getChildCount() == 0) {
TreeNode grandParent = parent.getParent();
treeModel.removeNodeFromParent((MutableTreeNode)parent);
parent = grandParent;
}
newParent.add(this);
treeModel.nodesWereInserted(newParent, new int[] {newParent.getChildCount()-1});
if (wasSelected) {
myTree.expandPath(TreeUtil.getPath(myRoot, newParent));
myTree.setSelectionPath(TreeUtil.getPath(myRoot, this));
}
}
finally {
myAutoScrollEnabled = autoScrollWasEnabled;
}
}
}
super.reloadNode(treeModel);
}
}
private static class ModuleGroupNode extends MyNode {
private final ModuleGroup myModuleGroup;
public ModuleGroupNode(@NotNull NamedConfigurable configurable, ModuleGroup moduleGroup) {
super(configurable, true);
myModuleGroup = moduleGroup;
}
}
private class FacetInModuleRemoveHandler extends RemoveConfigurableHandler<Facet> {
public FacetInModuleRemoveHandler() {
super(FacetConfigurable.class);
@@ -727,11 +800,10 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
if (selectionPath != null){
selection = (DefaultMutableTreeNode)selectionPath.getLastPathComponent();
}
final ModifiableModuleModel model = myContext.myModulesConfigurator.getModuleModel();
final Module[] modules = model.getModules();
for (Module module : modules) {
final String[] groupPath = model.getModuleGroupPath(module);
updateProjectTree(new Module[]{module}, groupPath != null ? new ModuleGroup(groupPath) : null);
ModuleGrouper grouper = getModuleGrouper();
for (Module module : grouper.getAllModules()) {
final List<String> groupPath = grouper.getGroupPath(module);
updateProjectTree(new Module[]{module}, !groupPath.isEmpty() ? new ModuleGroup(groupPath) : null);
}
if (state) {
removeModuleGroups();
@@ -36,7 +36,7 @@ public abstract class BaseClassesAnalysisAction extends BaseAnalysisAction {
super(title, analysisNoon);
}
protected abstract void analyzeClasses(final Project project, final AnalysisScope scope, ProgressIndicator indicator);
protected abstract void analyzeClasses(@NotNull Project project, @NotNull AnalysisScope scope, @NotNull ProgressIndicator indicator);
@Override
protected void analyze(@NotNull final Project project, @NotNull final AnalysisScope scope) {
@@ -79,7 +79,7 @@ public abstract class BaseClassesAnalysisAction extends BaseAnalysisAction {
});
}
private void doAnalyze(final Project project, final AnalysisScope scope) {
private void doAnalyze(@NotNull Project project, @NotNull AnalysisScope scope) {
ProgressManager.getInstance().run(new Task.Backgroundable(project, AnalysisScopeBundle.message("analyzing.project"), true) {
@Override
@Nullable
@@ -94,7 +94,7 @@ public abstract class BaseClassesAnalysisAction extends BaseAnalysisAction {
});
}
private void compileAndAnalyze(final Project project, final AnalysisScope scope) {
private void compileAndAnalyze(@NotNull Project project, @NotNull AnalysisScope scope) {
if (project.isDisposed()) {
return;
}
@@ -18,6 +18,7 @@ package com.intellij.packageDependencies.ui;
import com.intellij.analysis.AnalysisScopeBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.projectView.impl.ModuleGroup;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
@@ -71,6 +72,7 @@ public class TreeModelBuilder {
private final Map<ScopeType, Map<Module, ModuleNode>> myModuleNodes = new HashMap<>();
private final Map<ScopeType, Map<String, ModuleGroupNode>> myModuleGroupNodes = new HashMap<>();
private final Map<ScopeType, Map<OrderEntry, LibraryNode>> myLibraryNodes = new HashMap<>();
private final ModuleGrouper myModuleGrouper;
private int myScannedFileCount;
private int myTotalFileCount;
private int myMarkedFileCount;
@@ -84,7 +86,7 @@ public class TreeModelBuilder {
public static final String TEST_NAME = AnalysisScopeBundle.message("package.dependencies.test.node.text");
public static final String LIBRARY_NAME = AnalysisScopeBundle.message("package.dependencies.library.node.text");
public TreeModelBuilder(Project project, boolean showIndividualLibs, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
public TreeModelBuilder(@NotNull Project project, boolean showIndividualLibs, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
myProject = project;
final boolean multiModuleProject = ModuleManager.getInstance(project).getModules().length > 1;
myShowModules = settings.UI_SHOW_MODULES && multiModuleProject;
@@ -93,6 +95,7 @@ public class TreeModelBuilder {
myShowFiles = settings.UI_SHOW_FILES;
myShowIndividualLibs = showIndividualLibs;
myShowModuleGroups = settings.UI_SHOW_MODULE_GROUPS && multiModuleProject;
myModuleGrouper = ModuleGrouper.Companion.instanceFor(project);
myMarker = marker;
myAddUnmarkedFiles = !settings.UI_FILTER_LEGALS;
myRoot = new RootNode(project);
@@ -383,10 +386,9 @@ public class TreeModelBuilder {
}
ModuleNode node = getMap(myModuleNodes, scopeType).get(module);
if (node != null) return node;
node = new ModuleNode(module);
final ModuleManager moduleManager = ModuleManager.getInstance(myProject);
final String[] groupPath = moduleManager.getModuleGroupPath(module);
if (groupPath == null) {
node = new ModuleNode(module, myShowModuleGroups ? myModuleGrouper : null);
final List<String> groupPath = myModuleGrouper.getGroupPath(module);
if (groupPath.isEmpty()) {
getMap(myModuleNodes, scopeType).put(module, node);
getRootNode(scopeType).add(node);
return node;
@@ -400,7 +402,7 @@ public class TreeModelBuilder {
return node;
}
private PackageDependenciesNode getParentModuleGroup(String [] groupPath, ScopeType scopeType){
private PackageDependenciesNode getParentModuleGroup(List<String> groupPath, ScopeType scopeType){
final String key = StringUtil.join(groupPath, ",");
ModuleGroupNode groupNode = getMap(myModuleGroupNodes, scopeType).get(key);
if (groupNode == null) {
@@ -408,10 +410,8 @@ public class TreeModelBuilder {
getMap(myModuleGroupNodes, scopeType).put(key, groupNode);
getRootNode(scopeType).add(groupNode);
}
if (groupPath.length > 1) {
String [] path = new String[groupPath.length - 1];
System.arraycopy(groupPath, 0, path, 0, groupPath.length - 1);
final PackageDependenciesNode node = getParentModuleGroup(path, scopeType);
if (groupPath.size() > 1) {
final PackageDependenciesNode node = getParentModuleGroup(groupPath.subList(0, groupPath.size()-1), scopeType);
node.add(groupNode);
}
return groupNode;
@@ -24,7 +24,10 @@ import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.ui.Messages;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import java.util.Collections;
import java.util.List;
public class MoveModulesToSubGroupAction extends MoveModulesToGroupAction {
public MoveModulesToSubGroupAction(ModuleGroup moduleGroup) {
@@ -42,18 +45,18 @@ public class MoveModulesToSubGroupAction extends MoveModulesToGroupAction {
public void actionPerformed(AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
final Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(dataContext);
final String[] newGroup;
final List<String> newGroup;
if (myModuleGroup != null) {
String message = IdeBundle.message("prompt.specify.name.of.module.subgroup", myModuleGroup.presentableText(), whatToMove(modules));
String subgroup = Messages.showInputDialog(message, IdeBundle.message("title.module.sub.group"), Messages.getQuestionIcon());
if (subgroup == null || "".equals(subgroup.trim())) return;
newGroup = ArrayUtil.append(myModuleGroup.getGroupPath(), subgroup);
newGroup = ContainerUtil.append(myModuleGroup.getGroupPathList(), subgroup);
}
else {
String message = IdeBundle.message("prompt.specify.module.group.name", whatToMove(modules));
String group = Messages.showInputDialog(message, IdeBundle.message("title.module.group"), Messages.getQuestionIcon());
if (group == null || "".equals(group.trim())) return;
newGroup = new String[]{group};
newGroup = Collections.singletonList(group);
}
doMove(modules, new ModuleGroup(newGroup), dataContext);
@@ -17,22 +17,24 @@
package com.intellij.ide.projectView.impl;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.project.Project;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class ModuleGroup {
public static final DataKey<ModuleGroup[]> ARRAY_DATA_KEY = DataKey.create("moduleGroup.array");
private final List<String> myGroupPath;
private final String[] myGroupPath;
public ModuleGroup(@NotNull String[] groupPath) {
public ModuleGroup(@NotNull List<String> groupPath) {
myGroupPath = groupPath;
}
@@ -40,58 +42,45 @@ public class ModuleGroup {
if (this == o) return true;
if (!(o instanceof ModuleGroup)) return false;
final ModuleGroup moduleGroup = (ModuleGroup)o;
if (!Arrays.equals(myGroupPath, moduleGroup.myGroupPath)) return false;
return true;
return myGroupPath.equals(((ModuleGroup)o).myGroupPath);
}
public int hashCode() {
return myGroupPath[myGroupPath.length-1].hashCode();
return myGroupPath.hashCode();
}
public String[] getGroupPath() {
return ArrayUtil.toStringArray(myGroupPath);
}
public List<String> getGroupPathList() {
return myGroupPath;
}
@NotNull
public Collection<Module> modulesInGroup(Project project, boolean recursively) {
final Module[] allModules = ModuleManager.getInstance(project).getModules();
return modulesInGroup(ModuleGrouper.Companion.instanceFor(project), recursively);
}
@NotNull
public Collection<Module> modulesInGroup(ModuleGrouper grouper, boolean recursively) {
List<Module> result = new ArrayList<>();
for (final Module module : allModules) {
String[] group = ModuleManager.getInstance(project).getModuleGroupPath(module);
if (group == null) continue;
if (Arrays.equals(myGroupPath, group) || (recursively && isChild(myGroupPath, group))) {
for (final Module module : grouper.getAllModules()) {
List<String> group = grouper.getGroupPath(module);
if (myGroupPath.equals(group) || (recursively && isChild(myGroupPath, group))) {
result.add(module);
}
}
return result;
}
public Collection<ModuleGroup> childGroups(Project project) {
return childGroups(null, project);
}
public Collection<ModuleGroup> childGroups(ModifiableModuleModel model, Project project) {
final Module[] allModules;
if ( model != null ) {
allModules = model.getModules();
} else {
allModules = ModuleManager.getInstance(project).getModules();
}
@NotNull
public Collection<ModuleGroup> childGroups(ModuleGrouper grouper) {
Set<ModuleGroup> result = new THashSet<>();
for (Module module : allModules) {
String[] group;
if ( model != null ) {
group = model.getModuleGroupPath(module);
} else {
group = ModuleManager.getInstance(project).getModuleGroupPath(module);
}
if (group == null) continue;
final String[] directChild = directChild(myGroupPath, group);
if (directChild != null) {
for (Module module : grouper.getAllModules()) {
List<String> group = grouper.getGroupPath(module);
if (isChild(myGroupPath, group)) {
final List<String> directChild = ContainerUtil.append(myGroupPath, group.get(myGroupPath.size()));
result.add(new ModuleGroup(directChild));
}
}
@@ -99,25 +88,15 @@ public class ModuleGroup {
return result;
}
private static boolean isChild(final String[] parent, final String[] descendant) {
if (parent.length >= descendant.length) return false;
for (int i = 0; i < parent.length; i++) {
String group = parent[i];
if (!group.equals(descendant[i])) return false;
}
return true;
}
private static String[] directChild(final String[] parent, final String[] descendant) {
if (!isChild(parent, descendant)) return null;
return ArrayUtil.append(parent, descendant[parent.length]);
private static boolean isChild(final List<String> parent, final List<String> descendant) {
return descendant.size() > parent.size() && descendant.subList(0, parent.size()).equals(parent);
}
public String presentableText() {
return "'" + myGroupPath[myGroupPath.length - 1] + "'";
return "'" + myGroupPath.get(myGroupPath.size() - 1) + "'";
}
public String toString() {
return myGroupPath[myGroupPath.length - 1];
return myGroupPath.get(myGroupPath.size() - 1);
}
}
@@ -20,6 +20,8 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NonNls;
import java.util.List;
public class ModuleGroupUrl extends AbstractUrl {
@NonNls private static final String ELEMENT_TYPE = "module_group";
@@ -30,7 +32,7 @@ public class ModuleGroupUrl extends AbstractUrl {
@Override
public Object[] createPath(Project project) {
final String[] groupPath = url.split(";");
List<String> groupPath = StringUtil.split(url, ";");
return new Object[]{new ModuleGroup(groupPath)};
}
@@ -22,11 +22,10 @@
*/
package com.intellij.ide.projectView.impl;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ModuleGroupUtil {
@@ -38,11 +37,9 @@ public class ModuleGroupUtil {
final Map<ModuleGroup, T> map,
final Consumer<ParentChildRelation<T>> insertNode,
final Function<ModuleGroup, T> createNewNode) {
final ArrayList<String> path = new ArrayList<>();
final String[] groupPath = group.getGroupPath();
for (String pathElement : groupPath) {
path.add(pathElement);
final ModuleGroup moduleGroup = new ModuleGroup(ArrayUtil.toStringArray(path));
final List<String> groupPath = group.getGroupPathList();
for (int i = 0; i < groupPath.size(); i++) {
final ModuleGroup moduleGroup = new ModuleGroup(groupPath.subList(0, i+1));
T moduleGroupNode = map.get(moduleGroup);
if (moduleGroupNode == null) {
moduleGroupNode = createNewNode.fun(moduleGroup);
@@ -59,11 +56,9 @@ public class ModuleGroupUtil {
final Function<ModuleGroup, T> needToCreateNode,
final Consumer<ParentChildRelation<T>> insertNode,
final Function<ModuleGroup, T> createNewNode) {
final ArrayList<String> path = new ArrayList<>();
final String[] groupPath = group.getGroupPath();
for (String pathElement : groupPath) {
path.add(pathElement);
final ModuleGroup moduleGroup = new ModuleGroup(ArrayUtil.toStringArray(path));
final List<String> groupPath = group.getGroupPathList();
for (int i = 0; i < groupPath.size(); i++) {
final ModuleGroup moduleGroup = new ModuleGroup(groupPath.subList(0, i+1));
T moduleGroupNode = needToCreateNode.fun(moduleGroup);
if (moduleGroupNode == null) {
moduleGroupNode = createNewNode.fun(moduleGroup);
@@ -22,6 +22,7 @@ import com.intellij.ide.projectView.actions.MoveModulesToSubGroupAction;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -53,13 +54,16 @@ public class MoveModuleToGroup extends ActionGroup {
@NotNull
public AnAction[] getChildren(@Nullable AnActionEvent e) {
if (e == null) return EMPTY_ARRAY;
Project project = getEventProject(e);
if (project == null) return EMPTY_ARRAY;
ModifiableModuleModel modifiableModuleModel = LangDataKeys.MODIFIABLE_MODULE_MODEL.getData(e.getDataContext());
List<AnAction> result = new ArrayList<>();
result.add(new MoveModulesToGroupAction(myModuleGroup, IdeBundle.message("action.move.module.to.this.group")));
result.add(new MoveModulesToSubGroupAction(myModuleGroup));
result.add(Separator.getInstance());
result.addAll(myModuleGroup.childGroups(modifiableModuleModel, getEventProject(e)).stream().sorted((moduleGroup1, moduleGroup2) -> {
ModuleGrouper grouper = ModuleGrouper.Companion.instanceFor(project, modifiableModuleModel);
result.addAll(myModuleGroup.childGroups(grouper).stream().sorted((moduleGroup1, moduleGroup2) -> {
assert moduleGroup1.getGroupPath().length == moduleGroup2.getGroupPath().length;
return moduleGroup1.toString().compareToIgnoreCase(moduleGroup2.toString());
}).map(MoveModuleToGroup::new).collect(Collectors.toList()));
@@ -21,7 +21,8 @@ import com.intellij.ide.projectView.actions.MoveModulesToSubGroupAction;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.module.ModuleGrouperKt;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -34,51 +35,39 @@ public class MoveModuleToGroupTopLevel extends ActionGroup {
final DataContext dataContext = e.getDataContext();
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(dataContext);
boolean active = project != null && modules != null && modules.length != 0;
boolean active = project != null && modules != null && modules.length != 0 && !ModuleGrouperKt.isQualifiedModuleGroupsEnabled();
e.getPresentation().setVisible(active);
}
@Override
@NotNull
public AnAction[] getChildren(@Nullable AnActionEvent e) {
if (e == null) {
return EMPTY_ARRAY;
}
List<String> topLevelGroupNames = new ArrayList<>(getTopLevelGroupNames(e.getDataContext()));
Collections.sort ( topLevelGroupNames );
if (e == null) return EMPTY_ARRAY;
Project project = getEventProject(e);
if (project == null) return EMPTY_ARRAY;
ModifiableModuleModel moduleModel = LangDataKeys.MODIFIABLE_MODULE_MODEL.getData(e.getDataContext());
ModuleGrouper grouper = ModuleGrouper.Companion.instanceFor(project, moduleModel);
List<String> topLevelGroupNames = new ArrayList<>(getTopLevelGroupNames(grouper));
Collections.sort(topLevelGroupNames);
List<AnAction> result = new ArrayList<>();
result.add(new MoveModulesOutsideGroupAction());
result.add(new MoveModulesToSubGroupAction(null));
result.add(Separator.getInstance());
for (String name : topLevelGroupNames) {
result.add(new MoveModuleToGroup(new ModuleGroup(new String[]{name})));
result.add(new MoveModuleToGroup(new ModuleGroup(Collections.singletonList(name))));
}
return result.toArray(new AnAction[result.size()]);
}
private static Collection<String> getTopLevelGroupNames(final DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final ModifiableModuleModel model = LangDataKeys.MODIFIABLE_MODULE_MODEL.getData(dataContext);
Module[] allModules;
if ( model != null ) {
allModules = model.getModules();
} else {
allModules = ModuleManager.getInstance(project).getModules();
}
private static Collection<String> getTopLevelGroupNames(ModuleGrouper grouper) {
Set<String> topLevelGroupNames = new HashSet<>();
for (final Module child : allModules) {
String[] group;
if ( model != null ) {
group = model.getModuleGroupPath(child);
} else {
group = ModuleManager.getInstance(project).getModuleGroupPath(child);
}
if (group != null) {
topLevelGroupNames.add(group[0]);
for (final Module child : grouper.getAllModules()) {
List<String> group = grouper.getGroupPath(child);
if (!group.isEmpty()) {
topLevelGroupNames.add(group.get(0));
}
}
return topLevelGroupNames;
@@ -53,7 +53,7 @@ public abstract class AbstractProjectNode extends ProjectViewNode<Project> {
List<AbstractTreeNode> result = new ArrayList<>();
try {
for (String groupPath : groups.keySet()) {
result.add(createModuleGroupNode(new ModuleGroup(new String[]{groupPath})));
result.add(createModuleGroupNode(new ModuleGroup(Collections.singletonList(groupPath))));
}
for (Module module : nonGroupedModules) {
result.add(createModuleGroup(module));
@@ -23,6 +23,7 @@ import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.actions.MoveModulesToGroupAction;
import com.intellij.ide.projectView.impl.AbstractProjectViewPane;
import com.intellij.ide.projectView.impl.ModuleGroup;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.module.Module;
@@ -52,12 +53,13 @@ public abstract class ModuleGroupNode extends ProjectViewNode<ModuleGroup> imple
@Override
@NotNull
public Collection<AbstractTreeNode> getChildren() {
final Collection<ModuleGroup> childGroups = getValue().childGroups(getProject());
ModuleGrouper grouper = ModuleGrouper.Companion.instanceFor(getProject());
final Collection<ModuleGroup> childGroups = getValue().childGroups(grouper);
final List<AbstractTreeNode> result = new ArrayList<>();
for (final ModuleGroup childGroup : childGroups) {
result.add(createModuleGroupNode(childGroup));
}
Collection<Module> modules = getValue().modulesInGroup(getProject(), false);
Collection<Module> modules = getValue().modulesInGroup(grouper, false);
try {
for (Module module : modules) {
result.add(createModuleNode(module));
@@ -18,9 +18,9 @@ package com.intellij.ide.util;
import com.intellij.ide.projectView.impl.ModuleGroup;
import com.intellij.ide.projectView.impl.ModuleGroupUtil;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
@@ -32,8 +32,6 @@ import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.HashMap;
@@ -60,6 +58,7 @@ public class DirectoryChooserModuleTreeView implements DirectoryChooserView {
private final DefaultMutableTreeNode myRootNode;
private final ProjectFileIndex myFileIndex;
private final Project myProject;
private final ModuleGrouper myModuleGrouper;
public DirectoryChooserModuleTreeView(@NotNull Project project) {
myRootNode = new DefaultMutableTreeNode();
@@ -67,6 +66,7 @@ public class DirectoryChooserModuleTreeView implements DirectoryChooserView {
myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
myFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
myProject = project;
myModuleGrouper = ModuleGrouper.Companion.instanceFor(myProject);
myTree.setRootVisible(false);
myTree.setShowsRootHandles(true);
myTree.setCellRenderer(new MyTreeCellRenderer());
@@ -153,8 +153,8 @@ public class DirectoryChooserModuleTreeView implements DirectoryChooserView {
DefaultMutableTreeNode node = myModuleNodes.get(module);
if (node == null) {
node = new DefaultMutableTreeNode(module, true);
final String[] groupPath = module != null ? ModuleManager.getInstance(myProject).getModuleGroupPath(module) : null;
if (groupPath == null || groupPath.length == 0){
final List<String> groupPath = module != null ? myModuleGrouper.getGroupPath(module) : null;
if (groupPath == null || groupPath.isEmpty()) {
insertNode(node, myRootNode);
} else {
final DefaultMutableTreeNode parentNode = ModuleGroupUtil.buildModuleGroupPath(new ModuleGroup(groupPath),
@@ -242,7 +242,7 @@ public class DirectoryChooserModuleTreeView implements DirectoryChooserView {
}
else if (value instanceof Module) {
final Module module = (Module)value;
append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
append(myModuleGrouper.getPresentableName(module), SimpleTextAttributes.REGULAR_ATTRIBUTES);
setIcon(ModuleType.get(module).getIcon());
} else if (value instanceof ModuleGroup) {
append(value.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
@@ -20,6 +20,7 @@ import com.intellij.analysis.AnalysisScopeBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.dnd.aware.DnDAwareTree;
import com.intellij.ide.projectView.impl.ModuleGroup;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.ide.projectView.impl.nodes.ProjectViewDirectoryHelper;
import com.intellij.ide.scopeView.nodes.BasePsiNode;
import com.intellij.openapi.diagnostic.Logger;
@@ -52,6 +53,7 @@ import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -74,6 +76,7 @@ public class FileTreeModelBuilder {
private final Map<VirtualFile,DirectoryNode> myModuleDirNodes = new HashMap<>();
private final Map<Module, ModuleNode> myModuleNodes = new HashMap<>();
private final Map<String, ModuleGroupNode> myModuleGroupNodes = new HashMap<>();
private final ModuleGrouper myGrouper;
private GeneralGroupNode myExternalNode;
private int myScannedFileCount = 0;
@@ -84,12 +87,13 @@ public class FileTreeModelBuilder {
protected final VirtualFile myBaseDir;
protected VirtualFile[] myContentRoots;
public FileTreeModelBuilder(Project project, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
public FileTreeModelBuilder(@NotNull Project project, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
myProject = project;
myBaseDir = myProject.getBaseDir();
myContentRoots = ProjectRootManager.getInstance(myProject).getContentRoots();
final boolean multiModuleProject = ModuleManager.getInstance(myProject).getModules().length > 1;
myShowModules = settings.UI_SHOW_MODULES && multiModuleProject;
myGrouper = ModuleGrouper.Companion.instanceFor(project);
final ProjectViewDirectoryHelper directoryHelper = ProjectViewDirectoryHelper.getInstance(myProject);
myFlattenPackages = directoryHelper.supportsFlattenPackages() && settings.UI_FLATTEN_PACKAGES;
myCompactEmptyMiddlePackages = directoryHelper.supportsHideEmptyMiddlePackages() && settings.UI_COMPACT_EMPTY_MIDDLE_PACKAGES;
@@ -555,10 +559,9 @@ public class FileTreeModelBuilder {
}
ModuleNode node = myModuleNodes.get(module);
if (node != null) return node;
node = new ModuleNode(module);
final ModuleManager moduleManager = ModuleManager.getInstance(myProject);
final String[] groupPath = moduleManager.getModuleGroupPath(module);
if (groupPath == null) {
node = new ModuleNode(module, myShowModuleGroups ? myGrouper : null);
final List<String> groupPath = myGrouper.getGroupPath(module);
if (groupPath.isEmpty()) {
myModuleNodes.put(module, node);
myRoot.add(node);
return node;
@@ -572,18 +575,16 @@ public class FileTreeModelBuilder {
return node;
}
private PackageDependenciesNode getParentModuleGroup(String[] groupPath){
final String key = StringUtil.join(groupPath);
private PackageDependenciesNode getParentModuleGroup(List<String> groupPath){
final String key = StringUtil.join(groupPath, "");
ModuleGroupNode groupNode = myModuleGroupNodes.get(key);
if (groupNode == null) {
groupNode = new ModuleGroupNode(new ModuleGroup(groupPath), myProject);
myModuleGroupNodes.put(key, groupNode);
myRoot.add(groupNode);
}
if (groupPath.length > 1) {
String [] path = new String[groupPath.length - 1];
System.arraycopy(groupPath, 0, path, 0, groupPath.length - 1);
final PackageDependenciesNode node = getParentModuleGroup(path);
if (groupPath.size() > 1) {
final PackageDependenciesNode node = getParentModuleGroup(groupPath.subList(0, groupPath.size() - 1));
node.add(groupNode);
}
return groupNode;
@@ -15,6 +15,7 @@
*/
package com.intellij.packageDependencies.ui;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
@@ -23,16 +24,19 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.pom.NavigatableWithText;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Set;
public class ModuleNode extends PackageDependenciesNode implements NavigatableWithText {
private final @NotNull Module myModule;
private final ModuleGrouper myModuleGrouper;
public ModuleNode(@NotNull Module module) {
public ModuleNode(@NotNull Module module, @Nullable ModuleGrouper moduleGrouper) {
super(module.getProject());
myModule = module;
myModuleGrouper = moduleGrouper;
}
@Override
@@ -67,7 +71,7 @@ public class ModuleNode extends PackageDependenciesNode implements NavigatableWi
@Override
public String toString() {
return myModule.getName();
return myModuleGrouper != null ? myModuleGrouper.getPresentableName(myModule) : myModule.getName();
}
public String getModuleName() {
@@ -100,7 +100,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom
myState.setLastEditedConfigurable(getNodePathString(node)); //survive after rename;
myDetails.setText(node.getConfigurable().getBannerSlogan());
((DefaultTreeModel)myTree.getModel()).reload(node);
node.reloadNode((DefaultTreeModel)MasterDetailsComponent.this.myTree.getModel());
fireItemsChangedExternally();
}
};
@@ -869,6 +869,10 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom
}
return null;
}
protected void reloadNode(DefaultTreeModel treeModel) {
treeModel.reload(this);
}
}
@SuppressWarnings({"ConstantConditions"})
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.module
import com.intellij.ide.projectView.impl.ModuleGroup
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleGrouper
import com.intellij.openapi.module.ModuleManager
import com.intellij.testFramework.PlatformTestCase
import org.junit.Assert.assertArrayEquals
import org.junit.Test
/**
* @author nik
*/
class ExplicitModuleGroupTest : PlatformTestCase() {
@Test
fun `test single module`() {
val module = createModuleInGroup("module", "a", "b")
val path = ModuleManager.getInstance(myProject).getModuleGroupPath(module)!!
assertArrayEquals(path, arrayOf("a", "b"))
val parentGroup = ModuleGroup(listOf("a"))
assertEmpty(parentGroup.modulesInGroup(myProject, false))
assertSameElements(parentGroup.modulesInGroup(myProject, true), module)
val group = assertOneElement(parentGroup.childGroups(grouper))
assertArrayEquals(group.groupPath, arrayOf("a", "b"))
assertSameElements(group.modulesInGroup(grouper, false), module)
assertEmpty(group.childGroups(grouper))
}
fun `test two modules`() {
val module1 = createModuleInGroup("module1", "a")
val module2 = createModuleInGroup("module2", "a", "b")
val parentGroup = ModuleGroup(listOf("a"))
assertSameElements(parentGroup.modulesInGroup(myProject, false), module1)
assertSameElements(parentGroup.modulesInGroup(myProject, true), module1, module2)
val group = assertOneElement(parentGroup.childGroups(grouper))
assertArrayEquals(group.groupPath, arrayOf("a", "b"))
assertSameElements(group.modulesInGroup(myProject, false), module2)
assertEmpty(group.childGroups(grouper))
}
private val grouper: ModuleGrouper
get() = ModuleGrouper.instanceFor(myProject)
private fun createModuleInGroup(name: String, vararg path: String): Module {
val module = createModule(name)
val model = ModuleManager.getInstance(myProject).modifiableModel
model.setModuleGroupPath(module, path)
runWriteAction { model.commit() }
return module
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.module
import com.intellij.ide.projectView.impl.ModuleGroup
import com.intellij.openapi.module.ModuleGrouper
import com.intellij.openapi.util.registry.Registry
import com.intellij.testFramework.PlatformTestCase
import org.junit.Assert
import org.junit.Test
/**
* @author nik
*/
class GroupModulesByQualifiedNamesTest : PlatformTestCase() {
@Test
fun `test single module`() {
val module = createModule("a.b.module")
assertEquals("module", grouper.getPresentableName(module))
val parentGroup = ModuleGroup(listOf("a"))
assertEmpty(parentGroup.modulesInGroup(grouper, false))
assertSameElements(parentGroup.modulesInGroup(grouper, true), module)
val group = assertOneElement(parentGroup.childGroups(grouper))
Assert.assertArrayEquals(group.groupPath, arrayOf("a", "b"))
assertSameElements(group.modulesInGroup(grouper, false), module)
assertEmpty(group.childGroups(grouper))
}
fun `test two modules`() {
val module1 = createModule("a.module1")
val module2 = createModule("a.b.module2")
assertEquals("module1", grouper.getPresentableName(module1))
assertEquals("module2", grouper.getPresentableName(module2))
val parentGroup = ModuleGroup(listOf("a"))
assertSameElements(parentGroup.modulesInGroup(grouper, false), module1)
assertSameElements(parentGroup.modulesInGroup(grouper, true), module1, module2)
val group = assertOneElement(parentGroup.childGroups(grouper))
Assert.assertArrayEquals(group.groupPath, arrayOf("a", "b"))
assertSameElements(group.modulesInGroup(grouper, false), module2)
assertEmpty(group.childGroups(grouper))
}
private val grouper: ModuleGrouper
get() {
val property = Registry.get("project.qualified.module.names")
return try {
property.setValue(true)
ModuleGrouper.instanceFor(myProject)
}
finally {
property.setValue(false)
}
}
}
@@ -167,4 +167,6 @@ public abstract class ModuleManager extends SimpleModificationTracker {
*/
@Nullable
public abstract String[] getModuleGroupPath(@NotNull Module module);
public abstract boolean hasModuleGroups();
}
@@ -0,0 +1,67 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.module
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.util.text.StringUtil
import java.util.*
/**
* @author nik
*/
interface ModuleGrouper {
fun getGroupPath(module: Module): List<String>
fun getPresentableName(module: Module): String
fun getAllModules(): Array<Module>
companion object {
@JvmOverloads
fun instanceFor(project: Project, moduleModel: ModifiableModuleModel? = null): ModuleGrouper {
val hasGroups = moduleModel?.hasModuleGroups() ?: ModuleManager.getInstance(project).hasModuleGroups()
if (!isQualifiedModuleGroupsEnabled() || hasGroups) {
return ExplicitModuleGrouper(project, moduleModel)
}
return QualifiedNameGrouper(project, moduleModel)
}
}
}
fun isQualifiedModuleGroupsEnabled() = Registry.`is`("project.qualified.module.names")
private abstract class ModuleGrouperBase(protected val project: Project, protected val model: ModifiableModuleModel?) : ModuleGrouper {
override fun getAllModules(): Array<Module> = model?.modules ?: ModuleManager.getInstance(project).modules
protected fun getModuleName(module: Module) = model?.getNewName(module) ?: module.name
}
private class QualifiedNameGrouper(project: Project, model: ModifiableModuleModel?) : ModuleGrouperBase(project, model) {
override fun getGroupPath(module: Module): List<String> {
return getModuleName(module).split('.').dropLast(1)
}
override fun getPresentableName(module: Module) = StringUtil.getShortName(getModuleName(module))
}
private class ExplicitModuleGrouper(project: Project, model: ModifiableModuleModel?): ModuleGrouperBase(project, model) {
override fun getGroupPath(module: Module): List<String> {
val path = if (model != null) model.getModuleGroupPath(module) else ModuleManager.getInstance(project).getModuleGroupPath(module)
return if (path != null) Arrays.asList(*path) else emptyList()
}
override fun getPresentableName(module: Module) = getModuleName(module)
}
@@ -953,6 +953,11 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Disposa
return myModuleModel.getModuleGroupPath(module);
}
@Override
public boolean hasModuleGroups() {
return myModuleModel.hasModuleGroups();
}
public void setModuleGroupPath(Module module, String[] groupPath) {
myModuleModel.setModuleGroupPath(module, groupPath);
}
@@ -816,6 +816,9 @@ custom.folding.max.lookup.depth.description=Max lookup depth for custom folding
project.structure.add.tools.jar.to.new.jdk=false
project.structure.add.tools.jar.to.new.jdk.description=Automatically add tools.jar to 'Classpath' when new JDK is created
project.qualified.module.names=false
project.qualified.module.names.description=Organize module nodes accordingly to their qualified (dot-separated) names
tfs.set.connection.timeout=false
application.deactivation.timeout=1500
@@ -2758,6 +2758,11 @@ public class ContainerUtil extends ContainerUtilRt {
return set == null ? Collections.<T>emptySet() : set;
}
@Contract(pure = true)
public static <T> boolean startsWith(@NotNull List<T> list, @NotNull List<T> prefix) {
return list.size() >= prefix.size() && list.subList(0, prefix.size()).equals(prefix);
}
@Nullable
@Contract(pure=true)
public static <T, C extends Collection<T>> C nullize(@Nullable C collection) {
@@ -17,9 +17,9 @@ package org.jetbrains.idea.devkit.inspections.quickfix;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.module.ModuleGrouper;
import com.intellij.openapi.editor.Editor;
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.roots.ProjectRootManager;
@@ -150,8 +150,8 @@ public class PluginDescriptorChooser {
private static List<PluginDescriptorCandidate> createCandidates(final Module currentModule,
List<DomFileElement<IdeaPlugin>> elements) {
ModuleManager moduleManager = ModuleManager.getInstance(currentModule.getProject());
final String[] groupPath = moduleManager.getModuleGroupPath(currentModule);
ModuleGrouper grouper = ModuleGrouper.Companion.instanceFor(currentModule.getProject());
final List<String> groupPath = grouper.getGroupPath(currentModule);
elements.sort((o1, o2) -> {
// current module = first group
@@ -162,8 +162,8 @@ public class PluginDescriptorChooser {
if (currentModule.equals(module2)) return 1;
if (module1 != null && module2 != null) {
int groupComparison = Comparing.compare(groupMatchLevel(groupPath, moduleManager.getModuleGroupPath(module2)),
groupMatchLevel(groupPath, moduleManager.getModuleGroupPath(module1)));
int groupComparison = Comparing.compare(groupMatchLevel(groupPath, grouper.getGroupPath(module2)),
groupMatchLevel(groupPath, grouper.getGroupPath(module1)));
if (groupComparison != 0) {
return groupComparison;
}
@@ -196,16 +196,13 @@ public class PluginDescriptorChooser {
});
}
private static int groupMatchLevel(@Nullable String[] targetGroupPath, @Nullable String[] groupPath) {
if (targetGroupPath != null && groupPath != null) {
for (int i = 0; i < Math.min(targetGroupPath.length, groupPath.length); i++) {
if (!targetGroupPath[i].equals(groupPath[i])) {
return i;
}
private static int groupMatchLevel(@NotNull List<String> targetGroupPath, @NotNull List<String> groupPath) {
for (int i = 0; i < Math.min(targetGroupPath.size(), groupPath.size()); i++) {
if (!targetGroupPath.get(i).equals(groupPath.get(i))) {
return i;
}
return Math.min(targetGroupPath.length, groupPath.length);
}
return 0;
return Math.min(targetGroupPath.size(), groupPath.size());
}
public static List<DomFileElement<IdeaPlugin>> findAppropriateIntelliJModule(String moduleName,