Skip to content

Commit 5acd950

Browse files
authored
Support "not in" context key expression (microsoft#155261)
* Support "not in" context key expression Fixes microsoft#154582 * Tweak var name
1 parent a567b59 commit 5acd950

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

src/vs/platform/contextkey/common/contextkey.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export interface IContextKeyExprMapper {
5353
mapSmallerEquals(key: string, value: any): ContextKeyExpression;
5454
mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr;
5555
mapIn(key: string, valueKey: string): ContextKeyInExpr;
56+
mapNotIn(key: string, valueKey: string): ContextKeyNotInExpr;
5657
}
5758

5859
export interface IContextKeyExpression {
@@ -98,6 +99,9 @@ export abstract class ContextKeyExpr {
9899
public static in(key: string, value: string): ContextKeyExpression {
99100
return ContextKeyInExpr.create(key, value);
100101
}
102+
public static notIn(key: string, value: string): ContextKeyExpression {
103+
return ContextKeyNotInExpr.create(key, value);
104+
}
101105
public static not(key: string): ContextKeyExpression {
102106
return ContextKeyNotExpr.create(key);
103107
}
@@ -156,6 +160,11 @@ export abstract class ContextKeyExpr {
156160
return ContextKeyRegexExpr.create(pieces[0].trim(), this._deserializeRegexValue(pieces[1], strict));
157161
}
158162

163+
if (serializedOne.indexOf(' not in ') >= 0) {
164+
const pieces = serializedOne.split(' not in ');
165+
return ContextKeyNotInExpr.create(pieces[0].trim(), pieces[1].trim());
166+
}
167+
159168
if (serializedOne.indexOf(' in ') >= 0) {
160169
const pieces = serializedOne.split(' in ');
161170
return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim());
@@ -539,34 +548,39 @@ export class ContextKeyInExpr implements IContextKeyExpression {
539548

540549
public negate(): ContextKeyExpression {
541550
if (!this.negated) {
542-
this.negated = ContextKeyNotInExpr.create(this);
551+
this.negated = ContextKeyNotInExpr.create(this.key, this.valueKey);
543552
}
544553
return this.negated;
545554
}
546555
}
547556

548557
export class ContextKeyNotInExpr implements IContextKeyExpression {
549558

550-
public static create(actual: ContextKeyInExpr): ContextKeyNotInExpr {
551-
return new ContextKeyNotInExpr(actual);
559+
public static create(key: string, valueKey: string): ContextKeyNotInExpr {
560+
return new ContextKeyNotInExpr(key, valueKey);
552561
}
553562

554563
public readonly type = ContextKeyExprType.NotIn;
555564

556-
private constructor(private readonly _actual: ContextKeyInExpr) {
557-
//
565+
private readonly _negated: ContextKeyInExpr;
566+
567+
private constructor(
568+
private readonly key: string,
569+
private readonly valueKey: string,
570+
) {
571+
this._negated = ContextKeyInExpr.create(key, valueKey);
558572
}
559573

560574
public cmp(other: ContextKeyExpression): number {
561575
if (other.type !== this.type) {
562576
return this.type - other.type;
563577
}
564-
return this._actual.cmp(other._actual);
578+
return this._negated.cmp(other._negated);
565579
}
566580

567581
public equals(other: ContextKeyExpression): boolean {
568582
if (other.type === this.type) {
569-
return this._actual.equals(other._actual);
583+
return this._negated.equals(other._negated);
570584
}
571585
return false;
572586
}
@@ -576,23 +590,23 @@ export class ContextKeyNotInExpr implements IContextKeyExpression {
576590
}
577591

578592
public evaluate(context: IContext): boolean {
579-
return !this._actual.evaluate(context);
593+
return !this._negated.evaluate(context);
580594
}
581595

582596
public serialize(): string {
583-
throw new Error('Method not implemented.');
597+
return `${this.key} not in '${this.valueKey}'`;
584598
}
585599

586600
public keys(): string[] {
587-
return this._actual.keys();
601+
return this._negated.keys();
588602
}
589603

590604
public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
591-
return new ContextKeyNotInExpr(this._actual.map(mapFnc));
605+
return mapFnc.mapNotIn(this.key, this.valueKey);
592606
}
593607

594608
public negate(): ContextKeyExpression {
595-
return this._actual;
609+
return this._negated;
596610
}
597611
}
598612

src/vs/platform/contextkey/test/common/contextkey.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,21 @@ suite('ContextKeyExpr', () => {
179179
assert.strictEqual(ainb.evaluate(createContext({ 'a': 'prototype', 'b': {} })), false);
180180
});
181181

182+
test('ContextKeyNotInExpr', () => {
183+
const aNotInB = ContextKeyExpr.deserialize('a not in b')!;
184+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 3, 'b': [3, 2, 1] })), false);
185+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 3, 'b': [1, 2, 3] })), false);
186+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 3, 'b': [1, 2] })), true);
187+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 3 })), true);
188+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 3, 'b': null })), true);
189+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 'x', 'b': ['x'] })), false);
190+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 'x', 'b': ['y'] })), true);
191+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 'x', 'b': {} })), true);
192+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 'x', 'b': { 'x': false } })), false);
193+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 'x', 'b': { 'x': true } })), false);
194+
assert.strictEqual(aNotInB.evaluate(createContext({ 'a': 'prototype', 'b': {} })), true);
195+
});
196+
182197
test('issue #106524: distributing AND should normalize', () => {
183198
const actual = ContextKeyExpr.and(
184199
ContextKeyExpr.or(

src/vs/server/node/remoteAgentEnvironmentImpl.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
1515
import { ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1616
import { transformOutgoingURIs } from 'vs/base/common/uriIpc';
1717
import { ILogService } from 'vs/platform/log/common/log';
18-
import { ContextKeyExpr, ContextKeyDefinedExpr, ContextKeyNotExpr, ContextKeyEqualsExpr, ContextKeyNotEqualsExpr, ContextKeyRegexExpr, IContextKeyExprMapper, ContextKeyExpression, ContextKeyInExpr, ContextKeyGreaterExpr, ContextKeyGreaterEqualsExpr, ContextKeySmallerExpr, ContextKeySmallerEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
18+
import { ContextKeyExpr, ContextKeyDefinedExpr, ContextKeyNotExpr, ContextKeyEqualsExpr, ContextKeyNotEqualsExpr, ContextKeyRegexExpr, IContextKeyExprMapper, ContextKeyExpression, ContextKeyInExpr, ContextKeyGreaterExpr, ContextKeyGreaterEqualsExpr, ContextKeySmallerExpr, ContextKeySmallerEqualsExpr, ContextKeyNotInExpr } from 'vs/platform/contextkey/common/contextkey';
1919
import { listProcesses } from 'vs/base/node/ps';
2020
import { getMachineInfo, collectWorkspaceStats } from 'vs/platform/diagnostics/node/diagnosticsService';
2121
import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics';
@@ -236,6 +236,9 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel {
236236
mapIn(key: string, valueKey: string): ContextKeyInExpr {
237237
return ContextKeyInExpr.create(key, valueKey);
238238
}
239+
mapNotIn(key: string, valueKey: string): ContextKeyNotInExpr {
240+
return ContextKeyNotInExpr.create(key, valueKey);
241+
}
239242
};
240243

241244
const _massageWhenUser = (element: WhenUser) => {

0 commit comments

Comments
 (0)