Repository: microsoft/vscode (TypeScript)
Date: 2022-12-27 15:11:19+00:00
Task type: Feature

Issue:
Add chords to "show same keybindings"
Currently, to find conflicts caused by a chord, you need to search for the first sequence, without any quotes. This works, but since the search is global across all four columns of information for each keyboard rule, it brings up a variety of commands that you're not interested in. Additionally, it's probably not an obvious way to find conflicts for most VSCode users.

I wonder if "Show Same Keybindings" should not only show the exact same keybindings, but also any keybindings where the first sequence of a chord matches. Alternatively, there could be a second option added to the context menu that does this.


Issue discussion:
(Experimental duplicate detection)
Thanks for submitting this issue. Please also check if it is already covered by an existing one, like:
- [Incorrect keybinding shown on hover for opening viewlet when there are multiple keybindings that begin with the same chord (#51767)](https://www.github.com/microsoft/vscode/issues/51767) <!-- score: 0.466 -->
<!-- potential_duplicates_comment -->
I like the idea of having separate entry/mechanism to look for keybindings containing keys.
@sandy081 Similarly, when editing a keybinding through the keybinding edit widget, when it warns you with "x existing commands have this keybinding", it should probably also warn you if that keybinding is not a chord, but it appears in an existing chord sequence, which again, has the potential for conflicts.

With the two options being that those chord conflicts are either added to the existing warning count, or an additional warning is added with its own count.
Definitely. Some sort of mode (or at least an obvious switch) to search for first strokes in a chord would be amazing.

Solution patch:
diff --git a/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts
--- a/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts
+++ b/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts
@@ -19,6 +19,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
 import { getAllUnboundCommands } from 'vs/workbench/services/keybinding/browser/unboundCommands';
 import { IKeybindingItemEntry, KeybindingMatches, KeybindingMatch, IKeybindingItem } from 'vs/workbench/services/preferences/common/preferences';
 import { ICommandAction, ILocalizedString } from 'vs/platform/action/common/action';
+import { isEmptyObject } from 'vs/base/common/types';
 
 export const KEYBINDING_ENTRY_TEMPLATE_ID = 'keybinding.entry.template';
 
@@ -348,8 +349,13 @@ class KeybindingItemMatches {
 		if (matchedWords.length !== words.length) {
 			return null;
 		}
-		if (completeMatch && (!this.isCompleteMatch(firstPart, firstPartMatch) || !this.isCompleteMatch(chordPart, chordPartMatch))) {
-			return null;
+		if (completeMatch) {
+			if (!this.isCompleteMatch(firstPart, firstPartMatch)) {
+				return null;
+			}
+			if (!isEmptyObject(chordPartMatch) && !this.isCompleteMatch(chordPart, chordPartMatch)) {
+				return null;
+			}
 		}
 		return this.hasAnyMatch(firstPartMatch) || this.hasAnyMatch(chordPartMatch) ? { firstPart: firstPartMatch, chordPart: chordPartMatch } : null;
 	}


Test patch:
diff --git a/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts
--- a/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts
+++ b/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts
@@ -620,6 +620,18 @@ suite('KeybindingsEditorModel', () => {
 		assert.deepStrictEqual(actual[0].keybindingMatches!.firstPart, { keyCode: true });
 	});
 
+	test('filter exact matches also return chords', async () => {
+		const command = 'a' + uuid.generateUuid();
+		const expected = aResolvedKeybindingItem({ command, firstChord: { keyCode: KeyCode.KeyK, modifiers: { ctrlKey: true } }, secondChord: { keyCode: KeyCode.KeyC, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
+		prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstChord: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, secondChord: { keyCode: KeyCode.KeyC, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
+
+		await testObject.resolve(new Map<string, string>());
+		const actual = testObject.fetch('"control+k"').filter(element => element.keybindingItem.command === command);
+		assert.strictEqual(1, actual.length);
+		assert.deepStrictEqual(actual[0].keybindingMatches!.firstPart, { ctrlKey: true, keyCode: true });
+		assert.deepStrictEqual(actual[0].keybindingMatches!.chordPart, {});
+	});
+
 	test('filter modifiers are not matched when not completely matched (prefix)', async () => {
 		testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
 		const term = `alt.${uuid.generateUuid()}`;