Skip to content

Commit

Permalink
Performance improvements for initialization
Browse files Browse the repository at this point in the history
Signed-off-by: Sven Efftinge <sven.efftinge@typefox.io>
  • Loading branch information
svenefftinge committed Sep 13, 2019
1 parent d0690be commit 158eaa3
Show file tree
Hide file tree
Showing 12 changed files with 620 additions and 647 deletions.
5 changes: 4 additions & 1 deletion .theia/settings.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.minimap.enabled": false,
"[typescript]": {
"editor.tabSize": 4
"editor.tabSize": 4,
"editor.minimap.enabled": false
},
"[json]": {
"editor.minimap.enabled": false,
"editor.tabSize": 2
},
"[jsonc]": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
if (this.configurations.isSectionName(name)) {
return true;
}
if (Object.keys(this.combinedSchema.properties).some(p => p.startsWith(name + '.'))) {
return true;
}
const result = this.validateFunction({ [name]: value }) as boolean;
if (!result && !(name in this.combinedSchema.properties)) {
// in order to avoid reporting it on each change
Expand Down
12 changes: 2 additions & 10 deletions packages/core/src/browser/preferences/preference-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export interface PreferenceResolveResult<T> {
@injectable()
export abstract class PreferenceProvider implements Disposable {

protected readonly onDidPreferencesChangedEmitter = new Emitter<PreferenceProviderDataChanges | undefined>();
readonly onDidPreferencesChanged: Event<PreferenceProviderDataChanges | undefined> = this.onDidPreferencesChangedEmitter.event;
protected readonly onDidPreferencesChangedEmitter = new Emitter<PreferenceProviderDataChanges>();
readonly onDidPreferencesChanged: Event<PreferenceProviderDataChanges> = this.onDidPreferencesChangedEmitter.event;

protected readonly toDispose = new DisposableCollection();

Expand Down Expand Up @@ -74,14 +74,6 @@ export abstract class PreferenceProvider implements Disposable {
}
}

/**
* Informs the listeners that one or more preferences of this provider are changed.
* @deprecated Use emitPreferencesChangedEvent instead.
*/
protected fireOnDidPreferencesChanged(): void {
this.onDidPreferencesChangedEmitter.fire(undefined);
}

get<T>(preferenceName: string, resourceUri?: string): T | undefined {
return this.resolve<T>(preferenceName, resourceUri).value;
}
Expand Down
132 changes: 102 additions & 30 deletions packages/core/src/browser/preferences/preference-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

// tslint:disable:no-any

import { Disposable, DisposableCollection, Event, Emitter } from '../../common';
import { Disposable, Event } from '../../common';
import { PreferenceService } from './preference-service';
import { PreferenceSchema, OverridePreferenceName } from './preference-contribution';
import { PreferenceScope } from './preference-scope';

export interface PreferenceChangeEvent<T> {
readonly preferenceName: keyof T;
Expand All @@ -41,16 +42,14 @@ export interface PreferenceRetrieval<T> {

export type PreferenceProxy<T> = Readonly<T> & Disposable & PreferenceEventEmitter<T> & PreferenceRetrieval<T>;

export function createPreferenceProxy<T>(preferences: PreferenceService, schema: PreferenceSchema): PreferenceProxy<T> {
const toDispose = new DisposableCollection();
const onPreferenceChangedEmitter = new Emitter<PreferenceChangeEvent<T>>();
toDispose.push(onPreferenceChangedEmitter);
toDispose.push(preferences.onPreferenceChanged(e => {
export function createPreferenceProxy<T>(preferences: PreferenceService, schema: PreferenceSchema, prefix: string = '',
_resourceUri?: string, _overrideIdentifier?: string): PreferenceProxy<T> {
const onPreferenceChanged = (listener: (e: PreferenceChangeEvent<T>) => any, thisArgs?: any, disposables?: Disposable[]) => preferences.onPreferenceChanged(e => {
const overridden = preferences.overriddenPreferenceName(e.preferenceName);
const preferenceName: any = overridden ? overridden.preferenceName : e.preferenceName;
if (schema.properties[preferenceName]) {
const { newValue, oldValue } = e;
onPreferenceChangedEmitter.fire({
listener({
newValue, oldValue, preferenceName,
affects: (resourceUri, overrideIdentifier) => {
if (overrideIdentifier !== undefined) {
Expand All @@ -62,17 +61,18 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, schema:
}
});
}
}));
}, thisArgs, disposables);

const unsupportedOperation = (_: any, __: string) => {
throw new Error('Unsupported operation');
};

const getValue: PreferenceRetrieval<any>['get'] = (arg, defaultValue, resourceUri) => {
const isArgOverridePreferenceName = typeof arg === 'object' && arg.overrideIdentifier;
const preferenceName = isArgOverridePreferenceName ?
preferences.overridePreferenceName(<OverridePreferenceName>arg) :
<string>arg;
const value = preferences.get(preferenceName, defaultValue, resourceUri);
const value = preferences.get(preferenceName, defaultValue, resourceUri || _resourceUri);
if (preferences.validate(isArgOverridePreferenceName ? (<OverridePreferenceName>arg).preferenceName : preferenceName, value)) {
return value;
}
Expand All @@ -82,41 +82,113 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, schema:
const values = preferences.inspect(preferenceName, resourceUri);
return values && values.defaultValue;
};
return new Proxy({}, {
get: (_, property: string) => {
if (schema.properties[property]) {
const value = preferences.get(property);
if (preferences.validate(property, value)) {
return value;

const ownKeys: () => string[] = () => {
const properties = [];
for (const p of Object.keys(schema.properties)) {
if (p.startsWith(prefix)) {
const idx = p.indexOf('.', prefix.length);
if (idx !== -1) {
const pre = p.substr(prefix.length, idx - prefix.length);
if (properties.indexOf(pre) === -1) {
properties.push(pre);
}
}
const values = preferences.inspect(property);
return values && values.defaultValue;
properties.push(p.substr(prefix.length));
}
if (property === 'onPreferenceChanged') {
return onPreferenceChangedEmitter.event;
}
return properties;
};

const set: (target: any, prop: string, value: any, receiver: any) => boolean = (_, property: string | symbol | number, value: any) => {
if (typeof property !== 'string') {
throw new Error(`unexpected property: ${String(property)}`);
}
const fullProperty = prefix ? prefix + property : property;
if (schema.properties[fullProperty]) {
preferences.set(fullProperty, value, PreferenceScope.Default);
return true;
}
const newPrefix = fullProperty + '.';
for (const p of Object.keys(schema.properties)) {
if (p.startsWith(newPrefix)) {
const subProxy: { [k: string]: any } = createPreferenceProxy(preferences, schema, newPrefix, _resourceUri, _overrideIdentifier);
for (const k of Object.keys(value)) {
subProxy[k] = value[k];
}
}
if (property === 'dispose') {
return () => toDispose.dispose();
}
return false;
};

const get: (target: any, prop: string) => any = (_, property: string | symbol | number) => {
if (typeof property !== 'string') {
throw new Error(`unexpected property: ${String(property)}`);
}
const fullProperty = prefix ? prefix + property : property;
if (schema.properties[fullProperty]) {
let value;
if (_overrideIdentifier) {
value = preferences.get(preferences.overridePreferenceName({
overrideIdentifier: _overrideIdentifier,
preferenceName: fullProperty
}), undefined, _resourceUri);
}
if (property === 'ready') {
return preferences.ready;
if (value === undefined) {
value = preferences.get(fullProperty, undefined, _resourceUri);
}
if (property === 'get') {
return getValue;
if (preferences.validate(fullProperty, value)) {
return value;
}
throw new Error(`unexpected property: ${property}`);
},
ownKeys: () => Object.keys(schema.properties),
const values = preferences.inspect(fullProperty, _resourceUri);
return values && values.defaultValue;
}
if (property === 'onPreferenceChanged') {
return onPreferenceChanged;
}
if (property === 'dispose') {
return () => { /* do nothing */ };
}
if (property === 'ready') {
return preferences.ready;
}
if (property === 'get') {
return getValue;
}
if (property === 'toJSON') {
return toJSON();
}
const newPrefix = fullProperty + '.';
for (const p of Object.keys(schema.properties)) {
if (p.startsWith(newPrefix)) {
return createPreferenceProxy(preferences, schema, newPrefix, _resourceUri, _overrideIdentifier);
}
}
return undefined;
};

const toJSON = () => {
const result: any = {};
for (const k of ownKeys()) {
result[k] = get(undefined, k);
}
return result;
};

return new Proxy({}, {
get,
ownKeys,
getOwnPropertyDescriptor: (_, property: string) => {
if (schema.properties[property]) {
const fullProperty = (prefix || '') + property;
if (schema.properties[fullProperty] || ownKeys().indexOf(fullProperty) !== -1) {
return {
enumerable: true,
configurable: true
};
}
return {};
},
set: unsupportedOperation,
set,
deleteProperty: unsupportedOperation,
defineProperty: unsupportedOperation
});
Expand Down
Loading

0 comments on commit 158eaa3

Please sign in to comment.