diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index fac16754098..2bfa84d15eb 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -7,7 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction, ILocalizedString } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr, IContextKeyService, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKeyChangeEvent, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; export class MenuService implements IMenuService { @@ -125,7 +125,7 @@ class Menu implements IMenu { return result; } - private static _fillInKbExprKeys(exp: ContextKeyExpr | undefined, set: Set): void { + private static _fillInKbExprKeys(exp: ContextKeyExpression | undefined, set: Set): void { if (exp) { for (let key of exp.keys()) { set.add(key); diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index dcab732a541..d5f8cdb22f5 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -26,6 +26,17 @@ export interface IContextKeyExprMapper { mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr; } +export interface IContextKeyExpression { + cmp(other: ContextKeyExpression): number; + equals(other: ContextKeyExpression): boolean; + evaluate(context: IContext): boolean; + serialize(): string; + keys(): string[]; + map(mapFnc: IContextKeyExprMapper): ContextKeyExpression; + negate(): ContextKeyExpression; + +} + export type ContextKeyExpression = ( ContextKeyDefinedExpr | ContextKeyNotExpr | ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr @@ -158,55 +169,26 @@ export abstract class ContextKeyExpr { return null; } } - - public abstract getType(): ContextKeyExprType; - public abstract equals(other: ContextKeyExpression): boolean; - public abstract evaluate(context: IContext): boolean; - public abstract serialize(): string; - public abstract keys(): string[]; - public abstract map(mapFnc: IContextKeyExprMapper): ContextKeyExpression; - public abstract negate(): ContextKeyExpression; } function cmp(a: ContextKeyExpression, b: ContextKeyExpression): number { - let aType = a.getType(); - let bType = b.getType(); - if (aType !== bType) { - return aType - bType; - } - switch (aType) { - case ContextKeyExprType.Defined: - return (a).cmp(b); - case ContextKeyExprType.Not: - return (a).cmp(b); - case ContextKeyExprType.Equals: - return (a).cmp(b); - case ContextKeyExprType.NotEquals: - return (a).cmp(b); - case ContextKeyExprType.Regex: - return (a).cmp(b); - case ContextKeyExprType.NotRegex: - return (a).cmp(b); - case ContextKeyExprType.And: - return (a).cmp(b); - default: - throw new Error('Unknown ContextKeyExpr!'); - } + return a.cmp(b); } -export class ContextKeyDefinedExpr implements ContextKeyExpr { +export class ContextKeyDefinedExpr implements IContextKeyExpression { public static create(key: string): ContextKeyDefinedExpr { return new ContextKeyDefinedExpr(key); } - protected constructor(protected key: string) { + public readonly type = ContextKeyExprType.Defined; + + protected constructor(protected readonly key: string) { } - public getType(): ContextKeyExprType { - return ContextKeyExprType.Defined; - } - - public cmp(other: ContextKeyDefinedExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } if (this.key < other.key) { return -1; } @@ -217,7 +199,7 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr { } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyDefinedExpr) { + if (other.type === this.type) { return (this.key === other.key); } return false; @@ -244,7 +226,7 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr { } } -export class ContextKeyEqualsExpr implements ContextKeyExpr { +export class ContextKeyEqualsExpr implements IContextKeyExpression { public static create(key: string, value: any): ContextKeyExpression { if (typeof value === 'boolean') { @@ -256,14 +238,15 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { return new ContextKeyEqualsExpr(key, value); } + public readonly type = ContextKeyExprType.Equals; + private constructor(private readonly key: string, private readonly value: any) { } - public getType(): ContextKeyExprType { - return ContextKeyExprType.Equals; - } - - public cmp(other: ContextKeyEqualsExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } if (this.key < other.key) { return -1; } @@ -280,7 +263,7 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyEqualsExpr) { + if (other.type === this.type) { return (this.key === other.key && this.value === other.value); } return false; @@ -309,7 +292,7 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { } } -export class ContextKeyNotEqualsExpr implements ContextKeyExpr { +export class ContextKeyNotEqualsExpr implements IContextKeyExpression { public static create(key: string, value: any): ContextKeyExpression { if (typeof value === 'boolean') { @@ -321,14 +304,15 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { return new ContextKeyNotEqualsExpr(key, value); } - private constructor(private key: string, private value: any) { + public readonly type = ContextKeyExprType.NotEquals; + + private constructor(private readonly key: string, private readonly value: any) { } - public getType(): ContextKeyExprType { - return ContextKeyExprType.NotEquals; - } - - public cmp(other: ContextKeyNotEqualsExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } if (this.key < other.key) { return -1; } @@ -345,7 +329,7 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyNotEqualsExpr) { + if (other.type === this.type) { return (this.key === other.key && this.value === other.value); } return false; @@ -374,20 +358,21 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { } } -export class ContextKeyNotExpr implements ContextKeyExpr { +export class ContextKeyNotExpr implements IContextKeyExpression { public static create(key: string): ContextKeyExpression { return new ContextKeyNotExpr(key); } - private constructor(private key: string) { + public readonly type = ContextKeyExprType.Not; + + private constructor(private readonly key: string) { } - public getType(): ContextKeyExprType { - return ContextKeyExprType.Not; - } - - public cmp(other: ContextKeyNotExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } if (this.key < other.key) { return -1; } @@ -398,7 +383,7 @@ export class ContextKeyNotExpr implements ContextKeyExpr { } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyNotExpr) { + if (other.type === this.type) { return (this.key === other.key); } return false; @@ -425,21 +410,22 @@ export class ContextKeyNotExpr implements ContextKeyExpr { } } -export class ContextKeyRegexExpr implements ContextKeyExpr { +export class ContextKeyRegexExpr implements IContextKeyExpression { public static create(key: string, regexp: RegExp | null): ContextKeyRegexExpr { return new ContextKeyRegexExpr(key, regexp); } - private constructor(private key: string, private regexp: RegExp | null) { + public readonly type = ContextKeyExprType.Regex; + + private constructor(private readonly key: string, private readonly regexp: RegExp | null) { // } - public getType(): ContextKeyExprType { - return ContextKeyExprType.Regex; - } - - public cmp(other: ContextKeyRegexExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } if (this.key < other.key) { return -1; } @@ -458,7 +444,7 @@ export class ContextKeyRegexExpr implements ContextKeyExpr { } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyRegexExpr) { + if (other.type === this.type) { const thisSource = this.regexp ? this.regexp.source : ''; const otherSource = other.regexp ? other.regexp.source : ''; return (this.key === other.key && thisSource === otherSource); @@ -491,26 +477,27 @@ export class ContextKeyRegexExpr implements ContextKeyExpr { } } -export class ContextKeyNotRegexExpr implements ContextKeyExpr { +export class ContextKeyNotRegexExpr implements IContextKeyExpression { public static create(actual: ContextKeyRegexExpr): ContextKeyExpression { return new ContextKeyNotRegexExpr(actual); } + public readonly type = ContextKeyExprType.NotRegex; + private constructor(private readonly _actual: ContextKeyRegexExpr) { // } - public getType(): ContextKeyExprType { - return ContextKeyExprType.NotRegex; - } - - public cmp(other: ContextKeyNotRegexExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } return this._actual.cmp(other._actual); } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyNotRegexExpr) { + if (other.type === this.type) { return this._actual.equals(other._actual); } return false; @@ -537,7 +524,7 @@ export class ContextKeyNotRegexExpr implements ContextKeyExpr { } } -export class ContextKeyAndExpr implements ContextKeyExpr { +export class ContextKeyAndExpr implements IContextKeyExpression { public static create(_expr: ReadonlyArray): ContextKeyExpression | undefined { const expr = ContextKeyAndExpr._normalizeArr(_expr); @@ -552,14 +539,15 @@ export class ContextKeyAndExpr implements ContextKeyExpr { return new ContextKeyAndExpr(expr); } + public readonly type = ContextKeyExprType.And; + private constructor(public readonly expr: ContextKeyExpression[]) { } - public getType(): ContextKeyExprType { - return ContextKeyExprType.And; - } - - public cmp(other: ContextKeyAndExpr): number { + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } if (this.expr.length < other.expr.length) { return -1; } @@ -576,7 +564,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr { } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyAndExpr) { + if (other.type === this.type) { if (this.expr.length !== other.expr.length) { return false; } @@ -607,12 +595,12 @@ export class ContextKeyAndExpr implements ContextKeyExpr { continue; } - if (e instanceof ContextKeyAndExpr) { + if (e.type === ContextKeyExprType.And) { expr.push(...e.expr); continue; } - if (e instanceof ContextKeyOrExpr) { + if (e.type === ContextKeyExprType.Or) { // Not allowed, because we don't have parens! throw new Error(`It is not allowed to have an or expression here due to lack of parens! For example "a && (b||c)" is not supported, use "(a&&b) || (a&&c)" instead.`); } @@ -650,7 +638,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr { } } -export class ContextKeyOrExpr implements ContextKeyExpr { +export class ContextKeyOrExpr implements IContextKeyExpression { public static create(_expr: ReadonlyArray): ContextKeyExpression | undefined { const expr = ContextKeyOrExpr._normalizeArr(_expr); @@ -665,15 +653,32 @@ export class ContextKeyOrExpr implements ContextKeyExpr { return new ContextKeyOrExpr(expr); } + public readonly type = ContextKeyExprType.Or; + private constructor(public readonly expr: ContextKeyExpression[]) { } - public getType(): ContextKeyExprType { - return ContextKeyExprType.Or; + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + if (this.expr.length < other.expr.length) { + return -1; + } + if (this.expr.length > other.expr.length) { + return 1; + } + for (let i = 0, len = this.expr.length; i < len; i++) { + const r = cmp(this.expr[i], other.expr[i]); + if (r !== 0) { + return r; + } + } + return 0; } public equals(other: ContextKeyExpression): boolean { - if (other instanceof ContextKeyOrExpr) { + if (other.type === this.type) { if (this.expr.length !== other.expr.length) { return false; } @@ -706,7 +711,7 @@ export class ContextKeyOrExpr implements ContextKeyExpr { continue; } - if (e instanceof ContextKeyOrExpr) { + if (e.type === ContextKeyExprType.Or) { expr = expr.concat(e.expr); continue; } @@ -743,7 +748,7 @@ export class ContextKeyOrExpr implements ContextKeyExpr { } const terminals = (node: ContextKeyExpression) => { - if (node instanceof ContextKeyOrExpr) { + if (node.type === ContextKeyExprType.Or) { return node.expr; } return [node]; @@ -770,7 +775,7 @@ export class ContextKeyOrExpr implements ContextKeyExpr { export class RawContextKey extends ContextKeyDefinedExpr { - private _defaultValue: T | undefined; + private readonly _defaultValue: T | undefined; constructor(key: string, defaultValue: T | undefined) { super(key); diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 5e0a7fcff1a..a1821bddd85 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -6,7 +6,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr, IContext, ContextKeyOrExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; +import { IContext, ContextKeyExpression, ContextKeyExprType } from 'vs/platform/contextkey/common/contextkey'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { keys } from 'vs/base/common/map'; @@ -54,7 +54,7 @@ export class KeybindingResolver { } } - private static _isTargetedForRemoval(defaultKb: ResolvedKeybindingItem, keypressFirstPart: string | null, keypressChordPart: string | null, command: string, when: ContextKeyExpr | undefined): boolean { + private static _isTargetedForRemoval(defaultKb: ResolvedKeybindingItem, keypressFirstPart: string | null, keypressChordPart: string | null, command: string, when: ContextKeyExpression | undefined): boolean { if (defaultKb.command !== command) { return false; } @@ -193,7 +193,7 @@ export class KeybindingResolver { const notP = p.negate(); const terminals = (node: ContextKeyExpression) => { - if (node instanceof ContextKeyOrExpr) { + if (node.type === ContextKeyExprType.Or) { return node.expr; } return [node]; @@ -318,7 +318,7 @@ export class KeybindingResolver { return null; } - public static contextMatchesRules(context: IContext, rules: ContextKeyExpr | null | undefined): boolean { + public static contextMatchesRules(context: IContext, rules: ContextKeyExpression | null | undefined): boolean { if (!rules) { return true; }