/* * Copyright 2000-2014 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.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.keymap.ex.KeymapManagerEx; import com.intellij.openapi.keymap.impl.KeymapImpl; import com.intellij.openapi.keymap.impl.MacOSDefaultKeymap; import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.PlatformTestCase; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FactoryMap; import gnu.trove.THashMap; import gnu.trove.THashSet; import junit.framework.TestCase; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.*; /** * @author cdr */ public abstract class KeymapsTestCase extends PlatformTestCase { private static final boolean OUTPUT_TEST_DATA = false; public void testDuplicateShortcuts() { StringBuilder failMessage = new StringBuilder(); Map>> knownDuplicates = getKnownDuplicates(); for (Keymap keymap : KeymapManagerEx.getInstanceEx().getAllKeymaps()) { String failure = checkDuplicatesInKeymap(keymap, knownDuplicates); if (failure != null) { 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'"); } } // @formatter:off @NonNls @SuppressWarnings({"HardCodedStringLiteral"}) private static final Map DEFAULT_DUPLICATES = new THashMap(){{ put("$default", new String[][] { { "ADD", "ExpandTreeNode", "Graph.ZoomIn"}, { "BACK_SPACE", "EditorBackSpace", "Images.Thumbnails.UpFolder"}, { "ENTER", "EditorChooseLookupItem", "NextTemplateVariable", "EditorEnter", "Images.Thumbnails.EnterAction", "PropertyInspectorActions.EditValue", "Console.Execute"}, { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue"}, { "alt ENTER", "ShowIntentionActions", "Console.TableResult.EditValue"}, { "F5", "UML.ApplyCurrentLayout", "CopyElement"}, { "F7", "NextDiff", "StepInto"}, { "INSERT", "EditorToggleInsertState", "UsageView.Include", "DomElementsTreeView.AddElement", "DomCollectionControl.Add"}, { "SUBTRACT", "CollapseTreeNode", "Graph.ZoomOut"}, { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", "EditorTab", "NextTemplateParameter"}, { "alt DOWN", "ShowContent", "MethodDown"}, { "alt F1", "SelectIn", "ProjectViewChangeView"}, { "alt INSERT", "FileChooser.NewFolder", "Generate", "NewElement"}, { "control F10", "javaee.UpdateRunningApplication", "liveedit.UpdateRunningApplication"}, { "control 1", "FileChooser.GotoHome", "GotoBookmark1", "DuplicatesForm.SendToLeft"}, { "control 2", "FileChooser.GotoProject", "GotoBookmark2", "DuplicatesForm.SendToRight"}, { "control 3", "GotoBookmark3", "FileChooser.GotoModule"}, { "control ADD", "ExpandAll", "ExpandRegion"}, { "control DELETE", "EditorDeleteToWordEnd", "RemoveFromFavorites"}, { "control DIVIDE", "CommentByLineComment", "Images.Editor.ActualSize"}, { "control DOWN", "EditorScrollDown", "Console.History.Previous", "EditorLookupDown"}, { "control ENTER", "EditorSplitLine", "ViewSource", "Console.Jdbc.Execute", "Console.Jpa.Execute", "Groovy.Shell.Execute"}, { "control EQUALS", "ExpandAll", "ExpandRegion"}, { "control F5", "Refresh", "Rerun"}, { "control D", "CompareDirs", "EditorDuplicate", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"}, { "control M", "EditorScrollToCenter", "Vcs.ShowMessageHistory"}, { "control N", "FileChooser.NewFolder", "GotoClass"}, { "control P", "FileChooser.TogglePathShowing", "ParameterInfo"}, { "control R", "Replace", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, { "control SLASH", "CommentByLineComment", "Images.Editor.ActualSize"}, { "control U", "GotoSuperMethod", "CommanderSwapPanels"}, { "control UP", "EditorScrollUp", "Console.History.Next", "EditorLookupUp"}, { "control alt A", "ChangesView.AddUnversioned", "Diagram.DeselectAll"}, { "control alt E", "PerforceDirect.Edit", "Console.History.Browse"}, { "control alt DOWN", "NextOccurence", "Console.TableResult.NextPage"}, { "control alt G", "org.jetbrains.plugins.ruby.rails.actions.generators.GeneratorsPopupAction", "Mvc.RunTarget"}, { "control alt R", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction", "Django.RunManageTaskAction"}, { "control alt UP", "PreviousOccurence", "Console.TableResult.PreviousPage"}, { "control MINUS", "CollapseAll", "CollapseRegion"}, { "control PERIOD", "EditorChooseLookupItemDot", "CollapseSelection"}, { "shift DELETE", "$Cut", "Maven.Uml.Exclude"}, { "shift F4", "Debugger.EditTypeSource", "EditSourceInNewWindow"}, { "shift F7", "PreviousDiff", "SmartStepInto"}, { "shift TAB", "PreviousTemplateVariable", "PrevParameter", "EditorUnindentSelection", "PrevTemplateParameter"}, { "shift alt L", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"}, { "shift alt T", "tasks.switch", "tasks.switch.toolbar"}, { "shift control D", "Uml.ShowDiff", "TagDocumentationNavigation"}, { "shift control DOWN", "ResizeToolWindowDown", "MoveStatementDown"}, { "shift control ENTER", "EditorChooseLookupItemCompleteStatement", "EditorCompleteStatement", "Console.Jpa.GenerateSql"}, { "shift control F10", "Jdbc.OpenConsole", "Jdbc.RunSqlScript", "Jpa.OpenConsole", "RunClass", "RunTargetAction"}, { "shift control F8", "ViewBreakpoints", "EditBreakpoint"}, { "shift control G", "ClassTemplateNavigation", "GoToClass"}, { "shift control LEFT", "EditorPreviousWordWithSelection", "ResizeToolWindowLeft", }, { "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 DELETE", "Console.Jdbc.Terminate", "Console.Jpa.Terminate"}, { "shift control K", "hg4idea.push", "Git.Push"}, { "control E", "RecentFiles", "Vcs.ShowMessageHistory"}, }); put("Mac OS X 10.5+", new String[][] { { "BACK_SPACE", "$Delete", "EditorBackSpace", "Images.Thumbnails.UpFolder"}, { "shift BACK_SPACE", "EditorBackSpace", "UsageView.Include"}, { "meta BACK_SPACE", "EditorDeleteLine", "$Delete"}, { "control DOWN", "ShowContent", "EditorLookupDown", "MethodDown"}, { "control UP", "EditorLookupUp", "MethodUp"}, { "meta R", "Refresh", "Rerun", "Replace", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, { "control O", "ExportToTextFile", "OverrideMethods", }, { "control ENTER", "Generate", "NewElement"}, { "meta 1", "ActivateProjectToolWindow", "FileChooser.GotoHome", "DuplicatesForm.SendToLeft"}, { "meta 2", "ActivateFavoritesToolWindow", "FileChooser.GotoProject", "DuplicatesForm.SendToRight"}, { "meta 3", "ActivateFindToolWindow", "FileChooser.GotoModule"}, { "meta N", "FileChooser.NewFolder", "Generate", "NewElement"}, { "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"}, }); put("Mac OS X", new String[][] { { "BACK_SPACE", "$Delete", "EditorBackSpace", "Images.Thumbnails.UpFolder"}, { "control DOWN", "EditorLookupDown", "ShowContent", "MethodDown"}, { "control UP", "EditorLookupUp", "MethodUp"}, { "control ENTER", "Generate", "NewElement"}, { "control F5", "Refresh", "Rerun"}, { "meta 1", "ActivateProjectToolWindow", "FileChooser.GotoHome", "DuplicatesForm.SendToLeft"}, { "meta 2", "ActivateFavoritesToolWindow", "FileChooser.GotoProject", "DuplicatesForm.SendToRight"}, { "meta 3", "ActivateFindToolWindow", "FileChooser.GotoModule"}, { "shift meta LEFT", "EditorLineStartWithSelection", "ResizeToolWindowLeft", }, { "shift meta RIGHT", "EditorLineEndWithSelection", "ResizeToolWindowRight", }, { "shift control K", "hg4idea.push", "Git.Push"}, { "alt R", "Django.RunManageTaskAction", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction"}, { "meta E", "RecentFiles", "Vcs.ShowMessageHistory"}, }); put("Emacs", new String[][] { { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue"}, { "alt ENTER", "ShowIntentionActions", "Console.TableResult.EditValue"}, { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", "EmacsStyleIndent", "NextTemplateParameter"}, { "alt DOWN", "ShowContent", "MethodDown"}, { "alt SLASH", "CodeCompletion", "HippieCompletion"}, { "control 0", "Unsplit", "GotoBookmark0"}, { "control 1", "UnsplitAll", "FileChooser.GotoHome", "GotoBookmark1", "DuplicatesForm.SendToLeft"}, { "control 2", "FileChooser.GotoProject", "GotoBookmark2", "DuplicatesForm.SendToRight"}, { "control 3", "GotoBookmark3", "FileChooser.GotoModule"}, { "control 5", "ChangeSplitOrientation", "GotoBookmark5"}, { "control D", "CompareDirs", "$Delete", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"}, { "control K", "EditorCutLineEnd", "CheckinProject"}, { "control N", "EditorDown", "FileChooser.NewFolder"}, { "control P", "EditorUp", "FileChooser.TogglePathShowing"}, { "control R", "Console.TableResult.Reload", "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", "CloseAllEditorsButActive", "Switcher", "$SelectAll"}, { "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 control X", "GotoPreviousError", "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 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"}, }); put("Default for XWin", new String[][] { }); 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"}, { "shift alt RIGHT", "NextEditorTab", "Forward"}, }); put("Default for KDE", new String[][] { { "control 1", "FileChooser.GotoHome", "ShowErrorDescription", "DuplicatesForm.SendToLeft"}, { "control 2", "FileChooser.GotoProject", "Stop", "DuplicatesForm.SendToRight"}, { "control 3", "FindWordAtCaret", "FileChooser.GotoModule"}, { "control 5", "Refresh", "Rerun"}, { "shift alt 1", "SelectIn", "ProjectViewChangeView"}, { "shift alt 7", "IDEtalk.SearchUserHistory", "FindUsages"}, { "shift alt L", "ReformatCode", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"}, }); put("Eclipse", new String[][] { { "F2", "Console.TableResult.EditValue", "QuickJavaDoc"}, { "alt ENTER", "Console.TableResult.EditValue", "ShowIntentionActions"}, { "F5", "UML.ApplyCurrentLayout", "StepInto"}, { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", "EditorTab", "NextTemplateParameter"}, { "alt DOWN", "ShowContent", "MoveStatementDown"}, { "alt HOME", "ViewNavigationBar", "ShowNavBar"}, { "control F10", "ShowPopupMenu", "javaee.UpdateRunningApplication", "liveedit.UpdateRunningApplication"}, { "control F11", "Rerun", "ToggleBookmarkWithMnemonic"}, { "control D", "CompareDirs", "EditorDeleteLine", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"}, { "control N", "ShowPopupMenu", "FileChooser.NewFolder"}, { "control P", "FileChooser.TogglePathShowing", "Print"}, { "control R", "RunToCursor", "Console.TableResult.Reload", "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 L", "IntroduceVariable", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"}, { "shift alt S", "ShowPopupMenu", "context.save"}, { "shift alt T", "ShowPopupMenu", "tasks.switch", "tasks.switch.toolbar"}, { "shift control DOWN", "ResizeToolWindowDown", "MethodDown"}, { "shift control E", "EditSource", "RecentChangedFiles", "Graph.Faces.OpenSelectedPages"}, { "shift control F6", "PreviousTab", "ChangeTypeSignature"}, { "shift control F11", "ToggleBookmark", "FocusTracer"}, { "shift control G", "FindUsagesInFile", "ClassTemplateNavigation", "GoToClass"}, { "shift control I", "QuickImplementations", "Debugger.Inspect"}, { "shift control LEFT", "EditorPreviousWordWithSelection", "ResizeToolWindowLeft", }, { "shift control RIGHT", "EditorNextWordWithSelection", "ResizeToolWindowRight", }, { "shift control UP", "ResizeToolWindowUp", "MethodUp"}, { "shift control alt LEFT", "NextEditorTab", "HtmlTableCellNavigateLeft"}, { "shift control alt RIGHT", "PreviousEditorTab", "HtmlTableCellNavigateRight"}, { "shift control K", "hg4idea.push", "Git.Push", "FindPrevious"}, { "shift control X", "EditorToggleCase", "com.jetbrains.php.framework.FrameworkRunConsoleAction"}, }); put("NetBeans 6.5", new String[][] { { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue"}, { "F4", "RunToCursor", "EditSource"}, { "F5", "Debugger.ResumeThread", "Resume", "UML.ApplyCurrentLayout"}, { "alt DOWN", "NextOccurence", "ShowContent"}, { "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", "CommentByLineComment", "Images.Editor.ActualSize"}, { "control D", "EditorDuplicate", "CompareDirs", "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 PERIOD", "GotoNextError", "EditorChooseLookupItemDot"}, { "control alt DOWN", "MethodDown", "Console.TableResult.NextPage"}, { "control alt UP", "MethodUp", "Console.TableResult.PreviousPage"}, { "shift F4", "RecentFiles", "Debugger.EditTypeSource", "Vcs.ShowMessageHistory", "EditSourceInNewWindow"}, { "shift alt F9", "ChooseDebugConfiguration", "ValidateXml", "ValidateJsp"}, { "shift control DOWN", "EditorDuplicate", "ResizeToolWindowDown", }, { "shift control ENTER", "EditorChooseLookupItemCompleteStatement", "EditorCompleteStatement", "Console.Jpa.GenerateSql"}, { "shift control F7", "HighlightUsagesInFile", "Debugger.NewWatch"}, { "shift control UP", "EditorDuplicate", "ResizeToolWindowUp", }, { "shift control alt P", "Print", "Graph.Print"}, { "shift control K", "HippieCompletion", "hg4idea.push", "Git.Push"}, { "control alt E", "Console.History.Browse", "ExecuteInPyConsoleAction", "PerforceDirect.Edit"}, { "TAB", "NextTemplateVariable", "NextParameter", "EditorTab", "EditorChooseLookupItemReplace", "EditorIndentSelection", "NextTemplateParameter"}, { "shift TAB", "EditorUnindentSelection", "PreviousTemplateVariable", "PrevParameter", "PrevTemplateParameter"}, }); put("JBuilder", new String[][] { { "F2", "EditorTab", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue"}, { "F5", "ToggleBreakpointEnabled", "UML.ApplyCurrentLayout"}, { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", "EmacsStyleIndent", "NextTemplateParameter"}, { "control F6", "PreviousEditorTab", "PreviousTab", }, { "control M", "Vcs.ShowMessageHistory", "OverrideMethods", }, { "control N", "FileChooser.NewFolder", "GotoClass"}, { "control P", "FileChooser.TogglePathShowing", "FindInPath"}, { "shift control A", "SaveAll", "GotoAction"}, { "shift control E", "RecentChangedFiles", "ExtractMethod"}, { "shift control ENTER", "EditorChooseLookupItemCompleteStatement", "FindUsages", "Console.Jpa.GenerateSql"}, { "shift control F6", "NextTab", "ChangeTypeSignature"}, { "shift control G", "GotoSymbol", "ClassTemplateNavigation", "GoToClass"}, { "shift control K", "hg4idea.push", "Git.Push"}, { "control SUBTRACT", "CollapseAll", "CollapseRegion"}, { "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"}, { "F3", "GotoDeclaration", "EditSource"}, { "F5", "StepInto", "UML.ApplyCurrentLayout"}, { "control PERIOD", "EditorChooseLookupItemDot", "HippieCompletion"}, { "meta 1", "FileChooser.GotoHome", "ShowIntentionActions", "DuplicatesForm.SendToLeft"}, { "meta 3", "FileChooser.GotoModule", "GotoAction"}, { "meta D", "EditorDeleteLine", "CompareTwoFiles", "CompareDirs", "SendEOF", "FileChooser.GotoDesktop"}, { "meta P", "FileChooser.TogglePathShowing", "Print"}, { "meta R", "org.jetbrains.plugins.ruby.rails.console.ReloadSources", "RunToCursor"}, { "meta U", "CommanderSwapPanels", "EvaluateExpression"}, { "meta W", "CloseContent", "CloseActiveTab"}, { "meta alt DOWN", "Console.TableResult.NextPage", "EditorDuplicateLines"}, { "shift meta F11", "Run", "FocusTracer"}, { "shift meta G", "ClassTemplateNavigation", "GoToClass", "FindUsages"}, { "shift meta K", "hg4idea.push", "Git.Push", "FindPrevious"}, { "shift meta X", "EditorToggleCase", "com.jetbrains.php.framework.FrameworkRunConsoleAction"}, }); }}; // @formatter:on private Map>> getKnownDuplicates() { Map>> result = new LinkedHashMap>>(); collectKnownDuplicates(result); return result; } protected void collectKnownDuplicates(Map>> result) { appendKnownDuplicates(result, DEFAULT_DUPLICATES); } protected static void appendKnownDuplicates(Map>> result, Map duplicates) { for (Map.Entry eachKeymap : duplicates.entrySet()) { String keymapName = eachKeymap.getKey(); Map> mapping = result.get(keymapName); if (mapping == null) { result.put(keymapName, mapping = new LinkedHashMap>()); } for (String[] shortcuts : eachKeymap.getValue()) { TestCase.assertTrue("known duplicates list entry for '" + keymapName + "' must not contain empty array", shortcuts.length > 0); TestCase.assertTrue("known duplicates list entry for '" + keymapName + "', shortcut '" + shortcuts[0] + "' must contain at least two conflicting action ids", shortcuts.length > 2); mapping.put(shortcuts[0], ContainerUtil.newArrayList(shortcuts, 1, shortcuts.length)); } } } @SuppressWarnings({"HardCodedStringLiteral"}) private static String checkDuplicatesInKeymap(Keymap keymap, Map>> allKnownDuplicates) { Set shortcuts = new LinkedHashSet(); Set aids = new THashSet(Arrays.asList(keymap.getActionIds())); removeBoundActionIds(aids); nextId: for (String id : aids) { Map> knownDuplicates = allKnownDuplicates.get(keymap.getName()); if (knownDuplicates != null) { for (List actionsMapping : knownDuplicates.values()) { for (String eachAction : actionsMapping) { if (eachAction.equals(id)) continue nextId; } } } for (Shortcut shortcut : keymap.getShortcuts(id)) { if (!(shortcut instanceof KeyboardShortcut)) { continue; } shortcuts.add(shortcut); } } List sorted = new ArrayList(shortcuts); Collections.sort(sorted, new Comparator() { @Override public int compare(Shortcut o1, Shortcut o2) { return getText(o1).compareTo(getText(o2)); } }); 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 HashSet(Arrays.asList(here)).equals(new HashSet(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(Set aids) { // explicitly bound to another action for (Iterator it = aids.iterator(); it.hasNext();) { String id = it.next(); String sourceId = KeymapManagerEx.getInstanceEx().getActionBinding(id); if (sourceId != null) { it.remove(); } } } @NonNls private static final Set unknownActionIds = new THashSet(Arrays.asList( "ActivateChangesToolWindow", "ActivateFavoritesToolWindow", "ActivateCommanderToolWindow", "ActivateDebugToolWindow", "ActivateFindToolWindow", "ActivateHierarchyToolWindow", "ActivateMessagesToolWindow", "ActivateProjectToolWindow", "ActivateRunToolWindow", "ActivateStructureToolWindow", "ActivateTODOToolWindow", "ActivateWebToolWindow","ActivatePaletteToolWindow", "ActivateTerminalToolWindow", "IDEtalk.SearchUserHistory", "IDEtalk.SearchUserHistory", "IDEtalk.Rename", "" )); protected void collectUnknownActions(Set result) { result.addAll(unknownActionIds); } public void testValidActionIds() { THashSet unknownActions = new THashSet(); collectUnknownActions(unknownActions); @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") Map> missingActions = new FactoryMap>() { @Override protected Map> createMap() { return new LinkedHashMap>(); } @Nullable @Override protected List create(String key) { return new ArrayList(); } }; for (Keymap keymap : KeymapManagerEx.getInstanceEx().getAllKeymaps()) { String[] ids = keymap.getActionIds(); Arrays.sort(ids); Set noDuplicates = new LinkedHashSet(Arrays.asList(ids)); TestCase.assertEquals(new ArrayList(Arrays.asList(ids)), new ArrayList(noDuplicates)); 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); } } } } List reappearedAction = new ArrayList(); for (String id : unknownActions) { AnAction action = ActionManager.getInstance().getAction(id); if (action != null) { reappearedAction.add(id); } } 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 (!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"); } } fail("\n" + message); } } @SuppressWarnings({"HardCodedStringLiteral"}) public void testIdsListIsConsistent() { Map>> duplicates = getKnownDuplicates(); THashSet allMaps = new THashSet(ContainerUtil.map(KeymapManagerEx.getInstanceEx().getAllKeymaps(), new Function() { @Override public String fun(Keymap keymap) { return keymap.getName(); } })); TestCase.assertTrue("Modify 'known duplicates list' test data. Keymaps were added: " + ContainerUtil.subtract(allMaps, duplicates.keySet()), ContainerUtil.subtract(allMaps, duplicates.keySet()).isEmpty() ); TestCase.assertTrue("Modify 'known duplicates list' test data. Keymaps were removed: " + ContainerUtil.subtract(duplicates.keySet(), allMaps), ContainerUtil.subtract(duplicates.keySet(), allMaps).isEmpty() ); @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") Map> reassignedShortcuts = new FactoryMap>() { @Override protected Map> createMap() { return new LinkedHashMap>(); } @Nullable @Override protected List create(String key) { return new ArrayList(); } }; for (String name : duplicates.keySet()) { Keymap keymap = KeymapManagerEx.getInstanceEx().getKeymap(name); TestCase.assertNotNull("KeyMap " + name + " not found", keymap); Map> duplicateIdsList = duplicates.get(name); Set mentionedShortcuts = new THashSet(); for (Map.Entry> shortcutMappings : duplicateIdsList.entrySet()) { 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); String[] ids = keymap.getActionIds(shortcut); Set actualSc = new HashSet(Arrays.asList(ids)); removeBoundActionIds(actualSc); Set expectedSc = new HashSet(shortcutMappings.getValue()); for (String s : actualSc) { if (!expectedSc.contains(s)) { reassignedShortcuts.get(keymap.getName()).add(getText(shortcut)); } } for (String s : expectedSc) { if (!actualSc.contains(s)) { System.out.println("Expected action '" + s + "' does not reassign shortcut " + getText(shortcut) + " in keymap " + keymap + " or is not registered"); } } } } if (!reassignedShortcuts.isEmpty()) { StringBuilder message = new StringBuilder(); for (Map.Entry> keymapToShortcuts : reassignedShortcuts.entrySet()) { message.append("The following shortcuts was reassigned in keymap ").append(keymapToShortcuts.getKey()) .append(". Please modify known duplicates list:\n"); for (String eachShortcut : keymapToShortcuts.getValue()) { message.append(eachShortcut).append("\n"); } } TestCase.fail("\n" + message.toString()); } } 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]); } return new KeyboardShortcut(fst, snd); } private static String getText(Shortcut shortcut) { if (shortcut instanceof KeyboardShortcut) { KeyStroke fst = ((KeyboardShortcut)shortcut).getFirstKeyStroke(); String s = KeymapImpl.getKeyShortcutString(fst); KeyStroke snd = ((KeyboardShortcut)shortcut).getSecondKeyStroke(); if (snd != null) { s += "," + KeymapImpl.getKeyShortcutString(snd); } return s; } return KeymapUtil.getShortcutText(shortcut); } private static void convertMac(Shortcut[] there) { for (int i = 0; i < there.length; i++) { there[i] = MacOSDefaultKeymap.convertShortcutFromParent(there[i]); } } private static final Set LINUX_KEYMAPS = ContainerUtil.newHashSet("Default for XWin", "Default for GNOME", "Default for KDE"); public void testLinuxShortcuts() { for (Keymap keymap : KeymapManagerEx.getInstanceEx().getAllKeymaps()) { if (LINUX_KEYMAPS.contains(keymap.getName())) { checkLinuxKeymap(keymap); } } } private static void checkLinuxKeymap(final Keymap keymap) { for (String actionId : keymap.getActionIds()) { for (Shortcut shortcut : keymap.getShortcuts(actionId)) { if (shortcut instanceof KeyboardShortcut) { checkCtrlAltFn(keymap, shortcut, ((KeyboardShortcut)shortcut).getFirstKeyStroke()); checkCtrlAltFn(keymap, shortcut, ((KeyboardShortcut)shortcut).getSecondKeyStroke()); } } } } private static void checkCtrlAltFn(final Keymap keymap, final Shortcut shortcut, final KeyStroke stroke) { if (stroke != null) { final int modifiers = stroke.getModifiers(); final int keyCode = stroke.getKeyCode(); if (KeyEvent.VK_F1 <= keyCode && keyCode <= KeyEvent.VK_F12 && (modifiers & InputEvent.CTRL_MASK) != 0 && (modifiers & InputEvent.ALT_MASK) != 0 && (modifiers & InputEvent.SHIFT_MASK) == 0) { final String message = "Invalid shortcut '" + shortcut + "' for action(s) " + Arrays.asList(keymap.getActionIds(shortcut)) + " in keymap '" + keymap.getName() + "' " + "(Ctrl-Alt-Fn shortcuts switch Linux virtual terminals (causes newbie panic), " + "so either assign another shortcut, or remove it; see Keymap_XWin.xml for reference)."; TestCase.fail(message); } } } }