Skip to content

Commit

Permalink
Handle changing of 'launch' property in order to re-build preference …
Browse files Browse the repository at this point in the history
…schema

`UserLaunchProvider`, `WorkspaceLaunchProvider` and `FoldersLaunchProvider`
handle changing of 'launch' section of preferences without paying
attention whether configuration is valid or not. Once it happens a new
preference schema for launch configurations builds.

Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com>
  • Loading branch information
akurinnoy committed Mar 28, 2019
1 parent 0c4bb62 commit a206590
Show file tree
Hide file tree
Showing 8 changed files with 463 additions and 12 deletions.
24 changes: 22 additions & 2 deletions packages/core/src/browser/preferences/preference-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { inject, injectable, interfaces, named, postConstruct } from 'inversify'
import { ContributionProvider, bindContributionProvider, escapeRegExpCharacters, Emitter, Event } from '../../common';
import { PreferenceScope } from './preference-scope';
import { PreferenceProvider, PreferenceProviderPriority, PreferenceProviderDataChange } from './preference-provider';
import { IJSONSchema } from '../../common/json-schema';

import {
PreferenceSchema, PreferenceSchemaProperties, PreferenceDataSchema, PreferenceItem, PreferenceSchemaProperty, PreferenceDataProperty, JsonType
Expand Down Expand Up @@ -53,6 +54,7 @@ export class PreferenceSchemaProvider extends PreferenceProvider {

protected readonly preferences: { [name: string]: any } = {};
protected readonly combinedSchema: PreferenceDataSchema = { properties: {}, patternProperties: {} };
private remoteSchemas: IJSONSchema[] = [];

@inject(ContributionProvider) @named(PreferenceContribution)
protected readonly preferenceContributions: ContributionProvider<PreferenceContribution>;
Expand Down Expand Up @@ -182,7 +184,7 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
}

protected updateValidate(): void {
this.validateFunction = new Ajv().compile(this.combinedSchema);
this.validateFunction = new Ajv({ schemas: this.remoteSchemas }).compile(this.combinedSchema);
}

validate(name: string, value: any): boolean {
Expand All @@ -193,12 +195,30 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
return this.combinedSchema;
}

setSchema(schema: PreferenceSchema): void {
setSchema(schema: PreferenceSchema, remoteSchema?: IJSONSchema): void {
const changes = this.doSetSchema(schema);
if (remoteSchema) {
this.doSetRemoteSchema(remoteSchema);
}
this.fireDidPreferenceSchemaChanged();
this.emitPreferencesChangedEvent(changes);
}

protected doSetRemoteSchema(schema: IJSONSchema): void {
// remove existing remote schema if any
const existingSchemaIndex = this.remoteSchemas.findIndex(s => !!s.$id && !!s.$id && s.$id !== s.$id);
if (existingSchemaIndex) {
this.remoteSchemas.splice(existingSchemaIndex, 1);
}

this.remoteSchemas.push(schema);
}

setRemoteSchema(schema: IJSONSchema): void {
this.doSetRemoteSchema(schema);
this.fireDidPreferenceSchemaChanged();
}

getPreferences(): { [name: string]: any } {
return this.preferences;
}
Expand Down
157 changes: 157 additions & 0 deletions packages/debug/src/browser/abstract-launch-preference-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/********************************************************************************
* Copyright (C) 2019 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, postConstruct } from 'inversify';
import { PreferenceScope, PreferenceProvider } from '@theia/core/lib/browser';
import { Emitter, Event } from '@theia/core';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { DisposableCollection } from '@theia/core';
import { Disposable } from '@theia/core';

export interface GlobalLaunchConfig {
version: string;
compounds?: LaunchCompound[];
configurations: LaunchConfig[];
}

export namespace GlobalLaunchConfig {
/* tslint:disable-next-line:no-any */
export function is(data: any): data is GlobalLaunchConfig {
return !data || (!!data.version && (!data.compounds || Array.isArray(data.compounds)) && Array.isArray(data.configurations));
}
}

export interface LaunchConfig {
type: string;
request: string;
name: string;

/* tslint:disable-next-line:no-any */
[field: string]: any;
}

export interface LaunchCompound {
name: string;
configurations: (string | { name: string, folder: string })[];
}

export const LaunchPreferenceProvider = Symbol('LaunchConfigurationProvider');
export interface LaunchPreferenceProvider {

readonly onDidLaunchChanged: Event<void>;

ready: Promise<void>;

getConfigurationNames(withCompounds: boolean, resourceUri?: string): string[];

}

export const FolderLaunchProviderOptions = Symbol('FolderLaunchProviderOptions');
export interface FolderLaunchProviderOptions {
folderUri: string;
}

export const LaunchProviderProvider = Symbol('LaunchProviderProvider');
export type LaunchProviderProvider = (scope: PreferenceScope) => LaunchPreferenceProvider;

@injectable()
export abstract class AbstractLaunchPreferenceProvider implements LaunchPreferenceProvider, Disposable {

protected readonly onDidLaunchChangedEmitter = new Emitter<void>();
readonly onDidLaunchChanged: Event<void> = this.onDidLaunchChangedEmitter.event;

protected preferences: GlobalLaunchConfig | undefined;

protected _ready: Deferred<void> = new Deferred<void>();

protected readonly toDispose = new DisposableCollection();

protected readonly preferenceProvider: PreferenceProvider;

@postConstruct()
protected init(): void {
this.preferenceProvider.ready
.then(() => this._ready.resolve())
.catch(() => this._ready.resolve());

this.updatePreferences();
if (this.preferences !== undefined) {
this.emitLaunchChangedEvent();
}

this.toDispose.push(this.onDidLaunchChangedEmitter);
this.toDispose.push(
this.preferenceProvider.onDidNotValidPreferencesRead(prefs => {
if (!prefs || !GlobalLaunchConfig.is(prefs.launch)) {
return;
}
if (!prefs.launch && !this.preferences) {
return;
}
this.preferences = prefs.launch;
this.emitLaunchChangedEvent();
})
);
this.toDispose.push(
this.preferenceProvider.onDidPreferencesChanged(prefs => {
if (!prefs || !prefs.launch) {
return;
}
this.updatePreferences();
this.emitLaunchChangedEvent();
})
);
}

protected updatePreferences(): void {
const prefs = this.preferenceProvider.getPreferences();
if (GlobalLaunchConfig.is(prefs.launch)) {
this.preferences = prefs.launch;
}
}

protected emitLaunchChangedEvent(): void {
this.onDidLaunchChangedEmitter.fire(undefined);
}

get ready(): Promise<void> {
return this._ready.promise;
}

dispose(): void {
this.toDispose.dispose();
}

getConfigurationNames(withCompounds = true, resourceUri?: string): string[] {
const config = this.preferences;
if (!config) {
return [];
}

const names = config.configurations
.filter(launchConfig => launchConfig && typeof launchConfig.name === 'string')
.map(launchConfig => launchConfig.name);
if (withCompounds && config.compounds) {
const compoundNames = config.compounds
.filter(compoundConfig => typeof compoundConfig.name === 'string' && compoundConfig.configurations && compoundConfig.configurations.length)
.map(compoundConfig => compoundConfig.name);
names.push(...compoundNames);
}

return names;
}

}
12 changes: 11 additions & 1 deletion packages/debug/src/browser/debug-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ContainerModule, interfaces } from 'inversify';
import { DebugConfigurationManager } from './debug-configuration-manager';
import { DebugWidget } from './view/debug-widget';
import { DebugPath, DebugService } from '../common/debug-service';
import { WidgetFactory, WebSocketConnectionProvider, FrontendApplicationContribution, bindViewContribution, KeybindingContext } from '@theia/core/lib/browser';
import { WidgetFactory, WebSocketConnectionProvider, FrontendApplicationContribution, bindViewContribution, KeybindingContext, PreferenceScope } from '@theia/core/lib/browser';
import { DebugSessionManager } from './debug-session-manager';
import { DebugResourceResolver } from './debug-resource';
import {
Expand All @@ -44,6 +44,10 @@ import './debug-monaco-contribution';
import { bindDebugPreferences } from './debug-preferences';
import { DebugSchemaUpdater } from './debug-schema-updater';
import { DebugCallStackItemTypeKey } from './debug-call-stack-item-type-key';
import { LaunchProviderProvider, LaunchPreferenceProvider } from './abstract-launch-preference-provider';
import { WorkspaceLaunchProvider } from './workspace-launch-provider';
import { UserLaunchProvider } from './user-launch-provider';
import { FoldersLaunchProvider } from './folders-launch-provider';

export default new ContainerModule((bind: interfaces.Bind) => {
bind(DebugCallStackItemTypeKey).toDynamicValue(({ container }) =>
Expand Down Expand Up @@ -85,5 +89,11 @@ export default new ContainerModule((bind: interfaces.Bind) => {
bind(DebugSessionContributionRegistryImpl).toSelf().inSingletonScope();
bind(DebugSessionContributionRegistry).toService(DebugSessionContributionRegistryImpl);

bind(LaunchPreferenceProvider).to(UserLaunchProvider).inSingletonScope().whenTargetNamed(PreferenceScope.User);
bind(LaunchPreferenceProvider).to(WorkspaceLaunchProvider).inSingletonScope().whenTargetNamed(PreferenceScope.Workspace);
bind(LaunchPreferenceProvider).to(FoldersLaunchProvider).inSingletonScope().whenTargetNamed(PreferenceScope.Folder);
bind(LaunchProviderProvider).toFactory(ctx => (scope: PreferenceScope) =>
ctx.container.getNamed(LaunchPreferenceProvider, scope));

bindDebugPreferences(bind);
});
4 changes: 2 additions & 2 deletions packages/debug/src/browser/debug-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ export class DebugConfiguration {
export const DebugPreferences = Symbol('DebugPreferences');
export type DebugPreferences = PreferenceProxy<DebugConfiguration>;

export function createDebugreferences(preferences: PreferenceService): DebugPreferences {
export function createDebugPreferences(preferences: PreferenceService): DebugPreferences {
return createPreferenceProxy(preferences, debugPreferencesSchema);
}

export function bindDebugPreferences(bind: interfaces.Bind): void {
bind(DebugPreferences).toDynamicValue(ctx => {
const preferences = ctx.container.get<PreferenceService>(PreferenceService);
return createDebugreferences(preferences);
return createDebugPreferences(preferences);
}).inSingletonScope();

bind(PreferenceContribution).toConstantValue({ schema: debugPreferencesSchema });
Expand Down
Loading

0 comments on commit a206590

Please sign in to comment.