-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retrieving launch configurations using Plugin API #4743
Changes from all commits
90265e8
4dd3b91
f9634e8
73f89c9
397f360
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -53,6 +54,7 @@ export class PreferenceSchemaProvider extends PreferenceProvider { | |
|
||
protected readonly preferences: { [name: string]: any } = {}; | ||
protected readonly combinedSchema: PreferenceDataSchema = { properties: {}, patternProperties: {} }; | ||
private remoteSchemas: IJSONSchema[] = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is special about After reading code it does not seem to affect combined schemas, but just passed here to reuse
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You're right, but not only for launch folder provider but for any preference provider. Each time a launch configuration is added, renamed or removed we need new schema which restricts possible names of configuration available for compound sub-section.
User might have a global or workspace wide launch configuration placed in corresponding settings file, and a project might contain no launch file at all. That is why I treated launch configuration as any other property. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Wow, did not know it, ok, will test how it works. So i should get content assist in settings.json for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no content assist support for I'm still confused why we need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking of content assist I meant this:
That's the point. Remote schema doesn't affect setting a values. It affects only validation. Remote schema only makes things easier allowing to replace a part of preference schema without calling The main part of launch preference schema is set only once. Since then only remote part of schema become updated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will try again tomorrow. Tried the same today and did not get any content assist. The problematic part with it that these settings not respected by |
||
|
||
@inject(ContributionProvider) @named(PreferenceContribution) | ||
protected readonly preferenceContributions: ContributionProvider<PreferenceContribution>; | ||
|
@@ -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 { | ||
|
@@ -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; | ||
} | ||
|
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 { | ||
tsmaeder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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.onDidInvalidPreferencesRead(prefs => { | ||
if (!prefs || !GlobalLaunchConfig.is(prefs.launch)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why should it go here and cannot happen in launch preference provider? then we don't need a new event Also wondering after reading code, looks like if you don't use the validation function aware of setting schema for launch folder preferences maybe you won't get some false possible validation results and getting rid of an event would be easier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Event
I'm not sure I understood what you meant... Could you elaborate on this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to create preferences from invalid data? If it is valid then it should be part of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's because how launch preference schema is defined. In order to build this schema I need to get all configuration names to fill the Imagine, you have launch configuration
And you need to restrict possible values of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean if someone add How about having custom validation for launch preferences to exclude only bogus compound configuration, break a cycle between schema and data, avoid parsing data twice and introducing a new event? I don't think you need to check each property in this case of each configuration just top-level structure, like here https://github.com/akurinnoy/theia/blob/397f360422dae3dc590e58665fd526d04d7810c7/packages/debug/src/browser/debug-configuration-model.ts#L74 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder whether launch launch configs in user settings makes sense at all? Should not we just stub it with default values? Docs does not say anything about user settings:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you seen default settings and explanation near There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think launch config in user settings shouldn't be stabbed, and Theia should use a global launch config if a project doesn't have its own. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it mean that it's stubbed with such value and data from user settings json are ignored? Do I read it correctly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wrong. My user settings already had launch section that's why i got such results. If i try to changes it changes are reflected into expectations, so we should read launch section user settings as well. I will update tests with expectations that user settings does not contain launch section. |
||
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; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
breaking changes should go in its own section, see below for 0.5.0
It would be nice also cover what for they were changed, see examples in breaknig changes for other releases