diff --git a/platform/platform-api/src/com/intellij/openapi/keymap/KeymapUtil.java b/platform/platform-api/src/com/intellij/openapi/keymap/KeymapUtil.java
index 752e6bffcf0b..f9d9a5897843 100644
--- a/platform/platform-api/src/com/intellij/openapi/keymap/KeymapUtil.java
+++ b/platform/platform-api/src/com/intellij/openapi/keymap/KeymapUtil.java
@@ -300,6 +300,49 @@ public class KeymapUtil {
return new MouseShortcut(button, modifiers, clickCount);
}
+ /**
+ * @return string representation of passed mouse shortcut. This method should
+ * be used only for serializing of the MouseShortcut
+ */
+ public static String getMouseShortcutString(MouseShortcut shortcut) {
+ if (Registry.is("ide.mac.forceTouch") && shortcut instanceof PressureShortcut) {
+ return "Force touch";
+ }
+
+ StringBuilder buffer = new StringBuilder();
+
+ // modifiers
+ int modifiers = shortcut.getModifiers();
+ if ((InputEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
+ buffer.append(SHIFT);
+ buffer.append(' ');
+ }
+ if ((InputEvent.CTRL_DOWN_MASK & modifiers) > 0) {
+ buffer.append(CONTROL);
+ buffer.append(' ');
+ }
+ if ((InputEvent.META_DOWN_MASK & modifiers) > 0) {
+ buffer.append(META);
+ buffer.append(' ');
+ }
+ if ((InputEvent.ALT_DOWN_MASK & modifiers) > 0) {
+ buffer.append(ALT);
+ buffer.append(' ');
+ }
+ if ((InputEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
+ buffer.append(ALT_GRAPH);
+ buffer.append(' ');
+ }
+
+ // button
+ buffer.append("button").append(shortcut.getButton()).append(' ');
+
+ if (shortcut.getClickCount() > 1) {
+ buffer.append(DOUBLE_CLICK);
+ }
+ return buffer.toString().trim(); // trim trailing space (if any)
+ }
+
public static String getKeyModifiersTextForMacOSLeopard(@JdkConstants.InputEventMask int modifiers) {
StringBuilder buf = new StringBuilder();
if ((modifiers & InputEvent.META_MASK) != 0) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt
index 808eb5c044a3..ce698339c8af 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt
@@ -28,7 +28,6 @@ import com.intellij.openapi.options.ExternalizableSchemeAdapter
import com.intellij.openapi.options.SchemeState
import com.intellij.openapi.util.InvalidDataException
import com.intellij.openapi.util.SystemInfo
-import com.intellij.openapi.util.registry.Registry
import com.intellij.ui.KeyStrokeAdapter
import com.intellij.util.ArrayUtil
import com.intellij.util.ArrayUtilRt
@@ -38,7 +37,6 @@ import com.intellij.util.containers.mapSmart
import com.intellij.util.containers.nullize
import gnu.trove.THashMap
import org.jdom.Element
-import java.awt.event.InputEvent
import java.util.*
import javax.swing.KeyStroke
@@ -589,7 +587,7 @@ open class KeymapImpl @JvmOverloads constructor(private var dataHolder: SchemeDa
}
is MouseShortcut -> {
val element = Element(MOUSE_SHORTCUT)
- element.setAttribute(KEYSTROKE_ATTRIBUTE, getMouseShortcutString(shortcut))
+ element.setAttribute(KEYSTROKE_ATTRIBUTE, KeymapUtil.getMouseShortcutString(shortcut))
actionElement.addContent(element)
}
is KeyboardModifierGestureShortcut -> {
@@ -711,47 +709,4 @@ private fun areShortcutsEqual(shortcuts1: List, shortcuts2: List 0) {
- buffer.append("shift")
- buffer.append(' ')
- }
- if (InputEvent.CTRL_DOWN_MASK and modifiers > 0) {
- buffer.append("control")
- buffer.append(' ')
- }
- if (InputEvent.META_DOWN_MASK and modifiers > 0) {
- buffer.append("meta")
- buffer.append(' ')
- }
- if (InputEvent.ALT_DOWN_MASK and modifiers > 0) {
- buffer.append("alt")
- buffer.append(' ')
- }
- if (InputEvent.ALT_GRAPH_DOWN_MASK and modifiers > 0) {
- buffer.append("altGraph")
- buffer.append(' ')
- }
-
- // button
- buffer.append("button").append(shortcut.button).append(' ')
-
- if (shortcut.clickCount > 1) {
- buffer.append("doubleClick")
- }
- return buffer.toString().trim { it <= ' ' } // trim trailing space (if any)
}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/MacOSDefaultKeymap.kt b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/MacOSDefaultKeymap.kt
index 241ee3e093e9..0317a7725186 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/MacOSDefaultKeymap.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/MacOSDefaultKeymap.kt
@@ -30,7 +30,10 @@ class MacOSDefaultKeymap(dataHolder: SchemeDataHolder, defaultKeymap
if (shortcut is MouseShortcut) {
return _convertMouseShortcut(shortcut)
}
- return KeyboardShortcut(_convertKeyStroke((shortcut as KeyboardShortcut).firstKeyStroke), shortcut.secondKeyStroke?.let(::_convertKeyStroke))
+ if (shortcut is KeyboardShortcut) {
+ return KeyboardShortcut(_convertKeyStroke(shortcut.firstKeyStroke), shortcut.secondKeyStroke?.let(::_convertKeyStroke))
+ }
+ return shortcut
}
}
diff --git a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java
index 08e354e01323..63dfd847f1b5 100644
--- a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java
+++ b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java
@@ -31,7 +31,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "BACK_SPACE", "EditorBackSpace", "Images.Thumbnails.UpFolder"},
{ "ENTER", "EditorChooseLookupItem", "NextTemplateVariable", "EditorEnter", "Images.Thumbnails.EnterAction",
"PropertyInspectorActions.EditValue", "Console.Execute", "Console.TableResult.EditValue"},
- { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue", "XDebugger.SetValue", "XDebugger.EditWatch", "Arrangement.Rule.Edit"},
+ { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue", "XDebugger.SetValue", "Arrangement.Rule.Edit"},
{ "alt ENTER", "ShowIntentionActions", "Console.TableResult.EditValue", "DatabaseView.PropertiesAction"},
{ "F5", "UML.ApplyCurrentLayout", "CopyElement"},
{ "F7", "NextDiff", "StepInto"},
@@ -57,10 +57,11 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "control M", "EditorScrollToCenter", "Vcs.ShowMessageHistory"},
{ "control N", "FileChooser.NewFolder", "GotoClass", "GotoChangedFile"},
{ "control P", "FileChooser.TogglePathShowing", "ParameterInfo"},
- { "control R", "Replace", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"},
+ { "control R", "Replace", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"},
{ "control SLASH", "CommentByLineComment", "Images.Editor.ActualSize"},
{ "control U", "GotoSuperMethod", "CommanderSwapPanels"},
{ "control UP", "EditorScrollUp", "EditorLookupUp", "MethodOverloadSwitchUp"},
+ { "control SUBTRACT", "CollapseAll", "CollapseRegion"},
{ "control alt A", "ChangesView.AddUnversioned", "Diagram.DeselectAll"},
{ "control alt E", "PerforceDirect.Edit", "Console.History.Browse"},
{ "control alt DOWN", "NextOccurence", "Console.TableResult.NextPage"},
@@ -88,8 +89,6 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "shift control RIGHT", "EditorNextWordWithSelection", "ResizeToolWindowRight", },
{ "shift control T", "GotoTest", "Images.ShowThumbnails"},
{ "shift control UP", "ResizeToolWindowUp", "MoveStatementUp"},
- { "shift control alt DOWN", "VcsShowNextChangeMarker", "HtmlTableCellNavigateDown"},
- { "shift control alt UP", "VcsShowPrevChangeMarker", "HtmlTableCellNavigateUp"},
{ "shift control alt D", "UML.ShowChanges", "Console.TableResult.CloneColumn"},
{ "shift control U", "ShelveChanges.UnshelveWithDialog", "EditorToggleCase"},
{ "control E", "RecentFiles", "Vcs.ShowMessageHistory"},
@@ -100,7 +99,6 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "ctrl alt ENTER", "EditorStartNewLineBefore", "QuickActionPopup"},
});
put("Mac OS X 10.5+", new String[][] {
- { "F5", "CopyElement", "Console.TableResult.Reload", "UML.ApplyCurrentLayout"},
{ "BACK_SPACE", "$Delete", "EditorBackSpace", "Images.Thumbnails.UpFolder"},
{ "shift BACK_SPACE", "EditorBackSpace", "UsageView.Include"},
{ "meta BACK_SPACE", "EditorDeleteLine", "$Delete"},
@@ -122,7 +120,6 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "shift meta G", "ClassTemplateNavigation", "GoToClass", "FindPrevious"},
{ "shift meta LEFT", "EditorLineStartWithSelection", "ResizeToolWindowLeft", },
{ "shift meta RIGHT", "EditorLineEndWithSelection", "ResizeToolWindowRight", },
- { "meta E", "RecentFiles", "Vcs.ShowMessageHistory"},
{ "alt R", "Django.RunManageTaskAction", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction"},
{ "alt DOWN", "EditorUnSelectWord", "Arrangement.Rule.Match.Condition.Move.Down"},
{ "alt UP", "EditorSelectWord", "Arrangement.Rule.Match.Condition.Move.Up"},
@@ -135,79 +132,62 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "control ENTER", "Generate", "NewElement"},
{ "control F5", "Refresh", "Rerun"},
{ "control TAB", "Switcher", "Diff.FocusOppositePane"},
+ { "control N", "Generate", "NewElement"},
{ "shift control TAB", "Switcher", "Diff.FocusOppositePaneAndScroll"},
{ "meta 1", "ActivateProjectToolWindow", "FileChooser.GotoHome", "DuplicatesForm.SendToLeft"},
{ "meta 2", "ActivateFavoritesToolWindow", "FileChooser.GotoProject", "DuplicatesForm.SendToRight"},
{ "meta 3", "ActivateFindToolWindow", "FileChooser.GotoModule"},
- { "meta E", "RecentFiles", "Vcs.ShowMessageHistory"},
{ "shift meta LEFT", "EditorLineStartWithSelection", "ResizeToolWindowLeft", },
{ "shift meta RIGHT", "EditorLineEndWithSelection", "ResizeToolWindowRight", },
{ "alt R", "Django.RunManageTaskAction", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction"},
});
put("Emacs", new String[][] {
- { "ENTER", "EditorChooseLookupItem", "NextTemplateVariable", "EditorEnter", "Images.Thumbnails.EnterAction",
- "PropertyInspectorActions.EditValue", "Console.Execute", "Console.TableResult.EditValue"},
- { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue", "XDebugger.SetValue", "XDebugger.EditWatch", "Arrangement.Rule.Edit"},
- { "alt ENTER", "ShowIntentionActions", "Console.TableResult.EditValue", "DatabaseView.PropertiesAction"},
- { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection",
+ { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection",
"EmacsStyleIndent", "NextTemplateParameter", "ExpandLiveTemplateByTab"},
- { "alt DOWN", "ShowContent", "MethodDown", "Arrangement.Rule.Match.Condition.Move.Down"},
- { "alt UP", "MethodUp", "Arrangement.Rule.Match.Condition.Move.Up"},
{ "alt SLASH", "CodeCompletion", "HippieCompletion"},
- { "control 1", "FileChooser.GotoHome", "GotoBookmark1", "DuplicatesForm.SendToLeft"},
- { "control 2", "FileChooser.GotoProject", "GotoBookmark2", "DuplicatesForm.SendToRight"},
- { "control 3", "GotoBookmark3", "FileChooser.GotoModule"},
{ "control D", "$Delete", "Diff.ShowDiff", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"},
{ "control K", "EditorCutLineEnd", "CheckinProject"},
{ "control L", "EditorScrollToCenter", "Vcs.Log.FocusTextFilter"},
{ "control M", "EditorEnter", "EditorChooseLookupItem", "NextTemplateVariable", "Console.Execute"},
{ "control N", "EditorDown", "FileChooser.NewFolder"},
{ "control P", "EditorUp", "FileChooser.TogglePathShowing"},
- { "control R", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources", "FindPrevious"},
+ { "control R", "org.jetbrains.plugins.ruby.rails.console.ReloadSources", "FindPrevious"},
{ "control SLASH", "$Undo", "Images.Editor.ActualSize"},
- { "control X", "GotoFile", "SaveAll", "NextTab", "PreviousTab", "CloseContent", "CloseAllEditors", "NextSplitter",
- "GotoNextError", "NextProjectWindow", "EditorSwapSelectionBoundaries", "SplitVertically",
- "SplitHorizontally", "UnsplitAll", "Switcher", "$SelectAll"},
{ "control UP", "EditorBackwardParagraph", "EditorLookupUp", "MethodOverloadSwitchUp"},
{ "control DOWN", "EditorForwardParagraph", "EditorLookupDown", "MethodOverloadSwitchDown"},
{ "control alt A", "MethodUp", "ChangesView.AddUnversioned", "Diagram.DeselectAll"},
{ "control alt E", "MethodDown", "PerforceDirect.Edit", "Console.History.Browse"},
{ "control alt G", "GotoDeclaration", "org.jetbrains.plugins.ruby.rails.actions.generators.GeneratorsPopupAction", "Mvc.RunTarget"},
{ "control alt S", "ShowSettings", "Find"},
- { "shift DELETE", "$Cut", "Maven.Uml.Exclude"},
{ "shift alt S", "FindUsages", "context.save"},
{ "shift alt G", "GotoChangedFile", "GotoClass", "hg4idea.QGotoFromPatches"},
{ "shift alt P", "ParameterInfo", "hg4idea.QPushAction"},
- { "control alt I", "DatabaseView.GenerateScriptIntoConsole", "AutoIndentLines"},
- { "shift control X", "GotoPreviousError", "com.jetbrains.php.framework.FrameworkRunConsoleAction"},
+ { "shift control X", SECOND_STROKE, "com.jetbrains.php.framework.FrameworkRunConsoleAction"},
});
put("Visual Studio", new String[][] {
{ "F5", "Resume", "UML.ApplyCurrentLayout"},
{ "F7", "NextDiff", "CompileDirty"},
{ "alt F2", "ShowBookmarks", "WebOpenInAction"},
{ "alt F8", "ReformatCode", "ForceStepInto", "EvaluateExpression"},
- { "alt INSERT", "FileChooser.NewFolder", "Generate", "NewElement"},
- { "control DIVIDE", "CommentByLineComment", "Images.Editor.ActualSize"},
{ "control COMMA", "GotoClass", "GotoChangedFile"},
{ "control F1", "ExternalJavaDoc", "ShowErrorDescription"},
{ "control F10", "RunToCursor", "javaee.UpdateRunningApplication", "liveedit.UpdateRunningApplication"},
{ "control F5", "Rerun", "Run"},
{ "control N", "FileChooser.NewFolder", "Generate", },
{ "control P", "FileChooser.TogglePathShowing", "Print"},
- { "control SLASH", "CommentByLineComment", "Images.Editor.ActualSize"},
{ "control alt F", "ReformatCode", "IntroduceField"},
- { "shift F1", "QuickJavaDoc", "ExternalJavaDoc"},
{ "shift F12", "RestoreDefaultLayout", "FindUsagesInFile"},
{ "shift F2", "GotoPreviousError", "GotoDeclaration"},
{ "shift control F7", "FindUsagesInFile", "HighlightUsagesInFile"},
{ "shift control I", "ImplementMethods", "QuickImplementations"},
{ "alt F9", "ViewBreakpoints", "EditBreakpoint"},
{ "alt MULTIPLY", "ShowExecutionPoint", "Images.Thumbnails.ToggleRecursive"},
+ { "button2", "EditorPasteFromX11", "GotoDeclaration" },
});
put("Default for XWin", new String[][] {
+ { "shift ctrl alt button1", "EditorAddRectangularSelectionOnMouseDrag", "QuickEvaluateExpression"},
});
put("Default for GNOME", new String[][] {
- { "alt F1", "SelectIn", "ProjectViewChangeView"},
{ "shift alt 1", "SelectIn", "ProjectViewChangeView"},
{ "shift alt 7", "IDEtalk.SearchUserHistory", "FindUsages"},
{ "shift alt LEFT", "PreviousEditorTab", "Back"},
@@ -223,10 +203,8 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "shift alt L", "ReformatCode", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"},
});
put("Eclipse", new String[][] {
- { "F2", "Console.TableResult.EditValue", "QuickJavaDoc", "XDebugger.SetValue", "XDebugger.EditWatch", "Arrangement.Rule.Edit"},
- { "alt ENTER", "ShowIntentionActions", "Console.TableResult.EditValue", "DatabaseView.PropertiesAction"},
+ { "F2", "Console.TableResult.EditValue", "QuickJavaDoc", "XDebugger.SetValue", "Arrangement.Rule.Edit"},
{ "F5", "UML.ApplyCurrentLayout", "StepInto"},
- { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", "EditorTab", "NextTemplateParameter", "ExpandLiveTemplateByTab"},
{ "alt DOWN", "ShowContent", "MoveStatementDown", "Arrangement.Rule.Match.Condition.Move.Down"},
{ "alt UP", "MoveStatementUp", "Arrangement.Rule.Match.Condition.Move.Up"},
{ "alt HOME", "ViewNavigationBar", "ShowNavBar"},
@@ -235,11 +213,10 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "control L", "Vcs.Log.FocusTextFilter", "GotoLine"},
{ "control N", "ShowPopupMenu", "FileChooser.NewFolder"},
{ "control P", "FileChooser.TogglePathShowing", "Print"},
- { "control R", "RunToCursor", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"},
+ { "control R", "RunToCursor", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"},
{ "control U", "EvaluateExpression", "CommanderSwapPanels"},
{ "control alt DOWN", "Console.TableResult.NextPage", "EditorDuplicateLines"},
{ "control alt E", "Console.History.Browse", "ExecuteInPyConsoleAction", "PerforceDirect.Edit"},
- { "control alt G", "org.jetbrains.plugins.ruby.rails.actions.generators.GeneratorsPopupAction", "Mvc.RunTarget"},
{ "shift alt D", "hg4idea.QFold", "Debug"},
{ "shift alt G", "RerunTests", "hg4idea.QGotoFromPatches"},
{ "shift alt L", "IntroduceVariable", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"},
@@ -247,36 +224,30 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "shift alt S", "ShowPopupMenu", "context.save"},
{ "shift alt T", "ShowPopupMenu", "tasks.switch"},
{ "shift control DOWN", "ResizeToolWindowDown", "MethodDown"},
- { "shift control E", "EditSource", "RecentChangedFiles", "Graph.Faces.OpenSelectedPages"},
+ { "shift control E", "EditSource", "RecentChangedFiles"},
{ "shift control F6", "PreviousTab", "ChangeTypeSignature"},
{ "shift control F11", "ToggleBookmark", "FocusTracer"},
{ "shift control G", "FindUsagesInFile", "ClassTemplateNavigation", "GoToClass"},
{ "shift control I", "QuickImplementations", "XDebugger.Inspect"},
- { "shift control LEFT", "EditorPreviousWordWithSelection", "ResizeToolWindowLeft", },
- { "shift control RIGHT", "EditorNextWordWithSelection", "ResizeToolWindowRight", },
{ "shift control UP", "ResizeToolWindowUp", "MethodUp"},
{ "shift control K", "Vcs.Push", "FindPrevious"},
{ "shift control X", "EditorToggleCase", "com.jetbrains.php.framework.FrameworkRunConsoleAction"},
- { "shift control U", "ShelveChanges.UnshelveWithDialog", "EditorToggleCase"},
{ "shift control T", "GotoClass", "GotoChangedFile"},
});
put("NetBeans 6.5", new String[][] {
- { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue", "XDebugger.SetValue", "XDebugger.EditWatch", "Arrangement.Rule.Edit"},
{ "F4", "RunToCursor", "EditSource"},
{ "F5", "Debugger.ResumeThread", "Resume", "UML.ApplyCurrentLayout"},
{ "alt DOWN", "NextOccurence", "ShowContent", "Arrangement.Rule.Match.Condition.Move.Down"},
{ "alt UP", "PreviousOccurence", "Arrangement.Rule.Match.Condition.Move.Up"},
- { "alt INSERT", "FileChooser.NewFolder", "Generate", "NewElement"},
{ "control 1", "ActivateProjectToolWindow", "DuplicatesForm.SendToLeft"},
{ "control 2", "ActivateProjectToolWindow", "FileChooser.GotoProject", "DuplicatesForm.SendToRight"},
{ "control 3", "ActivateProjectToolWindow", "FileChooser.GotoModule"},
{ "control BACK_SPACE", "EditorDeleteToWordStart", "ToggleDockMode"},
{ "control DIVIDE", "CollapseRegionRecursively", "Images.Editor.ActualSize"},
- { "control D", "EditorDuplicate", "Diff.ShowDiff", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"},
{ "control M", "Vcs.ShowMessageHistory", "Move"},
- { "control R", "RenameElement", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"},
- { "control SLASH", "CommentByLineComment", "Images.Editor.ActualSize"},
- { "control U", "EditorToggleCase", "CommanderSwapPanels"},
+ { "control N", "NewElement", "FileChooser.NewFolder"},
+ { "control R", "RenameElement", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"},
+ { "control U", SECOND_STROKE, "CommanderSwapPanels"},
{ "control O", "GotoClass", "GotoChangedFile"},
{ "control PERIOD", "GotoNextError", "EditorChooseLookupItemDot"},
{ "control alt DOWN", "MethodDown", "Console.TableResult.NextPage"},
@@ -285,58 +256,52 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
{ "shift alt F9", "ChooseDebugConfiguration", "ValidateXml", "ValidateJsp"},
{ "shift alt D", "ToggleFloatingMode", "hg4idea.QFold"},
{ "shift control DOWN", "EditorDuplicate", "ResizeToolWindowDown", },
- { "shift control ENTER", "EditorCompleteStatement", "Console.Jpa.GenerateSql"},
{ "shift control F7", "HighlightUsagesInFile", "XDebugger.NewWatch"},
{ "shift control UP", "EditorDuplicate", "ResizeToolWindowUp", },
{ "shift control alt P", "Print", "Graph.Print"},
{ "shift control K", "HippieCompletion", "Vcs.Push"},
{ "control alt E", "Console.History.Browse", "ExecuteInPyConsoleAction", "PerforceDirect.Edit"},
- { "TAB", "NextTemplateVariable", "NextParameter", "EditorTab", "EditorChooseLookupItemReplace", "EditorIndentSelection", "NextTemplateParameter", "ExpandLiveTemplateByTab"},
- { "shift TAB", "EditorUnindentSelection", "PreviousTemplateVariable", "PrevParameter", "PrevTemplateParameter"},
});
put("JBuilder", new String[][] {
- { "F2", "EditorTab", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue", "XDebugger.SetValue", "XDebugger.EditWatch", "Arrangement.Rule.Edit"},
+ { "F2", "EditorTab", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue", "XDebugger.SetValue", "Arrangement.Rule.Edit"},
{ "F5", "ToggleBreakpointEnabled", "UML.ApplyCurrentLayout"},
{ "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", "EmacsStyleIndent", "NextTemplateParameter", "ExpandLiveTemplateByTab"},
{ "control F6", "PreviousEditorTab", "PreviousTab", },
{ "control L", "Vcs.Log.FocusTextFilter", "EditorSelectLine"},
{ "control M", "Vcs.ShowMessageHistory", "OverrideMethods", },
- { "control N", "FileChooser.NewFolder", "GotoClass", "GotoChangedFile"},
{ "control P", "FileChooser.TogglePathShowing", "FindInPath"},
{ "shift control A", "SaveAll", "GotoAction"},
{ "shift control E", "RecentChangedFiles", "ExtractMethod"},
{ "shift control ENTER", "FindUsages", "Console.Jpa.GenerateSql"},
{ "shift control F6", "NextTab", "ChangeTypeSignature"},
{ "shift control G", "GotoSymbol", "ClassTemplateNavigation", "GoToClass"},
- { "control SUBTRACT", "CollapseAll", "CollapseRegion"},
- { "control alt I", "DatabaseView.GenerateScriptIntoConsole", "AutoIndentLines"},
{ "shift control X", "EditorToggleShowWhitespaces", "com.jetbrains.php.framework.FrameworkRunConsoleAction"},
});
put("Eclipse (Mac OS X)", new String[][] {
- { "meta BACK_SPACE", "EditorDeleteToWordStart", "$Delete"},
- { "F2", "Console.TableResult.EditValue", "QuickJavaDoc", "XDebugger.SetValue", "XDebugger.EditWatch", "Arrangement.Rule.Edit"},
- { "F3", "GotoDeclaration", "EditSource"},
- { "F5", "StepInto", "Console.TableResult.Reload", "UML.ApplyCurrentLayout"},
- { "alt DOWN", "MoveStatementDown", "Arrangement.Rule.Match.Condition.Move.Down"},
- { "alt UP", "MoveStatementUp", "Arrangement.Rule.Match.Condition.Move.Up"},
- { "control PERIOD", "EditorChooseLookupItemDot", "HippieCompletion"},
- { "meta 1", "FileChooser.GotoHome", "ShowIntentionActions", "DuplicatesForm.SendToLeft"},
- { "meta 3", "FileChooser.GotoModule", "GotoAction"},
- { "meta D", "EditorDeleteLine", "Diff.ShowDiff", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"},
- { "meta I", "DatabaseView.PropertiesAction", "AutoIndentLines"},
- { "meta P", "FileChooser.TogglePathShowing", "Print"},
- { "meta R", "org.jetbrains.plugins.ruby.rails.console.ReloadSources", "RunToCursor"},
- { "meta U", "CommanderSwapPanels", "EvaluateExpression"},
- { "meta W", "CloseContent", "CloseActiveTab"},
- { "shift meta T", "GotoClass", "GotoChangedFile"},
- { "meta alt DOWN", "Console.TableResult.NextPage", "EditorDuplicateLines"},
- { "shift meta F11", "Run", "FocusTracer"},
- { "shift meta G", "ClassTemplateNavigation", "GoToClass", "FindUsages"},
- { "shift meta K", "Vcs.Push", "FindPrevious"},
- { "shift meta X", "EditorToggleCase", "com.jetbrains.php.framework.FrameworkRunConsoleAction"},
- { "shift meta U", "FindUsagesInFile", "ShelveChanges.UnshelveWithDialog"},
- { "control shift alt Z", "Vcs.RollbackChangedLines", "ChangesView.Revert"},
- { "meta alt I", "Inline", "DatabaseView.GenerateScriptIntoConsole"}
+ { "meta BACK_SPACE", "EditorDeleteToWordStart", "$Delete"},
+ { "F2", "Console.TableResult.EditValue", "QuickJavaDoc", "XDebugger.SetValue", "Arrangement.Rule.Edit"},
+ { "F3", "GotoDeclaration", "EditSource"},
+ { "F5", "StepInto", "UML.ApplyCurrentLayout"},
+ { "alt DOWN", "MoveStatementDown", "Arrangement.Rule.Match.Condition.Move.Down"},
+ { "alt UP", "MoveStatementUp", "Arrangement.Rule.Match.Condition.Move.Up"},
+ { "control PERIOD", "EditorChooseLookupItemDot", "HippieCompletion"},
+ { "meta 1", "FileChooser.GotoHome", "ShowIntentionActions", "DuplicatesForm.SendToLeft"},
+ { "meta 3", "FileChooser.GotoModule", "GotoAction"},
+ { "meta D", "EditorDeleteLine", "Diff.ShowDiff", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"},
+ { "meta I", "DatabaseView.PropertiesAction", "AutoIndentLines"},
+ { "meta P", "FileChooser.TogglePathShowing", "Print"},
+ { "meta R", "org.jetbrains.plugins.ruby.rails.console.ReloadSources", "RunToCursor"},
+ { "meta U", "CommanderSwapPanels", "EvaluateExpression"},
+ { "meta W", "CloseContent", "CloseActiveTab"},
+ { "shift meta T", "GotoClass", "GotoChangedFile"},
+ { "meta alt DOWN", "Console.TableResult.NextPage", "EditorDuplicateLines"},
+ { "shift meta F11", "Run", "FocusTracer"},
+ { "shift meta G", "ClassTemplateNavigation", "GoToClass", "FindUsages"},
+ { "shift meta K", "Vcs.Push", "FindPrevious"},
+ { "shift meta X", "EditorToggleCase", "com.jetbrains.php.framework.FrameworkRunConsoleAction"},
+ { "shift meta U", "FindUsagesInFile", "ShelveChanges.UnshelveWithDialog"},
+ { "control shift alt Z", "Vcs.RollbackChangedLines", "ChangesView.Revert"},
+ { "meta alt I", "Inline", "DatabaseView.GenerateScriptIntoConsole"}
});
}};
// @formatter:on
diff --git a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCaseBase.java b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCaseBase.java
index 4298504731d6..5c752d74bd10 100644
--- a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCaseBase.java
+++ b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCaseBase.java
@@ -15,26 +15,21 @@
*/
package com.intellij.keymap;
-import com.intellij.openapi.actionSystem.ActionManager;
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.KeyboardShortcut;
-import com.intellij.openapi.actionSystem.Shortcut;
-import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
+import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.keymap.Keymap;
+import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.keymap.impl.KeymapImpl;
import com.intellij.openapi.keymap.impl.MacOSDefaultKeymap;
+import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.ui.KeyStrokeAdapter;
import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.FactoryMap;
import com.intellij.util.containers.MultiMap;
-import gnu.trove.THashSet;
import junit.framework.TestCase;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.InputEvent;
@@ -44,9 +39,8 @@ import java.util.*;
import static com.intellij.testFramework.assertions.Assertions.assertThat;
public abstract class KeymapsTestCaseBase extends PlatformTestCase {
- private static final boolean OUTPUT_TEST_DATA = false;
-
private static final Set LINUX_KEYMAPS = ContainerUtil.newHashSet("Default for XWin", "Default for GNOME", "Default for KDE");
+ protected static final String SECOND_STROKE = "SECOND_STROKE_SHORTCUT";
/**
@@ -80,177 +74,67 @@ public abstract class KeymapsTestCaseBase extends PlatformTestCase {
return result;
}
+ /**
+ * Drops records from baseDuplicates for shortcuts, that are registered in non-included plugin.xml
+ */
protected static Map>> parseKnownDuplicates(Map baseDuplicates,
Map ideDuplicates) {
- Map>> baseMap = parseKnownDuplicates(baseDuplicates);
- baseMap.keySet().retainAll(ContainerUtil.map(KeymapManagerEx.getInstanceEx().getAllKeymaps(), Keymap::getName));
+ Map>> baseMapping = parseKnownDuplicates(baseDuplicates);
+ Map>> ideMapping = parseKnownDuplicates(ideDuplicates);
- Map>> ideMap = parseKnownDuplicates(ideDuplicates);
+ HashMap>> result = new HashMap<>();
- Map>> result = new HashMap<>();
- for (String keymap : ContainerUtil.union(baseMap.keySet(), ideMap.keySet())) {
- Map> mapping = new HashMap<>();
- result.put(keymap, mapping);
+ KeymapManagerEx km = KeymapManagerEx.getInstanceEx();
+ List availableKeymaps = ContainerUtil.map(km.getAllKeymaps(), Keymap::getName);
- Map> baseMapping = baseMap.get(keymap);
- Map> ideMapping = ideMap.get(keymap);
+ for (String keymapName : availableKeymaps) {
+ Map> map = result.computeIfAbsent(keymapName, key -> new HashMap<>());
+ Map> baseMap = baseMapping.get(keymapName);
+ if (baseMap == null) continue;
- if (baseMapping != null) mapping.putAll(baseMapping);
- if (ideMapping != null) mapping.putAll(ideMapping);
+ Keymap keymap = km.getKeymap(keymapName);
+ for (String shortcut : baseMap.keySet()) {
+ Shortcut sc = parseShortcut(shortcut);
+
+ List actionIds = ContainerUtil.filter(baseMap.get(shortcut), actionId -> {
+ return ActionManager.getInstance().getAction(actionId) != null ||
+ keymap.getShortcuts(actionId).length > 0;
+ });
+
+ if (actionIds.size() >= 2) {
+ map.put(shortcut, actionIds);
+ }
+ }
}
+
+ for (String keymap : ideMapping.keySet()) {
+ Map> map = result.computeIfAbsent(keymap, key -> new HashMap<>());
+ Map> ideMap = ideMapping.get(keymap);
+
+ map.putAll(ideMap);
+ }
+
return result;
}
- public void testDuplicateShortcuts() {
+ public void testUnknownActionIds() {
StringBuilder failMessage = new StringBuilder();
- Map>> knownDuplicates = getKnownDuplicates();
- for (Keymap keymap : KeymapManagerEx.getInstanceEx().getSchemeManager().getAllSchemes()) {
- String failure = checkDuplicatesInKeymap((KeymapImpl)keymap, knownDuplicates);
- if (failMessage.length() > 0) {
- failMessage.append("\n");
- }
- failMessage.append(failure);
- }
- if (failMessage.length() > 0) {
- TestCase.fail(failMessage +
- "\n" +
- "Please specify 'use-shortcut-of' attribute for your action if it is similar to another action (but it won't appear in Settings/Keymap),\n" +
- "reassign shortcut or, if absolutely must, modify the 'known duplicates list'");
- }
- }
-
- @NotNull
- @SuppressWarnings({"HardCodedStringLiteral"})
- private static String checkDuplicatesInKeymap(@NotNull KeymapImpl keymap,
- @NotNull Map>> allKnownDuplicates) {
- Set aids = keymap.getActionIdList();
- removeBoundActionIds(aids);
-
- Set shortcuts = new THashSet<>();
-
- nextId:
- for (String id : aids) {
- Map> knownDuplicates = allKnownDuplicates.get(keymap.getName());
- if (knownDuplicates != null) {
- for (List actionsMapping : knownDuplicates.values()) {
- if (actionsMapping.contains(id)) {
- continue nextId;
- }
- }
- }
-
- for (Shortcut shortcut : keymap.getShortcuts(id)) {
- if (shortcut instanceof KeyboardShortcut) {
- shortcuts.add(shortcut);
- }
- }
- }
- List sorted = new ArrayList<>(shortcuts);
- Collections.sort(sorted, Comparator.comparing(KeymapsTestCaseBase::getText));
-
- if (OUTPUT_TEST_DATA) {
- System.out.println("put(\"" + keymap.getName() + "\", new String[][] {");
- }
- else {
- System.out.println(keymap.getName());
- }
- StringBuilder failMessage = new StringBuilder();
- for (Shortcut shortcut : sorted) {
- if (!(shortcut instanceof KeyboardShortcut)) {
- continue;
- }
-
- Set ids = new THashSet<>(Arrays.asList(keymap.getActionIds(shortcut)));
- removeBoundActionIds(ids);
- if (ids.size() == 1) {
- continue;
- }
-
- Keymap parent = keymap.getParent();
- if (parent != null) {
- // ignore duplicates from default keymap
- boolean differFromParent = false;
- for (String id : ids) {
- Shortcut[] here = keymap.getShortcuts(id);
- Shortcut[] there = parent.getShortcuts(id);
- if (keymap.getName().startsWith("Mac")) {
- convertMac(there);
- }
-
- if (!new THashSet<>(Arrays.asList(here)).equals(new THashSet<>(Arrays.asList(there)))) {
- differFromParent = true;
- break;
- }
- }
- if (!differFromParent) continue;
- }
-
- String def = "{ "
- + "\"" + getText(shortcut) + "\","
- + StringUtil.repeatSymbol(' ', 25 - getText(shortcut).length())
- + StringUtil.join(ids, StringUtil.QUOTER, ", ")
- + "},";
- if (OUTPUT_TEST_DATA) {
- System.out.println(def);
- }
- else {
- if (failMessage.length() == 0) {
- failMessage.append("Shortcut '").append(getText(shortcut)).append("' conflicts found in keymap '")
- .append(keymap.getName()).append("':\n");
- }
- failMessage.append(def).append("\n");
- }
- }
- if (OUTPUT_TEST_DATA) {
- System.out.println("});");
- }
- return failMessage.toString();
- }
-
- private static void removeBoundActionIds(@NotNull Set aids) {
- KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
- // explicitly bound to another action
- for (Iterator it = aids.iterator(); it.hasNext(); ) {
- String id = it.next();
- String sourceId = keymapManager.getActionBinding(id);
- if (sourceId != null) {
- it.remove();
- }
- }
- }
-
- public void testValidActionIds() {
Set unknownActions = getUnknownActions();
- @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
- Map> missingActions = new FactoryMap>() {
- @Override
- protected Map> createMap() {
- return new LinkedHashMap<>();
- }
+ MultiMap missingActions = new MultiMap<>();
- @Nullable
- @Override
- protected List create(String key) {
- return new ArrayList<>();
- }
- };
for (Keymap keymap : KeymapManagerEx.getInstanceEx().getAllKeymaps()) {
- List ids = new ArrayList<>(keymap.getActionIdList());
- ids.sort(null);
- assertThat(ids).isEqualTo(new ArrayList<>(new LinkedHashSet<>(ids)));
+ Collection ids = keymap.getActionIdList();
+ assertSameElements(ids, new HashSet<>(ids));
+
for (String cid : ids) {
if (unknownActions.contains(cid)) continue;
+
AnAction action = ActionManager.getInstance().getAction(cid);
if (action == null) {
- if (OUTPUT_TEST_DATA) {
- System.out.print("\"" + cid + "\", ");
- }
- else {
- missingActions.get(keymap.getName()).add(cid);
- }
+ missingActions.putValue(keymap.getName(), cid);
}
}
}
@@ -263,103 +147,214 @@ public abstract class KeymapsTestCaseBase extends PlatformTestCase {
}
}
- if (!missingActions.isEmpty() || !reappearedAction.isEmpty()) {
- StringBuilder message = new StringBuilder();
- if (!missingActions.isEmpty()) {
- for (Map.Entry> keymapAndActions : missingActions.entrySet()) {
- message.append("Unknown actions in keymap ").append(keymapAndActions.getKey()).append(", add them to unknown actions list:\n");
- for (String action : keymapAndActions.getValue()) {
- message.append("\"").append(action).append("\",").append("\n");
- }
+
+ if (!missingActions.isEmpty()) {
+ for (String keymap : missingActions.keySet()) {
+ failMessage.append("Unknown actions in keymap ").append(keymap).append(", add them to unknown actions list:\n");
+ for (String action : missingActions.get(keymap)) {
+ failMessage.append("\"").append(action).append("\",").append("\n");
}
}
- if (!reappearedAction.isEmpty()) {
- message.append("The following actions have reappeared, remove them from unknown action list:\n");
- for (String action : reappearedAction) {
- message.append(action).append("\n");
- }
+ }
+
+ if (!reappearedAction.isEmpty()) {
+ failMessage.append("The following actions have reappeared, remove them from unknown action list:\n");
+ for (String action : reappearedAction) {
+ failMessage.append(action).append("\n");
}
- fail("\n" + message);
+ }
+
+ if (failMessage.length() > 0) {
+ fail("\n" + failMessage);
}
}
- @SuppressWarnings({"HardCodedStringLiteral"})
- public void testIdsListIsConsistent() {
- Map>> duplicates = getKnownDuplicates();
- Set allMaps = new THashSet<>(ContainerUtil.map(KeymapManagerEx.getInstanceEx().getAllKeymaps(), keymap -> keymap.getName()));
- assertThat(ContainerUtil.subtract(allMaps, duplicates.keySet()))
- .overridingErrorMessage("Modify 'known duplicates list' test data. Keymaps were added: %s",
- ContainerUtil.subtract(allMaps, duplicates.keySet()))
+ public void testDuplicateShortcuts() {
+ StringBuilder failMessage = new StringBuilder();
+
+ Map>> expectedDuplicates = collectExpectedDuplicatedShortcuts();
+ Map>> actualDuplicates = collectActualDuplicatedShortcuts();
+
+
+ Set allKeymaps = ContainerUtil.union(expectedDuplicates.keySet(), actualDuplicates.keySet());
+ Collection newKeymaps = ContainerUtil.subtract(allKeymaps, expectedDuplicates.keySet());
+ Collection missingKeymaps = ContainerUtil.subtract(allKeymaps, actualDuplicates.keySet());
+
+ assertThat(newKeymaps)
+ .overridingErrorMessage("Modify 'known duplicates list' test data. Keymaps were added: %s", newKeymaps)
+ .isEmpty();
+ assertThat(newKeymaps)
+ .overridingErrorMessage("Modify 'known duplicates list' test data. Keymaps were removed: %s", missingKeymaps)
.isEmpty();
- assertThat(ContainerUtil.subtract(duplicates.keySet(), allMaps))
- .overridingErrorMessage("Modify 'known duplicates list' test data. Keymaps were removed: %s",
- ContainerUtil.subtract(duplicates.keySet(), allMaps))
- .isEmpty();
- @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
- MultiMap reassignedShortcuts = MultiMap.createLinked();
- for (String name : duplicates.keySet()) {
- Keymap keymap = KeymapManagerEx.getInstanceEx().getKeymap(name);
- assertThat(keymap).overridingErrorMessage("KeyMap %s not found", name).isNotNull();
- Map> duplicateIdsList = duplicates.get(name);
- Set mentionedShortcuts = new THashSet<>();
- for (Map.Entry> shortcutMappings : duplicateIdsList.entrySet()) {
+ for (String keymap : ContainerUtil.sorted(allKeymaps)) {
+ Map> actual = actualDuplicates.get(keymap);
+ Map> expected = expectedDuplicates.get(keymap);
- String shortcutString = shortcutMappings.getKey();
- if (!mentionedShortcuts.add(shortcutString)) {
- TestCase.fail("Shortcut '" + shortcutString + "' duplicate in keymap '" + keymap + "'. Please modify 'known duplicates list'");
- }
- Shortcut shortcut = parse(shortcutString);
- Set actualShortcuts = new THashSet<>(Arrays.asList(keymap.getActionIds(shortcut)));
+ StringBuilder keymapFailure = new StringBuilder();
+ for (Shortcut shortcut : ContainerUtil.union(actual.keySet(), expected.keySet())) {
+ List expectedActions = expected.get(shortcut);
+ List actualActions = actual.get(shortcut);
+ if (expectedActions == null || actualActions == null || !Comparing.haveEqualElements(expectedActions, actualActions)) {
+ String key = getText(shortcut);
- removeBoundActionIds(actualShortcuts);
+ if (actualActions == null) {
+ keymapFailure.append(" ").append(key).append(" - \n");
+ }
+ else {
+ int keyLength = 24;
- Set expectedSc = new THashSet<>(shortcutMappings.getValue());
- for (String s : actualShortcuts) {
- if (!expectedSc.contains(s)) {
- reassignedShortcuts.putValue(keymap, shortcut);
- }
- }
- for (String s : expectedSc) {
- if (!actualShortcuts.contains(s)) {
- System.out.println("Expected action '" + s + "' does not reassign shortcut " + getText(shortcut) + " in keymap " + keymap + " or is not registered");
+ keymapFailure.append(" { ");
+ keymapFailure.append("\"").append(key).append("\"").append(", ");
+ keymapFailure.append(StringUtil.repeat(" ", Math.max(0, keyLength - key.length())));
+
+ List values = ContainerUtil.map(ContainerUtil.sorted(actualActions), it -> "\"" + it + "\"");
+ keymapFailure.append(StringUtil.join(ContainerUtil.sorted(values), ", "));
+
+ keymapFailure.append("},\n");
}
}
}
+
+ if (keymapFailure.length() > 0) {
+ failMessage.append(String.format("Shortcut conflicts found in keymap '%s':\n", keymap));
+ failMessage.append(keymapFailure);
+ failMessage.append("\n");
+ }
}
- if (!reassignedShortcuts.isEmpty()) {
- StringBuilder message = new StringBuilder();
- for (Map.Entry> keymapToShortcuts : reassignedShortcuts.entrySet()) {
- Keymap keymap = keymapToShortcuts.getKey();
- message
- .append("The following shortcuts was reassigned in keymap ").append(keymap.getName())
- .append(". Please modify known duplicates list:\n");
- for (Shortcut eachShortcut : keymapToShortcuts.getValue()) {
- message.append(" { ").append(StringUtil.wrapWithDoubleQuote(getText(eachShortcut))).append(",\t")
- .append(StringUtil.join(keymap.getActionIds(eachShortcut), s -> StringUtil.wrapWithDoubleQuote(s), ", "))
- .append("},\n");
- }
- }
- TestCase.fail("\n" + message.toString());
+
+ if (failMessage.length() > 0) {
+ TestCase.fail(failMessage +
+ "\n" +
+ "Please specify 'use-shortcut-of' attribute for your action if it is similar to another action (but it won't appear in Settings/Keymap),\n" +
+ "reassign shortcut or, if absolutely must, modify the 'known duplicates list'");
}
}
- private static Shortcut parse(String s) {
- String[] sc = s.split(",");
- KeyStroke fst = ActionManagerEx.getKeyStroke(sc[0]);
- assert fst != null : s;
- KeyStroke snd = null;
- if (sc.length == 2) {
- snd = ActionManagerEx.getKeyStroke(sc[1]);
+ @NotNull
+ private Map>> collectExpectedDuplicatedShortcuts() {
+ Map>> knownDuplicates = getKnownDuplicates();
+
+ Map>> expectedDuplicates = new HashMap<>();
+ for (String keymap : knownDuplicates.keySet()) {
+ Map> keyDuplicates = new HashMap<>();
+ expectedDuplicates.put(keymap, keyDuplicates);
+
+ Map> duplicates = knownDuplicates.get(keymap);
+ for (String shortcut : duplicates.keySet()) {
+ Shortcut keyboardShortcut = parseShortcut(shortcut);
+ List actions = duplicates.computeIfAbsent(shortcut, key -> new ArrayList<>());
+ keyDuplicates.put(keyboardShortcut, actions);
+ }
+ }
+ return expectedDuplicates;
+ }
+
+ @NotNull
+ private static Map>> collectActualDuplicatedShortcuts() {
+ Map>> result = new HashMap<>();
+
+ KeymapManagerEx km = KeymapManagerEx.getInstanceEx();
+ Set boundActions = km.getBoundActions();
+ Keymap[] keymaps = km.getAllKeymaps();
+
+ // fill shortcuts
+ for (Keymap keymap : keymaps) {
+ Map> map = new HashMap<>();
+ result.put(keymap.getName(), map);
+
+ for (String actionId : keymap.getActionIds()) {
+ if (boundActions.contains(actionId)) continue;
+
+ for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
+ List actionList = map.computeIfAbsent(shortcut, key -> new ArrayList<>());
+ actionList.add(actionId);
+
+ if (shortcut instanceof KeyboardShortcut && ((KeyboardShortcut)shortcut).getSecondKeyStroke() != null) {
+ KeyboardShortcut firstStroke = new KeyboardShortcut(((KeyboardShortcut)shortcut).getFirstKeyStroke(), null);
+ List firstStrokeActionList = map.computeIfAbsent(firstStroke, key -> new ArrayList<>());
+ if (!firstStrokeActionList.contains(SECOND_STROKE)) {
+ firstStrokeActionList.add(SECOND_STROKE);
+ }
+ }
+ }
+ }
+ }
+
+ // remove shortcuts, reused from parent keymap
+ for (Keymap keymap : keymaps) {
+ Map> map = result.get(keymap.getName());
+
+ List reusedShortcuts = new ArrayList<>();
+
+ for (Shortcut key : map.keySet()) {
+ Shortcut parentKey = convertShortcutForParent(key, keymap);
+ Keymap parent = keymap.getParent();
+
+ while (parent != null) {
+ Map> parentMap = result.get(parent.getName());
+
+ List shortcut = map.get(key);
+ List parentShortcut = parentMap.get(parentKey);
+ if (parentShortcut != null && parentShortcut.containsAll(shortcut)) {
+ reusedShortcuts.add(key);
+ break;
+ }
+
+ parentKey = convertShortcutForParent(parentKey, parent);
+ parent = parent.getParent();
+ }
+ }
+
+ for (Shortcut shortcut : reusedShortcuts) {
+ map.remove(shortcut);
+ }
+ }
+
+ // remove non-duplicated shortcuts
+ for (Keymap keymap : keymaps) {
+ Map> map = result.get(keymap.getName());
+
+ List nonDuplicates = new ArrayList<>();
+ for (Map.Entry> entry : map.entrySet()) {
+ if (entry.getValue().size() < 2) nonDuplicates.add(entry.getKey());
+ }
+
+ for (Shortcut key : nonDuplicates) {
+ map.remove(key);
+ }
+ }
+
+ return result;
+ }
+
+
+ @NotNull
+ private static Shortcut parseShortcut(@NotNull String s) {
+ if (s.contains("button")) {
+ return KeymapUtil.parseMouseShortcut(s);
+ }
+ else {
+ String[] sc = s.split(",");
+ assert sc.length <= 2 : s;
+ KeyStroke fst = KeyStrokeAdapter.getKeyStroke(sc[0]);
+ assert fst != null : s;
+ KeyStroke snd = null;
+ if (sc.length == 2) {
+ snd = KeyStrokeAdapter.getKeyStroke(sc[1]);
+ }
+ return new KeyboardShortcut(fst, snd);
}
- return new KeyboardShortcut(fst, snd);
}
@NotNull
private static String getText(@NotNull Shortcut shortcut) {
- if (shortcut instanceof KeyboardShortcut) {
+ if (shortcut instanceof MouseShortcut) {
+ return KeymapUtil.getMouseShortcutString((MouseShortcut)shortcut);
+ }
+ else if (shortcut instanceof KeyboardShortcut) {
KeyStroke fst = ((KeyboardShortcut)shortcut).getFirstKeyStroke();
String s = getText(fst);
@@ -379,10 +374,11 @@ public abstract class KeymapsTestCaseBase extends PlatformTestCase {
return text.substring(0, offset) + text.substring(offset).toUpperCase(Locale.ENGLISH);
}
- private static void convertMac(@NotNull Shortcut[] there) {
- for (int i = 0; i < there.length; i++) {
- there[i] = MacOSDefaultKeymap.convertShortcutFromParent(there[i]);
+ private static Shortcut convertShortcutForParent(Shortcut key, @NotNull Keymap keymap) {
+ if (keymap.getName().startsWith(KeymapManager.MAC_OS_X_KEYMAP)) {
+ return MacOSDefaultKeymap.convertShortcutFromParent(key);
}
+ return key;
}