mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-13 23:44:09 +01:00
312 lines
11 KiB
TypeScript
312 lines
11 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
'use strict';
|
|
|
|
import assert = require('assert');
|
|
import {CommonKeybindingResolver} from 'vs/platform/keybinding/common/commonKeybindingResolver';
|
|
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
|
|
import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils';
|
|
import Platform = require('vs/base/common/platform');
|
|
import {IKeybindingContextRule, IKeybindingItem} from 'vs/platform/keybinding/common/keybindingService';
|
|
import {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes';
|
|
|
|
suite('Keybinding Service', () => {
|
|
|
|
test('resolve key', function() {
|
|
var keybinding = KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z;
|
|
var contextRules = [{
|
|
key: 'bar',
|
|
operator: 'equal',
|
|
operand: 'baz'
|
|
}]
|
|
var keybindingItem: IKeybindingItem = {
|
|
command: 'yes',
|
|
context: contextRules,
|
|
keybinding: keybinding,
|
|
weight1: 0,
|
|
weight2: 0
|
|
};
|
|
|
|
assert.equal(CommonKeybindingResolver.contextMatchesRules({ bar: 'baz' }, contextRules), true);
|
|
assert.equal(CommonKeybindingResolver.contextMatchesRules({ bar: 'bz' }, contextRules), false);
|
|
|
|
var resolver = new CommonKeybindingResolver([keybindingItem], []);
|
|
assert.equal(resolver.resolve({ bar: 'baz' }, 0, keybinding).commandId, 'yes');
|
|
assert.equal(resolver.resolve({ bar: 'bz' }, 0, keybinding), null);
|
|
});
|
|
|
|
function createEqualContextRule(key: string, operand: any): IKeybindingContextRule {
|
|
return {
|
|
key: key,
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_EQUAL,
|
|
operand: operand
|
|
};
|
|
}
|
|
|
|
function createNotEqualContextRule(key: string, operand: any): IKeybindingContextRule {
|
|
return {
|
|
key: key,
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_NOT_EQUAL,
|
|
operand: operand
|
|
};
|
|
}
|
|
|
|
test('normalizeRule', function () {
|
|
var key1IsTrue = createEqualContextRule('key1', true);
|
|
var key1IsNotFalse = createNotEqualContextRule('key1', false);
|
|
var key1IsFalse = createEqualContextRule('key1', false);
|
|
var key1IsNotTrue = createNotEqualContextRule('key1', true);
|
|
|
|
assert.deepEqual(CommonKeybindingResolver.normalizeRule(key1IsTrue), key1IsTrue);
|
|
assert.deepEqual(CommonKeybindingResolver.normalizeRule(key1IsNotFalse), key1IsTrue);
|
|
assert.deepEqual(CommonKeybindingResolver.normalizeRule(key1IsFalse), key1IsFalse);
|
|
assert.deepEqual(CommonKeybindingResolver.normalizeRule(key1IsNotTrue), key1IsFalse);
|
|
});
|
|
|
|
test('contextIsEntirelyIncluded', function () {
|
|
var assertIsIncluded = (a: IKeybindingContextRule[], b: IKeybindingContextRule[]) => {
|
|
assert.equal(CommonKeybindingResolver.contextIsEntirelyIncluded(false, a, b), true);
|
|
};
|
|
var assertIsNotIncluded = (a: IKeybindingContextRule[], b: IKeybindingContextRule[]) => {
|
|
assert.equal(CommonKeybindingResolver.contextIsEntirelyIncluded(false, a, b), false);
|
|
};
|
|
var key1IsTrue = createEqualContextRule('key1', true);
|
|
var key1IsNotFalse = createNotEqualContextRule('key1', false);
|
|
var key1IsFalse = createEqualContextRule('key1', false);
|
|
var key1IsNotTrue = createNotEqualContextRule('key1', true);
|
|
var key2IsTrue = createEqualContextRule('key2', true);
|
|
var key2IsNotFalse = createNotEqualContextRule('key2', false);
|
|
var key3IsTrue = createEqualContextRule('key3', true);
|
|
var key4IsTrue = createEqualContextRule('key4', true);
|
|
|
|
assertIsIncluded([key1IsTrue], null);
|
|
assertIsIncluded([key1IsTrue], []);
|
|
assertIsIncluded([key1IsTrue], [key1IsTrue]);
|
|
assertIsIncluded([key1IsTrue], [key1IsNotFalse]);
|
|
|
|
assertIsIncluded([key1IsFalse], []);
|
|
assertIsIncluded([key1IsFalse], [key1IsFalse]);
|
|
assertIsIncluded([key1IsFalse], [key1IsNotTrue]);
|
|
|
|
assertIsIncluded([key2IsNotFalse], []);
|
|
assertIsIncluded([key2IsNotFalse], [key2IsNotFalse]);
|
|
assertIsIncluded([key2IsNotFalse], [key2IsTrue]);
|
|
|
|
assertIsIncluded([key1IsTrue, key2IsNotFalse], [key2IsTrue]);
|
|
assertIsIncluded([key1IsTrue, key2IsNotFalse], [key2IsNotFalse]);
|
|
assertIsIncluded([key1IsTrue, key2IsNotFalse], [key1IsTrue]);
|
|
assertIsIncluded([key1IsTrue, key2IsNotFalse], [key1IsNotFalse]);
|
|
assertIsIncluded([key1IsTrue, key2IsNotFalse], []);
|
|
|
|
assertIsNotIncluded([key1IsTrue], [key1IsFalse]);
|
|
assertIsNotIncluded([key1IsTrue], [key1IsNotTrue]);
|
|
assertIsNotIncluded([key1IsNotFalse], [key1IsFalse]);
|
|
assertIsNotIncluded([key1IsNotFalse], [key1IsNotTrue]);
|
|
|
|
assertIsNotIncluded([key1IsFalse], [key1IsTrue]);
|
|
assertIsNotIncluded([key1IsFalse], [key1IsNotFalse]);
|
|
assertIsNotIncluded([key1IsNotTrue], [key1IsTrue]);
|
|
assertIsNotIncluded([key1IsNotTrue], [key1IsNotFalse]);
|
|
|
|
assertIsNotIncluded([key1IsTrue, key2IsNotFalse], [key3IsTrue]);
|
|
assertIsNotIncluded([key1IsTrue, key2IsNotFalse], [key4IsTrue]);
|
|
assertIsNotIncluded([key1IsTrue], [key2IsTrue]);
|
|
assertIsNotIncluded([], [key2IsTrue]);
|
|
assertIsNotIncluded(null, [key2IsTrue]);
|
|
});
|
|
|
|
test('resolve command', function () {
|
|
|
|
var items: IKeybindingItem[] = [
|
|
// This one will never match because its context is always overwritten by another one
|
|
{
|
|
keybinding: KeyCode.KEY_X,
|
|
context: [{
|
|
key: 'key1',
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_EQUAL,
|
|
operand: true
|
|
}, {
|
|
key: 'key2',
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_NOT_EQUAL,
|
|
operand: false
|
|
}],
|
|
command: 'first',
|
|
weight1: 1,
|
|
weight2: 0
|
|
},
|
|
// This one always overwrites first
|
|
{
|
|
keybinding: KeyCode.KEY_X,
|
|
context: [{
|
|
key: 'key2',
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_EQUAL,
|
|
operand: true
|
|
}],
|
|
command: 'second',
|
|
weight1: 2,
|
|
weight2: 0
|
|
},
|
|
// This one is a secondary mapping for `second`
|
|
{
|
|
keybinding: KeyCode.KEY_Z,
|
|
context: [],
|
|
command: 'second',
|
|
weight1: 2.5,
|
|
weight2: 0
|
|
},
|
|
// This one sometimes overwrites first
|
|
{
|
|
keybinding: KeyCode.KEY_X,
|
|
context: [{
|
|
key: 'key3',
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_EQUAL,
|
|
operand: true
|
|
}],
|
|
command: 'third',
|
|
weight1: 3,
|
|
weight2: 0
|
|
},
|
|
// This one is always overwritten by another one
|
|
{
|
|
keybinding: KeyMod.CtrlCmd | KeyCode.KEY_Y,
|
|
context: [{
|
|
key: 'key4',
|
|
operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_EQUAL,
|
|
operand: true
|
|
}],
|
|
command: 'fourth',
|
|
weight1: 4,
|
|
weight2: 0
|
|
},
|
|
// This one overwrites with a chord the previous one
|
|
{
|
|
keybinding: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z),
|
|
context: [],
|
|
command: 'fifth',
|
|
weight1: 5,
|
|
weight2: 0
|
|
},
|
|
// This one has no keybinding
|
|
{
|
|
keybinding: 0,
|
|
context: [],
|
|
command: 'sixth',
|
|
weight1: 6,
|
|
weight2: 0
|
|
},
|
|
{
|
|
keybinding: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U),
|
|
context: [],
|
|
command: 'seventh',
|
|
weight1: 6.5,
|
|
weight2: 0
|
|
},
|
|
{
|
|
keybinding: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K),
|
|
context: [],
|
|
command: 'seventh',
|
|
weight1: 6.5,
|
|
weight2: 0
|
|
},
|
|
{
|
|
keybinding: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U),
|
|
context: [],
|
|
command: 'uncomment lines',
|
|
weight1: 7,
|
|
weight2: 0
|
|
},
|
|
{
|
|
keybinding: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C),
|
|
context: [],
|
|
command: 'comment lines',
|
|
weight1: 8,
|
|
weight2: 0
|
|
},
|
|
{
|
|
keybinding: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_G, KeyMod.CtrlCmd | KeyCode.KEY_C),
|
|
context: [],
|
|
command: 'unreachablechord',
|
|
weight1: 10,
|
|
weight2: 0
|
|
},
|
|
{
|
|
keybinding: KeyMod.CtrlCmd | KeyCode.KEY_G,
|
|
context: [],
|
|
command: 'eleven',
|
|
weight1: 11,
|
|
weight2: 0
|
|
}
|
|
];
|
|
|
|
var resolver = new CommonKeybindingResolver(items, []);
|
|
|
|
|
|
|
|
var testKey = (commandId: string, expectedKeys: number[]) => {
|
|
// Test lookup
|
|
var lookupResult = resolver.lookupKeybinding(commandId);
|
|
assert.equal(lookupResult.length, expectedKeys.length, 'Length mismatch @ commandId ' + commandId + '; GOT: ' + JSON.stringify(lookupResult, null, '\t'));
|
|
for (let i = 0, len = lookupResult.length; i < len; i++) {
|
|
assert.equal(lookupResult[i].value, expectedKeys[i]);
|
|
}
|
|
};
|
|
|
|
var testResolve = (ctx:any, expectedKey:number, commandId:string) => {
|
|
|
|
if (BinaryKeybindings.hasChord(expectedKey)) {
|
|
let firstPart = BinaryKeybindings.extractFirstPart(expectedKey);
|
|
let chordPart = BinaryKeybindings.extractChordPart(expectedKey);
|
|
|
|
let result = resolver.resolve(ctx, 0, firstPart);
|
|
assert.ok(result !== null, 'Enters chord for ' + commandId);
|
|
assert.equal(result.commandId, null, 'Enters chord for ' + commandId);
|
|
assert.equal(result.enterChord, firstPart, 'Enters chord for ' + commandId);
|
|
|
|
result = resolver.resolve(ctx, firstPart, chordPart);
|
|
assert.ok(result !== null, 'Enters chord for ' + commandId);
|
|
assert.equal(result.commandId, commandId, 'Finds chorded command ' + commandId);
|
|
assert.equal(result.enterChord, 0, 'Finds chorded command ' + commandId);
|
|
} else {
|
|
let result = resolver.resolve(ctx, 0, expectedKey);
|
|
assert.ok(result !== null, 'Finds command ' + commandId);
|
|
assert.equal(result.commandId, commandId, 'Finds command ' + commandId);
|
|
assert.equal(result.enterChord, 0, 'Finds command ' + commandId);
|
|
}
|
|
}
|
|
|
|
testKey('first', []);
|
|
|
|
testKey('second', [KeyCode.KEY_Z, KeyCode.KEY_X]);
|
|
testResolve({key2: true}, KeyCode.KEY_X, 'second');
|
|
testResolve({}, KeyCode.KEY_Z, 'second');
|
|
|
|
testKey('third', [KeyCode.KEY_X]);
|
|
testResolve({key3:true}, KeyCode.KEY_X, 'third');
|
|
|
|
testKey('fourth', []);
|
|
|
|
testKey('fifth', [KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z)]);
|
|
testResolve({}, KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z), 'fifth');
|
|
|
|
testKey('seventh', [KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K)]);
|
|
testResolve({}, KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), 'seventh');
|
|
|
|
testKey('uncomment lines', [KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U)]);
|
|
testResolve({}, KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U), 'uncomment lines');
|
|
|
|
testKey('comment lines', [KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C)]);
|
|
testResolve({}, KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), 'comment lines');
|
|
|
|
testKey('unreachablechord', []);
|
|
|
|
testKey('eleven', [KeyMod.CtrlCmd | KeyCode.KEY_G]);
|
|
testResolve({}, KeyMod.CtrlCmd | KeyCode.KEY_G, 'eleven');
|
|
|
|
testKey('sixth', []);
|
|
|
|
|
|
});
|
|
});
|