Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -2,24 +2,24 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Emitter, Event, mapEvent } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { keys } from 'vs/base/common/map';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr, IContextKeyChangeEvent, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService, IConfigurationChangeEvent, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { Event, Emitter, debounceEvent } from 'vs/base/common/event';
const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context';
export class Context implements IContext {
protected _parent: Context;
protected _parent: Context | null;
protected _value: { [key: string]: any; };
protected _id: number;
constructor(id: number, parent: Context) {
constructor(id: number, parent: Context | null) {
this._id = id;
this._parent = parent;
this._value = Object.create(null);
@@ -44,7 +44,7 @@ export class Context implements IContext {
return false;
}
public getValue<T>(key: string): T {
public getValue<T>(key: string): T | undefined {
const ret = this._value[key];
if (typeof ret === 'undefined' && this._parent) {
return this._parent.getValue<T>(key);
@@ -60,84 +60,106 @@ export class Context implements IContext {
}
}
class NullContext extends Context {
static readonly INSTANCE = new NullContext();
constructor() {
super(-1, null);
}
public setValue(key: string, value: any): boolean {
return false;
}
public removeValue(key: string): boolean {
return false;
}
public getValue<T>(key: string): T | undefined {
return undefined;
}
collectAllValues(): { [key: string]: any; } {
return Object.create(null);
}
}
class ConfigAwareContextValuesContainer extends Context {
private readonly _emitter: Emitter<string | string[]>;
private readonly _subscription: IDisposable;
private readonly _configurationService: IConfigurationService;
private static _keyPrefix = 'config.';
constructor(id: number, configurationService: IConfigurationService, emitter: Emitter<string | string[]>) {
private readonly _values = new Map<string, any>();
private readonly _listener: IDisposable;
constructor(
id: number,
private readonly _configurationService: IConfigurationService,
emitter: Emitter<string | string[]>
) {
super(id, null);
this._emitter = emitter;
this._configurationService = configurationService;
this._subscription = configurationService.onDidChangeConfiguration(this._onConfigurationUpdated, this);
this._initFromConfiguration();
}
public dispose() {
this._subscription.dispose();
}
private _onConfigurationUpdated(event: IConfigurationChangeEvent): void {
if (event.source === ConfigurationTarget.DEFAULT) {
// new setting, rebuild everything
this._initFromConfiguration();
} else {
// update those that we know
for (const configKey of event.affectedKeys) {
const contextKey = `config.${configKey}`;
if (contextKey in this._value) {
this._value[contextKey] = this._configurationService.getValue(configKey);
this._emitter.fire(contextKey);
}
}
}
}
private _initFromConfiguration() {
const prefix = 'config.';
const config = this._configurationService.getValue();
const configKeys: { [key: string]: boolean } = Object.create(null);
const configKeysChanged: string[] = [];
// add new value from config
const walk = (obj: any, keys: string[]) => {
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
keys.push(key);
let value = obj[key];
if (typeof value === 'boolean') {
const configKey = keys.join('.');
const oldValue = this._value[configKey];
this._value[configKey] = value;
if (oldValue !== value) {
configKeysChanged.push(configKey);
configKeys[configKey] = true;
} else {
configKeys[configKey] = false;
}
} else if (typeof value === 'object') {
walk(value, keys);
this._listener = this._configurationService.onDidChangeConfiguration(event => {
if (event.source === ConfigurationTarget.DEFAULT) {
// new setting, reset everything
const allKeys = keys(this._values);
this._values.clear();
emitter.fire(allKeys);
} else {
const changedKeys: string[] = [];
for (const configKey of event.affectedKeys) {
const contextKey = `config.${configKey}`;
if (this._values.has(contextKey)) {
this._values.delete(contextKey);
changedKeys.push(contextKey);
}
keys.pop();
}
emitter.fire(changedKeys);
}
};
walk(config, ['config']);
});
}
// remove unused keys
for (let key in this._value) {
if (key.indexOf(prefix) === 0 && configKeys[key] === undefined) {
delete this._value[key];
configKeys[key] = true;
configKeysChanged.push(key);
}
dispose(): void {
this._listener.dispose();
}
getValue(key: string): any {
if (key.indexOf(ConfigAwareContextValuesContainer._keyPrefix) !== 0) {
return super.getValue(key);
}
// send events
this._emitter.fire(configKeysChanged);
if (this._values.has(key)) {
return this._values.get(key);
}
const configKey = key.substr(ConfigAwareContextValuesContainer._keyPrefix.length);
const configValue = this._configurationService.getValue(configKey);
let value: any = undefined;
switch (typeof configValue) {
case 'number':
case 'boolean':
case 'string':
value = configValue;
break;
}
this._values.set(key, value);
return value;
}
setValue(key: string, value: any): boolean {
return super.setValue(key, value);
}
removeValue(key: string): boolean {
return super.removeValue(key);
}
collectAllValues(): { [key: string]: any; } {
const result: { [key: string]: any } = Object.create(null);
this._values.forEach((value, index) => result[index] = value);
return { ...result, ...super.collectAllValues() };
}
}
@@ -145,9 +167,9 @@ class ContextKey<T> implements IContextKey<T> {
private _parent: AbstractContextKeyService;
private _key: string;
private _defaultValue: T;
private _defaultValue: T | undefined;
constructor(parent: AbstractContextKeyService, key: string, defaultValue: T) {
constructor(parent: AbstractContextKeyService, key: string, defaultValue: T | undefined) {
this._parent = parent;
this._key = key;
this._defaultValue = defaultValue;
@@ -166,19 +188,19 @@ class ContextKey<T> implements IContextKey<T> {
}
}
public get(): T {
public get(): T | undefined {
return this._parent.getContextKeyValue<T>(this._key);
}
}
export class ContextKeyChangeEvent implements IContextKeyChangeEvent {
private _keys: string[] = [];
collect(oneOrManyKeys: string | string[]): void {
this._keys = this._keys.concat(oneOrManyKeys);
class SimpleContextKeyChangeEvent implements IContextKeyChangeEvent {
constructor(private readonly _key: string) { }
affectsSome(keys: IReadableSet<string>): boolean {
return keys.has(this._key);
}
}
class ArrayContextKeyChangeEvent implements IContextKeyChangeEvent {
constructor(private readonly _keys: string[]) { }
affectsSome(keys: IReadableSet<string>): boolean {
for (const key of this._keys) {
if (keys.has(key)) {
@@ -192,39 +214,48 @@ export class ContextKeyChangeEvent implements IContextKeyChangeEvent {
export abstract class AbstractContextKeyService implements IContextKeyService {
public _serviceBrand: any;
protected _isDisposed: boolean;
protected _onDidChangeContext: Event<IContextKeyChangeEvent>;
protected _onDidChangeContextKey: Emitter<string | string[]>;
protected _myContextId: number;
constructor(myContextId: number) {
this._isDisposed = false;
this._myContextId = myContextId;
this._onDidChangeContextKey = new Emitter<string>();
}
abstract dispose(): void;
public createKey<T>(key: string, defaultValue: T): IContextKey<T> {
public createKey<T>(key: string, defaultValue: T | undefined): IContextKey<T> {
if (this._isDisposed) {
throw new Error(`AbstractContextKeyService has been disposed`);
}
return new ContextKey(this, key, defaultValue);
}
public get onDidChangeContext(): Event<IContextKeyChangeEvent> {
if (!this._onDidChangeContext) {
this._onDidChangeContext = debounceEvent<string | string[], ContextKeyChangeEvent>(this._onDidChangeContextKey.event, (prev, cur) => {
if (!prev) {
prev = new ContextKeyChangeEvent();
}
prev.collect(cur);
return prev;
}, 25);
this._onDidChangeContext = mapEvent(this._onDidChangeContextKey.event, ((changedKeyOrKeys): IContextKeyChangeEvent => {
return typeof changedKeyOrKeys === 'string'
? new SimpleContextKeyChangeEvent(changedKeyOrKeys)
: new ArrayContextKeyChangeEvent(changedKeyOrKeys);
}));
}
return this._onDidChangeContext;
}
public createScoped(domNode: IContextKeyServiceTarget): IContextKeyService {
if (this._isDisposed) {
throw new Error(`AbstractContextKeyService has been disposed`);
}
return new ScopedContextKeyService(this, this._onDidChangeContextKey, domNode);
}
public contextMatchesRules(rules: ContextKeyExpr): boolean {
public contextMatchesRules(rules: ContextKeyExpr | null): boolean {
if (this._isDisposed) {
throw new Error(`AbstractContextKeyService has been disposed`);
}
const context = this.getContextValuesContainer(this._myContextId);
const result = KeybindingResolver.contextMatchesRules(context, rules);
// console.group(rules.serialize() + ' -> ' + result);
@@ -233,11 +264,17 @@ export abstract class AbstractContextKeyService implements IContextKeyService {
return result;
}
public getContextKeyValue<T>(key: string): T {
public getContextKeyValue<T>(key: string): T | undefined {
if (this._isDisposed) {
return undefined;
}
return this.getContextValuesContainer(this._myContextId).getValue<T>(key);
}
public setContext(key: string, value: any): void {
if (this._isDisposed) {
return;
}
const myContext = this.getContextValuesContainer(this._myContextId);
if (!myContext) {
return;
@@ -248,12 +285,18 @@ export abstract class AbstractContextKeyService implements IContextKeyService {
}
public removeContext(key: string): void {
if (this._isDisposed) {
return;
}
if (this.getContextValuesContainer(this._myContextId).removeValue(key)) {
this._onDidChangeContextKey.fire(key);
}
}
public getContext(target: IContextKeyServiceTarget): IContext {
public getContext(target: IContextKeyServiceTarget | null): IContext {
if (this._isDisposed) {
return NullContext.INSTANCE;
}
return this.getContextValuesContainer(findContextAttr(target));
}
@@ -281,7 +324,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
this._toDispose.push(myContext);
// Uncomment this to see the contexts continuously logged
// let lastLoggedValue: string = null;
// let lastLoggedValue: string | null = null;
// setInterval(() => {
// let values = Object.keys(this._contexts).map((key) => this._contexts[key]);
// let logValue = values.map(v => JSON.stringify(v._value, null, '\t')).join('\n');
@@ -293,20 +336,30 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
}
public dispose(): void {
this._isDisposed = true;
this._toDispose = dispose(this._toDispose);
}
public getContextValuesContainer(contextId: number): Context {
if (this._isDisposed) {
return NullContext.INSTANCE;
}
return this._contexts[String(contextId)];
}
public createChildContext(parentContextId: number = this._myContextId): number {
if (this._isDisposed) {
throw new Error(`ContextKeyService has been disposed`);
}
let id = (++this._lastContextId);
this._contexts[String(id)] = new Context(id, this.getContextValuesContainer(parentContextId));
return id;
}
public disposeContext(contextId: number): void {
if (this._isDisposed) {
return;
}
delete this._contexts[String(contextId)];
}
}
@@ -314,7 +367,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
class ScopedContextKeyService extends AbstractContextKeyService {
private _parent: AbstractContextKeyService;
private _domNode: IContextKeyServiceTarget;
private _domNode: IContextKeyServiceTarget | undefined;
constructor(parent: AbstractContextKeyService, emitter: Emitter<string | string[]>, domNode?: IContextKeyServiceTarget) {
super(parent.createChildContext());
@@ -328,6 +381,7 @@ class ScopedContextKeyService extends AbstractContextKeyService {
}
public dispose(): void {
this._isDisposed = true;
this._parent.disposeContext(this._myContextId);
if (this._domNode) {
this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR);
@@ -340,22 +394,35 @@ class ScopedContextKeyService extends AbstractContextKeyService {
}
public getContextValuesContainer(contextId: number): Context {
if (this._isDisposed) {
return NullContext.INSTANCE;
}
return this._parent.getContextValuesContainer(contextId);
}
public createChildContext(parentContextId: number = this._myContextId): number {
if (this._isDisposed) {
throw new Error(`ScopedContextKeyService has been disposed`);
}
return this._parent.createChildContext(parentContextId);
}
public disposeContext(contextId: number): void {
if (this._isDisposed) {
return;
}
this._parent.disposeContext(contextId);
}
}
function findContextAttr(domNode: IContextKeyServiceTarget): number {
function findContextAttr(domNode: IContextKeyServiceTarget | null): number {
while (domNode) {
if (domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) {
return parseInt(domNode.getAttribute(KEYBINDING_CONTEXT_ATTR), 10);
const attr = domNode.getAttribute(KEYBINDING_CONTEXT_ATTR);
if (attr) {
return parseInt(attr, 10);
}
return NaN;
}
domNode = domNode.parentElement;
}

View File

@@ -2,13 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export enum ContextKeyExprType {
export const enum ContextKeyExprType {
Defined = 1,
Not = 2,
Equals = 3,
@@ -43,7 +42,7 @@ export abstract class ContextKeyExpr {
return new ContextKeyNotExpr(key);
}
public static and(...expr: ContextKeyExpr[]): ContextKeyExpr {
public static and(...expr: (ContextKeyExpr | undefined | null)[]): ContextKeyExpr {
return new ContextKeyAndExpr(expr);
}
@@ -56,7 +55,7 @@ export abstract class ContextKeyExpr {
}
//
public static deserialize(serialized: string): ContextKeyExpr {
public static deserialize(serialized: string | null | undefined): ContextKeyExpr | null {
if (!serialized) {
return null;
}
@@ -121,7 +120,7 @@ export abstract class ContextKeyExpr {
return serializedValue;
}
private static _deserializeRegexValue(serializedValue: string): RegExp {
private static _deserializeRegexValue(serializedValue: string): RegExp | null {
if (isFalsyOrWhitespace(serializedValue)) {
console.warn('missing regexp-value for =~-expression');
@@ -148,7 +147,7 @@ export abstract class ContextKeyExpr {
public abstract getType(): ContextKeyExprType;
public abstract equals(other: ContextKeyExpr): boolean;
public abstract evaluate(context: IContext): boolean;
public abstract normalize(): ContextKeyExpr;
public abstract normalize(): ContextKeyExpr | null;
public abstract serialize(): string;
public abstract keys(): string[];
}
@@ -389,7 +388,7 @@ export class ContextKeyNotExpr implements ContextKeyExpr {
export class ContextKeyRegexExpr implements ContextKeyExpr {
constructor(private key: string, private regexp: RegExp) {
constructor(private key: string, private regexp: RegExp | null) {
//
}
@@ -404,11 +403,12 @@ export class ContextKeyRegexExpr implements ContextKeyExpr {
if (this.key > other.key) {
return 1;
}
const source = this.regexp ? this.regexp.source : undefined;
if (source < other.regexp.source) {
const thisSource = this.regexp ? this.regexp.source : '';
const otherSource = other.regexp ? other.regexp.source : '';
if (thisSource < otherSource) {
return -1;
}
if (source > other.regexp.source) {
if (thisSource > otherSource) {
return 1;
}
return 0;
@@ -416,14 +416,16 @@ export class ContextKeyRegexExpr implements ContextKeyExpr {
public equals(other: ContextKeyExpr): boolean {
if (other instanceof ContextKeyRegexExpr) {
const source = this.regexp ? this.regexp.source : undefined;
return (this.key === other.key && source === other.regexp.source);
const thisSource = this.regexp ? this.regexp.source : '';
const otherSource = other.regexp ? other.regexp.source : '';
return (this.key === other.key && thisSource === otherSource);
}
return false;
}
public evaluate(context: IContext): boolean {
return this.regexp ? this.regexp.test(context.getValue(this.key)) : false;
let value = context.getValue<any>(this.key);
return this.regexp ? this.regexp.test(value) : false;
}
public normalize(): ContextKeyExpr {
@@ -445,7 +447,7 @@ export class ContextKeyRegexExpr implements ContextKeyExpr {
export class ContextKeyAndExpr implements ContextKeyExpr {
public readonly expr: ContextKeyExpr[];
constructor(expr: ContextKeyExpr[]) {
constructor(expr: (ContextKeyExpr | null | undefined)[]) {
this.expr = ContextKeyAndExpr._normalizeArr(expr);
}
@@ -477,12 +479,12 @@ export class ContextKeyAndExpr implements ContextKeyExpr {
return true;
}
private static _normalizeArr(arr: ContextKeyExpr[]): ContextKeyExpr[] {
private static _normalizeArr(arr: (ContextKeyExpr | null | undefined)[]): ContextKeyExpr[] {
let expr: ContextKeyExpr[] = [];
if (arr) {
for (let i = 0, len = arr.length; i < len; i++) {
let e = arr[i];
let e: ContextKeyExpr | null | undefined = arr[i];
if (!e) {
continue;
}
@@ -506,7 +508,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr {
return expr;
}
public normalize(): ContextKeyExpr {
public normalize(): ContextKeyExpr | null {
if (this.expr.length === 0) {
return null;
}
@@ -523,7 +525,11 @@ export class ContextKeyAndExpr implements ContextKeyExpr {
return '';
}
if (this.expr.length === 1) {
return this.normalize().serialize();
const normalized = this.normalize();
if (!normalized) {
return '';
}
return normalized.serialize();
}
return this.expr.map(e => e.serialize()).join(' && ');
}
@@ -641,9 +647,9 @@ export class ContextKeyLessThanEqualsExpr implements ContextKeyExpr {
export class RawContextKey<T> extends ContextKeyDefinedExpr {
private _defaultValue: T;
private _defaultValue: T | undefined;
constructor(key: string, defaultValue: T) {
constructor(key: string, defaultValue: T | undefined) {
super(key);
this._defaultValue = defaultValue;
}
@@ -652,7 +658,7 @@ export class RawContextKey<T> extends ContextKeyDefinedExpr {
return target.createKey(this.key, this._defaultValue);
}
public getValue(target: IContextKeyService): T {
public getValue(target: IContextKeyService): T | undefined {
return target.getContextKeyValue<T>(this.key);
}
@@ -670,21 +676,21 @@ export class RawContextKey<T> extends ContextKeyDefinedExpr {
}
export interface IContext {
getValue<T>(key: string): T;
getValue<T>(key: string): T | undefined;
}
export interface IContextKey<T> {
set(value: T): void;
reset(): void;
get(): T;
get(): T | undefined;
}
export interface IContextKeyServiceTarget {
parentElement: IContextKeyServiceTarget;
parentElement: IContextKeyServiceTarget | null;
setAttribute(attr: string, value: string): void;
removeAttribute(attr: string): void;
hasAttribute(attr: string): boolean;
getAttribute(attr: string): string;
getAttribute(attr: string): string | null;
}
export const IContextKeyService = createDecorator<IContextKeyService>('contextKeyService');
@@ -702,12 +708,12 @@ export interface IContextKeyService {
dispose(): void;
onDidChangeContext: Event<IContextKeyChangeEvent>;
createKey<T>(key: string, defaultValue: T): IContextKey<T>;
contextMatchesRules(rules: ContextKeyExpr): boolean;
getContextKeyValue<T>(key: string): T;
createKey<T>(key: string, defaultValue: T | undefined): IContextKey<T>;
contextMatchesRules(rules: ContextKeyExpr | null): boolean;
getContextKeyValue<T>(key: string): T | undefined;
createScoped(target?: IContextKeyServiceTarget): IContextKeyService;
getContext(target: IContextKeyServiceTarget): IContext;
getContext(target: IContextKeyServiceTarget | null): IContext;
}
export const SET_CONTEXT_COMMAND_ID = 'setContext';

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@@ -16,7 +14,7 @@ function createContext(ctx: any) {
}
suite('ContextKeyExpr', () => {
test('ContextKeyExpr.equals', function () {
test('ContextKeyExpr.equals', () => {
let a = ContextKeyExpr.and(
ContextKeyExpr.has('a1'),
ContextKeyExpr.and(ContextKeyExpr.has('and.a')),
@@ -57,7 +55,7 @@ suite('ContextKeyExpr', () => {
assert(a.equals(b), 'expressions should be equal');
});
test('normalize', function () {
test('normalize', () => {
let key1IsTrue = ContextKeyExpr.equals('key1', true);
let key1IsNotFalse = ContextKeyExpr.notEquals('key1', false);
let key1IsFalse = ContextKeyExpr.equals('key1', false);
@@ -69,7 +67,7 @@ suite('ContextKeyExpr', () => {
assert.ok(key1IsNotTrue.normalize().equals(ContextKeyExpr.not('key1')));
});
test('evaluate', function () {
test('evaluate', () => {
/* tslint:disable:triple-equals */
let context = createContext({
'a': true,