Repository: microsoft/vscode (TypeScript)
Date: 2022-10-07 11:48:51+00:00
Task type: Bug Fix

Issue:
Auto-quoting of context keys prevents removal of keybindings via UI
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- 🕮 Read our guide about submitting issues: https://github.com/microsoft/vscode/wiki/Submitting-Bugs-and-Suggestions -->
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
<!-- 🧪 Test using the latest Insiders build to see if your issue has already been fixed: https://code.visualstudio.com/insiders/ -->
<!-- 💡 Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. -->
<!-- 🔧 Launch with `code --disable-extensions` to check. -->
Does this issue occur when all extensions are disabled?: Probably, but hard to reproduce

<!-- 🪓 If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. -->
<!-- 📣 Issues caused by an extension need to be reported directly to the extension publisher. The 'Help > Report Issue' dialog can assist with this. -->
- VS Code Version: 1.70.0 and insiders
- OS Version: Linux x64 5.18.16-1-MANJARO

Steps to Reproduce:

1. Install an extension that contributes a keybinding with a `when` clause containing an `foo in extension.bar` check (e.g. the Julia extension).
2. Go to the Keyboard Shortcuts UI via `workbench.action.openGlobalKeybindings`
3. Right click -> remove the keybinding.
4. VS Code tries to remove the keybinding by inserting  a new one into the `User/keybindings.json`, where the command is prefixed with a minus and, crucially, all complex context keys are quoted:
```
[
    {
        "key": "ctrl+enter",
        "command": "-extension.my-command",
        "when": "foo in 'extension.bar'"
    }
]
```
5. This doesn't actually remove the keybinding from the UI or prevent VS Code from resolving it.
6. Remove the single quotes around `'extension.bar'`
7. The keybinding is properly removed from the UI and cannot be used anymore, as expected.

As far as I can tell, this behaviour is specific to `in` clauses; it's also hard to repro this without an extension because user-supplied keybindings are just removed outright from keybindings.json instead of adding an additional negated entry.

![Peek 2022-08-10 11-53](https://user-images.githubusercontent.com/6735977/183872750-e4118c6e-5c95-442f-8a63-2c3441175b9c.gif)





Issue discussion:

Solution patch:
diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts
--- a/src/vs/platform/contextkey/common/contextkey.ts
+++ b/src/vs/platform/contextkey/common/contextkey.ts
@@ -162,12 +162,12 @@ export abstract class ContextKeyExpr {
 
 		if (serializedOne.indexOf(' not in ') >= 0) {
 			const pieces = serializedOne.split(' not in ');
-			return ContextKeyNotInExpr.create(pieces[0].trim(), pieces[1].trim());
+			return ContextKeyNotInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
 		}
 
 		if (serializedOne.indexOf(' in ') >= 0) {
 			const pieces = serializedOne.split(' in ');
-			return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim());
+			return ContextKeyInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
 		}
 
 		if (/^[^<=>]+>=[^<=>]+$/.test(serializedOne)) {


Test patch:
diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts
--- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts
+++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts
@@ -235,6 +235,17 @@ suite('KeybindingResolver', () => {
 		]);
 	});
 
+	test('issue #157751: Keyboard Shortcuts: Change When Expression might actually remove keybinding in Insiders', () => {
+		const defaults = [
+			kbItem(KeyCode.KeyA, 'command1', null, ContextKeyExpr.deserialize(`editorTextFocus && activeEditor != workbench.editor.notebook && editorLangId in julia.supportedLanguageIds`), true),
+		];
+		const overrides = [
+			kbItem(KeyCode.KeyA, '-command1', null, ContextKeyExpr.deserialize(`editorTextFocus && activeEditor != 'workbench.editor.notebook' && editorLangId in 'julia.supportedLanguageIds'`), false),
+		];
+		const actual = KeybindingResolver.handleRemovals([...defaults, ...overrides]);
+		assert.deepStrictEqual(actual, []);
+	});
+
 	test('contextIsEntirelyIncluded', () => {
 		const toContextKeyExpression = (expr: ContextKeyExpression | string | null) => {
 			if (typeof expr === 'string' || !expr) {