From 4e2d923eb04e43d11230fe63062fe1f7f91c8cb3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 14 Mar 2017 21:41:01 +0100 Subject: [PATCH] :hammer: create IContext --- .../contextkey/browser/contextKeyService.ts | 41 +++++++------------ .../platform/contextkey/common/contextkey.ts | 26 +++++++----- .../contextkey/test/common/contextkey.test.ts | 6 ++- .../common/abstractKeybindingService.ts | 4 +- .../keybinding/common/keybindingResolver.ts | 8 ++-- .../common/abstractKeybindingService.test.ts | 28 +++++++------ .../test/common/keybindingResolver.test.ts | 32 ++++++++------- .../test/common/mockKeybindingService.ts | 2 +- 8 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 554a16f7e5e..45d993851a4 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -7,18 +7,19 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { IContextKey, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; -export class ContextValuesContainer { - protected _parent: ContextValuesContainer; +export class Context implements IContext { + + protected _parent: Context; protected _value: { [key: string]: any; }; protected _id: number; - constructor(id: number, parent: ContextValuesContainer) { + constructor(id: number, parent: Context) { this._id = id; this._parent = parent; this._value = Object.create(null); @@ -46,18 +47,9 @@ export class ContextValuesContainer { } return ret; } - - public fillInContext(bucket: any): void { - if (this._parent) { - this._parent.fillInContext(bucket); - } - for (let key in this._value) { - bucket[key] = this._value[key]; - } - } } -class ConfigAwareContextValuesContainer extends ContextValuesContainer { +class ConfigAwareContextValuesContainer extends Context { private _emitter: Emitter; private _subscription: IDisposable; @@ -169,9 +161,8 @@ export abstract class AbstractContextKeyService { } public contextMatchesRules(rules: ContextKeyExpr): boolean { - const ctx = Object.create(null); - this.getContextValuesContainer(this._myContextId).fillInContext(ctx); - const result = KeybindingResolver.contextMatchesRules(ctx, rules); + const context = this.getContextValuesContainer(this._myContextId); + const result = KeybindingResolver.contextMatchesRules(context, rules); // console.group(rules.serialize() + ' -> ' + result); // rules.keys().forEach(key => { console.log(key, ctx[key]); }); // console.groupEnd(); @@ -194,13 +185,11 @@ export abstract class AbstractContextKeyService { } } - public getContextValue(target: IContextKeyServiceTarget): any { - let res = Object.create(null); - this.getContextValuesContainer(findContextAttr(target)).fillInContext(res); - return res; + public getContext(target: IContextKeyServiceTarget): IContext { + return this.getContextValuesContainer(findContextAttr(target)); } - public abstract getContextValuesContainer(contextId: number): ContextValuesContainer; + public abstract getContextValuesContainer(contextId: number): Context; public abstract createChildContext(parentContextId?: number): number; public abstract disposeContext(contextId: number): void; } @@ -209,7 +198,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon private _lastContextId: number; private _contexts: { - [contextId: string]: ContextValuesContainer; + [contextId: string]: Context; }; private _toDispose: IDisposable[] = []; @@ -239,13 +228,13 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon this._toDispose = dispose(this._toDispose); } - public getContextValuesContainer(contextId: number): ContextValuesContainer { + public getContextValuesContainer(contextId: number): Context { return this._contexts[String(contextId)]; } public createChildContext(parentContextId: number = this._myContextId): number { let id = (++this._lastContextId); - this._contexts[String(id)] = new ContextValuesContainer(id, this.getContextValuesContainer(parentContextId)); + this._contexts[String(id)] = new Context(id, this.getContextValuesContainer(parentContextId)); return id; } @@ -281,7 +270,7 @@ class ScopedContextKeyService extends AbstractContextKeyService { return this._parent.onDidChangeContext; } - public getContextValuesContainer(contextId: number): ContextValuesContainer { + public getContextValuesContainer(contextId: number): Context { return this._parent.getContextValuesContainer(contextId); } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 7d8c53c96da..36602e49031 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -88,7 +88,7 @@ export abstract class ContextKeyExpr { public abstract getType(): ContextKeyExprType; public abstract equals(other: ContextKeyExpr): boolean; - public abstract evaluate(context: any): boolean; + public abstract evaluate(context: IContext): boolean; public abstract normalize(): ContextKeyExpr; public abstract serialize(): string; public abstract keys(): string[]; @@ -139,8 +139,8 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr { return false; } - public evaluate(context: any): boolean { - return (!!context[this.key]); + public evaluate(context: IContext): boolean { + return (!!context.getValue(this.key)); } public normalize(): ContextKeyExpr { @@ -187,10 +187,10 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { return false; } - public evaluate(context: any): boolean { + public evaluate(context: IContext): boolean { /* tslint:disable:triple-equals */ // Intentional == - return (context[this.key] == this.value); + return (context.getValue(this.key) == this.value); /* tslint:enable:triple-equals */ } @@ -248,10 +248,10 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { return false; } - public evaluate(context: any): boolean { + public evaluate(context: IContext): boolean { /* tslint:disable:triple-equals */ // Intentional != - return (context[this.key] != this.value); + return (context.getValue(this.key) != this.value); /* tslint:enable:triple-equals */ } @@ -303,8 +303,8 @@ export class ContextKeyNotExpr implements ContextKeyExpr { return false; } - public evaluate(context: any): boolean { - return (!context[this.key]); + public evaluate(context: IContext): boolean { + return (!context.getValue(this.key)); } public normalize(): ContextKeyExpr { @@ -346,7 +346,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr { return false; } - public evaluate(context: any): boolean { + public evaluate(context: IContext): boolean { for (let i = 0, len = this.expr.length; i < len; i++) { if (!this.expr[i].evaluate(context)) { return false; @@ -441,6 +441,10 @@ export class RawContextKey extends ContextKeyDefinedExpr { } } +export interface IContext { + getValue(key: string): T; +} + export interface IContextKey { set(value: T): void; reset(): void; @@ -467,7 +471,7 @@ export interface IContextKeyService { getContextKeyValue(key: string): T; createScoped(target?: IContextKeyServiceTarget): IContextKeyService; - getContextValue(target: IContextKeyServiceTarget): any; + getContext(target: IContextKeyServiceTarget): IContext; } export const SET_CONTEXT_COMMAND_ID = 'setContext'; diff --git a/src/vs/platform/contextkey/test/common/contextkey.test.ts b/src/vs/platform/contextkey/test/common/contextkey.test.ts index 1f75c9dab35..84562268907 100644 --- a/src/vs/platform/contextkey/test/common/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/common/contextkey.test.ts @@ -7,6 +7,8 @@ import * as assert from 'assert'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +const createContext = ctx => ({ getValue: key => ctx[key] }); + suite('ContextKeyExpr', () => { test('ContextKeyExpr.equals', function () { let a = ContextKeyExpr.and( @@ -48,11 +50,11 @@ suite('ContextKeyExpr', () => { test('evaluate', function () { /* tslint:disable:triple-equals */ - let context = { + let context = createContext({ 'a': true, 'b': false, 'c': '5' - }; + }); function testExpression(expr: string, expected: boolean): void { let rules = ContextKeyExpr.deserialize(expr); assert.equal(rules.evaluate(context), expected, expr); diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 722dd085ce7..28e4c3967c8 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -230,7 +230,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService { return null; } - const contextValue = this._contextKeyService.getContextValue(target); + const contextValue = this._contextKeyService.getContext(target); const currentChord = this._currentChord ? this._currentChord.keypress : null; const keypress = keybinding.value.toString(); return this._getResolver().resolve(contextValue, currentChord, keypress); @@ -244,7 +244,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService { return shouldPreventDefault; } - const contextValue = this._contextKeyService.getContextValue(target); + const contextValue = this._contextKeyService.getContext(target); const currentChord = this._currentChord ? this._currentChord.keypress : null; const keypress = keybinding.value.toString(); const keypressLabel = this._createResolvedKeybinding(keybinding).getLabel(); diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 7cbef446355..0799d3767e9 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; import { NormalizedKeybindingItem } from 'vs/platform/keybinding/common/normalizedKeybindingItem'; export interface IResolveResult { @@ -227,7 +227,7 @@ export class KeybindingResolver { return items[items.length - 1]; } - public resolve(context: any, currentChord: string, keypress: string): IResolveResult { + public resolve(context: IContext, currentChord: string, keypress: string): IResolveResult { let lookupMap: NormalizedKeybindingItem[] = null; if (currentChord !== null) { @@ -278,7 +278,7 @@ export class KeybindingResolver { }; } - private _findCommand(context: any, matches: NormalizedKeybindingItem[]): NormalizedKeybindingItem { + private _findCommand(context: IContext, matches: NormalizedKeybindingItem[]): NormalizedKeybindingItem { for (let i = matches.length - 1; i >= 0; i--) { let k = matches[i]; @@ -292,7 +292,7 @@ export class KeybindingResolver { return null; } - public static contextMatchesRules(context: any, rules: ContextKeyExpr): boolean { + public static contextMatchesRules(context: IContext, rules: ContextKeyExpr): boolean { if (!rules) { return true; } diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index 43ce87875bb..ec10d09f1bd 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -11,13 +11,15 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { ContextKeyExpr, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { IContext, ContextKeyExpr, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IMessageService } from 'vs/platform/message/common/message'; import { TPromise } from 'vs/base/common/winjs.base'; import { NormalizedKeybindingItem } from 'vs/platform/keybinding/common/normalizedKeybindingItem'; import { OS } from 'vs/base/common/platform'; +const createContext = ctx => ({ getValue: key => ctx[key] }); + suite('AbstractKeybindingService', () => { class TestKeybindingService extends AbstractKeybindingService { @@ -48,7 +50,7 @@ suite('AbstractKeybindingService', () => { } let createTestKeybindingService: (items: NormalizedKeybindingItem[], contextValue?: any) => TestKeybindingService = null; - let currentContextValue: any = null; + let currentContextValue: IContext = null; let executeCommandCalls: { commandId: string; args: any[]; }[] = null; let showMessageCalls: { sev: Severity, message: any; }[] = null; let statusMessageCalls: string[] = null; @@ -70,7 +72,7 @@ suite('AbstractKeybindingService', () => { contextMatchesRules: undefined, getContextKeyValue: undefined, createScoped: undefined, - getContextValue: (target: IContextKeyServiceTarget): any => { + getContext: (target: IContextKeyServiceTarget): any => { return currentContextValue; } }; @@ -248,9 +250,9 @@ suite('AbstractKeybindingService', () => { // send Ctrl/Cmd + K - currentContextValue = { + currentContextValue = createContext({ key1: true - }; + }); let shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_K)); assert.equal(shouldPreventDefault, true); assert.deepEqual(executeCommandCalls, [{ @@ -266,7 +268,7 @@ suite('AbstractKeybindingService', () => { statusMessageCallsDisposed = []; // send Ctrl/Cmd + K - currentContextValue = {}; + currentContextValue = createContext({}); shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_K)); assert.equal(shouldPreventDefault, true); assert.deepEqual(executeCommandCalls, []); @@ -281,7 +283,7 @@ suite('AbstractKeybindingService', () => { statusMessageCallsDisposed = []; // send Ctrl/Cmd + X - currentContextValue = {}; + currentContextValue = createContext({}); shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_X)); assert.equal(shouldPreventDefault, true); assert.deepEqual(executeCommandCalls, [{ @@ -310,7 +312,7 @@ suite('AbstractKeybindingService', () => { // send Ctrl/Cmd + K - currentContextValue = {}; + currentContextValue = createContext({}); let shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_K)); assert.equal(shouldPreventDefault, true); assert.deepEqual(executeCommandCalls, [{ @@ -326,9 +328,9 @@ suite('AbstractKeybindingService', () => { statusMessageCallsDisposed = []; // send Ctrl/Cmd + K - currentContextValue = { + currentContextValue = createContext({ key1: true - }; + }); shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_K)); assert.equal(shouldPreventDefault, true); assert.deepEqual(executeCommandCalls, [{ @@ -344,9 +346,9 @@ suite('AbstractKeybindingService', () => { statusMessageCallsDisposed = []; // send Ctrl/Cmd + X - currentContextValue = { + currentContextValue = createContext({ key1: true - }; + }); shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_X)); assert.equal(shouldPreventDefault, false); assert.deepEqual(executeCommandCalls, []); @@ -368,7 +370,7 @@ suite('AbstractKeybindingService', () => { ]); // send Ctrl/Cmd + K - currentContextValue = {}; + currentContextValue = createContext({}); let shouldPreventDefault = kbService.dispatch(new SimpleKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_K)); assert.equal(shouldPreventDefault, false); assert.deepEqual(executeCommandCalls, [{ diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 5e9da9e5b7e..15e3d668490 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -7,9 +7,11 @@ import * as assert from 'assert'; import { createKeybinding, SimpleKeybinding, KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { ContextKeyAndExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IContext, ContextKeyAndExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { NormalizedKeybindingItem } from 'vs/platform/keybinding/common/normalizedKeybindingItem'; +const createContext = ctx => ({ getValue: key => ctx[key] }); + suite('KeybindingResolver', () => { function kbItem(keybinding: number, command: string, commandArgs: any, when: ContextKeyExpr, isDefault: boolean): NormalizedKeybindingItem { @@ -28,12 +30,12 @@ suite('KeybindingResolver', () => { let contextRules = ContextKeyExpr.equals('bar', 'baz'); let keybindingItem = kbItem(keybinding, 'yes', null, contextRules, true); - assert.equal(KeybindingResolver.contextMatchesRules({ bar: 'baz' }, contextRules), true); - assert.equal(KeybindingResolver.contextMatchesRules({ bar: 'bz' }, contextRules), false); + assert.equal(KeybindingResolver.contextMatchesRules(createContext({ bar: 'baz' }), contextRules), true); + assert.equal(KeybindingResolver.contextMatchesRules(createContext({ bar: 'bz' }), contextRules), false); let resolver = new KeybindingResolver([keybindingItem], []); - assert.equal(resolver.resolve({ bar: 'baz' }, null, new SimpleKeybinding(keybinding).value.toString()).commandId, 'yes'); - assert.equal(resolver.resolve({ bar: 'bz' }, null, new SimpleKeybinding(keybinding).value.toString()), null); + assert.equal(resolver.resolve(createContext({ bar: 'baz' }), null, new SimpleKeybinding(keybinding).value.toString()).commandId, 'yes'); + assert.equal(resolver.resolve(createContext({ bar: 'bz' }), null, new SimpleKeybinding(keybinding).value.toString()), null); }); test('resolve key with arguments', function () { @@ -43,7 +45,7 @@ suite('KeybindingResolver', () => { let keybindingItem = kbItem(keybinding, 'yes', commandArgs, contextRules, true); let resolver = new KeybindingResolver([keybindingItem], []); - assert.equal(resolver.resolve({ bar: 'baz' }, null, new SimpleKeybinding(keybinding).value.toString()).commandArgs, commandArgs); + assert.equal(resolver.resolve(createContext({ bar: 'baz' }), null, new SimpleKeybinding(keybinding).value.toString()).commandArgs, commandArgs); }); test('KeybindingResolver.combine simple 1', function () { @@ -323,7 +325,7 @@ suite('KeybindingResolver', () => { } }; - let testResolve = (ctx: any, _expectedKey: number, commandId: string) => { + let testResolve = (ctx: IContext, _expectedKey: number, commandId: string) => { let expectedKey = createKeybinding(_expectedKey); if (expectedKey.isChord()) { @@ -350,30 +352,30 @@ suite('KeybindingResolver', () => { testKey('first', []); testKey('second', [KeyCode.KEY_Z, KeyCode.KEY_X]); - testResolve({ key2: true }, KeyCode.KEY_X, 'second'); - testResolve({}, KeyCode.KEY_Z, 'second'); + testResolve(createContext({ key2: true }), KeyCode.KEY_X, 'second'); + testResolve(createContext({}), KeyCode.KEY_Z, 'second'); testKey('third', [KeyCode.KEY_X]); - testResolve({ key3: true }, KeyCode.KEY_X, 'third'); + testResolve(createContext({ key3: true }), KeyCode.KEY_X, 'third'); testKey('fourth', []); testKey('fifth', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z)]); - testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z), 'fifth'); + testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z), 'fifth'); testKey('seventh', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K)]); - testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), 'seventh'); + testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), 'seventh'); testKey('uncomment lines', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U)]); - testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U), 'uncomment lines'); + testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U), 'uncomment lines'); testKey('comment lines', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C)]); - testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), 'comment lines'); + testResolve(createContext({}), KeyChord(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'); + testResolve(createContext({}), KeyMod.CtrlCmd | KeyCode.KEY_G, 'eleven'); testKey('sixth', []); }); diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 2ef4f9aa3ae..37aec8adf01 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -53,7 +53,7 @@ export class MockKeybindingService implements IContextKeyService { public getContextKeyValue(key: string) { return; } - public getContextValue(domNode: HTMLElement): any { + public getContext(domNode: HTMLElement): any { return null; } public createScoped(domNode: HTMLElement): IContextKeyService { -- GitLab