mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 09:42:34 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
300
src/vs/platform/contextkey/browser/contextKeyService.ts
Normal file
300
src/vs/platform/contextkey/browser/contextKeyService.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
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, 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 Context implements IContext {
|
||||
|
||||
protected _parent: Context;
|
||||
protected _value: { [key: string]: any; };
|
||||
protected _id: number;
|
||||
|
||||
constructor(id: number, parent: Context) {
|
||||
this._id = id;
|
||||
this._parent = parent;
|
||||
this._value = Object.create(null);
|
||||
this._value['_contextId'] = id;
|
||||
}
|
||||
|
||||
public setValue(key: string, value: any): boolean {
|
||||
// console.log('SET ' + key + ' = ' + value + ' ON ' + this._id);
|
||||
if (this._value[key] !== value) {
|
||||
this._value[key] = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public removeValue(key: string): boolean {
|
||||
// console.log('REMOVE ' + key + ' FROM ' + this._id);
|
||||
return delete this._value[key];
|
||||
}
|
||||
|
||||
public getValue<T>(key: string): T {
|
||||
const ret = this._value[key];
|
||||
if (typeof ret === 'undefined' && this._parent) {
|
||||
return this._parent.getValue<T>(key);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigAwareContextValuesContainer extends Context {
|
||||
|
||||
private _emitter: Emitter<string>;
|
||||
private _subscription: IDisposable;
|
||||
|
||||
constructor(id: number, configurationService: IConfigurationService, emitter: Emitter<string>) {
|
||||
super(id, null);
|
||||
|
||||
this._emitter = emitter;
|
||||
this._subscription = configurationService.onDidUpdateConfiguration(e => this._updateConfigurationContext(configurationService.getConfiguration()));
|
||||
this._updateConfigurationContext(configurationService.getConfiguration());
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._subscription.dispose();
|
||||
}
|
||||
|
||||
private _updateConfigurationContext(config: any) {
|
||||
|
||||
// remove old config.xyz values
|
||||
for (let key in this._value) {
|
||||
if (key.indexOf('config.') === 0) {
|
||||
delete this._value[key];
|
||||
}
|
||||
}
|
||||
|
||||
// add new value from config
|
||||
const walk = (obj: any, keys: string[]) => {
|
||||
for (let key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
keys.push(key);
|
||||
let value = obj[key];
|
||||
if (typeof value === 'boolean') {
|
||||
const configKey = keys.join('.');
|
||||
this._value[configKey] = value;
|
||||
this._emitter.fire(configKey);
|
||||
} else if (typeof value === 'object') {
|
||||
walk(value, keys);
|
||||
}
|
||||
keys.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
walk(config, ['config']);
|
||||
}
|
||||
}
|
||||
|
||||
class ContextKey<T> implements IContextKey<T> {
|
||||
|
||||
private _parent: AbstractContextKeyService;
|
||||
private _key: string;
|
||||
private _defaultValue: T;
|
||||
|
||||
constructor(parent: AbstractContextKeyService, key: string, defaultValue: T) {
|
||||
this._parent = parent;
|
||||
this._key = key;
|
||||
this._defaultValue = defaultValue;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
public set(value: T): void {
|
||||
this._parent.setContext(this._key, value);
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
if (typeof this._defaultValue === 'undefined') {
|
||||
this._parent.removeContext(this._key);
|
||||
} else {
|
||||
this._parent.setContext(this._key, this._defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
return this._parent.getContextKeyValue<T>(this._key);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractContextKeyService implements IContextKeyService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
protected _onDidChangeContext: Event<string[]>;
|
||||
protected _onDidChangeContextKey: Emitter<string>;
|
||||
protected _myContextId: number;
|
||||
|
||||
constructor(myContextId: number) {
|
||||
this._myContextId = myContextId;
|
||||
this._onDidChangeContextKey = new Emitter<string>();
|
||||
}
|
||||
|
||||
abstract dispose(): void;
|
||||
|
||||
public createKey<T>(key: string, defaultValue: T): IContextKey<T> {
|
||||
return new ContextKey(this, key, defaultValue);
|
||||
}
|
||||
|
||||
public get onDidChangeContext(): Event<string[]> {
|
||||
if (!this._onDidChangeContext) {
|
||||
this._onDidChangeContext = debounceEvent(this._onDidChangeContextKey.event, (prev: string[], cur) => {
|
||||
if (!prev) {
|
||||
prev = [cur];
|
||||
} else if (prev.indexOf(cur) < 0) {
|
||||
prev.push(cur);
|
||||
}
|
||||
return prev;
|
||||
}, 25);
|
||||
}
|
||||
return this._onDidChangeContext;
|
||||
}
|
||||
|
||||
public createScoped(domNode: IContextKeyServiceTarget): IContextKeyService {
|
||||
return new ScopedContextKeyService(this, this._onDidChangeContextKey, domNode);
|
||||
}
|
||||
|
||||
public contextMatchesRules(rules: ContextKeyExpr): boolean {
|
||||
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();
|
||||
return result;
|
||||
}
|
||||
|
||||
public getContextKeyValue<T>(key: string): T {
|
||||
return this.getContextValuesContainer(this._myContextId).getValue<T>(key);
|
||||
}
|
||||
|
||||
public setContext(key: string, value: any): void {
|
||||
if (this.getContextValuesContainer(this._myContextId).setValue(key, value)) {
|
||||
this._onDidChangeContextKey.fire(key);
|
||||
}
|
||||
}
|
||||
|
||||
public removeContext(key: string): void {
|
||||
if (this.getContextValuesContainer(this._myContextId).removeValue(key)) {
|
||||
this._onDidChangeContextKey.fire(key);
|
||||
}
|
||||
}
|
||||
|
||||
public getContext(target: IContextKeyServiceTarget): IContext {
|
||||
return this.getContextValuesContainer(findContextAttr(target));
|
||||
}
|
||||
|
||||
public abstract getContextValuesContainer(contextId: number): Context;
|
||||
public abstract createChildContext(parentContextId?: number): number;
|
||||
public abstract disposeContext(contextId: number): void;
|
||||
}
|
||||
|
||||
export class ContextKeyService extends AbstractContextKeyService implements IContextKeyService {
|
||||
|
||||
private _lastContextId: number;
|
||||
private _contexts: {
|
||||
[contextId: string]: Context;
|
||||
};
|
||||
|
||||
private _toDispose: IDisposable[] = [];
|
||||
|
||||
constructor( @IConfigurationService configurationService: IConfigurationService) {
|
||||
super(0);
|
||||
this._lastContextId = 0;
|
||||
this._contexts = Object.create(null);
|
||||
|
||||
const myContext = new ConfigAwareContextValuesContainer(this._myContextId, configurationService, this._onDidChangeContextKey);
|
||||
this._contexts[String(this._myContextId)] = myContext;
|
||||
this._toDispose.push(myContext);
|
||||
|
||||
// Uncomment this to see the contexts continuously logged
|
||||
// let lastLoggedValue: string = null;
|
||||
// setInterval(() => {
|
||||
// let values = Object.keys(this._contexts).map((key) => this._contexts[key]);
|
||||
// let logValue = values.map(v => JSON.stringify(v._value, null, '\t')).join('\n');
|
||||
// if (lastLoggedValue !== logValue) {
|
||||
// lastLoggedValue = logValue;
|
||||
// console.log(lastLoggedValue);
|
||||
// }
|
||||
// }, 2000);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
|
||||
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 Context(id, this.getContextValuesContainer(parentContextId));
|
||||
return id;
|
||||
}
|
||||
|
||||
public disposeContext(contextId: number): void {
|
||||
delete this._contexts[String(contextId)];
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedContextKeyService extends AbstractContextKeyService {
|
||||
|
||||
private _parent: AbstractContextKeyService;
|
||||
private _domNode: IContextKeyServiceTarget;
|
||||
|
||||
constructor(parent: AbstractContextKeyService, emitter: Emitter<string>, domNode?: IContextKeyServiceTarget) {
|
||||
super(parent.createChildContext());
|
||||
this._parent = parent;
|
||||
this._onDidChangeContextKey = emitter;
|
||||
|
||||
if (domNode) {
|
||||
this._domNode = domNode;
|
||||
this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId));
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._parent.disposeContext(this._myContextId);
|
||||
if (this._domNode) {
|
||||
this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR);
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidChangeContext(): Event<string[]> {
|
||||
return this._parent.onDidChangeContext;
|
||||
}
|
||||
|
||||
public getContextValuesContainer(contextId: number): Context {
|
||||
return this._parent.getContextValuesContainer(contextId);
|
||||
}
|
||||
|
||||
public createChildContext(parentContextId: number = this._myContextId): number {
|
||||
return this._parent.createChildContext(parentContextId);
|
||||
}
|
||||
|
||||
public disposeContext(contextId: number): void {
|
||||
this._parent.disposeContext(contextId);
|
||||
}
|
||||
}
|
||||
|
||||
function findContextAttr(domNode: IContextKeyServiceTarget): number {
|
||||
while (domNode) {
|
||||
if (domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) {
|
||||
return parseInt(domNode.getAttribute(KEYBINDING_CONTEXT_ATTR), 10);
|
||||
}
|
||||
domNode = domNode.parentElement;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand(SET_CONTEXT_COMMAND_ID, function (accessor, contextKey: any, contextValue: any) {
|
||||
accessor.get(IContextKeyService).createKey(String(contextKey), contextValue);
|
||||
});
|
||||
477
src/vs/platform/contextkey/common/contextkey.ts
Normal file
477
src/vs/platform/contextkey/common/contextkey.ts
Normal file
@@ -0,0 +1,477 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Event from 'vs/base/common/event';
|
||||
|
||||
export enum ContextKeyExprType {
|
||||
Defined = 1,
|
||||
Not = 2,
|
||||
Equals = 3,
|
||||
NotEquals = 4,
|
||||
And = 5
|
||||
}
|
||||
|
||||
export abstract class ContextKeyExpr {
|
||||
|
||||
public static has(key: string): ContextKeyExpr {
|
||||
return new ContextKeyDefinedExpr(key);
|
||||
}
|
||||
|
||||
public static equals(key: string, value: any): ContextKeyExpr {
|
||||
return new ContextKeyEqualsExpr(key, value);
|
||||
}
|
||||
|
||||
public static notEquals(key: string, value: any): ContextKeyExpr {
|
||||
return new ContextKeyNotEqualsExpr(key, value);
|
||||
}
|
||||
|
||||
public static not(key: string): ContextKeyExpr {
|
||||
return new ContextKeyNotExpr(key);
|
||||
}
|
||||
|
||||
public static and(...expr: ContextKeyExpr[]): ContextKeyExpr {
|
||||
return new ContextKeyAndExpr(expr);
|
||||
}
|
||||
|
||||
public static deserialize(serialized: string): ContextKeyExpr {
|
||||
if (!serialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let pieces = serialized.split('&&');
|
||||
let result = new ContextKeyAndExpr(pieces.map(p => this._deserializeOne(p)));
|
||||
return result.normalize();
|
||||
}
|
||||
|
||||
private static _deserializeOne(serializedOne: string): ContextKeyExpr {
|
||||
serializedOne = serializedOne.trim();
|
||||
|
||||
if (serializedOne.indexOf('!=') >= 0) {
|
||||
let pieces = serializedOne.split('!=');
|
||||
return new ContextKeyNotEqualsExpr(pieces[0].trim(), this._deserializeValue(pieces[1]));
|
||||
}
|
||||
|
||||
if (serializedOne.indexOf('==') >= 0) {
|
||||
let pieces = serializedOne.split('==');
|
||||
return new ContextKeyEqualsExpr(pieces[0].trim(), this._deserializeValue(pieces[1]));
|
||||
}
|
||||
|
||||
if (/^\!\s*/.test(serializedOne)) {
|
||||
return new ContextKeyNotExpr(serializedOne.substr(1).trim());
|
||||
}
|
||||
|
||||
return new ContextKeyDefinedExpr(serializedOne);
|
||||
}
|
||||
|
||||
private static _deserializeValue(serializedValue: string): any {
|
||||
serializedValue = serializedValue.trim();
|
||||
|
||||
if (serializedValue === 'true') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (serializedValue === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let m = /^'([^']*)'$/.exec(serializedValue);
|
||||
if (m) {
|
||||
return m[1].trim();
|
||||
}
|
||||
|
||||
return serializedValue;
|
||||
}
|
||||
|
||||
public abstract getType(): ContextKeyExprType;
|
||||
public abstract equals(other: ContextKeyExpr): boolean;
|
||||
public abstract evaluate(context: IContext): boolean;
|
||||
public abstract normalize(): ContextKeyExpr;
|
||||
public abstract serialize(): string;
|
||||
public abstract keys(): string[];
|
||||
}
|
||||
|
||||
function cmp(a: ContextKeyExpr, b: ContextKeyExpr): number {
|
||||
let aType = a.getType();
|
||||
let bType = b.getType();
|
||||
if (aType !== bType) {
|
||||
return aType - bType;
|
||||
}
|
||||
switch (aType) {
|
||||
case ContextKeyExprType.Defined:
|
||||
return (<ContextKeyDefinedExpr>a).cmp(<ContextKeyDefinedExpr>b);
|
||||
case ContextKeyExprType.Not:
|
||||
return (<ContextKeyNotExpr>a).cmp(<ContextKeyNotExpr>b);
|
||||
case ContextKeyExprType.Equals:
|
||||
return (<ContextKeyEqualsExpr>a).cmp(<ContextKeyEqualsExpr>b);
|
||||
case ContextKeyExprType.NotEquals:
|
||||
return (<ContextKeyNotEqualsExpr>a).cmp(<ContextKeyNotEqualsExpr>b);
|
||||
default:
|
||||
throw new Error('Unknown ContextKeyExpr!');
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextKeyDefinedExpr implements ContextKeyExpr {
|
||||
constructor(protected key: string) {
|
||||
}
|
||||
|
||||
public getType(): ContextKeyExprType {
|
||||
return ContextKeyExprType.Defined;
|
||||
}
|
||||
|
||||
public cmp(other: ContextKeyDefinedExpr): number {
|
||||
if (this.key < other.key) {
|
||||
return -1;
|
||||
}
|
||||
if (this.key > other.key) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public equals(other: ContextKeyExpr): boolean {
|
||||
if (other instanceof ContextKeyDefinedExpr) {
|
||||
return (this.key === other.key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public evaluate(context: IContext): boolean {
|
||||
return (!!context.getValue(this.key));
|
||||
}
|
||||
|
||||
public normalize(): ContextKeyExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
public serialize(): string {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return [this.key];
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextKeyEqualsExpr implements ContextKeyExpr {
|
||||
constructor(private key: string, private value: any) {
|
||||
}
|
||||
|
||||
public getType(): ContextKeyExprType {
|
||||
return ContextKeyExprType.Equals;
|
||||
}
|
||||
|
||||
public cmp(other: ContextKeyEqualsExpr): number {
|
||||
if (this.key < other.key) {
|
||||
return -1;
|
||||
}
|
||||
if (this.key > other.key) {
|
||||
return 1;
|
||||
}
|
||||
if (this.value < other.value) {
|
||||
return -1;
|
||||
}
|
||||
if (this.value > other.value) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public equals(other: ContextKeyExpr): boolean {
|
||||
if (other instanceof ContextKeyEqualsExpr) {
|
||||
return (this.key === other.key && this.value === other.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public evaluate(context: IContext): boolean {
|
||||
/* tslint:disable:triple-equals */
|
||||
// Intentional ==
|
||||
return (context.getValue(this.key) == this.value);
|
||||
/* tslint:enable:triple-equals */
|
||||
}
|
||||
|
||||
public normalize(): ContextKeyExpr {
|
||||
if (typeof this.value === 'boolean') {
|
||||
if (this.value) {
|
||||
return new ContextKeyDefinedExpr(this.key);
|
||||
}
|
||||
return new ContextKeyNotExpr(this.key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public serialize(): string {
|
||||
if (typeof this.value === 'boolean') {
|
||||
return this.normalize().serialize();
|
||||
}
|
||||
|
||||
return this.key + ' == \'' + this.value + '\'';
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return [this.key];
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextKeyNotEqualsExpr implements ContextKeyExpr {
|
||||
constructor(private key: string, private value: any) {
|
||||
}
|
||||
|
||||
public getType(): ContextKeyExprType {
|
||||
return ContextKeyExprType.NotEquals;
|
||||
}
|
||||
|
||||
public cmp(other: ContextKeyNotEqualsExpr): number {
|
||||
if (this.key < other.key) {
|
||||
return -1;
|
||||
}
|
||||
if (this.key > other.key) {
|
||||
return 1;
|
||||
}
|
||||
if (this.value < other.value) {
|
||||
return -1;
|
||||
}
|
||||
if (this.value > other.value) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public equals(other: ContextKeyExpr): boolean {
|
||||
if (other instanceof ContextKeyNotEqualsExpr) {
|
||||
return (this.key === other.key && this.value === other.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public evaluate(context: IContext): boolean {
|
||||
/* tslint:disable:triple-equals */
|
||||
// Intentional !=
|
||||
return (context.getValue(this.key) != this.value);
|
||||
/* tslint:enable:triple-equals */
|
||||
}
|
||||
|
||||
public normalize(): ContextKeyExpr {
|
||||
if (typeof this.value === 'boolean') {
|
||||
if (this.value) {
|
||||
return new ContextKeyNotExpr(this.key);
|
||||
}
|
||||
return new ContextKeyDefinedExpr(this.key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public serialize(): string {
|
||||
if (typeof this.value === 'boolean') {
|
||||
return this.normalize().serialize();
|
||||
}
|
||||
|
||||
return this.key + ' != \'' + this.value + '\'';
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return [this.key];
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextKeyNotExpr implements ContextKeyExpr {
|
||||
constructor(private key: string) {
|
||||
}
|
||||
|
||||
public getType(): ContextKeyExprType {
|
||||
return ContextKeyExprType.Not;
|
||||
}
|
||||
|
||||
public cmp(other: ContextKeyNotExpr): number {
|
||||
if (this.key < other.key) {
|
||||
return -1;
|
||||
}
|
||||
if (this.key > other.key) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public equals(other: ContextKeyExpr): boolean {
|
||||
if (other instanceof ContextKeyNotExpr) {
|
||||
return (this.key === other.key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public evaluate(context: IContext): boolean {
|
||||
return (!context.getValue(this.key));
|
||||
}
|
||||
|
||||
public normalize(): ContextKeyExpr {
|
||||
return this;
|
||||
}
|
||||
|
||||
public serialize(): string {
|
||||
return '!' + this.key;
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return [this.key];
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextKeyAndExpr implements ContextKeyExpr {
|
||||
private expr: ContextKeyExpr[];
|
||||
|
||||
constructor(expr: ContextKeyExpr[]) {
|
||||
this.expr = ContextKeyAndExpr._normalizeArr(expr);
|
||||
}
|
||||
|
||||
public getType(): ContextKeyExprType {
|
||||
return ContextKeyExprType.And;
|
||||
}
|
||||
|
||||
public equals(other: ContextKeyExpr): boolean {
|
||||
if (other instanceof ContextKeyAndExpr) {
|
||||
if (this.expr.length !== other.expr.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0, len = this.expr.length; i < len; i++) {
|
||||
if (!this.expr[i].equals(other.expr[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public evaluate(context: IContext): boolean {
|
||||
for (let i = 0, len = this.expr.length; i < len; i++) {
|
||||
if (!this.expr[i].evaluate(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static _normalizeArr(arr: ContextKeyExpr[]): ContextKeyExpr[] {
|
||||
let expr: ContextKeyExpr[] = [];
|
||||
|
||||
if (arr) {
|
||||
for (let i = 0, len = arr.length; i < len; i++) {
|
||||
let e = arr[i];
|
||||
if (!e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
e = e.normalize();
|
||||
if (!e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e instanceof ContextKeyAndExpr) {
|
||||
expr = expr.concat(e.expr);
|
||||
continue;
|
||||
}
|
||||
|
||||
expr.push(e);
|
||||
}
|
||||
|
||||
expr.sort(cmp);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
public normalize(): ContextKeyExpr {
|
||||
if (this.expr.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.expr.length === 1) {
|
||||
return this.expr[0];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public serialize(): string {
|
||||
if (this.expr.length === 0) {
|
||||
return '';
|
||||
}
|
||||
if (this.expr.length === 1) {
|
||||
return this.normalize().serialize();
|
||||
}
|
||||
return this.expr.map(e => e.serialize()).join(' && ');
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
const result: string[] = [];
|
||||
for (let expr of this.expr) {
|
||||
result.push(...expr.keys());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class RawContextKey<T> extends ContextKeyDefinedExpr {
|
||||
|
||||
private _defaultValue: T;
|
||||
|
||||
constructor(key: string, defaultValue: T) {
|
||||
super(key);
|
||||
this._defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public bindTo(target: IContextKeyService): IContextKey<T> {
|
||||
return target.createKey(this.key, this._defaultValue);
|
||||
}
|
||||
|
||||
public getValue(target: IContextKeyService): T {
|
||||
return target.getContextKeyValue<T>(this.key);
|
||||
}
|
||||
|
||||
public toNegated(): ContextKeyExpr {
|
||||
return ContextKeyExpr.not(this.key);
|
||||
}
|
||||
|
||||
public isEqualTo(value: string): ContextKeyExpr {
|
||||
return ContextKeyExpr.equals(this.key, value);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IContext {
|
||||
getValue<T>(key: string): T;
|
||||
}
|
||||
|
||||
export interface IContextKey<T> {
|
||||
set(value: T): void;
|
||||
reset(): void;
|
||||
get(): T;
|
||||
}
|
||||
|
||||
export interface IContextKeyServiceTarget {
|
||||
parentElement: IContextKeyServiceTarget;
|
||||
setAttribute(attr: string, value: string): void;
|
||||
removeAttribute(attr: string): void;
|
||||
hasAttribute(attr: string): boolean;
|
||||
getAttribute(attr: string): string;
|
||||
}
|
||||
|
||||
export const IContextKeyService = createDecorator<IContextKeyService>('contextKeyService');
|
||||
|
||||
export interface IContextKeyService {
|
||||
_serviceBrand: any;
|
||||
dispose(): void;
|
||||
|
||||
onDidChangeContext: Event<string[]>;
|
||||
createKey<T>(key: string, defaultValue: T): IContextKey<T>;
|
||||
contextMatchesRules(rules: ContextKeyExpr): boolean;
|
||||
getContextKeyValue<T>(key: string): T;
|
||||
|
||||
createScoped(target?: IContextKeyServiceTarget): IContextKeyService;
|
||||
getContext(target: IContextKeyServiceTarget): IContext;
|
||||
}
|
||||
|
||||
export const SET_CONTEXT_COMMAND_ID = 'setContext';
|
||||
89
src/vs/platform/contextkey/test/common/contextkey.test.ts
Normal file
89
src/vs/platform/contextkey/test/common/contextkey.test.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
function createContext(ctx: any) {
|
||||
return {
|
||||
getValue: (key: string) => {
|
||||
return ctx[key];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
suite('ContextKeyExpr', () => {
|
||||
test('ContextKeyExpr.equals', function () {
|
||||
let a = ContextKeyExpr.and(
|
||||
ContextKeyExpr.has('a1'),
|
||||
ContextKeyExpr.and(ContextKeyExpr.has('and.a')),
|
||||
ContextKeyExpr.has('a2'),
|
||||
ContextKeyExpr.equals('b1', 'bb1'),
|
||||
ContextKeyExpr.equals('b2', 'bb2'),
|
||||
ContextKeyExpr.notEquals('c1', 'cc1'),
|
||||
ContextKeyExpr.notEquals('c2', 'cc2'),
|
||||
ContextKeyExpr.not('d1'),
|
||||
ContextKeyExpr.not('d2')
|
||||
);
|
||||
let b = ContextKeyExpr.and(
|
||||
ContextKeyExpr.equals('b2', 'bb2'),
|
||||
ContextKeyExpr.notEquals('c1', 'cc1'),
|
||||
ContextKeyExpr.not('d1'),
|
||||
ContextKeyExpr.notEquals('c2', 'cc2'),
|
||||
ContextKeyExpr.has('a2'),
|
||||
ContextKeyExpr.equals('b1', 'bb1'),
|
||||
ContextKeyExpr.has('a1'),
|
||||
ContextKeyExpr.and(ContextKeyExpr.equals('and.a', true)),
|
||||
ContextKeyExpr.not('d2')
|
||||
);
|
||||
assert(a.equals(b), 'expressions should be equal');
|
||||
});
|
||||
|
||||
test('normalize', function () {
|
||||
let key1IsTrue = ContextKeyExpr.equals('key1', true);
|
||||
let key1IsNotFalse = ContextKeyExpr.notEquals('key1', false);
|
||||
let key1IsFalse = ContextKeyExpr.equals('key1', false);
|
||||
let key1IsNotTrue = ContextKeyExpr.notEquals('key1', true);
|
||||
|
||||
assert.ok(key1IsTrue.normalize().equals(ContextKeyExpr.has('key1')));
|
||||
assert.ok(key1IsNotFalse.normalize().equals(ContextKeyExpr.has('key1')));
|
||||
assert.ok(key1IsFalse.normalize().equals(ContextKeyExpr.not('key1')));
|
||||
assert.ok(key1IsNotTrue.normalize().equals(ContextKeyExpr.not('key1')));
|
||||
});
|
||||
|
||||
test('evaluate', function () {
|
||||
/* tslint:disable:triple-equals */
|
||||
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);
|
||||
}
|
||||
function testBatch(expr: string, value: any): void {
|
||||
testExpression(expr, !!value);
|
||||
testExpression(expr + ' == true', !!value);
|
||||
testExpression(expr + ' != true', !value);
|
||||
testExpression(expr + ' == false', !value);
|
||||
testExpression(expr + ' != false', !!value);
|
||||
testExpression(expr + ' == 5', value == <any>'5');
|
||||
testExpression(expr + ' != 5', value != <any>'5');
|
||||
testExpression('!' + expr, !value);
|
||||
}
|
||||
|
||||
testBatch('a', true);
|
||||
testBatch('b', false);
|
||||
testBatch('c', '5');
|
||||
testBatch('z', undefined);
|
||||
|
||||
testExpression('a && !b', true && !false);
|
||||
testExpression('a && b', true && false);
|
||||
testExpression('a && !b && c == 5', true && !false && '5' == '5');
|
||||
/* tslint:enable:triple-equals */
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user