project configuration: provide action to change names for multiple modules

Since now modules are grouped automatically accordingly to their names there should be a convenient way to specify or change a group for several modules (IDEA-186246).
This commit is contained in:
nik
2018-02-12 15:42:33 +03:00
parent acb3a8c661
commit 132dcb8a76
3 changed files with 92 additions and 1 deletions
@@ -0,0 +1,69 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.roots.ui.configuration.actions
import com.intellij.ide.highlighter.ModuleFileType
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.isQualifiedModuleNamesEnabled
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.ProjectBundle
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService
import com.intellij.openapi.ui.InputValidatorEx
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.TextRange
import com.intellij.util.PathUtilRt
class ChangeModuleNamesAction : DumbAwareAction(ProjectBundle.message("action.text.change.module.names"),
ProjectBundle.message("action.description.change.module.names"), null) {
override fun update(e: AnActionEvent) {
val project = e.project
e.presentation.isVisible = project != null && isQualifiedModuleNamesEnabled(project) && e.getData(LangDataKeys.MODIFIABLE_MODULE_MODEL) != null
val modules = e.getData(LangDataKeys.MODULE_CONTEXT_ARRAY)
e.presentation.isEnabled = modules != null && modules.size > 1
}
override fun actionPerformed(e: AnActionEvent) {
val modules = e.getData(LangDataKeys.MODULE_CONTEXT_ARRAY) ?: return
val model = e.getData(LangDataKeys.MODIFIABLE_MODULE_MODEL) ?: return
fun getGroupName(module: Module) = model.getActualName(module).substringBeforeLast('.') + "."
val commonPrefix = modules.fold(getGroupName(modules[0])) { prefix, m -> prefix.commonPrefixWith(getGroupName(m)) }
.substringBeforeLast('.', "")
val isPrefixEqualToModuleName = modules.any { model.getActualName(it) == commonPrefix }
val validator = object : InputValidatorEx {
override fun getErrorText(inputString: String): String? {
if (inputString.isNotEmpty() && inputString.split('.').any { it.isEmpty() }) {
return ProjectBundle.message("error.message.module.name.prefix.contains.empty.string")
}
if (isPrefixEqualToModuleName && inputString.isEmpty()) {
return ProjectBundle.message("error.message.module.name.cannot.be.empty")
}
if (!PathUtilRt.isValidFileName(inputString + ModuleFileType.DOT_DEFAULT_EXTENSION, true)) {
return ProjectBundle.message("error.message.module.name.prefix.contains.invalid.chars")
}
return null
}
override fun checkInput(inputString: String) = getErrorText(inputString) == null
override fun canClose(inputString: String) = getErrorText(inputString) == null
}
val newPrefix = Messages.showInputDialog(e.project, ProjectBundle.message("dialog.text.enter.common.prefix", modules.size),
ProjectBundle.message("dialog.title.change.module.names"), null, commonPrefix, validator,
TextRange.allOf(commonPrefix), ProjectBundle.message("dialog.text.enter.common.prefix.comment"))
if (newPrefix == null) return
val prefixToRemove = if (commonPrefix.isEmpty()) "" else "$commonPrefix."
val prefixToPrepend = if (newPrefix.isEmpty() || newPrefix.endsWith('.')) newPrefix else "$newPrefix."
modules.forEach {
val oldName = model.getActualName(it)
val newName = if (oldName == commonPrefix) newPrefix else prefixToPrepend + oldName.removePrefix(prefixToRemove)
model.renameModule(it, newName)
}
ProjectSettingsService.getInstance(e.project).processModulesMoved(modules, null)
}
}
@@ -34,6 +34,7 @@ import com.intellij.openapi.roots.ui.configuration.ClasspathEditor;
import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.actions.ChangeModuleNamesAction;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
@@ -137,6 +138,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
protected ArrayList<AnAction> getAdditionalActions() {
final ArrayList<AnAction> result = new ArrayList<>();
result.add(ActionManager.getInstance().getAction(IdeActions.GROUP_MOVE_MODULE_TO_GROUP));
result.add(new ChangeModuleNamesAction());
return result;
}
@@ -736,7 +738,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
if (LangDataKeys.MODULE_CONTEXT_ARRAY.is(dataId)) {
final TreePath[] paths = myTree.getSelectionPaths();
if (paths != null) {
ArrayList<Module> modules = new ArrayList<>();
Set<Module> modules = new LinkedHashSet<>();
for (TreePath path : paths) {
MyNode node = (MyNode)path.getLastPathComponent();
final NamedConfigurable configurable = node.getConfigurable();
@@ -745,6 +747,16 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
if (o instanceof Module) {
modules.add((Module)o);
}
else if (node instanceof ModuleGroupNode && ((ModuleGroupNode)node).getModuleGroup() != null) {
TreeUtil.treeNodeTraverser(node).forEach(descendant -> {
if (descendant instanceof MyNode) {
Object object = ((MyNode)descendant).getConfigurable().getEditableObject();
if (object instanceof Module) {
modules.add((Module)object);
}
}
});
}
}
return !modules.isEmpty() ? modules.toArray(Module.EMPTY_ARRAY) : null;
}
@@ -388,6 +388,16 @@ project.roots.external.annotations.description=<html>Manage external annotations
add.external.annotations.path.title=Add path to external annotations
add.external.annotations.path.description=Select path in which external annotations for module sources is located
action.text.change.module.names=Change Module Names...
action.description.change.module.names=Adjust automatic grouping of modules by changing the common prefix for their names
dialog.title.change.module.names=Change Module Names
dialog.text.enter.common.prefix=Specify prefix for names of {0} selected modules:
dialog.text.enter.common.prefix.comment=<html>Modules are grouped in the IDE accordingly to their names; <br>if names of several modules have a common \
prefix they will be shown together.</html>
error.message.module.name.prefix.contains.empty.string=A prefix must be a sequence of words separated by dots
error.message.module.name.cannot.be.empty=A module name cannot be empty
error.message.module.name.prefix.contains.invalid.chars=A prefix must be composed of characters which can be used in a file name
dialog.title.packaging.choose.library=Choose Library
dialog.title.packaging.choose.module=Choose Module