mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
ui: rewrite KeymapsTestCase
* detect duplicated in mouse shortcuts * detect removed duplicates * do not repeat same duplicates in dependent keymaps * require to specify all duplicated shortcuts for actions pair * detect duplicates between second-stroke and single-stroke shortcuts
This commit is contained in:
committed by
Aleksey Pivovarov
parent
603a1620fe
commit
9fffa74cf5
@@ -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 <code>MouseShortcut</code>
|
||||
*/
|
||||
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) {
|
||||
|
||||
@@ -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<Shortcut>, shortcuts2: List<Short
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string representation of passed mouse shortcut. This method should
|
||||
* * be used only for serializing of the `MouseShortcut`
|
||||
*/
|
||||
private fun getMouseShortcutString(shortcut: MouseShortcut): String {
|
||||
if (Registry.`is`("ide.mac.forceTouch") && shortcut is PressureShortcut) {
|
||||
return "Force touch"
|
||||
}
|
||||
|
||||
val buffer = StringBuilder()
|
||||
|
||||
// modifiers
|
||||
val modifiers = shortcut.modifiers
|
||||
if (InputEvent.SHIFT_DOWN_MASK and modifiers > 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)
|
||||
}
|
||||
@@ -30,7 +30,10 @@ class MacOSDefaultKeymap(dataHolder: SchemeDataHolder<KeymapImpl>, 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+231
-235
@@ -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<String> 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<String, Map<String, List<String>>> parseKnownDuplicates(Map<String, String[][]> baseDuplicates,
|
||||
Map<String, String[][]> ideDuplicates) {
|
||||
Map<String, Map<String, List<String>>> baseMap = parseKnownDuplicates(baseDuplicates);
|
||||
baseMap.keySet().retainAll(ContainerUtil.map(KeymapManagerEx.getInstanceEx().getAllKeymaps(), Keymap::getName));
|
||||
Map<String, Map<String, List<String>>> baseMapping = parseKnownDuplicates(baseDuplicates);
|
||||
Map<String, Map<String, List<String>>> ideMapping = parseKnownDuplicates(ideDuplicates);
|
||||
|
||||
Map<String, Map<String, List<String>>> ideMap = parseKnownDuplicates(ideDuplicates);
|
||||
HashMap<String, Map<String, List<String>>> result = new HashMap<>();
|
||||
|
||||
Map<String, Map<String, List<String>>> result = new HashMap<>();
|
||||
for (String keymap : ContainerUtil.union(baseMap.keySet(), ideMap.keySet())) {
|
||||
Map<String, List<String>> mapping = new HashMap<>();
|
||||
result.put(keymap, mapping);
|
||||
KeymapManagerEx km = KeymapManagerEx.getInstanceEx();
|
||||
List<String> availableKeymaps = ContainerUtil.map(km.getAllKeymaps(), Keymap::getName);
|
||||
|
||||
Map<String, List<String>> baseMapping = baseMap.get(keymap);
|
||||
Map<String, List<String>> ideMapping = ideMap.get(keymap);
|
||||
for (String keymapName : availableKeymaps) {
|
||||
Map<String, List<String>> map = result.computeIfAbsent(keymapName, key -> new HashMap<>());
|
||||
Map<String, List<String>> 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<String> 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<String, List<String>> map = result.computeIfAbsent(keymap, key -> new HashMap<>());
|
||||
Map<String, List<String>> ideMap = ideMapping.get(keymap);
|
||||
|
||||
map.putAll(ideMap);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public void testDuplicateShortcuts() {
|
||||
public void testUnknownActionIds() {
|
||||
StringBuilder failMessage = new StringBuilder();
|
||||
Map<String, Map<String, List<String>>> 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<String, Map<String, List<String>>> allKnownDuplicates) {
|
||||
Set<String> aids = keymap.getActionIdList();
|
||||
removeBoundActionIds(aids);
|
||||
|
||||
Set<Shortcut> shortcuts = new THashSet<>();
|
||||
|
||||
nextId:
|
||||
for (String id : aids) {
|
||||
Map<String, List<String>> knownDuplicates = allKnownDuplicates.get(keymap.getName());
|
||||
if (knownDuplicates != null) {
|
||||
for (List<String> actionsMapping : knownDuplicates.values()) {
|
||||
if (actionsMapping.contains(id)) {
|
||||
continue nextId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Shortcut shortcut : keymap.getShortcuts(id)) {
|
||||
if (shortcut instanceof KeyboardShortcut) {
|
||||
shortcuts.add(shortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Shortcut> 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<String> 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<String> aids) {
|
||||
KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
|
||||
// explicitly bound to another action
|
||||
for (Iterator<String> it = aids.iterator(); it.hasNext(); ) {
|
||||
String id = it.next();
|
||||
String sourceId = keymapManager.getActionBinding(id);
|
||||
if (sourceId != null) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testValidActionIds() {
|
||||
Set<String> unknownActions = getUnknownActions();
|
||||
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
Map<String, List<String>> missingActions = new FactoryMap<String, List<String>>() {
|
||||
@Override
|
||||
protected Map<String, List<String>> createMap() {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
MultiMap<String, String> missingActions = new MultiMap<>();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected List<String> create(String key) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
};
|
||||
for (Keymap keymap : KeymapManagerEx.getInstanceEx().getAllKeymaps()) {
|
||||
List<String> ids = new ArrayList<>(keymap.getActionIdList());
|
||||
ids.sort(null);
|
||||
assertThat(ids).isEqualTo(new ArrayList<>(new LinkedHashSet<>(ids)));
|
||||
Collection<String> 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<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 (!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<String, Map<String, List<String>>> duplicates = getKnownDuplicates();
|
||||
|
||||
Set<String> 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<String, Map<Shortcut, List<String>>> expectedDuplicates = collectExpectedDuplicatedShortcuts();
|
||||
Map<String, Map<Shortcut, List<String>>> actualDuplicates = collectActualDuplicatedShortcuts();
|
||||
|
||||
|
||||
Set<String> allKeymaps = ContainerUtil.union(expectedDuplicates.keySet(), actualDuplicates.keySet());
|
||||
Collection<String> newKeymaps = ContainerUtil.subtract(allKeymaps, expectedDuplicates.keySet());
|
||||
Collection<String> 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<Keymap, Shortcut> reassignedShortcuts = MultiMap.createLinked();
|
||||
for (String name : duplicates.keySet()) {
|
||||
Keymap keymap = KeymapManagerEx.getInstanceEx().getKeymap(name);
|
||||
assertThat(keymap).overridingErrorMessage("KeyMap %s not found", name).isNotNull();
|
||||
Map<String, List<String>> duplicateIdsList = duplicates.get(name);
|
||||
Set<String> mentionedShortcuts = new THashSet<>();
|
||||
for (Map.Entry<String, List<String>> shortcutMappings : duplicateIdsList.entrySet()) {
|
||||
for (String keymap : ContainerUtil.sorted(allKeymaps)) {
|
||||
Map<Shortcut, List<String>> actual = actualDuplicates.get(keymap);
|
||||
Map<Shortcut, List<String>> 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<String> actualShortcuts = new THashSet<>(Arrays.asList(keymap.getActionIds(shortcut)));
|
||||
StringBuilder keymapFailure = new StringBuilder();
|
||||
for (Shortcut shortcut : ContainerUtil.union(actual.keySet(), expected.keySet())) {
|
||||
List<String> expectedActions = expected.get(shortcut);
|
||||
List<String> 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(" - <empty>\n");
|
||||
}
|
||||
else {
|
||||
int keyLength = 24;
|
||||
|
||||
Set<String> 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<String> 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<Keymap, Collection<Shortcut>> 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<String, Map<Shortcut, List<String>>> collectExpectedDuplicatedShortcuts() {
|
||||
Map<String, Map<String, List<String>>> knownDuplicates = getKnownDuplicates();
|
||||
|
||||
Map<String, Map<Shortcut, List<String>>> expectedDuplicates = new HashMap<>();
|
||||
for (String keymap : knownDuplicates.keySet()) {
|
||||
Map<Shortcut, List<String>> keyDuplicates = new HashMap<>();
|
||||
expectedDuplicates.put(keymap, keyDuplicates);
|
||||
|
||||
Map<String, List<String>> duplicates = knownDuplicates.get(keymap);
|
||||
for (String shortcut : duplicates.keySet()) {
|
||||
Shortcut keyboardShortcut = parseShortcut(shortcut);
|
||||
List<String> actions = duplicates.computeIfAbsent(shortcut, key -> new ArrayList<>());
|
||||
keyDuplicates.put(keyboardShortcut, actions);
|
||||
}
|
||||
}
|
||||
return expectedDuplicates;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Map<String, Map<Shortcut, List<String>>> collectActualDuplicatedShortcuts() {
|
||||
Map<String, Map<Shortcut, List<String>>> result = new HashMap<>();
|
||||
|
||||
KeymapManagerEx km = KeymapManagerEx.getInstanceEx();
|
||||
Set<String> boundActions = km.getBoundActions();
|
||||
Keymap[] keymaps = km.getAllKeymaps();
|
||||
|
||||
// fill shortcuts
|
||||
for (Keymap keymap : keymaps) {
|
||||
Map<Shortcut, List<String>> 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<String> 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<String> 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<Shortcut, List<String>> map = result.get(keymap.getName());
|
||||
|
||||
List<Shortcut> reusedShortcuts = new ArrayList<>();
|
||||
|
||||
for (Shortcut key : map.keySet()) {
|
||||
Shortcut parentKey = convertShortcutForParent(key, keymap);
|
||||
Keymap parent = keymap.getParent();
|
||||
|
||||
while (parent != null) {
|
||||
Map<Shortcut, List<String>> parentMap = result.get(parent.getName());
|
||||
|
||||
List<String> shortcut = map.get(key);
|
||||
List<String> 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<Shortcut, List<String>> map = result.get(keymap.getName());
|
||||
|
||||
List<Shortcut> nonDuplicates = new ArrayList<>();
|
||||
for (Map.Entry<Shortcut, List<String>> 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user