blob: 4375701bc62c7a4bcb263c79e128a4788e879862 [file] [log] [blame]
/*
* 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<String, Map<String, List<String>>> 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<String, String[][]> DEFAULT_DUPLICATES = new THashMap<String, String[][]>(){{
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<String, Map<String, List<String>>> getKnownDuplicates() {
Map<String, Map<String, List<String>>> result = new LinkedHashMap<String, Map<String, List<String>>>();
collectKnownDuplicates(result);
return result;
}
protected void collectKnownDuplicates(Map<String, Map<String, List<String>>> result) {
appendKnownDuplicates(result, DEFAULT_DUPLICATES);
}
protected static void appendKnownDuplicates(Map<String, Map<String, List<String>>> result, Map<String, String[][]> duplicates) {
for (Map.Entry<String, String[][]> eachKeymap : duplicates.entrySet()) {
String keymapName = eachKeymap.getKey();
Map<String, List<String>> mapping = result.get(keymapName);
if (mapping == null) {
result.put(keymapName, mapping = new LinkedHashMap<String, List<String>>());
}
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<String, Map<String, List<String>>> allKnownDuplicates) {
Set<Shortcut> shortcuts = new LinkedHashSet<Shortcut>();
Set<String> aids = new THashSet<String>(Arrays.asList(keymap.getActionIds()));
removeBoundActionIds(aids);
nextId:
for (String id : aids) {
Map<String, List<String>> knownDuplicates = allKnownDuplicates.get(keymap.getName());
if (knownDuplicates != null) {
for (List<String> 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<Shortcut> sorted = new ArrayList<Shortcut>(shortcuts);
Collections.sort(sorted, new Comparator<Shortcut>() {
@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<String> ids = new THashSet<String>(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<Shortcut>(Arrays.asList(here)).equals(new HashSet<Shortcut>(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<String> aids) {
// explicitly bound to another action
for (Iterator<String> 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<String> unknownActionIds = new THashSet<String>(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<String> result) {
result.addAll(unknownActionIds);
}
public void testValidActionIds() {
THashSet<String> unknownActions = new THashSet<String>();
collectUnknownActions(unknownActions);
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
Map<String, List<String>> missingActions = new FactoryMap<String, List<String>>() {
@Override
protected Map<String, List<String>> createMap() {
return new LinkedHashMap<String, List<String>>();
}
@Nullable
@Override
protected List<String> create(String key) {
return new ArrayList<String>();
}
};
for (Keymap keymap : KeymapManagerEx.getInstanceEx().getAllKeymaps()) {
String[] ids = keymap.getActionIds();
Arrays.sort(ids);
Set<String> noDuplicates = new LinkedHashSet<String>(Arrays.asList(ids));
TestCase.assertEquals(new ArrayList<String>(Arrays.asList(ids)), new ArrayList<String>(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<String> reappearedAction = new ArrayList<String>();
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<String, List<String>> 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<String, Map<String, List<String>>> duplicates = getKnownDuplicates();
THashSet<String> allMaps =
new THashSet<String>(ContainerUtil.map(KeymapManagerEx.getInstanceEx().getAllKeymaps(), new Function<Keymap, String>() {
@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<String, List<String>> reassignedShortcuts = new FactoryMap<String, List<String>>() {
@Override
protected Map<String, List<String>> createMap() {
return new LinkedHashMap<String, List<String>>();
}
@Nullable
@Override
protected List<String> create(String key) {
return new ArrayList<String>();
}
};
for (String name : duplicates.keySet()) {
Keymap keymap = KeymapManagerEx.getInstanceEx().getKeymap(name);
TestCase.assertNotNull("KeyMap " + name + " not found", keymap);
Map<String, List<String>> duplicateIdsList = duplicates.get(name);
Set<String> mentionedShortcuts = new THashSet<String>();
for (Map.Entry<String, List<String>> 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<String> actualSc = new HashSet<String>(Arrays.asList(ids));
removeBoundActionIds(actualSc);
Set<String> expectedSc = new HashSet<String>(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<String, List<String>> 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<String> 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);
}
}
}
}