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

2183 LOC · 1848 covered · 335 uncovered · 602 ranges · 11390 concepts · 169 introducers · 5850 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- contextkey.ts ×201
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CharCode } from '../../../base/common/charCode.js';
7 > import { Event } from '../../../base/common/event.js';
8 > import { isChrome, isEdge, isFirefox, isLinux, isMacintosh, isSafari, isWeb, isWindows } from '../../../base/common/platform.js';
9 > import { isFalsyOrWhitespace } from '../../../base/common/strings.js';
10 > import { Scanner, LexingError, Token, TokenType } from './scanner.js';
11 > import { createDecorator } from '../../instantiation/common/instantiation.js';
12 > import { localize } from '../../../nls.js';
13 > import { IDisposable } from '../../../base/common/lifecycle.js';
14 > import { illegalArgument } from '../../../base/common/errors.js';
15 >
16 > const CONSTANT_VALUES = new Map<string, boolean>();
17 > CONSTANT_VALUES.set('false', false);
18 > CONSTANT_VALUES.set('true', true);
19 > CONSTANT_VALUES.set('isMac', isMacintosh);
20 > CONSTANT_VALUES.set('isLinux', isLinux);
21 > CONSTANT_VALUES.set('isWindows', isWindows);
22 > CONSTANT_VALUES.set('isWeb', isWeb);
23 > CONSTANT_VALUES.set('isMacNative', isMacintosh && !isWeb);
24 > CONSTANT_VALUES.set('isEdge', isEdge);
25 > CONSTANT_VALUES.set('isFirefox', isFirefox);
26 > CONSTANT_VALUES.set('isChrome', isChrome);
27 > CONSTANT_VALUES.set('isSafari', isSafari);
28 >
29 > /** allow register constant context keys that are known only after startup; requires running `substituteConstants` on the context key - https://github.com/microsoft/vscode/issues/174218#issuecomment-1437972127 */
30 > export function setConstant(key: string, value: boolean) {
31 if (CONSTANT_VALUES.get(key) !== undefined) { throw illegalArgument('contextkey.setConstant(k, v) invoked with already set constant `k`'); }
32
33 CONSTANT_VALUES.set(key, value);
34 }
36 > const hasOwnProperty = Object.prototype.hasOwnProperty;
37 >
38 > export const enum ContextKeyExprType {
39 > False = 0,
40 > True = 1,
41 > Defined = 2,
42 > Not = 3,
43 > Equals = 4,
44 > NotEquals = 5,
45 > And = 6,
46 > Regex = 7,
47 > NotRegex = 8,
48 > Or = 9,
49 > In = 10,
50 > NotIn = 11,
51 > Greater = 12,
52 > GreaterEquals = 13,
53 > Smaller = 14,
54 > SmallerEquals = 15,
55 > }
56 >
57 > export interface IContextKeyExprMapper {
58 > mapDefined(key: string): ContextKeyExpression;
59 > mapNot(key: string): ContextKeyExpression;
60 > mapEquals(key: string, value: any): ContextKeyExpression;
61 > mapNotEquals(key: string, value: any): ContextKeyExpression;
62 > mapGreater(key: string, value: any): ContextKeyExpression;
63 > mapGreaterEquals(key: string, value: any): ContextKeyExpression;
64 > mapSmaller(key: string, value: any): ContextKeyExpression;
65 > mapSmallerEquals(key: string, value: any): ContextKeyExpression;
66 > mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr;
67 > mapIn(key: string, valueKey: string): ContextKeyInExpr;
68 > mapNotIn(key: string, valueKey: string): ContextKeyNotInExpr;
69 > }
70 >
71 > export interface IContextKeyExpression {
72 > cmp(other: ContextKeyExpression): number;
73 > equals(other: ContextKeyExpression): boolean;
74 > substituteConstants(): ContextKeyExpression | undefined;
75 > evaluate(context: IContext): boolean;
76 > serialize(): string;
77 > keys(): string[];
78 > map(mapFnc: IContextKeyExprMapper): ContextKeyExpression;
79 > negate(): ContextKeyExpression;
80 >
81 > }
82 >
83 > export type ContextKeyExpression = (
84 > ContextKeyFalseExpr | ContextKeyTrueExpr | ContextKeyDefinedExpr | ContextKeyNotExpr
85 > | ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr
86 > | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr
87 > | ContextKeyNotInExpr | ContextKeyGreaterExpr | ContextKeyGreaterEqualsExpr
88 > | ContextKeySmallerExpr | ContextKeySmallerEqualsExpr
89 > );
90 >
91 >
92 > /*
93 >
94 > Syntax grammar:
95 >
96 > ```ebnf
97 >
98 > expression ::= or
99 >
100 > or ::= and { '||' and }*
101 >
102 > and ::= term { '&&' term }*
103 >
104 > term ::=
105 > | '!' (KEY | true | false | parenthesized)
106 > | primary
107 >
108 > primary ::=
109 > | 'true'
110 > | 'false'
111 > | parenthesized
112 > | KEY '=~' REGEX
113 > | KEY [ ('==' | '!=' | '<' | '<=' | '>' | '>=' | 'not' 'in' | 'in') value ]
114 >
115 > parenthesized ::=
116 > | '(' expression ')'
117 >
118 > value ::=
119 > | 'true'
120 > | 'false'
121 > | 'in' // we support `in` as a value because there's an extension that uses it, ie "when": "languageId == in"
122 > | VALUE // matched by the same regex as KEY; consider putting the value in single quotes if it's a string (e.g., with spaces)
123 > | SINGLE_QUOTED_STR
124 > | EMPTY_STR // this allows "when": "foo == " which's used by existing extensions
125 >
126 > ```
127 > */
128 >
129 > export type ParserConfig = {
130 > /**
131 > * with this option enabled, the parser can recover from regex parsing errors, e.g., unescaped slashes: `/src//` is accepted as `/src\//` would be
132 > */
133 > regexParsingWithErrorRecovery: boolean;
134 > };
135 >
136 > const defaultConfig: ParserConfig = {
137 > regexParsingWithErrorRecovery: true
138 > };
139 >
140 > export type ParsingError = {
141 > message: string;
142 > offset: number;
143 > lexeme: string;
144 > additionalInfo?: string;
145 > };
146 >
147 > const errorEmptyString = localize('contextkey.parser.error.emptyString', "Empty context key expression");
148 > const hintEmptyString = localize('contextkey.parser.error.emptyString.hint', "Did you forget to write an expression? You can also put 'false' or 'true' to always evaluate to false or true, respectively.");
149 > const errorNoInAfterNot = localize('contextkey.parser.error.noInAfterNot', "'in' after 'not'.");
150 > const errorClosingParenthesis = localize('contextkey.parser.error.closingParenthesis', "closing parenthesis ')'");
151 > const errorUnexpectedToken = localize('contextkey.parser.error.unexpectedToken', "Unexpected token");
152 > const hintUnexpectedToken = localize('contextkey.parser.error.unexpectedToken.hint', "Did you forget to put && or || before the token?");
153 > const errorUnexpectedEOF = localize('contextkey.parser.error.unexpectedEOF', "Unexpected end of expression");
154 > const hintUnexpectedEOF = localize('contextkey.parser.error.unexpectedEOF.hint', "Did you forget to put a context key?");
155 >
156 > /**
157 > * A parser for context key expressions.
158 > *
159 > * Example:
160 > * ```ts
161 > * const parser = new Parser();
162 > * const expr = parser.parse('foo == "bar" && baz == true');
163 > *
164 > * if (expr === undefined) {
165 > * // there were lexing or parsing errors
166 > * // process lexing errors with `parser.lexingErrors`
167 > * // process parsing errors with `parser.parsingErrors`
168 > * } else {
169 > * // expr is a valid expression
170 > * }
171 > * ```
172 > */
173 > export class Parser {
174 > // Note: this doesn't produce an exact syntax tree but a normalized one
175 > // ContextKeyExpression's that we use as AST nodes do not expose constructors that do not normalize
176 >
177 > private static _parseError = new Error();
178 >
179 > // lifetime note: `_scanner` lives as long as the parser does, i.e., is not reset between calls to `parse`
180 > private readonly _scanner = new Scanner();
181 >
182 > // lifetime note: `_tokens`, `_current`, and `_parsingErrors` must be reset between calls to `parse`
183 > private _tokens: Token[] = [];
184 > private _current = 0; // invariant: 0 <= this._current < this._tokens.length ; any incrementation of this value must first call `_isAtEnd`
185 > private _parsingErrors: ParsingError[] = [];
186 >
187 > get lexingErrors(): Readonly<LexingError[]> {
188 > return this._scanner.errors; contextkey.ts ×4
189 > }
191 > get parsingErrors(): Readonly<ParsingError[]> {
192 > return this._parsingErrors; contextkey.ts ×4
193 > }
195 > constructor(private readonly _config: ParserConfig = defaultConfig) {
196 > }
197 >
198 > /**
199 > * Parse a context key expression.
200 > *
201 > * @param input the expression to parse
202 > * @returns the parsed expression or `undefined` if there's an error - call `lexingErrors` and `parsingErrors` to see the errors
203 > */
204 > parse(input: string): ContextKeyExpression | undefined {
206 > if (input === '') {
207 > this._parsingErrors.push({ message: errorEmptyString, offset: 0, lexeme: '', additionalInfo: hintEmptyString }); contextkey.ts ×2
208 > return undefined;
209 > }
211 > this._tokens = this._scanner.reset(input).scan();
212 > // @ulugbekna: we do not stop parsing if there are lexing errors to be able to reconstruct regexes with unescaped slashes; TODO@ulugbekna: make this respect config option for recovery
213 >
214 > this._current = 0;
215 > this._parsingErrors = [];
216 >
217 > try {
218 > const expr = this._expr();
219 > if (!this._isAtEnd()) {
220 > const peek = this._peek(); contextkey.ts ×1
221 > const additionalInfo = peek.type === TokenType.Str ? hintUnexpectedToken : undefined;
222 > this._parsingErrors.push({ message: errorUnexpectedToken, offset: peek.offset, lexeme: Scanner.getLexeme(peek), additionalInfo });
223 > throw Parser._parseError;
224 > }
225 > return expr; contextkey.ts ×1
226 > } catch (e) { contextkey.ts ×15
227 > if (!(e === Parser._parseError)) { contextkey.ts ×4
228 throw e;
229 }
230 > return undefined; contextkey.ts ×4
231 > }
234 > private _expr(): ContextKeyExpression | undefined {
235 > return this._or(); contextkey.ts ×15
236 > }
238 > private _or(): ContextKeyExpression | undefined {
239 > const expr = [this._and()]; contextkey.ts ×15
240 >
241 > while (this._matchOne(TokenType.Or)) {
242 > const right = this._and(); contextkey.ts ×1
243 > expr.push(right);
244 > }
246 > return expr.length === 1 ? expr[0] : ContextKeyExpr.or(...expr); contextkey.ts ×15
247 > }
249 > private _and(): ContextKeyExpression | undefined {
250 > const expr = [this._term()]; contextkey.ts ×15
251 >
252 > while (this._matchOne(TokenType.And)) {
253 > const right = this._term(); contextkey.ts ×1
254 > expr.push(right);
255 > }
257 > return expr.length === 1 ? expr[0] : ContextKeyExpr.and(...expr); contextkey.ts ×15
258 > }
260 > private _term(): ContextKeyExpression | undefined {
261 > if (this._matchOne(TokenType.Neg)) { contextkey.ts ×15
262 > const peek = this._peek(); contextkey.ts ×6
263 > switch (peek.type) {
264 > case TokenType.True:
265 > this._advance(); contextkey.ts ×5
266 > return ContextKeyFalseExpr.INSTANCE;
267 > case TokenType.False: contextkey.ts ×6
268 > this._advance(); contextkey.ts ×5
269 > return ContextKeyTrueExpr.INSTANCE;
270 > case TokenType.LParen: { contextkey.ts ×6
271 > this._advance(); contextkey.ts ×1
272 > const expr = this._expr();
273 > this._consume(TokenType.RParen, errorClosingParenthesis);
274 > return expr?.negate();
275 > }
276 > case TokenType.Str: contextkey.ts ×6
277 > this._advance(); contextkey.ts ×1
278 > return ContextKeyNotExpr.create(peek.lexeme);
279 > default: contextkey.ts ×6
280 > throw this._errExpectedButGot(`KEY | true | false | '(' expression ')'`, peek); contextkey.ts ×1
282 > }
283 > return this._primary(); contextkey.ts ×8
286 > private _primary(): ContextKeyExpression | undefined {
288 > const peek = this._peek();
289 > switch (peek.type) {
290 > case TokenType.True:
291 > this._advance(); contextkey.ts ×3
292 > return ContextKeyExpr.true();
294 > case TokenType.False:
295 > this._advance(); contextkey.ts ×3
296 > return ContextKeyExpr.false();
298 > case TokenType.LParen: {
299 > this._advance(); contextkey.ts ×1
300 > const expr = this._expr();
301 > this._consume(TokenType.RParen, errorClosingParenthesis);
302 > return expr;
303 > }
305 > case TokenType.Str: {
306 > // KEY contextkey.ts ×9
307 > const key = peek.lexeme;
308 > this._advance();
309 >
310 > // =~ regex
311 > if (this._matchOne(TokenType.RegexOp)) {
313 > // @ulugbekna: we need to reconstruct the regex from the tokens because some extensions use unescaped slashes in regexes
314 > const expr = this._peek();
315 >
316 > if (!this._config.regexParsingWithErrorRecovery) {
317 > this._advance(); contextkey.ts ×19
318 > if (expr.type !== TokenType.RegexStr) {
319 throw this._errExpectedButGot(`REGEX`, expr);
320 }
321 > const regexLexeme = expr.lexeme; contextkey.ts ×19
322 > const closingSlashIndex = regexLexeme.lastIndexOf('/');
323 > const flags = closingSlashIndex === regexLexeme.length - 1 ? undefined : this._removeFlagsGY(regexLexeme.substring(closingSlashIndex + 1));
324 > let regexp: RegExp | null;
325 > try {
326 > regexp = new RegExp(regexLexeme.substring(1, closingSlashIndex), flags);
327 > } catch (e) {
328 throw this._errExpectedButGot(`REGEX`, expr);
329 }
330 > return ContextKeyRegexExpr.create(key, regexp); contextkey.ts ×19
331 > }
333 > switch (expr.type) {
334 > case TokenType.RegexStr:
335 > case TokenType.Error: { // also handle an ErrorToken in case of smth such as /(/file)/ contextkey.ts ×5
336 > const lexemeReconstruction = [expr.lexeme]; // /REGEX/ or /REGEX/FLAGS contextkey.ts ×8
337 > this._advance();
338 >
339 > let followingToken = this._peek();
340 > let parenBalance = 0;
341 > for (let i = 0; i < expr.lexeme.length; i++) {
342 > if (expr.lexeme.charCodeAt(i) === CharCode.OpenParen) {
343 > parenBalance++; contextkey.ts ×1
344 > } else if (expr.lexeme.charCodeAt(i) === CharCode.CloseParen) { contextkey.ts ×8
345 > parenBalance--; contextkey.ts ×1
346 > }
348 >
349 > while (!this._isAtEnd() && followingToken.type !== TokenType.And && followingToken.type !== TokenType.Or) {
350 > switch (followingToken.type) { contextkey.ts ×4
351 > case TokenType.LParen:
352 > parenBalance++;
353 > break;
354 > case TokenType.RParen:
355 > parenBalance--;
356 > break;
357 > case TokenType.RegexStr:
358 > case TokenType.QuotedStr:
359 > for (let i = 0; i < followingToken.lexeme.length; i++) {
360 > if (followingToken.lexeme.charCodeAt(i) === CharCode.OpenParen) {
361 > parenBalance++; contextkey.ts ×1
362 > } else if (expr.lexeme.charCodeAt(i) === CharCode.CloseParen) { contextkey.ts ×4
363 parenBalance--;
364 }
366 > }
367 > if (parenBalance < 0) {
368 break;
369 }
370 > lexemeReconstruction.push(Scanner.getLexeme(followingToken)); contextkey.ts ×4
371 > this._advance();
372 > followingToken = this._peek();
373 > }
375 > const regexLexeme = lexemeReconstruction.join('');
376 > const closingSlashIndex = regexLexeme.lastIndexOf('/');
377 > const flags = closingSlashIndex === regexLexeme.length - 1 ? undefined : this._removeFlagsGY(regexLexeme.substring(closingSlashIndex + 1));
378 > let regexp: RegExp | null;
379 > try {
380 > regexp = new RegExp(regexLexeme.substring(1, closingSlashIndex), flags);
381 > } catch (e) {
382 throw this._errExpectedButGot(`REGEX`, expr);
383 }
384 > return ContextKeyExpr.regex(key, regexp); contextkey.ts ×8
385 > }
387 > case TokenType.QuotedStr: {
388 const serializedValue = expr.lexeme;
389 this._advance();
390 // replicate old regex parsing behavior
391
392 let regex: RegExp | null = null;
393
394 if (!isFalsyOrWhitespace(serializedValue)) {
395 const start = serializedValue.indexOf('/');
396 const end = serializedValue.lastIndexOf('/');
397 if (start !== end && start >= 0) {
398
399 const value = serializedValue.slice(start + 1, end);
400 const caseIgnoreFlag = serializedValue[end + 1] === 'i' ? 'i' : '';
401 try {
402 regex = new RegExp(value, caseIgnoreFlag);
403 } catch (_e) {
404 throw this._errExpectedButGot(`REGEX`, expr);
405 }
406 }
407 }
408
409 if (regex === null) {
410 throw this._errExpectedButGot('REGEX', expr);
411 }
412
413 return ContextKeyRegexExpr.create(key, regex);
414 }
416 > default:
417 throw this._errExpectedButGot('REGEX', this._peek());
419 > }
421 > // [ 'not' 'in' value ]
422 > if (this._matchOne(TokenType.Not)) {
423 > this._consume(TokenType.In, errorNoInAfterNot); contextkey.ts ×4
424 > const right = this._value();
425 > return ContextKeyExpr.notIn(key, right);
426 > }
428 > // [ ('==' | '!=' | '<' | '<=' | '>' | '>=' | 'in') value ]
429 > const maybeOp = this._peek().type;
430 > switch (maybeOp) {
431 > case TokenType.Eq: {
432 > this._advance(); contextkey.ts ×4
433 >
434 > const right = this._value();
435 > if (this._previous().type === TokenType.QuotedStr) { // to preserve old parser behavior: "foo == 'true'" is preserved as "foo == 'true'", but "foo == true" is optimized as "foo"
436 > return ContextKeyExpr.equals(key, right); contextkey.ts ×1
437 > }
438 > switch (right) { contextkey.ts ×2
439 > case 'true':
440 > return ContextKeyExpr.has(key); contextkey.ts ×19
441 > case 'false': contextkey.ts ×4
442 > return ContextKeyExpr.not(key); contextkey.ts ×19
443 > default: contextkey.ts ×4
444 > return ContextKeyExpr.equals(key, right); contextkey.ts ×2
446 > }
448 > case TokenType.NotEq: {
449 > this._advance(); contextkey.ts ×4
450 >
451 > const right = this._value();
452 > if (this._previous().type === TokenType.QuotedStr) { // same as above with "foo != 'true'"
453 > return ContextKeyExpr.notEquals(key, right); contextkey.ts ×7
454 > }
455 > switch (right) { contextkey.ts ×4
456 > case 'true':
457 > return ContextKeyExpr.not(key); contextkey.ts ×19
458 > case 'false': contextkey.ts ×4
459 > return ContextKeyExpr.has(key); contextkey.ts ×19
460 > default: contextkey.ts ×4
461 > return ContextKeyExpr.notEquals(key, right);
462 > }
463 > }
464 > // TODO: ContextKeyExpr.smaller(key, right) accepts only `number` as `right` AND during eval of this node, we just eval to `false` if `right` is not a number contextkey.ts ×9
465 > // consequently, package.json linter should _warn_ the user if they're passing undesired things to ops
466 > case TokenType.Lt:
467 > this._advance(); contextkey.ts ×2
468 > return ContextKeySmallerExpr.create(key, this._value());
470 > case TokenType.LtEq:
471 > this._advance(); contextkey.ts ×2
472 > return ContextKeySmallerEqualsExpr.create(key, this._value());
474 > case TokenType.Gt:
475 > this._advance(); contextkey.ts ×2
476 > return ContextKeyGreaterExpr.create(key, this._value());
478 > case TokenType.GtEq:
479 > this._advance(); contextkey.ts ×2
480 > return ContextKeyGreaterEqualsExpr.create(key, this._value());
482 > case TokenType.In:
483 > this._advance(); contextkey.ts ×2
484 > return ContextKeyExpr.in(key, this._value());
486 > default:
487 > return ContextKeyExpr.has(key); contextkey.ts ×1
489 > }
491 > case TokenType.EOF:
492 this._parsingErrors.push({ message: errorUnexpectedEOF, offset: peek.offset, lexeme: '', additionalInfo: hintUnexpectedEOF });
493 throw Parser._parseError;
495 > default:
496 > throw this._errExpectedButGot(`true | false | KEY \n\t| KEY '=~' REGEX \n\t| KEY ('==' | '!=' | '<' | '<=' | '>' | '>=' | 'in' | 'not' 'in') value`, this._peek()); contextkey.ts ×1
498 > }
499 > }
501 > private _value(): string {
502 > const token = this._peek(); contextkey.ts ×5
503 > switch (token.type) {
504 > case TokenType.Str:
505 > case TokenType.QuotedStr:
506 > this._advance();
507 > return token.lexeme;
508 > case TokenType.True:
509 > this._advance(); contextkey.ts ×19
510 > return 'true';
511 > case TokenType.False: contextkey.ts ×5
512 > this._advance(); contextkey.ts ×19
513 > return 'false';
514 > case TokenType.In: // we support `in` as a value, e.g., "when": "languageId == in" - exists in existing extensions contextkey.ts ×5
515 this._advance();
516 return 'in';
517 > default: contextkey.ts ×5
518 // this allows "when": "foo == " which's used by existing extensions
519 // we do not call `_advance` on purpose - we don't want to eat unintended tokens
520 return '';
522 > }
524 > private _flagsGYRe = /g|y/g;
525 > private _removeFlagsGY(flags: string): string {
526 > return flags.replaceAll(this._flagsGYRe, ''); contextkey.ts ×1
527 > }
529 > // careful: this can throw if current token is the initial one (ie index = 0)
530 > private _previous() {
531 > return this._tokens[this._current - 1]; contextkey.ts ×3
532 > }
534 > private _matchOne(token: TokenType) {
535 > if (this._check(token)) { contextkey.ts ×15
536 > this._advance(); contextkey.ts ×1
537 > return true;
538 > }
540 > return false;
543 > private _advance() {
544 > if (!this._isAtEnd()) { contextkey.ts ×3
545 > this._current++;
546 > }
547 > return this._previous();
548 > }
550 > private _consume(type: TokenType, message: string) {
551 > if (this._check(type)) { contextkey.ts ×2
552 > return this._advance();
553 > }
554
555 throw this._errExpectedButGot(message, this._peek());
558 > private _errExpectedButGot(expected: string, got: Token, additionalInfo?: string) {
559 > const message = localize('contextkey.parser.error.expectedButGot', "Expected: {0}\nReceived: '{1}'.", expected, Scanner.getLexeme(got)); contextkey.ts ×1
560 > const offset = got.offset;
561 > const lexeme = Scanner.getLexeme(got);
562 > this._parsingErrors.push({ message, offset, lexeme, additionalInfo });
563 > return Parser._parseError;
564 > }
566 > private _check(type: TokenType) {
567 > return this._peek().type === type; contextkey.ts ×15
568 > }
570 > private _peek() {
571 > return this._tokens[this._current]; contextkey.ts ×15
572 > }
574 > private _isAtEnd() {
575 > return this._peek().type === TokenType.EOF; contextkey.ts ×3
576 > }
578 >
579 > export abstract class ContextKeyExpr {
580 >
581 > public static false(): ContextKeyExpression {
582 > return ContextKeyFalseExpr.INSTANCE; contextkey.ts ×3
583 > }
584 > public static true(): ContextKeyExpression { contextkey.ts ×201
585 > return ContextKeyTrueExpr.INSTANCE; contextkey.ts ×1
586 > }
587 > public static has(key: string): ContextKeyExpression { contextkey.ts ×201
588 > return ContextKeyDefinedExpr.create(key); contextkey.ts ×1
589 > }
590 > public static equals(key: string, value: any): ContextKeyExpression { contextkey.ts ×201
591 > return ContextKeyEqualsExpr.create(key, value); contextkey.ts ×1
592 > }
593 > public static notEquals(key: string, value: any): ContextKeyExpression { contextkey.ts ×201
594 > return ContextKeyNotEqualsExpr.create(key, value); contextkey.ts ×1
595 > }
596 > public static regex(key: string, value: RegExp): ContextKeyExpression { contextkey.ts ×201
597 > return ContextKeyRegexExpr.create(key, value); contextkey.ts ×1
598 > }
599 > public static in(key: string, value: string): ContextKeyExpression { contextkey.ts ×201
600 > return ContextKeyInExpr.create(key, value); contextkey.ts ×2
601 > }
602 > public static notIn(key: string, value: string): ContextKeyExpression { contextkey.ts ×201
603 > return ContextKeyNotInExpr.create(key, value); contextkey.ts ×4
604 > }
605 > public static not(key: string): ContextKeyExpression { contextkey.ts ×201
606 > return ContextKeyNotExpr.create(key); contextkey.ts ×1
607 > }
608 > public static and(...expr: Array<ContextKeyExpression | undefined | null>): ContextKeyExpression | undefined { contextkey.ts ×201
609 > return ContextKeyAndExpr.create(expr, null, true); contextkey.ts ×9
610 > }
611 > public static or(...expr: Array<ContextKeyExpression | undefined | null>): ContextKeyExpression | undefined { contextkey.ts ×201
612 > return ContextKeyOrExpr.create(expr, null, true); contextkey.ts ×1
613 > }
614 > public static greater(key: string, value: number): ContextKeyExpression { contextkey.ts ×201
615 > return ContextKeyGreaterExpr.create(key, value); contextkey.ts ×12
616 > }
617 > public static greaterEquals(key: string, value: number): ContextKeyExpression { contextkey.ts ×201
618 > return ContextKeyGreaterEqualsExpr.create(key, value); contextkey.ts ×12
619 > }
620 > public static smaller(key: string, value: number): ContextKeyExpression { contextkey.ts ×201
621 > return ContextKeySmallerExpr.create(key, value); contextkey.ts ×12
622 > }
623 > public static smallerEquals(key: string, value: number): ContextKeyExpression { contextkey.ts ×201
624 > return ContextKeySmallerEqualsExpr.create(key, value); contextkey.ts ×12
625 > }
627 > private static _parser = new Parser({ regexParsingWithErrorRecovery: false });
628 > public static deserialize(serialized: string | null | undefined): ContextKeyExpression | undefined {
629 > if (serialized === undefined || serialized === null) { // an empty string needs to be handled by the parser to get a corresponding parsing error reported contextkey.ts ×2
630 > return undefined; contextkey.ts ×1
631 > }
633 > const expr = this._parser.parse(serialized);
634 > return expr;
637 > }
638 >
639 >
640 > export function validateWhenClauses(whenClauses: string[]): any {
641
642 const parser = new Parser({ regexParsingWithErrorRecovery: false }); // we run with no recovery to guide users to use correct regexes
643
644 return whenClauses.map(whenClause => {
645 parser.parse(whenClause);
646
647 if (parser.lexingErrors.length > 0) {
648 return parser.lexingErrors.map((se: LexingError) => ({
649 errorMessage: se.additionalInfo ?
650 localize('contextkey.scanner.errorForLinterWithHint', "Unexpected token. Hint: {0}", se.additionalInfo) :
651 localize('contextkey.scanner.errorForLinter', "Unexpected token."),
652 offset: se.offset,
653 length: se.lexeme.length,
654 }));
655 } else if (parser.parsingErrors.length > 0) {
656 return parser.parsingErrors.map((pe: ParsingError) => ({
657 errorMessage: pe.additionalInfo ? `${pe.message}. ${pe.additionalInfo}` : pe.message,
658 offset: pe.offset,
659 length: pe.lexeme.length,
660 }));
661 } else {
662 return [];
663 }
664 });
665 }
667 > export function expressionsAreEqualWithConstantSubstitution(a: ContextKeyExpression | null | undefined, b: ContextKeyExpression | null | undefined): boolean {
668 const aExpr = a ? a.substituteConstants() : undefined;
669 const bExpr = b ? b.substituteConstants() : undefined;
670 if (!aExpr && !bExpr) {
671 return true;
672 }
673 if (!aExpr || !bExpr) {
674 return false;
675 }
676 return aExpr.equals(bExpr);
677 }
679 > function cmp(a: ContextKeyExpression, b: ContextKeyExpression): number { contextkey.ts ×2
680 > return a.cmp(b);
681 > }
683 > export class ContextKeyFalseExpr implements IContextKeyExpression {
684 > public static INSTANCE = new ContextKeyFalseExpr();
685 >
686 > public readonly type = ContextKeyExprType.False;
687 >
688 > protected constructor() {
689 > }
690 >
691 > public cmp(other: ContextKeyExpression): number {
692 return this.type - other.type;
693 }
695 > public equals(other: ContextKeyExpression): boolean {
696 return (other.type === this.type);
697 }
699 > public substituteConstants(): ContextKeyExpression | undefined {
700 return this;
701 }
703 > public evaluate(context: IContext): boolean {
704 > return false; contextkey.ts ×19
705 > }
707 > public serialize(): string {
708 > return 'false'; contextkey.ts ×1
709 > }
711 > public keys(): string[] {
712 return [];
713 }
715 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
716 return this;
717 }
719 > public negate(): ContextKeyExpression {
720 > return ContextKeyTrueExpr.INSTANCE; contextkey.ts ×2
721 > }
723 >
724 > export class ContextKeyTrueExpr implements IContextKeyExpression {
725 > public static INSTANCE = new ContextKeyTrueExpr();
726 >
727 > public readonly type = ContextKeyExprType.True;
728 >
729 > protected constructor() {
730 > }
731 >
732 > public cmp(other: ContextKeyExpression): number {
733 return this.type - other.type;
734 }
736 > public equals(other: ContextKeyExpression): boolean {
737 return (other.type === this.type);
738 }
740 > public substituteConstants(): ContextKeyExpression | undefined {
741 return this;
742 }
744 > public evaluate(context: IContext): boolean {
745 > return true; contextkey.ts ×19
746 > }
748 > public serialize(): string {
749 > return 'true'; contextkey.ts ×1
750 > }
752 > public keys(): string[] {
753 return [];
754 }
756 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
757 return this;
758 }
760 > public negate(): ContextKeyExpression {
761 > return ContextKeyFalseExpr.INSTANCE; contextkey.ts ×2
762 > }
764 >
765 > export class ContextKeyDefinedExpr implements IContextKeyExpression {
766 > public static create(key: string, negated: ContextKeyExpression | null = null): ContextKeyExpression {
767 > const constantValue = CONSTANT_VALUES.get(key);
768 > if (typeof constantValue === 'boolean') {
769 > return constantValue ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE; contextkey.ts ×1
770 > }
771 > return new ContextKeyDefinedExpr(key, negated); contextkey.ts ×201
772 > }
773 >
774 > public readonly type = ContextKeyExprType.Defined;
775 >
776 > protected constructor(
777 > readonly key: string, contextkey.ts ×1
778 > private negated: ContextKeyExpression | null
779 > ) {
780 > }
782 > public cmp(other: ContextKeyExpression): number {
783 > if (other.type !== this.type) { contextkey.ts ×2
784 > return this.type - other.type; contextkey.ts ×1
785 > }
786 > return cmp1(this.key, other.key); contextkey.ts ×1
789 > public equals(other: ContextKeyExpression): boolean {
790 > if (other.type === this.type) { contextkey.ts ×2
791 > return (this.key === other.key); contextkey.ts ×1
792 > }
793 > return false; contextkey.ts ×1
796 > public substituteConstants(): ContextKeyExpression | undefined {
797 > const constantValue = CONSTANT_VALUES.get(this.key); contextkey.ts ×2
798 > if (typeof constantValue === 'boolean') {
799 return constantValue ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE;
800 }
801 > return this; contextkey.ts ×2
802 > }
804 > public evaluate(context: IContext): boolean {
805 > return (!!context.getValue(this.key)); contextkey.ts ×1
806 > }
808 > public serialize(): string {
809 > return this.key; contextkey.ts ×1
810 > }
812 > public keys(): string[] {
813 > return [this.key]; contextkey.ts ×1
814 > }
816 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
817 return mapFnc.mapDefined(this.key);
818 }
820 > public negate(): ContextKeyExpression {
821 > if (!this.negated) { contextkey.ts ×2
822 > this.negated = ContextKeyNotExpr.create(this.key, this);
823 > }
824 > return this.negated;
825 > }
827 >
828 > export class ContextKeyEqualsExpr implements IContextKeyExpression {
829 >
830 > public static create(key: string, value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
831 > if (typeof value === 'boolean') {
832 > return (value ? ContextKeyDefinedExpr.create(key, negated) : ContextKeyNotExpr.create(key, negated)); contextkey.ts ×1
833 > }
834 > const constantValue = CONSTANT_VALUES.get(key); contextkey.ts ×2
835 > if (typeof constantValue === 'boolean') {
836 > const trueValue = constantValue ? 'true' : 'false'; contextkey.ts ×1
837 > return (value === trueValue ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE);
838 > }
839 > return new ContextKeyEqualsExpr(key, value, negated); contextkey.ts ×2
841 >
842 > public readonly type = ContextKeyExprType.Equals;
843 >
844 > private constructor(
845 > private readonly key: string, contextkey.ts ×1
846 > private readonly value: any,
847 > private negated: ContextKeyExpression | null
848 > ) {
849 > }
851 > public cmp(other: ContextKeyExpression): number {
852 > if (other.type !== this.type) { contextkey.ts ×2
853 > return this.type - other.type; contextkey.ts ×1
854 > }
855 > return cmp2(this.key, this.value, other.key, other.value); contextkey.ts ×10
858 > public equals(other: ContextKeyExpression): boolean {
859 > if (other.type === this.type) { contextkey.ts ×2
860 > return (this.key === other.key && this.value === other.value); contextkey.ts ×1
861 > }
862 > return false; contextkey.ts ×3
865 > public substituteConstants(): ContextKeyExpression | undefined {
866 > const constantValue = CONSTANT_VALUES.get(this.key); contextkey.ts ×2
867 > if (typeof constantValue === 'boolean') {
868 const trueValue = constantValue ? 'true' : 'false';
869 return (this.value === trueValue ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE);
870 }
871 > return this; contextkey.ts ×2
872 > }
874 > public evaluate(context: IContext): boolean {
875 > // Intentional == contextkey.ts ×1
876 > // eslint-disable-next-line eqeqeq
877 > return (context.getValue(this.key) == this.value);
878 > }
880 > public serialize(): string {
881 > return `${this.key} == '${this.value}'`; contextkey.ts ×1
882 > }
884 > public keys(): string[] {
885 > return [this.key]; promptsServiceImpl.ts ×6
886 > }
888 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
889 return mapFnc.mapEquals(this.key, this.value);
890 }
892 > public negate(): ContextKeyExpression {
893 > if (!this.negated) { contextkey.ts ×10
894 > this.negated = ContextKeyNotEqualsExpr.create(this.key, this.value, this);
895 > }
896 > return this.negated;
897 > }
899 >
900 > export class ContextKeyInExpr implements IContextKeyExpression {
901 >
902 > public static create(key: string, valueKey: string): ContextKeyInExpr {
903 > return new ContextKeyInExpr(key, valueKey);
904 > }
905 >
906 > public readonly type = ContextKeyExprType.In;
907 > private negated: ContextKeyExpression | null = null;
908 >
909 > private constructor(
910 > private readonly key: string, contextkey.ts ×1
911 > private readonly valueKey: string,
912 > ) {
913 > }
915 > public cmp(other: ContextKeyExpression): number {
916 > if (other.type !== this.type) { contextkey.ts ×7
917 > return this.type - other.type;
918 > }
919 > return cmp2(this.key, this.valueKey, other.key, other.valueKey);
920 > }
922 > public equals(other: ContextKeyExpression): boolean {
923 if (other.type === this.type) {
924 return (this.key === other.key && this.valueKey === other.valueKey);
925 }
926 return false;
927 }
929 > public substituteConstants(): ContextKeyExpression | undefined {
930 > return this; contextkey.ts ×7
931 > }
933 > public evaluate(context: IContext): boolean {
934 > const source = context.getValue(this.valueKey); contextkey.ts ×3
935 >
936 > const item = context.getValue(this.key);
937 >
938 > if (Array.isArray(source)) {
939 > // eslint-disable-next-line local/code-no-any-casts
940 > if (source.includes(item as any)) {
941 > return true;
942 > }
943 > // On Windows, file paths are case-insensitive so file URI
944 > // comparisons must be done in a case-insensitive manner.
945 > if (isWindows && typeof item === 'string' && item.startsWith('file:///')) {
946 const itemLower = item.toLowerCase();
947 return source.some(s => typeof s === 'string' && s.toLowerCase() === itemLower);
948 }
949 > return false; contextkey.ts ×3
950 > }
951 >
952 > if (typeof item === 'string' && typeof source === 'object' && source !== null) {
953 > if (hasOwnProperty.call(source, item)) {
954 > return true;
955 > }
956 > // On Windows, file paths are case-insensitive so file URI
957 > // property lookups must be done in a case-insensitive manner.
958 > if (isWindows && item.startsWith('file:///')) {
959 const itemLower = item.toLowerCase();
960 return Object.keys(source).some(key => key.toLowerCase() === itemLower);
961 }
962 > return false; contextkey.ts ×3
963 > }
964 > return false;
965 > }
967 > public serialize(): string {
968 return `${this.key} in '${this.valueKey}'`;
969 }
971 > public keys(): string[] {
972 return [this.key, this.valueKey];
973 }
975 > public map(mapFnc: IContextKeyExprMapper): ContextKeyInExpr {
976 return mapFnc.mapIn(this.key, this.valueKey);
977 }
979 > public negate(): ContextKeyExpression {
980 if (!this.negated) {
981 this.negated = ContextKeyNotInExpr.create(this.key, this.valueKey);
982 }
983 return this.negated;
984 }
986 >
987 > export class ContextKeyNotInExpr implements IContextKeyExpression {
988 >
989 > public static create(key: string, valueKey: string): ContextKeyNotInExpr {
990 > return new ContextKeyNotInExpr(key, valueKey);
991 > }
992 >
993 > public readonly type = ContextKeyExprType.NotIn;
994 >
995 > private readonly _negated: ContextKeyInExpr;
996 >
997 > private constructor(
998 > private readonly key: string, contextkey.ts ×4
999 > private readonly valueKey: string,
1000 > ) {
1001 > this._negated = ContextKeyInExpr.create(key, valueKey);
1002 > }
1004 > public cmp(other: ContextKeyExpression): number {
1005 if (other.type !== this.type) {
1006 return this.type - other.type;
1007 }
1008 return this._negated.cmp(other._negated);
1009 }
1011 > public equals(other: ContextKeyExpression): boolean {
1012 if (other.type === this.type) {
1013 return this._negated.equals(other._negated);
1014 }
1015 return false;
1016 }
1018 > public substituteConstants(): ContextKeyExpression | undefined {
1019 return this;
1020 }
1022 > public evaluate(context: IContext): boolean {
1023 > return !this._negated.evaluate(context); contextkey.ts ×4
1024 > }
1026 > public serialize(): string {
1027 return `${this.key} not in '${this.valueKey}'`;
1028 }
1030 > public keys(): string[] {
1031 return this._negated.keys();
1032 }
1034 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1035 return mapFnc.mapNotIn(this.key, this.valueKey);
1036 }
1038 > public negate(): ContextKeyExpression {
1039 return this._negated;
1040 }
1042 >
1043 > export class ContextKeyNotEqualsExpr implements IContextKeyExpression {
1044 >
1045 > public static create(key: string, value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1046 > if (typeof value === 'boolean') {
1047 > if (value) { contextkey.ts ×2
1048 > return ContextKeyNotExpr.create(key, negated); contextkey.ts ×1
1049 > }
1050 > return ContextKeyDefinedExpr.create(key, negated); contextkey.ts ×2
1051 > }
1052 > const constantValue = CONSTANT_VALUES.get(key); contextkey.ts ×2
1053 > if (typeof constantValue === 'boolean') {
1054 > const falseValue = constantValue ? 'true' : 'false'; contextkey.ts ×1
1055 > return (value === falseValue ? ContextKeyFalseExpr.INSTANCE : ContextKeyTrueExpr.INSTANCE);
1056 > }
1057 > return new ContextKeyNotEqualsExpr(key, value, negated); contextkey.ts ×2
1059 >
1060 > public readonly type = ContextKeyExprType.NotEquals;
1061 >
1062 > private constructor(
1063 > private readonly key: string, contextkey.ts ×1
1064 > private readonly value: any,
1065 > private negated: ContextKeyExpression | null
1066 > ) {
1067 > }
1069 > public cmp(other: ContextKeyExpression): number {
1070 > if (other.type !== this.type) { contextkey.ts ×3
1071 > return this.type - other.type;
1072 > }
1073 > return cmp2(this.key, this.value, other.key, other.value);
1074 > }
1076 > public equals(other: ContextKeyExpression): boolean {
1077 > if (other.type === this.type) { contextkey.ts ×4
1078 > return (this.key === other.key && this.value === other.value); contextkey.ts ×5
1079 > }
1080 > return false; contextkey.ts ×4
1081 > }
1083 > public substituteConstants(): ContextKeyExpression | undefined {
1084 > const constantValue = CONSTANT_VALUES.get(this.key); contextkey.ts ×7
1085 > if (typeof constantValue === 'boolean') {
1086 const falseValue = constantValue ? 'true' : 'false';
1087 return (this.value === falseValue ? ContextKeyFalseExpr.INSTANCE : ContextKeyTrueExpr.INSTANCE);
1088 }
1089 > return this; contextkey.ts ×7
1090 > }
1092 > public evaluate(context: IContext): boolean {
1093 > // Intentional != contextkey.ts ×19
1094 > // eslint-disable-next-line eqeqeq
1095 > return (context.getValue(this.key) != this.value);
1096 > }
1098 > public serialize(): string {
1099 return `${this.key} != '${this.value}'`;
1100 }
1102 > public keys(): string[] {
1103 return [this.key];
1104 }
1106 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1107 return mapFnc.mapNotEquals(this.key, this.value);
1108 }
1110 > public negate(): ContextKeyExpression {
1111 > if (!this.negated) { contextkey.ts ×3
1112 > this.negated = ContextKeyEqualsExpr.create(this.key, this.value, this);
1113 > }
1114 > return this.negated;
1115 > }
1117 >
1118 > export class ContextKeyNotExpr implements IContextKeyExpression {
1119 >
1120 > public static create(key: string, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1121 > const constantValue = CONSTANT_VALUES.get(key);
1122 > if (typeof constantValue === 'boolean') {
1123 > return (constantValue ? ContextKeyFalseExpr.INSTANCE : ContextKeyTrueExpr.INSTANCE); contextkey.ts ×1
1124 > }
1125 > return new ContextKeyNotExpr(key, negated); contextkey.ts ×201
1126 > }
1127 >
1128 > public readonly type = ContextKeyExprType.Not;
1129 >
1130 > private constructor(
1131 > private readonly key: string, contextkey.ts ×1
1132 > private negated: ContextKeyExpression | null
1133 > ) {
1134 > }
1136 > public cmp(other: ContextKeyExpression): number {
1137 > if (other.type !== this.type) { contextkey.ts ×2
1138 > return this.type - other.type; contextkey.ts ×1
1139 > }
1140 > return cmp1(this.key, other.key); contextkey.ts ×1
1143 > public equals(other: ContextKeyExpression): boolean {
1144 > if (other.type === this.type) { contextkey.ts ×2
1145 > return (this.key === other.key); contextkey.ts ×1
1146 > }
1147 > return false; contextkey.ts ×1
1150 > public substituteConstants(): ContextKeyExpression | undefined {
1151 > const constantValue = CONSTANT_VALUES.get(this.key); contextkey.ts ×2
1152 > if (typeof constantValue === 'boolean') {
1153 return (constantValue ? ContextKeyFalseExpr.INSTANCE : ContextKeyTrueExpr.INSTANCE);
1154 }
1155 > return this; contextkey.ts ×2
1156 > }
1158 > public evaluate(context: IContext): boolean {
1159 > return (!context.getValue(this.key)); contextkey.ts ×19
1160 > }
1162 > public serialize(): string {
1163 > return `!${this.key}`; contextkey.ts ×1
1164 > }
1166 > public keys(): string[] {
1167 return [this.key];
1168 }
1170 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1171 return mapFnc.mapNot(this.key);
1172 }
1174 > public negate(): ContextKeyExpression {
1175 > if (!this.negated) { contextkey.ts ×2
1176 > this.negated = ContextKeyDefinedExpr.create(this.key, this); contextkey.ts ×1
1177 > }
1178 > return this.negated; contextkey.ts ×2
1179 > }
1181 >
1182 > function withFloatOrStr<T extends ContextKeyExpression>(value: any, callback: (value: number | string) => T): T | ContextKeyFalseExpr { contextkey.ts ×1
1183 > if (typeof value === 'string') {
1184 > const n = parseFloat(value);
1185 > if (!isNaN(n)) {
1186 > value = n;
1187 > }
1188 > }
1189 > if (typeof value === 'string' || typeof value === 'number') {
1190 > return callback(value);
1191 > }
1192 return ContextKeyFalseExpr.INSTANCE;
1193 }
1195 > export class ContextKeyGreaterExpr implements IContextKeyExpression {
1196 >
1197 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1198 > return withFloatOrStr(_value, (value) => new ContextKeyGreaterExpr(key, value, negated));
1199 > }
1200 >
1201 > public readonly type = ContextKeyExprType.Greater;
1202 >
1203 > private constructor(
1204 > private readonly key: string, contextkey.ts ×2
1205 > private readonly value: number | string,
1206 > private negated: ContextKeyExpression | null
1207 > ) { }
1209 > public cmp(other: ContextKeyExpression): number {
1210 if (other.type !== this.type) {
1211 return this.type - other.type;
1212 }
1213 return cmp2(this.key, this.value, other.key, other.value);
1214 }
1216 > public equals(other: ContextKeyExpression): boolean {
1217 > if (other.type === this.type) { contextkey.ts ×12
1218 > return (this.key === other.key && this.value === other.value);
1219 > }
1220 return false;
1223 > public substituteConstants(): ContextKeyExpression | undefined {
1224 return this;
1225 }
1227 > public evaluate(context: IContext): boolean {
1228 > if (typeof this.value === 'string') { contextkey.ts ×7
1229 > return false;
1230 > }
1231 > return (parseFloat(context.getValue<any>(this.key)) > this.value);
1232 > }
1234 > public serialize(): string {
1235 > return `${this.key} > ${this.value}`; contextkey.ts ×5
1236 > }
1238 > public keys(): string[] {
1239 return [this.key];
1240 }
1242 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1243 return mapFnc.mapGreater(this.key, this.value);
1244 }
1246 > public negate(): ContextKeyExpression {
1247 > if (!this.negated) { contextkey.ts ×5
1248 > this.negated = ContextKeySmallerEqualsExpr.create(this.key, this.value, this);
1249 > }
1250 > return this.negated;
1251 > }
1253 >
1254 > export class ContextKeyGreaterEqualsExpr implements IContextKeyExpression {
1255 >
1256 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1257 > return withFloatOrStr(_value, (value) => new ContextKeyGreaterEqualsExpr(key, value, negated));
1258 > }
1259 >
1260 > public readonly type = ContextKeyExprType.GreaterEquals;
1261 >
1262 > private constructor(
1263 > private readonly key: string, contextkey.ts ×2
1264 > private readonly value: number | string,
1265 > private negated: ContextKeyExpression | null
1266 > ) { }
1268 > public cmp(other: ContextKeyExpression): number {
1269 if (other.type !== this.type) {
1270 return this.type - other.type;
1271 }
1272 return cmp2(this.key, this.value, other.key, other.value);
1273 }
1275 > public equals(other: ContextKeyExpression): boolean {
1276 > if (other.type === this.type) { contextkey.ts ×12
1277 > return (this.key === other.key && this.value === other.value);
1278 > }
1279 return false;
1282 > public substituteConstants(): ContextKeyExpression | undefined {
1283 return this;
1284 }
1286 > public evaluate(context: IContext): boolean {
1287 > if (typeof this.value === 'string') { contextkey.ts ×7
1288 return false;
1289 }
1290 > return (parseFloat(context.getValue<any>(this.key)) >= this.value); contextkey.ts ×7
1291 > }
1293 > public serialize(): string {
1294 > return `${this.key} >= ${this.value}`; contextkey.ts ×1
1295 > }
1297 > public keys(): string[] {
1298 return [this.key];
1299 }
1301 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1302 return mapFnc.mapGreaterEquals(this.key, this.value);
1303 }
1305 > public negate(): ContextKeyExpression {
1306 > if (!this.negated) { contextkey.ts ×5
1307 > this.negated = ContextKeySmallerExpr.create(this.key, this.value, this);
1308 > }
1309 > return this.negated;
1310 > }
1312 >
1313 > export class ContextKeySmallerExpr implements IContextKeyExpression {
1314 >
1315 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1316 > return withFloatOrStr(_value, (value) => new ContextKeySmallerExpr(key, value, negated));
1317 > }
1318 >
1319 > public readonly type = ContextKeyExprType.Smaller;
1320 >
1321 > private constructor(
1322 > private readonly key: string, contextkey.ts ×2
1323 > private readonly value: number | string,
1324 > private negated: ContextKeyExpression | null
1325 > ) {
1326 > }
1328 > public cmp(other: ContextKeyExpression): number {
1329 if (other.type !== this.type) {
1330 return this.type - other.type;
1331 }
1332 return cmp2(this.key, this.value, other.key, other.value);
1333 }
1335 > public equals(other: ContextKeyExpression): boolean {
1336 > if (other.type === this.type) { contextkey.ts ×12
1337 > return (this.key === other.key && this.value === other.value);
1338 > }
1339 return false;
1342 > public substituteConstants(): ContextKeyExpression | undefined {
1343 return this;
1344 }
1346 > public evaluate(context: IContext): boolean {
1347 > if (typeof this.value === 'string') { contextkey.ts ×7
1348 return false;
1349 }
1350 > return (parseFloat(context.getValue<any>(this.key)) < this.value); contextkey.ts ×7
1351 > }
1353 > public serialize(): string {
1354 > return `${this.key} < ${this.value}`; contextkey.ts ×1
1355 > }
1357 > public keys(): string[] {
1358 return [this.key];
1359 }
1361 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1362 return mapFnc.mapSmaller(this.key, this.value);
1363 }
1365 > public negate(): ContextKeyExpression {
1366 > if (!this.negated) { contextkey.ts ×5
1367 > this.negated = ContextKeyGreaterEqualsExpr.create(this.key, this.value, this);
1368 > }
1369 > return this.negated;
1370 > }
1372 >
1373 > export class ContextKeySmallerEqualsExpr implements IContextKeyExpression {
1374 >
1375 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1376 > return withFloatOrStr(_value, (value) => new ContextKeySmallerEqualsExpr(key, value, negated));
1377 > }
1378 >
1379 > public readonly type = ContextKeyExprType.SmallerEquals;
1380 >
1381 > private constructor(
1382 > private readonly key: string, contextkey.ts ×2
1383 > private readonly value: number | string,
1384 > private negated: ContextKeyExpression | null
1385 > ) {
1386 > }
1388 > public cmp(other: ContextKeyExpression): number {
1389 if (other.type !== this.type) {
1390 return this.type - other.type;
1391 }
1392 return cmp2(this.key, this.value, other.key, other.value);
1393 }
1395 > public equals(other: ContextKeyExpression): boolean {
1396 > if (other.type === this.type) { contextkey.ts ×12
1397 > return (this.key === other.key && this.value === other.value);
1398 > }
1399 return false;
1402 > public substituteConstants(): ContextKeyExpression | undefined {
1403 return this;
1404 }
1406 > public evaluate(context: IContext): boolean {
1407 > if (typeof this.value === 'string') { contextkey.ts ×7
1408 return false;
1409 }
1410 > return (parseFloat(context.getValue<any>(this.key)) <= this.value); contextkey.ts ×7
1411 > }
1413 > public serialize(): string {
1414 > return `${this.key} <= ${this.value}`; contextkey.ts ×1
1415 > }
1417 > public keys(): string[] {
1418 return [this.key];
1419 }
1421 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1422 return mapFnc.mapSmallerEquals(this.key, this.value);
1423 }
1425 > public negate(): ContextKeyExpression {
1426 > if (!this.negated) { contextkey.ts ×5
1427 > this.negated = ContextKeyGreaterExpr.create(this.key, this.value, this);
1428 > }
1429 > return this.negated;
1430 > }
1432 >
1433 > export class ContextKeyRegexExpr implements IContextKeyExpression {
1434 >
1435 > public static create(key: string, regexp: RegExp | null): ContextKeyRegexExpr {
1436 > return new ContextKeyRegexExpr(key, regexp);
1437 > }
1438 >
1439 > public readonly type = ContextKeyExprType.Regex;
1440 > private negated: ContextKeyExpression | null = null;
1441 >
1442 > private constructor(
1443 > private readonly key: string, contextkey.ts ×1
1444 > private readonly regexp: RegExp | null
1445 > ) {
1446 > //
1447 > }
1449 > public cmp(other: ContextKeyExpression): number {
1450 > if (other.type !== this.type) { contextkey.ts ×3
1451 > return this.type - other.type; contextkey.ts ×1
1452 > }
1453 > if (this.key < other.key) { contextkey.ts ×10
1454 > return -1; contextkey.ts ×5
1455 > }
1456 > if (this.key > other.key) { contextkey.ts ×10
1457 > return 1; contextkey.ts ×5
1458 > }
1459 > const thisSource = this.regexp ? this.regexp.source : ''; contextkey.ts ×3
1460 > const otherSource = other.regexp ? other.regexp.source : '';
1461 > if (thisSource < otherSource) {
1462 return -1;
1463 }
1464 > if (thisSource > otherSource) { languageModels.ts ×93
1465 > return 1;
1466 > }
1467 return 0;
1470 > public equals(other: ContextKeyExpression): boolean {
1471 > if (other.type === this.type) { contextkey.ts ×10
1472 > const thisSource = this.regexp ? this.regexp.source : '';
1473 > const otherSource = other.regexp ? other.regexp.source : '';
1474 > return (this.key === other.key && thisSource === otherSource);
1475 > }
1476 return false;
1479 > public substituteConstants(): ContextKeyExpression | undefined {
1480 return this;
1481 }
1483 > public evaluate(context: IContext): boolean {
1484 > const value = context.getValue<any>(this.key); contextkey.ts ×19
1485 > return this.regexp ? this.regexp.test(value) : false;
1486 > }
1488 > public serialize(): string {
1489 > const value = this.regexp contextkey.ts ×8
1490 > ? `/${this.regexp.source}/${this.regexp.flags}`
1491 : '/invalid/';
1492 > return `${this.key} =~ ${value}`; contextkey.ts ×8
1493 > }
1495 > public keys(): string[] {
1496 return [this.key];
1497 }
1499 > public map(mapFnc: IContextKeyExprMapper): ContextKeyRegexExpr {
1500 return mapFnc.mapRegex(this.key, this.regexp);
1501 }
1503 > public negate(): ContextKeyExpression {
1504 > if (!this.negated) { contextkey.ts ×10
1505 > this.negated = ContextKeyNotRegexExpr.create(this);
1506 > }
1507 > return this.negated;
1508 > }
1510 >
1511 > export class ContextKeyNotRegexExpr implements IContextKeyExpression {
1512 >
1513 > public static create(actual: ContextKeyRegexExpr): ContextKeyExpression {
1514 > return new ContextKeyNotRegexExpr(actual);
1515 > }
1516 >
1517 > public readonly type = ContextKeyExprType.NotRegex;
1518 >
1519 > private constructor(private readonly _actual: ContextKeyRegexExpr) {
1520 > // contextkey.ts ×10
1521 > }
1523 > public cmp(other: ContextKeyExpression): number {
1524 if (other.type !== this.type) {
1525 return this.type - other.type;
1526 }
1527 return this._actual.cmp(other._actual);
1528 }
1530 > public equals(other: ContextKeyExpression): boolean {
1531 > if (other.type === this.type) { contextkey.ts ×10
1532 return this._actual.equals(other._actual);
1533 }
1534 > return false; contextkey.ts ×10
1535 > }
1537 > public substituteConstants(): ContextKeyExpression | undefined {
1538 return this;
1539 }
1541 > public evaluate(context: IContext): boolean {
1542 return !this._actual.evaluate(context);
1543 }
1545 > public serialize(): string {
1546 return `!(${this._actual.serialize()})`;
1547 }
1549 > public keys(): string[] {
1550 return this._actual.keys();
1551 }
1553 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1554 return new ContextKeyNotRegexExpr(this._actual.map(mapFnc));
1555 }
1557 > public negate(): ContextKeyExpression {
1558 return this._actual;
1559 }
1561 >
1562 > /**
1563 > * @returns the same instance if nothing changed.
1564 > */
1565 > function eliminateConstantsInArray(arr: ContextKeyExpression[]): (ContextKeyExpression | undefined)[] { contextkey.ts ×5
1566 > // Allocate array only if there is a difference
1567 > let newArr: (ContextKeyExpression | undefined)[] | null = null;
1568 > for (let i = 0, len = arr.length; i < len; i++) {
1569 > const newExpr = arr[i].substituteConstants();
1570 >
1571 > if (arr[i] !== newExpr) {
1572 // something has changed!
1573
1574 // allocate array on first difference
1575 if (newArr === null) {
1576 newArr = [];
1577 for (let j = 0; j < i; j++) {
1578 newArr[j] = arr[j];
1579 }
1580 }
1581 }
1583 > if (newArr !== null) {
1584 newArr[i] = newExpr;
1585 }
1587 >
1588 > if (newArr === null) {
1589 > return arr;
1590 > }
1591 return newArr;
1592 }
1594 > export class ContextKeyAndExpr implements IContextKeyExpression {
1595 >
1596 > public static create(_expr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1597 > return ContextKeyAndExpr._normalizeArr(_expr, negated, extraRedundantCheck);
1598 > }
1599 >
1600 > public readonly type = ContextKeyExprType.And;
1601 >
1602 > private constructor(
1603 > public readonly expr: ContextKeyExpression[], contextkey.ts ×3
1604 > private negated: ContextKeyExpression | null
1605 > ) {
1606 > }
1608 > public cmp(other: ContextKeyExpression): number {
1609 > if (other.type !== this.type) { contextkey.ts ×2
1610 > return this.type - other.type; contextkey.ts ×1
1611 > }
1612 > if (this.expr.length < other.expr.length) { contextkey.ts ×4
1613 return -1;
1614 }
1615 > if (this.expr.length > other.expr.length) { contextkey.ts ×4
1616 > return 1; contextkey.ts ×2
1617 > }
1618 > for (let i = 0, len = this.expr.length; i < len; i++) { contextkey.ts ×4
1619 > const r = cmp(this.expr[i], other.expr[i]);
1620 > if (r !== 0) {
1621 > return r; contextkey.ts ×4
1622 > }
1624 > return 0; contextkey.ts ×19
1627 > public equals(other: ContextKeyExpression): boolean {
1628 > if (other.type === this.type) { contextkey.ts ×4
1629 > if (this.expr.length !== other.expr.length) {
1630 > return false; contextkey.ts ×2
1631 > }
1632 > for (let i = 0, len = this.expr.length; i < len; i++) { contextkey.ts ×4
1633 > if (!this.expr[i].equals(other.expr[i])) {
1634 > return false; contextkey.ts ×4
1635 > }
1637 > return true; contextkey.ts ×1
1638 > }
1639 return false;
1642 > public substituteConstants(): ContextKeyExpression | undefined {
1643 > const exprArr = eliminateConstantsInArray(this.expr); contextkey.ts ×5
1644 > if (exprArr === this.expr) {
1645 > // no change
1646 > return this;
1647 > }
1648 return ContextKeyAndExpr.create(exprArr, this.negated, false);
1651 > public evaluate(context: IContext): boolean {
1652 > for (let i = 0, len = this.expr.length; i < len; i++) { contextkey.ts ×19
1653 > if (!this.expr[i].evaluate(context)) {
1654 > return false;
1655 > }
1656 > }
1657 > return true;
1658 > }
1660 > private static _normalizeArr(arr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1661 > const expr: ContextKeyExpression[] = []; contextkey.ts ×9
1662 > let hasTrue = false;
1663 >
1664 > for (const e of arr) {
1665 > if (!e) {
1666 continue;
1667 }
1669 > if (e.type === ContextKeyExprType.True) {
1670 > // anything && true ==> anything contextkey.ts ×5
1671 > hasTrue = true;
1672 > continue;
1673 > }
1675 > if (e.type === ContextKeyExprType.False) {
1676 > // anything && false ==> false contextkey.ts ×5
1677 > return ContextKeyFalseExpr.INSTANCE;
1678 > }
1680 > if (e.type === ContextKeyExprType.And) {
1681 > expr.push(...e.expr); contextkey.ts ×1
1682 > continue;
1683 > }
1685 > expr.push(e);
1686 > }
1687 >
1688 > if (expr.length === 0 && hasTrue) {
1689 return ContextKeyTrueExpr.INSTANCE;
1690 }
1692 > if (expr.length === 0) {
1693 return undefined;
1694 }
1696 > if (expr.length === 1) {
1697 > return expr[0]; contextkey.ts ×1
1698 > }
1700 > expr.sort(cmp);
1701 >
1702 > // eliminate duplicate terms
1703 > for (let i = 1; i < expr.length; i++) {
1704 > if (expr[i - 1].equals(expr[i])) {
1705 > expr.splice(i, 1); contextkey.ts ×1
1706 > i--;
1707 > }
1709 >
1710 > if (expr.length === 1) {
1711 > return expr[0]; contextkey.ts ×1
1712 > }
1714 > // We must distribute any OR expression because we don't support parens
1715 > // OR extensions will be at the end (due to sorting rules)
1716 > while (expr.length > 1) {
1717 > const lastElement = expr[expr.length - 1];
1718 > if (lastElement.type !== ContextKeyExprType.Or) {
1719 > break;
1720 > }
1721 > // pop the last element contextkey.ts ×2
1722 > expr.pop();
1723 >
1724 > // pop the second to last element
1725 > const secondToLastElement = expr.pop()!;
1726 >
1727 > const isFinished = (expr.length === 0);
1728 >
1729 > // distribute `lastElement` over `secondToLastElement`
1730 > const resultElement = ContextKeyOrExpr.create(
1731 > lastElement.expr.map(el => ContextKeyAndExpr.create([el, secondToLastElement], null, extraRedundantCheck)),
1732 > null,
1733 > isFinished
1734 > );
1735 >
1736 > if (resultElement) {
1737 > expr.push(resultElement);
1738 > expr.sort(cmp);
1739 > }
1741 >
1742 > if (expr.length === 1) {
1743 > return expr[0]; contextkey.ts ×2
1744 > }
1746 > // resolve false AND expressions
1747 > if (extraRedundantCheck) {
1748 > for (let i = 0; i < expr.length; i++) {
1749 > for (let j = i + 1; j < expr.length; j++) {
1750 > if (expr[i].negate().equals(expr[j])) {
1751 > // A && !A case contextkey.ts ×1
1752 > return ContextKeyFalseExpr.INSTANCE;
1753 > }
1756 >
1757 > if (expr.length === 1) {
1758 return expr[0];
1759 }
1762 > return new ContextKeyAndExpr(expr, negated);
1765 > public serialize(): string {
1766 > return this.expr.map(e => e.serialize()).join(' && '); contextkey.ts ×1
1767 > }
1769 > public keys(): string[] {
1770 const result: string[] = [];
1771 for (const expr of this.expr) {
1772 result.push(...expr.keys());
1773 }
1774 return result;
1775 }
1777 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1778 return new ContextKeyAndExpr(this.expr.map(expr => expr.map(mapFnc)), null);
1779 }
1781 > public negate(): ContextKeyExpression {
1782 > if (!this.negated) { contextkey.ts ×1
1783 > const result: ContextKeyExpression[] = [];
1784 > for (const expr of this.expr) {
1785 > result.push(expr.negate());
1786 > }
1787 > this.negated = ContextKeyOrExpr.create(result, this, true)!;
1788 > }
1789 > return this.negated;
1790 > }
1792 >
1793 > export class ContextKeyOrExpr implements IContextKeyExpression {
1794 >
1795 > public static create(_expr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1796 > return ContextKeyOrExpr._normalizeArr(_expr, negated, extraRedundantCheck);
1797 > }
1798 >
1799 > public readonly type = ContextKeyExprType.Or;
1800 >
1801 > private constructor(
1802 > public readonly expr: ContextKeyExpression[], contextkey.ts ×3
1803 > private negated: ContextKeyExpression | null
1804 > ) {
1805 > }
1807 > public cmp(other: ContextKeyExpression): number {
1808 > if (other.type !== this.type) { contextkey.ts ×2
1809 > return this.type - other.type;
1810 > }
1811 > if (this.expr.length < other.expr.length) { contextkey.ts ×4
1812 return -1;
1813 }
1814 > if (this.expr.length > other.expr.length) { contextkey.ts ×4
1815 return 1;
1816 }
1817 > for (let i = 0, len = this.expr.length; i < len; i++) { contextkey.ts ×4
1818 > const r = cmp(this.expr[i], other.expr[i]);
1819 > if (r !== 0) {
1820 > return r;
1821 > }
1822 > }
1823 return 0;
1826 > public equals(other: ContextKeyExpression): boolean {
1827 > if (other.type === this.type) { contextkey.ts ×4
1828 > if (this.expr.length !== other.expr.length) { contextkey.ts ×3
1829 return false;
1830 }
1831 > for (let i = 0, len = this.expr.length; i < len; i++) { contextkey.ts ×3
1832 > if (!this.expr[i].equals(other.expr[i])) {
1833 > return false; contextkey.ts ×4
1834 > }
1836 > return true; contextkey.ts ×1
1837 > }
1838 > return false; contextkey.ts ×4
1839 > }
1841 > public substituteConstants(): ContextKeyExpression | undefined {
1842 const exprArr = eliminateConstantsInArray(this.expr);
1843 if (exprArr === this.expr) {
1844 // no change
1845 return this;
1846 }
1847 return ContextKeyOrExpr.create(exprArr, this.negated, false);
1848 }
1850 > public evaluate(context: IContext): boolean {
1851 > for (let i = 0, len = this.expr.length; i < len; i++) { contextkey.ts ×19
1852 > if (this.expr[i].evaluate(context)) {
1853 > return true;
1854 > }
1855 > }
1856 return false;
1859 > private static _normalizeArr(arr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1860 > let expr: ContextKeyExpression[] = []; contextkey.ts ×8
1861 > let hasFalse = false;
1862 >
1863 > if (arr) {
1864 > for (let i = 0, len = arr.length; i < len; i++) {
1865 > const e = arr[i];
1866 > if (!e) {
1867 continue;
1868 }
1870 > if (e.type === ContextKeyExprType.False) {
1871 > // anything || false ==> anything contextkey.ts ×5
1872 > hasFalse = true;
1873 > continue;
1874 > }
1876 > if (e.type === ContextKeyExprType.True) {
1877 > // anything || true ==> true contextkey.ts ×1
1878 > return ContextKeyTrueExpr.INSTANCE;
1879 > }
1881 > if (e.type === ContextKeyExprType.Or) {
1882 > expr = expr.concat(e.expr); contextkey.ts ×1
1883 > continue;
1884 > }
1886 > expr.push(e);
1887 > }
1888 >
1889 > if (expr.length === 0 && hasFalse) {
1890 return ContextKeyFalseExpr.INSTANCE;
1891 }
1893 > expr.sort(cmp);
1894 > }
1895 >
1896 > if (expr.length === 0) {
1897 return undefined;
1898 }
1900 > if (expr.length === 1) {
1901 > return expr[0]; contextkey.ts ×1
1902 > }
1904 > // eliminate duplicate terms
1905 > for (let i = 1; i < expr.length; i++) {
1906 > if (expr[i - 1].equals(expr[i])) {
1907 > expr.splice(i, 1); contextkey.ts ×1
1908 > i--;
1909 > }
1911 >
1912 > if (expr.length === 1) {
1913 > return expr[0]; contextkey.ts ×19
1914 > }
1916 > // resolve true OR expressions
1917 > if (extraRedundantCheck) {
1918 > for (let i = 0; i < expr.length; i++) {
1919 > for (let j = i + 1; j < expr.length; j++) {
1920 > if (expr[i].negate().equals(expr[j])) {
1921 > // A || !A case contextkey.ts ×1
1922 > return ContextKeyTrueExpr.INSTANCE;
1923 > }
1926 >
1927 > if (expr.length === 1) {
1928 return expr[0];
1929 }
1932 > return new ContextKeyOrExpr(expr, negated);
1935 > public serialize(): string {
1936 > return this.expr.map(e => e.serialize()).join(' || '); contextkey.ts ×1
1937 > }
1939 > public keys(): string[] {
1940 const result: string[] = [];
1941 for (const expr of this.expr) {
1942 result.push(...expr.keys());
1943 }
1944 return result;
1945 }
1947 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1948 return new ContextKeyOrExpr(this.expr.map(expr => expr.map(mapFnc)), null);
1949 }
1951 > public negate(): ContextKeyExpression {
1952 > if (!this.negated) { contextkey.ts ×2
1953 > const result: ContextKeyExpression[] = [];
1954 > for (const expr of this.expr) {
1955 > result.push(expr.negate());
1956 > }
1957 >
1958 > // We don't support parens, so here we distribute the AND over the OR terminals
1959 > // We always take the first 2 AND pairs and distribute them
1960 > while (result.length > 1) {
1961 > const LEFT = result.shift()!;
1962 > const RIGHT = result.shift()!;
1963 >
1964 > const all: ContextKeyExpression[] = [];
1965 > for (const left of getTerminals(LEFT)) {
1966 > for (const right of getTerminals(RIGHT)) {
1967 > all.push(ContextKeyAndExpr.create([left, right], null, false)!);
1968 > }
1969 > }
1970 >
1971 > result.unshift(ContextKeyOrExpr.create(all, null, false)!);
1972 > }
1973 >
1974 > this.negated = ContextKeyOrExpr.create(result, this, true)!;
1975 > }
1976 > return this.negated;
1977 > }
1979 >
1980 > export interface ContextKeyInfo {
1981 > readonly key: string;
1982 > readonly type?: string;
1983 > readonly description?: string;
1984 > }
1985 >
1986 > export class RawContextKey<T extends ContextKeyValue> extends ContextKeyDefinedExpr {
1987 >
1988 > private static _info: ContextKeyInfo[] = [];
1989 >
1990 > static all(): IterableIterator<ContextKeyInfo> {
1991 return RawContextKey._info.values();
1992 }
1994 > private readonly _defaultValue: T | undefined;
1995 >
1996 > constructor(key: string, defaultValue: T | undefined, metaOrHide?: string | true | { type: string; description: string }) {
1997 > super(key, null); contextkey.ts ×3
1998 > this._defaultValue = defaultValue;
1999 >
2000 > // collect all context keys into a central place
2001 > if (typeof metaOrHide === 'object') {
2002 > RawContextKey._info.push({ ...metaOrHide, key }); contextkey.ts ×1
2003 > } else if (metaOrHide !== true) { contextkey.ts ×3
2004 > RawContextKey._info.push({ key, description: metaOrHide, type: defaultValue !== null && defaultValue !== undefined ? typeof defaultValue : undefined }); contextkey.ts ×1
2005 > }
2008 > public bindTo(target: IContextKeyService): IContextKey<T> {
2009 > return target.createKey(this.key, this._defaultValue); mockKeybindingService.ts ×2
2010 > }
2012 > public getValue(target: IContextKeyService): T | undefined {
2013 > return target.getContextKeyValue<T>(this.key); contextkey.ts ×1
2014 > }
2016 > public toNegated(): ContextKeyExpression {
2017 > return this.negate(); constants.ts ×18
2018 > }
2020 > public isEqualTo(value: any): ContextKeyExpression {
2021 > return ContextKeyEqualsExpr.create(this.key, value); languageModels.ts ×93
2022 > }
2024 > public notEqualsTo(value: any): ContextKeyExpression {
2025 > return ContextKeyNotEqualsExpr.create(this.key, value); languageModels.ts ×93
2026 > }
2028 > public greater(value: any): ContextKeyExpression {
2029 return ContextKeyGreaterExpr.create(this.key, value);
2030 }
2032 >
2033 > export type ContextKeyValue = null | undefined | boolean | number | string
2034 > | Array<null | undefined | boolean | number | string>
2035 > | Record<string, null | undefined | boolean | number | string>;
2036 >
2037 > export interface IContext {
2038 > getValue<T extends ContextKeyValue = ContextKeyValue>(key: string): T | undefined;
2039 > }
2040 >
2041 > export interface IContextKey<T extends ContextKeyValue = ContextKeyValue> {
2042 > set(value: T): void;
2043 > reset(): void;
2044 > get(): T | undefined;
2045 > }
2046 >
2047 > export interface IContextKeyServiceTarget {
2048 > parentElement: IContextKeyServiceTarget | null;
2049 > setAttribute(attr: string, value: string): void;
2050 > removeAttribute(attr: string): void;
2051 > hasAttribute(attr: string): boolean;
2052 > getAttribute(attr: string): string | null;
2053 > }
2054 >
2055 > export const IContextKeyService = createDecorator<IContextKeyService>('contextKeyService');
2056 >
2057 > export interface IReadableSet<T> {
2058 > has(value: T): boolean;
2059 > }
2060 >
2061 > export interface IContextKeyChangeEvent {
2062 > affectsSome(keys: IReadableSet<string>): boolean;
2063 > allKeysContainedIn(keys: IReadableSet<string>): boolean;
2064 > }
2065 >
2066 > export type IScopedContextKeyService = IContextKeyService & IDisposable;
2067 >
2068 > export interface IContextKeyService {
2069 > readonly _serviceBrand: undefined;
2070 >
2071 > readonly onDidChangeContext: Event<IContextKeyChangeEvent>;
2072 > bufferChangeEvents(callback: Function): void;
2073 >
2074 > createKey<T extends ContextKeyValue>(key: string, defaultValue: T | undefined): IContextKey<T>;
2075 > contextMatchesRules(rules: ContextKeyExpression | undefined): boolean;
2076 > getContextKeyValue<T>(key: string): T | undefined;
2077 >
2078 > createScoped(target: IContextKeyServiceTarget): IScopedContextKeyService;
2079 > createOverlay(overlay: Iterable<[string, any]>): IContextKeyService;
2080 > getContext(target: IContextKeyServiceTarget | null): IContext;
2081 >
2082 > updateParent(parentContextKeyService: IContextKeyService): void;
2083 > }
2084 >
2085 > function cmp1(key1: string, key2: string): number { contextkey.ts ×1
2086 > if (key1 < key2) {
2087 > return -1; contextkey.ts ×1
2088 > }
2089 > if (key1 > key2) { contextkey.ts ×1
2090 > return 1;
2091 > }
2092 > return 0; contextkey.ts ×1
2093 > }
2095 > function cmp2(key1: string, value1: any, key2: string, value2: any): number { contextkey.ts ×4
2096 > if (key1 < key2) {
2097 > return -1; contextkey.ts ×5
2098 > }
2099 > if (key1 > key2) { contextkey.ts ×4
2100 > return 1; contextkey.ts ×5
2101 > }
2102 > if (value1 < value2) { contextkey.ts ×1
2103 > return -1; languageModels.ts ×93
2104 > }
2105 > if (value1 > value2) { contextkey.ts ×7
2106 return 1;
2107 }
2108 > return 0; contextkey.ts ×7
2109 > }
2111 > /**
2112 > * Returns true if it is provable `p` implies `q`.
2113 > */
2114 > export function implies(p: ContextKeyExpression, q: ContextKeyExpression): boolean {
2116 > if (p.type === ContextKeyExprType.False || q.type === ContextKeyExprType.True) {
2117 // false implies anything
2118 // anything implies true
2119 return true;
2120 }
2122 > if (p.type === ContextKeyExprType.Or) {
2123 > if (q.type === ContextKeyExprType.Or) { contextkey.ts ×2
2124 > // `a || b || c` can only imply something like `a || b || c || d` contextkey.ts ×4
2125 > return allElementsIncluded(p.expr, q.expr);
2126 > }
2127 > return false; contextkey.ts ×2
2128 > }
2130 > if (q.type === ContextKeyExprType.Or) {
2131 > for (const element of q.expr) { contextkey.ts ×2
2132 > if (implies(p, element)) {
2133 > return true;
2134 > }
2135 > }
2136 > return false; contextkey.ts ×4
2137 > }
2139 > if (p.type === ContextKeyExprType.And) {
2140 > if (q.type === ContextKeyExprType.And) {
2141 > // `a && b && c` implies `a && c` contextkey.ts ×4
2142 > return allElementsIncluded(q.expr, p.expr);
2143 > }
2144 > for (const element of p.expr) { contextkey.ts ×6
2145 > if (implies(element, q)) {
2146 > return true;
2147 > }
2148 > }
2149 > return false; contextkey.ts ×1
2150 > }
2152 > return p.equals(q);
2153 > }
2155 > /**
2156 > * Returns true if all elements in `p` are also present in `q`.
2157 > * The two arrays are assumed to be sorted
2158 > */
2159 > function allElementsIncluded(p: ContextKeyExpression[], q: ContextKeyExpression[]): boolean { contextkey.ts ×4
2160 > let pIndex = 0;
2161 > let qIndex = 0;
2162 > while (pIndex < p.length && qIndex < q.length) {
2163 > const cmp = p[pIndex].cmp(q[qIndex]);
2164 >
2165 > if (cmp < 0) {
2166 > // an element from `p` is missing from `q` contextkey.ts ×4
2167 > return false;
2168 > } else if (cmp === 0) { contextkey.ts ×4
2169 > pIndex++;
2170 > qIndex++;
2171 > } else {
2172 > qIndex++; contextkey.ts ×4
2173 > }
2175 > return (pIndex === p.length);
2176 > }
2178 > function getTerminals(node: ContextKeyExpression) { contextkey.ts ×2
2179 > if (node.type === ContextKeyExprType.Or) {
2180 > return node.expr;
2181 > }
2182 > return [node]; contextkey.ts ×1
2183 > }