mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-13 15:35:20 +01:00
- Introduce policy model with file policy
- Introduce policy configuration using policy model - Use policy configuration while reading, inspecting and writing configuration - Adopt json settings editor
This commit is contained in:
@@ -222,7 +222,7 @@ class SharedProcessMain extends Disposable {
|
||||
fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider);
|
||||
|
||||
// Configuration
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService));
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService));
|
||||
services.set(IConfigurationService, configurationService);
|
||||
|
||||
// Storage (global access only)
|
||||
|
||||
@@ -167,7 +167,7 @@ class CodeMain {
|
||||
services.set(ILoggerService, new LoggerService(logService, fileService));
|
||||
|
||||
// Configuration
|
||||
const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService);
|
||||
const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService, environmentMainService, logService);
|
||||
services.set(IConfigurationService, configurationService);
|
||||
|
||||
// Lifecycle
|
||||
|
||||
@@ -129,7 +129,7 @@ class CliMain extends Disposable {
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
// Configuration
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService));
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService));
|
||||
services.set(IConfigurationService, configurationService);
|
||||
|
||||
// Init config
|
||||
|
||||
@@ -29,7 +29,7 @@ import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService
|
||||
import { ITextResourceConfigurationService, ITextResourcePropertiesService, ITextResourceConfigurationChangeEvent } from 'vs/editor/common/services/textResourceConfiguration';
|
||||
import { CommandsRegistry, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel, IConfigurationValue, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Configuration, ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Configuration, ConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { createDecorator, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -88,6 +88,7 @@ import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/com
|
||||
import { staticObservableValue } from 'vs/base/common/observableValue';
|
||||
|
||||
import 'vs/editor/common/services/languageFeaturesService';
|
||||
import { DefaultConfigurationModel } from 'vs/platform/configuration/common/configurations';
|
||||
|
||||
class SimpleModel implements IResolvedTextEditorModel {
|
||||
|
||||
@@ -521,7 +522,7 @@ export class StandaloneConfigurationService implements IConfigurationService {
|
||||
private readonly _configuration: Configuration;
|
||||
|
||||
constructor() {
|
||||
this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
|
||||
this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
}
|
||||
|
||||
getValue<T>(): T;
|
||||
@@ -582,6 +583,7 @@ export class StandaloneConfigurationService implements IConfigurationService {
|
||||
};
|
||||
return {
|
||||
defaults: emptyModel,
|
||||
policy: emptyModel,
|
||||
user: emptyModel,
|
||||
workspace: emptyModel,
|
||||
folders: []
|
||||
|
||||
@@ -80,6 +80,7 @@ export interface IConfigurationValue<T> {
|
||||
readonly workspaceValue?: T;
|
||||
readonly workspaceFolderValue?: T;
|
||||
readonly memoryValue?: T;
|
||||
readonly policyValue?: T;
|
||||
readonly value?: T;
|
||||
|
||||
readonly default?: { value?: T; override?: T };
|
||||
@@ -89,6 +90,7 @@ export interface IConfigurationValue<T> {
|
||||
readonly workspace?: { value?: T; override?: T };
|
||||
readonly workspaceFolder?: { value?: T; override?: T };
|
||||
readonly memory?: { value?: T; override?: T };
|
||||
readonly policy?: { value?: T };
|
||||
|
||||
readonly overrideIdentifiers?: string[];
|
||||
}
|
||||
@@ -163,6 +165,7 @@ export interface IOverrides {
|
||||
|
||||
export interface IConfigurationData {
|
||||
defaults: IConfigurationModel;
|
||||
policy: IConfigurationModel;
|
||||
user: IConfigurationModel;
|
||||
workspace: IConfigurationModel;
|
||||
folders: [UriComponents, IConfigurationModel][];
|
||||
|
||||
@@ -232,33 +232,6 @@ export class ConfigurationModel implements IConfigurationModel {
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultConfigurationModel extends ConfigurationModel {
|
||||
|
||||
constructor(configurationDefaultsOverrides: IStringDictionary<any> = {}) {
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
const keys = Object.keys(properties);
|
||||
const contents: any = Object.create(null);
|
||||
const overrides: IOverrides[] = [];
|
||||
|
||||
for (const key in properties) {
|
||||
const defaultOverrideValue = configurationDefaultsOverrides[key];
|
||||
const value = defaultOverrideValue !== undefined ? defaultOverrideValue : properties[key].default;
|
||||
addToValueTree(contents, key, value, message => console.error(`Conflict in default settings: ${message}`));
|
||||
}
|
||||
for (const key of Object.keys(contents)) {
|
||||
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
|
||||
overrides.push({
|
||||
identifiers: overrideIdentifiersFromKey(key),
|
||||
keys: Object.keys(contents[key]),
|
||||
contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
super(contents, keys, overrides);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ConfigurationParseOptions {
|
||||
scopes: ConfigurationScope[] | undefined;
|
||||
skipRestricted?: boolean;
|
||||
@@ -473,6 +446,7 @@ export class Configuration {
|
||||
|
||||
constructor(
|
||||
private _defaultConfiguration: ConfigurationModel,
|
||||
private _policyConfiguration: ConfigurationModel,
|
||||
private _localUserConfiguration: ConfigurationModel,
|
||||
private _remoteUserConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(),
|
||||
@@ -483,7 +457,7 @@ export class Configuration {
|
||||
}
|
||||
|
||||
getValue(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): any {
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace);
|
||||
const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(section, overrides, workspace);
|
||||
return consolidateConfigurationModel.getValue(section);
|
||||
}
|
||||
|
||||
@@ -511,11 +485,12 @@ export class Configuration {
|
||||
}
|
||||
|
||||
inspect<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue<C> {
|
||||
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace);
|
||||
const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(key, overrides, workspace);
|
||||
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace);
|
||||
const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration;
|
||||
|
||||
const defaultValue = overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this._defaultConfiguration.freeze().getValue<C>(key);
|
||||
const policyValue = this._policyConfiguration.freeze().getValue<C>(key);
|
||||
const userValue = overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.userConfiguration.freeze().getValue<C>(key);
|
||||
const userLocalValue = overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.localUserConfiguration.freeze().getValue<C>(key);
|
||||
const userRemoteValue = overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.remoteUserConfiguration.freeze().getValue<C>(key);
|
||||
@@ -523,19 +498,21 @@ export class Configuration {
|
||||
const workspaceFolderValue = folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : folderConfigurationModel.freeze().getValue<C>(key) : undefined;
|
||||
const memoryValue = overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue<C>(key) : memoryConfigurationModel.getValue<C>(key);
|
||||
const value = consolidateConfigurationModel.getValue<C>(key);
|
||||
const overrideIdentifiers: string[] = arrays.distinct(arrays.flatten(consolidateConfigurationModel.overrides.map(override => override.identifiers))).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined);
|
||||
const overrideIdentifiers: string[] = arrays.distinct(consolidateConfigurationModel.overrides.map(override => override.identifiers).flat()).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined);
|
||||
|
||||
return {
|
||||
defaultValue: defaultValue,
|
||||
userValue: userValue,
|
||||
userLocalValue: userLocalValue,
|
||||
userRemoteValue: userRemoteValue,
|
||||
workspaceValue: workspaceValue,
|
||||
workspaceFolderValue: workspaceFolderValue,
|
||||
memoryValue: memoryValue,
|
||||
defaultValue,
|
||||
policyValue,
|
||||
userValue,
|
||||
userLocalValue,
|
||||
userRemoteValue,
|
||||
workspaceValue,
|
||||
workspaceFolderValue,
|
||||
memoryValue,
|
||||
value,
|
||||
|
||||
default: defaultValue !== undefined ? { value: this._defaultConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._defaultConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
policy: policyValue !== undefined ? { value: policyValue } : undefined,
|
||||
user: userValue !== undefined ? { value: this.userConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.userConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
userLocal: userLocalValue !== undefined ? { value: this.localUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.localUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
userRemote: userRemoteValue !== undefined ? { value: this.remoteUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
|
||||
@@ -568,6 +545,10 @@ export class Configuration {
|
||||
this._foldersConsolidatedConfigurations.clear();
|
||||
}
|
||||
|
||||
updatePolicyConfiguration(policyConfiguration: ConfigurationModel): void {
|
||||
this._policyConfiguration = policyConfiguration;
|
||||
}
|
||||
|
||||
updateLocalUserConfiguration(localUserConfiguration: ConfigurationModel): void {
|
||||
this._localUserConfiguration = localUserConfiguration;
|
||||
this._userConfiguration = null;
|
||||
@@ -620,6 +601,15 @@ export class Configuration {
|
||||
return { keys, overrides };
|
||||
}
|
||||
|
||||
compareAndUpdatePolicyConfiguration(policyConfiguration: ConfigurationModel): IConfigurationChange {
|
||||
const { added, updated, removed } = compare(this._policyConfiguration, policyConfiguration);
|
||||
const keys = [...added, ...updated, ...removed];
|
||||
if (keys.length) {
|
||||
this.updatePolicyConfiguration(policyConfiguration);
|
||||
}
|
||||
return { keys, overrides: [] };
|
||||
}
|
||||
|
||||
compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): IConfigurationChange {
|
||||
const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user);
|
||||
const keys = [...added, ...updated, ...removed];
|
||||
@@ -698,9 +688,15 @@ export class Configuration {
|
||||
return this._folderConfigurations;
|
||||
}
|
||||
|
||||
private getConsolidateConfigurationModel(overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
|
||||
private getConsolidatedConfigurationModel(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
|
||||
let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace);
|
||||
return overrides.overrideIdentifier ? configurationModel.override(overrides.overrideIdentifier) : configurationModel;
|
||||
if (overrides.overrideIdentifier) {
|
||||
configurationModel = configurationModel.override(overrides.overrideIdentifier);
|
||||
}
|
||||
if (!this._policyConfiguration.isEmpty() && this._policyConfiguration.getValue(section) !== undefined) {
|
||||
configurationModel = configurationModel.merge(this._policyConfiguration);
|
||||
}
|
||||
return configurationModel;
|
||||
}
|
||||
|
||||
private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel {
|
||||
@@ -765,6 +761,11 @@ export class Configuration {
|
||||
overrides: this._defaultConfiguration.overrides,
|
||||
keys: this._defaultConfiguration.keys
|
||||
},
|
||||
policy: {
|
||||
contents: this._policyConfiguration.contents,
|
||||
overrides: this._policyConfiguration.overrides,
|
||||
keys: this._policyConfiguration.keys
|
||||
},
|
||||
user: {
|
||||
contents: this.userConfiguration.contents,
|
||||
overrides: this.userConfiguration.overrides,
|
||||
@@ -812,13 +813,14 @@ export class Configuration {
|
||||
|
||||
static parse(data: IConfigurationData): Configuration {
|
||||
const defaultConfiguration = this.parseConfigurationModel(data.defaults);
|
||||
const policyConfiguration = this.parseConfigurationModel(data.policy);
|
||||
const userConfiguration = this.parseConfigurationModel(data.user);
|
||||
const workspaceConfiguration = this.parseConfigurationModel(data.workspace);
|
||||
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
|
||||
result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1]));
|
||||
return result;
|
||||
}, new ResourceMap<ConfigurationModel>());
|
||||
return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
|
||||
return new Configuration(defaultConfiguration, policyConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
|
||||
}
|
||||
|
||||
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
|
||||
|
||||
@@ -9,17 +9,20 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
|
||||
import { Configuration, ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Configuration, ConfigurationChangeEvent, ConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private configuration: Configuration;
|
||||
private userConfiguration: UserSettings;
|
||||
private readonly defaultConfiguration: DefaultConfiguration;
|
||||
private readonly policyConfiguration: PolicyConfiguration;
|
||||
private readonly userConfiguration: UserSettings;
|
||||
private readonly reloadConfigurationScheduler: RunOnceScheduler;
|
||||
|
||||
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
||||
@@ -27,20 +30,25 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
|
||||
|
||||
constructor(
|
||||
private readonly settingsResource: URI,
|
||||
fileService: IFileService
|
||||
fileService: IFileService,
|
||||
environmentService: IEnvironmentService,
|
||||
logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
this.defaultConfiguration = this._register(new DefaultConfiguration());
|
||||
this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, fileService, environmentService, logService));
|
||||
this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, extUriBiasedIgnorePathCase, fileService));
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
|
||||
this.configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel());
|
||||
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties)));
|
||||
this._register(this.defaultConfiguration.onDidChangeConfiguration(({ defaults, properties }) => this.onDidDefaultConfigurationChange(defaults, properties)));
|
||||
this._register(this.policyConfiguration.onDidChangeConfiguration(model => this.onDidPolicyConfigurationChange(model)));
|
||||
this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
const userConfiguration = await this.userConfiguration.loadConfiguration();
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration);
|
||||
const [defaultModel, policyModel, userModel] = await Promise.all([this.defaultConfiguration.initialize(), this.policyConfiguration.initialize(), this.userConfiguration.loadConfiguration()]);
|
||||
this.configuration = new Configuration(defaultModel, policyModel, userModel);
|
||||
}
|
||||
|
||||
getConfigurationData(): IConfigurationData {
|
||||
@@ -89,9 +97,15 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
|
||||
this.trigger(change, previous, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
private onDidDefaultConfigurationChange(properties: string[]): void {
|
||||
private onDidDefaultConfigurationChange(defaultConfigurationModel: ConfigurationModel, properties: string[]): void {
|
||||
const previous = this.configuration.toData();
|
||||
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), properties);
|
||||
const change = this.configuration.compareAndUpdateDefaultConfiguration(defaultConfigurationModel, properties);
|
||||
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
|
||||
}
|
||||
|
||||
private onDidPolicyConfigurationChange(policyConfiguration: ConfigurationModel): void {
|
||||
const previous = this.configuration.toData();
|
||||
const change = this.configuration.compareAndUpdatePolicyConfiguration(policyConfiguration);
|
||||
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { addToValueTree, IOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { PolicyModel } from 'vs/platform/policy/common/policy';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
export class DefaultConfiguration extends Disposable {
|
||||
|
||||
private readonly _onDidChangeConfiguration = this._register(new Emitter<{ defaults: ConfigurationModel; properties: string[] }>());
|
||||
readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event;
|
||||
|
||||
private _configurationModel: ConfigurationModel | undefined;
|
||||
get configurationModel(): ConfigurationModel {
|
||||
if (!this._configurationModel) {
|
||||
this._configurationModel = new DefaultConfigurationModel(this.getConfigurationDefaultOverrides());
|
||||
}
|
||||
return this._configurationModel;
|
||||
}
|
||||
|
||||
async initialize(): Promise<ConfigurationModel> {
|
||||
this._configurationModel = undefined;
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(({ properties, defaultsOverrides }) => this.onDidUpdateConfiguration(properties, defaultsOverrides)));
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
reload(): ConfigurationModel {
|
||||
this._configurationModel = undefined;
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
protected onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void {
|
||||
this._configurationModel = undefined;
|
||||
this._onDidChangeConfiguration.fire({ defaults: this.configurationModel, properties });
|
||||
}
|
||||
|
||||
protected getConfigurationDefaultOverrides(): IStringDictionary<any> {
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DefaultConfigurationModel extends ConfigurationModel {
|
||||
|
||||
constructor(configurationDefaultsOverrides: IStringDictionary<any> = {}) {
|
||||
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
const keys = Object.keys(properties);
|
||||
const contents: any = Object.create(null);
|
||||
const overrides: IOverrides[] = [];
|
||||
|
||||
for (const key in properties) {
|
||||
const defaultOverrideValue = configurationDefaultsOverrides[key];
|
||||
const value = defaultOverrideValue !== undefined ? defaultOverrideValue : properties[key].default;
|
||||
addToValueTree(contents, key, value, message => console.error(`Conflict in default settings: ${message}`));
|
||||
}
|
||||
for (const key of Object.keys(contents)) {
|
||||
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
|
||||
overrides.push({
|
||||
identifiers: overrideIdentifiersFromKey(key),
|
||||
keys: Object.keys(contents[key]),
|
||||
contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
super(contents, keys, overrides);
|
||||
}
|
||||
}
|
||||
|
||||
export class PolicyConfiguration extends Disposable {
|
||||
|
||||
private readonly _onDidChangeConfiguration = this._register(new Emitter<ConfigurationModel>());
|
||||
readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event;
|
||||
|
||||
private readonly policies: PolicyModel;
|
||||
|
||||
constructor(
|
||||
private readonly defaultConfiguration: DefaultConfiguration,
|
||||
fileService: IFileService,
|
||||
environmentService: IEnvironmentService,
|
||||
logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this.policies = new PolicyModel(fileService, environmentService, logService);
|
||||
}
|
||||
|
||||
private _configurationModel: ConfigurationModel | undefined;
|
||||
get configurationModel(): ConfigurationModel {
|
||||
if (!this._configurationModel) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
const keys: string[] = [];
|
||||
const contents: any = Object.create(null);
|
||||
for (const key of this.defaultConfiguration.configurationModel.keys) {
|
||||
const policyName = configurationProperties[key].policy?.name;
|
||||
if (!policyName) {
|
||||
continue;
|
||||
}
|
||||
const value = this.policies.getPolicy(policyName);
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
keys.push(key);
|
||||
addToValueTree(contents, key, value, message => console.error(`Conflict in policy settings: ${message}`));
|
||||
}
|
||||
this._configurationModel = new ConfigurationModel(contents, keys, []);
|
||||
}
|
||||
return this._configurationModel;
|
||||
}
|
||||
|
||||
async initialize(): Promise<ConfigurationModel> {
|
||||
await this.policies.initialize();
|
||||
this._register(this.policies.onDidChange(e => this.onDidChange()));
|
||||
this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties)));
|
||||
return this.reload();
|
||||
}
|
||||
|
||||
reload(): ConfigurationModel {
|
||||
this._configurationModel = undefined;
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
private onDidDefaultConfigurationChange(properties: string[]): void {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
|
||||
if (properties.some(key => configurationProperties[key].policy?.name)) {
|
||||
this.onDidChange();
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChange(): void {
|
||||
this._onDidChangeConfiguration.fire(this.reload());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import * as assert from 'assert';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { AllKeysConfigurationChangeEvent, Configuration, ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser, DefaultConfigurationModel, mergeChanges } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { AllKeysConfigurationChangeEvent, Configuration, ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser, mergeChanges } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { DefaultConfigurationModel } from 'vs/platform/configuration/common/configurations';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
@@ -496,7 +497,7 @@ suite('Configuration', () => {
|
||||
test('Test update value', () => {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parse(JSON.stringify({ 'a': 1 }));
|
||||
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
|
||||
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel(), new ConfigurationModel());
|
||||
|
||||
testObject.updateValue('a', 2);
|
||||
|
||||
@@ -506,7 +507,7 @@ suite('Configuration', () => {
|
||||
test('Test update value after inspect', () => {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parse(JSON.stringify({ 'a': 1 }));
|
||||
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
|
||||
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel(), new ConfigurationModel());
|
||||
|
||||
testObject.inspect('a', {}, undefined);
|
||||
testObject.updateValue('a', 2);
|
||||
@@ -515,7 +516,7 @@ suite('Configuration', () => {
|
||||
});
|
||||
|
||||
test('Test compare and update default configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateDefaultConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'on',
|
||||
}));
|
||||
@@ -532,7 +533,7 @@ suite('Configuration', () => {
|
||||
});
|
||||
|
||||
test('Test compare and update user configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
@@ -555,7 +556,7 @@ suite('Configuration', () => {
|
||||
});
|
||||
|
||||
test('Test compare and update workspace configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateWorkspaceConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
@@ -578,7 +579,7 @@ suite('Configuration', () => {
|
||||
});
|
||||
|
||||
test('Test compare and update workspace folder configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
@@ -601,7 +602,7 @@ suite('Configuration', () => {
|
||||
});
|
||||
|
||||
test('Test compare and delete workspace folder configuration', () => {
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'editor.fontSize': 12,
|
||||
@@ -627,7 +628,7 @@ suite('Configuration', () => {
|
||||
suite('ConfigurationChangeEvent', () => {
|
||||
|
||||
test('changeEvent affecting keys with new configuration', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'window.zoomLevel': 1,
|
||||
'workbench.editor.enablePreview': false,
|
||||
@@ -653,7 +654,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
});
|
||||
|
||||
test('changeEvent affecting keys when configuration changed', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'window.zoomLevel': 2,
|
||||
'workbench.editor.enablePreview': true,
|
||||
@@ -682,7 +683,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
});
|
||||
|
||||
test('changeEvent affecting overrides with new configuration', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'files.autoSave': 'off',
|
||||
'[markdown]': {
|
||||
@@ -724,7 +725,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
});
|
||||
|
||||
test('changeEvent affecting overrides when configuration changed', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateLocalUserConfiguration(toConfigurationModel({
|
||||
'workbench.editor.enablePreview': true,
|
||||
'[markdown]': {
|
||||
@@ -791,7 +792,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
});
|
||||
|
||||
test('changeEvent affecting workspace folders', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' }));
|
||||
configuration.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
|
||||
configuration.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true }));
|
||||
@@ -881,7 +882,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
});
|
||||
|
||||
test('changeEvent - all', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
|
||||
const data = configuration.toData();
|
||||
const change = mergeChanges(
|
||||
@@ -976,7 +977,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
});
|
||||
|
||||
test('changeEvent affecting tasks and launches', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
|
||||
'launch': {
|
||||
'configuraiton': {}
|
||||
@@ -999,7 +1000,7 @@ suite('ConfigurationChangeEvent', () => {
|
||||
suite('AllKeysConfigurationChangeEvent', () => {
|
||||
|
||||
test('changeEvent', () => {
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
|
||||
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel());
|
||||
configuration.updateDefaultConfiguration(toConfigurationModel({
|
||||
'editor.lineNumbers': 'off',
|
||||
'[markdown]': {
|
||||
|
||||
@@ -12,9 +12,11 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
@@ -22,6 +24,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
suite('ConfigurationService', () => {
|
||||
|
||||
let fileService: IFileService;
|
||||
let environmentService: IEnvironmentService;
|
||||
let settingsResource: URI;
|
||||
const disposables: DisposableStore = new DisposableStore();
|
||||
|
||||
@@ -30,13 +33,14 @@ suite('ConfigurationService', () => {
|
||||
const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider());
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
settingsResource = URI.file('settings.json');
|
||||
environmentService = new TestInstantiationService().mock(IEnvironmentService) as IEnvironmentService;
|
||||
});
|
||||
|
||||
teardown(() => disposables.clear());
|
||||
|
||||
test('simple', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
const config = testObject.getValue<{
|
||||
foo: string;
|
||||
@@ -49,7 +53,7 @@ suite('ConfigurationService', () => {
|
||||
test('config gets flattened', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
const config = testObject.getValue<{
|
||||
testworkbench: {
|
||||
@@ -68,7 +72,7 @@ suite('ConfigurationService', () => {
|
||||
test('error case does not explode', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,'));
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
const config = testObject.getValue<{
|
||||
foo: string;
|
||||
@@ -78,7 +82,7 @@ suite('ConfigurationService', () => {
|
||||
});
|
||||
|
||||
test('missing file does not explode', async () => {
|
||||
const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
|
||||
const config = testObject.getValue<{ foo: string }>();
|
||||
@@ -87,7 +91,7 @@ suite('ConfigurationService', () => {
|
||||
});
|
||||
|
||||
test('trigger configuration change event when file does not exist', async () => {
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
return new Promise<void>((c, e) => {
|
||||
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => {
|
||||
@@ -100,7 +104,7 @@ suite('ConfigurationService', () => {
|
||||
});
|
||||
|
||||
test('trigger configuration change event when file exists', async () => {
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
await testObject.initialize();
|
||||
|
||||
@@ -116,7 +120,7 @@ suite('ConfigurationService', () => {
|
||||
test('reloadConfiguration', async () => {
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
let config = testObject.getValue<{
|
||||
foo: string;
|
||||
@@ -155,7 +159,7 @@ suite('ConfigurationService', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService));
|
||||
let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, environmentService, new NullLogService()));
|
||||
await testObject.initialize();
|
||||
let setting = testObject.getValue<ITestSetting>();
|
||||
|
||||
@@ -163,7 +167,7 @@ suite('ConfigurationService', () => {
|
||||
assert.strictEqual(setting.configuration.service.testSetting, 'isSet');
|
||||
|
||||
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
|
||||
testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
|
||||
setting = testObject.getValue<ITestSetting>();
|
||||
|
||||
@@ -191,7 +195,7 @@ suite('ConfigurationService', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
testObject.initialize();
|
||||
|
||||
let res = testObject.inspect('something.missing');
|
||||
@@ -226,7 +230,7 @@ suite('ConfigurationService', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
|
||||
const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService()));
|
||||
testObject.initialize();
|
||||
|
||||
let res = testObject.inspect('lookup.service.testNullSetting');
|
||||
|
||||
@@ -88,6 +88,7 @@ export interface NativeParsedArgs {
|
||||
'sync'?: 'on' | 'off';
|
||||
'__sandbox'?: boolean;
|
||||
'logsPath'?: string;
|
||||
'policy-file'?: string;
|
||||
|
||||
// chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches
|
||||
'no-proxy-server'?: boolean;
|
||||
|
||||
@@ -87,6 +87,9 @@ export interface IEnvironmentService {
|
||||
telemetryLogResource: URI;
|
||||
serviceMachineIdResource: URI;
|
||||
|
||||
// --- Policy
|
||||
policyFile?: URI;
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
// NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
|
||||
|
||||
@@ -237,6 +237,9 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
|
||||
@memoize
|
||||
get disableWorkspaceTrust(): boolean { return !!this.args['disable-workspace-trust']; }
|
||||
|
||||
@memoize
|
||||
get policyFile(): URI | undefined { return this.args['policy-file'] ? URI.file(this.args['policy-file']) : undefined; }
|
||||
|
||||
get args(): NativeParsedArgs { return this._args; }
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -73,6 +73,7 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
|
||||
'disable-extensions': { type: 'boolean', deprecates: ['disableExtensions'], cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
|
||||
'disable-extension': { type: 'string[]', cat: 't', args: 'ext-id', description: localize('disableExtension', "Disable an extension.") },
|
||||
'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off."), args: ['on | off'] },
|
||||
'policy-file': { type: 'string', cat: 't', description: localize('policyFile', "Path of the file defining policies") },
|
||||
|
||||
'inspect-extensions': { type: 'string', deprecates: ['debugPluginHost'], args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") },
|
||||
'inspect-brk-extensions': { type: 'string', deprecates: ['debugBrkPluginHost'], args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") },
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export type PolicyValue = string | boolean | number;
|
||||
export type Policies = IStringDictionary<PolicyValue>;
|
||||
|
||||
export class PolicyModel extends Disposable {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChange = new Emitter<readonly string[]>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private policies: Policies = {};
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
const policy = this.environmentService.policyFile ? new FilePolicy(this.environmentService.policyFile, this.fileService, this.logService) : new NullPolicy();
|
||||
this.policies = await policy.read();
|
||||
this._register(policy.onDidChange(({ changed, policies }) => {
|
||||
this.policies = policies;
|
||||
this._onDidChange.fire(changed);
|
||||
}));
|
||||
}
|
||||
|
||||
getPolicy(name: string): PolicyValue | undefined {
|
||||
return this.policies[name];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPolicy {
|
||||
readonly onDidChange: Event<{ changed: string[]; policies: Policies }>;
|
||||
read(): Promise<Policies>;
|
||||
}
|
||||
|
||||
class FilePolicy extends Disposable implements IPolicy {
|
||||
|
||||
private policies: Policies = {};
|
||||
|
||||
private readonly _onDidChange = new Emitter<{ changed: string[]; policies: Policies }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private readonly throttledDelayer = this._register(new ThrottledDelayer(500));
|
||||
|
||||
constructor(
|
||||
private readonly file: URI,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this._register(fileService.watch(file));
|
||||
this._register(Event.filter(fileService.onDidFilesChange, e => e.affects(file))(e => this.throttledDelayer.trigger(() => this.onDidFileChange())));
|
||||
}
|
||||
|
||||
async read(): Promise<Policies> {
|
||||
try {
|
||||
const content = await this.fileService.readFile(this.file);
|
||||
this.policies = JSON.parse(content.value.toString());
|
||||
} catch (error) {
|
||||
this.logService.error(error);
|
||||
this.policies = {};
|
||||
}
|
||||
return this.policies;
|
||||
}
|
||||
|
||||
private async onDidFileChange(): Promise<void> {
|
||||
const old = this.policies;
|
||||
await this.read();
|
||||
const changed = this.compare(old, this.policies);
|
||||
if (changed.length > 0) {
|
||||
this._onDidChange.fire({ changed, policies: this.policies });
|
||||
}
|
||||
}
|
||||
|
||||
private compare(old: Policies, current: Policies): string[] {
|
||||
const changed: string[] = [];
|
||||
for (const key of Object.keys(old)) {
|
||||
if (old[key] !== current[key]) {
|
||||
changed.push(key);
|
||||
}
|
||||
}
|
||||
for (const key of Object.keys(current)) {
|
||||
if (old[key] === undefined) {
|
||||
changed.push(key);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NullPolicy extends Disposable implements IPolicy {
|
||||
readonly onDidChange = Event.None;
|
||||
async read(): Promise<Policies> { return {}; }
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export class UserDataSyncClient extends Disposable {
|
||||
|
||||
this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService()));
|
||||
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService));
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService));
|
||||
await configurationService.initialize();
|
||||
this.instantiationService.stub(IConfigurationService, configurationService);
|
||||
this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService));
|
||||
|
||||
@@ -91,7 +91,7 @@ class CliMain extends Disposable {
|
||||
fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService)));
|
||||
|
||||
// Configuration
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService));
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService));
|
||||
await configurationService.initialize();
|
||||
services.set(IConfigurationService, configurationService);
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
|
||||
services.set(IFileService, fileService);
|
||||
fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService)));
|
||||
|
||||
const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService);
|
||||
const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, environmentService, logService);
|
||||
services.set(IConfigurationService, configurationService);
|
||||
|
||||
const extensionHostStatusService = new ExtensionHostStatusService();
|
||||
|
||||
@@ -256,7 +256,7 @@ export class ExtHostConfigProvider {
|
||||
return {
|
||||
key,
|
||||
|
||||
defaultValue: config.default?.value,
|
||||
defaultValue: config.policy?.value ?? config.default?.value,
|
||||
globalValue: config.user?.value,
|
||||
workspaceValue: config.workspace?.value,
|
||||
workspaceFolderValue: config.workspaceFolder?.value,
|
||||
|
||||
@@ -43,6 +43,7 @@ suite('ExtHostConfiguration', function () {
|
||||
function createConfigurationData(contents: any): IConfigurationInitData {
|
||||
return {
|
||||
defaults: new ConfigurationModel(contents),
|
||||
policy: new ConfigurationModel(),
|
||||
user: new ConfigurationModel(contents),
|
||||
workspace: new ConfigurationModel(),
|
||||
folders: [],
|
||||
@@ -279,6 +280,7 @@ suite('ExtHostConfiguration', function () {
|
||||
'wordWrap': 'off'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
policy: new ConfigurationModel(),
|
||||
user: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
@@ -328,6 +330,7 @@ suite('ExtHostConfiguration', function () {
|
||||
'wordWrap': 'off'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
policy: new ConfigurationModel(),
|
||||
user: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
@@ -405,6 +408,7 @@ suite('ExtHostConfiguration', function () {
|
||||
'lineNumbers': 'on'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
policy: new ConfigurationModel(),
|
||||
user: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
@@ -508,6 +512,7 @@ suite('ExtHostConfiguration', function () {
|
||||
'editor.wordWrap': 'bounded',
|
||||
}
|
||||
}),
|
||||
policy: new ConfigurationModel(),
|
||||
user: toConfigurationModel({
|
||||
'editor.wordWrap': 'bounded',
|
||||
'[typescript]': {
|
||||
|
||||
@@ -175,6 +175,7 @@ class EditSettingRenderer extends Disposable {
|
||||
|
||||
constructor(private editor: ICodeEditor, private primarySettingsModel: ISettingsEditorModel,
|
||||
private settingHighlighter: SettingHighlighter,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService
|
||||
) {
|
||||
@@ -283,6 +284,9 @@ class EditSettingRenderer extends Disposable {
|
||||
return this.getSettingsAtLineNumber(lineNumber).filter(setting => {
|
||||
const configurationNode = configurationMap[setting.key];
|
||||
if (configurationNode) {
|
||||
if (configurationNode.policy && this.configurationService.inspect(setting.key).policyValue !== undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this.isDefaultSettings()) {
|
||||
if (setting.key === 'launch') {
|
||||
// Do not show because of https://github.com/microsoft/vscode/issues/32593
|
||||
@@ -522,6 +526,9 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc
|
||||
for (const setting of section.settings) {
|
||||
const configuration = configurationRegistry[setting.key];
|
||||
if (configuration) {
|
||||
if (this.handlePolicyConfiguration(setting, configuration, markerData)) {
|
||||
continue;
|
||||
}
|
||||
switch (this.settingsEditorModel.configurationTarget) {
|
||||
case ConfigurationTarget.USER_LOCAL:
|
||||
this.handleLocalUserConfiguration(setting, configuration, markerData);
|
||||
@@ -550,6 +557,25 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc
|
||||
return markerData;
|
||||
}
|
||||
|
||||
private handlePolicyConfiguration(setting: ISetting, configuration: IConfigurationPropertySchema, markerData: IMarkerData[]): boolean {
|
||||
if (!configuration.policy) {
|
||||
return false;
|
||||
}
|
||||
if (this.configurationService.inspect(setting.key).policyValue === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this.settingsEditorModel.configurationTarget === ConfigurationTarget.DEFAULT) {
|
||||
return false;
|
||||
}
|
||||
markerData.push({
|
||||
severity: MarkerSeverity.Hint,
|
||||
tags: [MarkerTag.Unnecessary],
|
||||
...setting.range,
|
||||
message: nls.localize('unsupportedPolicySetting', "This setting cannot be applied because it is configured in the system policy.")
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private handleLocalUserConfiguration(setting: ISetting, configuration: IConfigurationPropertySchema, markerData: IMarkerData[]): void {
|
||||
if (this.environmentService.remoteAuthority && (configuration.scope === ConfigurationScope.MACHINE || configuration.scope === ConfigurationScope.MACHINE_OVERRIDABLE)) {
|
||||
markerData.push({
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as errors from 'vs/base/common/errors';
|
||||
import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult, FileOperation, FileOperationEvent } from 'vs/platform/files/common/files';
|
||||
import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, DefaultConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, UserSettings } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
|
||||
import { TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||
@@ -26,8 +26,9 @@ import { joinPath } from 'vs/base/common/resources';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
import { DefaultConfiguration as BaseDefaultConfiguration } from 'vs/platform/configuration/common/configurations';
|
||||
|
||||
export class DefaultConfiguration extends Disposable {
|
||||
export class DefaultConfiguration extends BaseDefaultConfiguration {
|
||||
|
||||
static readonly DEFAULT_OVERRIDES_CACHE_EXISTS_KEY = 'DefaultOverridesCacheExists';
|
||||
|
||||
@@ -35,9 +36,6 @@ export class DefaultConfiguration extends Disposable {
|
||||
private cachedConfigurationDefaultsOverrides: IStringDictionary<any> = {};
|
||||
private readonly cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' };
|
||||
|
||||
private readonly _onDidChangeConfiguration = this._register(new Emitter<{ defaults: ConfigurationModel; properties: string[] }>());
|
||||
readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event;
|
||||
|
||||
private updateCache: boolean = false;
|
||||
|
||||
constructor(
|
||||
@@ -50,27 +48,20 @@ export class DefaultConfiguration extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _configurationModel: ConfigurationModel | undefined;
|
||||
get configurationModel(): ConfigurationModel {
|
||||
if (!this._configurationModel) {
|
||||
this._configurationModel = new DefaultConfigurationModel(this.cachedConfigurationDefaultsOverrides);
|
||||
}
|
||||
return this._configurationModel;
|
||||
protected override getConfigurationDefaultOverrides(): IStringDictionary<any> {
|
||||
return this.cachedConfigurationDefaultsOverrides;
|
||||
}
|
||||
|
||||
async initialize(): Promise<ConfigurationModel> {
|
||||
override async initialize(): Promise<ConfigurationModel> {
|
||||
await this.initializeCachedConfigurationDefaultsOverrides();
|
||||
this._configurationModel = undefined;
|
||||
this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties, defaultsOverrides }) => this.onDidUpdateConfiguration(properties, defaultsOverrides)));
|
||||
return this.configurationModel;
|
||||
return super.initialize();
|
||||
}
|
||||
|
||||
reload(): ConfigurationModel {
|
||||
override reload(): ConfigurationModel {
|
||||
this.updateCache = true;
|
||||
this.cachedConfigurationDefaultsOverrides = {};
|
||||
this._configurationModel = undefined;
|
||||
this.updateCachedConfigurationDefaultsOverrides();
|
||||
return this.configurationModel;
|
||||
return super.reload();
|
||||
}
|
||||
|
||||
private initiaizeCachedConfigurationDefaultsOverridesPromise: Promise<void> | undefined;
|
||||
@@ -92,9 +83,8 @@ export class DefaultConfiguration extends Disposable {
|
||||
return this.initiaizeCachedConfigurationDefaultsOverridesPromise;
|
||||
}
|
||||
|
||||
private onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void {
|
||||
this._configurationModel = undefined;
|
||||
this._onDidChangeConfiguration.fire({ defaults: this.configurationModel, properties });
|
||||
protected override onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void {
|
||||
super.onDidUpdateConfiguration(properties, defaultsOverrides);
|
||||
if (defaultsOverrides) {
|
||||
this.updateCachedConfigurationDefaultsOverrides();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat
|
||||
import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
import { ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { PolicyConfiguration } from 'vs/platform/configuration/common/configurations';
|
||||
import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels';
|
||||
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -54,7 +55,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
|
||||
private readonly configurationCache: IConfigurationCache;
|
||||
private _configuration: Configuration;
|
||||
private initialized: boolean = false;
|
||||
private defaultConfiguration: DefaultConfiguration;
|
||||
private readonly defaultConfiguration: DefaultConfiguration;
|
||||
private readonly policyConfiguration: PolicyConfiguration;
|
||||
private localUserConfiguration: UserConfiguration;
|
||||
private remoteUserConfiguration: RemoteUserConfiguration | null = null;
|
||||
private workspaceConfiguration: WorkspaceConfiguration;
|
||||
@@ -109,12 +111,13 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
|
||||
|
||||
this.initRemoteUserConfigurationBarrier = new Barrier();
|
||||
this.completeWorkspaceBarrier = new Barrier();
|
||||
this.defaultConfiguration = new DefaultConfiguration(configurationCache, environmentService);
|
||||
this.defaultConfiguration = this._register(new DefaultConfiguration(configurationCache, environmentService));
|
||||
this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, fileService, environmentService, logService));
|
||||
this.configurationCache = configurationCache;
|
||||
this.fileService = fileService;
|
||||
this.uriIdentityService = uriIdentityService;
|
||||
this.logService = logService;
|
||||
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
|
||||
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
|
||||
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
|
||||
this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService));
|
||||
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
|
||||
@@ -138,6 +141,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
|
||||
}));
|
||||
|
||||
this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties, defaults }) => this.onDefaultConfigurationChanged(defaults, properties)));
|
||||
this._register(this.policyConfiguration.onDidChangeConfiguration(configurationModel => this.onPolicyConfigurationChanged(configurationModel)));
|
||||
|
||||
this.workspaceEditingQueue = new Queue<void>();
|
||||
}
|
||||
@@ -572,12 +576,18 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
|
||||
private async initializeConfiguration(): Promise<void> {
|
||||
await this.defaultConfiguration.initialize();
|
||||
|
||||
mark('code/willInitUserConfiguration');
|
||||
const { local, remote } = await this.initializeUserConfiguration();
|
||||
mark('code/didInitUserConfiguration');
|
||||
const [, user] = await Promise.all([
|
||||
this.policyConfiguration.initialize(),
|
||||
(async () => {
|
||||
mark('code/willInitUserConfiguration');
|
||||
const result = await this.initializeUserConfiguration();
|
||||
mark('code/didInitUserConfiguration');
|
||||
return result;
|
||||
})()
|
||||
]);
|
||||
|
||||
mark('code/willInitWorkspaceConfiguration');
|
||||
await this.loadConfiguration(local, remote);
|
||||
await this.loadConfiguration(user.local, user.remote);
|
||||
mark('code/didInitWorkspaceConfiguration');
|
||||
}
|
||||
|
||||
@@ -640,7 +650,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
|
||||
folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));
|
||||
|
||||
const currentConfiguration = this._configuration;
|
||||
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
|
||||
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
|
||||
|
||||
if (this.initialized) {
|
||||
const change = this._configuration.compare(currentConfiguration);
|
||||
@@ -692,6 +702,12 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
|
||||
}
|
||||
}
|
||||
|
||||
private onPolicyConfigurationChanged(policyConfiguration: ConfigurationModel): void {
|
||||
const previous = { data: this._configuration.toData(), workspace: this.workspace };
|
||||
const change = this._configuration.compareAndUpdatePolicyConfiguration(policyConfiguration);
|
||||
this.triggerConfigurationChange(change, previous, ConfigurationTarget.DEFAULT);
|
||||
}
|
||||
|
||||
private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void {
|
||||
const previous = { data: this._configuration.toData(), workspace: this.workspace };
|
||||
const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration);
|
||||
|
||||
@@ -92,6 +92,11 @@ export const enum ConfigurationEditingErrorCode {
|
||||
*/
|
||||
ERROR_INVALID_CONFIGURATION,
|
||||
|
||||
/**
|
||||
* Error when trying to write a policy configuration
|
||||
*/
|
||||
ERROR_POLICY_CONFIGURATION,
|
||||
|
||||
/**
|
||||
* Internal Error.
|
||||
*/
|
||||
@@ -359,6 +364,7 @@ export class ConfigurationEditingService {
|
||||
switch (error) {
|
||||
|
||||
// API constraints
|
||||
case ConfigurationEditingErrorCode.ERROR_POLICY_CONFIGURATION: return nls.localize('errorPolicyConfiguration', "Unable to write {0} because it is configured in system policy.", operation.key);
|
||||
case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to {0} because {1} is not a registered configuration.", this.stringifyTarget(target), operation.key);
|
||||
case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION: return nls.localize('errorInvalidWorkspaceConfigurationApplication', "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", operation.key);
|
||||
case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_MACHINE: return nls.localize('errorInvalidWorkspaceConfigurationMachine', "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", operation.key);
|
||||
@@ -492,6 +498,10 @@ export class ConfigurationEditingService {
|
||||
|
||||
private async validate(target: EditableConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationUpdateOverrides): Promise<void> {
|
||||
|
||||
if (this.configurationService.inspect(operation.key).policyValue !== undefined) {
|
||||
throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_POLICY_CONFIGURATION, target, operation);
|
||||
}
|
||||
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
|
||||
const configurationScope = configurationProperties[operation.key]?.scope;
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ export class Configuration extends BaseConfiguration {
|
||||
|
||||
constructor(
|
||||
defaults: ConfigurationModel,
|
||||
policy: ConfigurationModel,
|
||||
localUser: ConfigurationModel,
|
||||
remoteUser: ConfigurationModel,
|
||||
workspaceConfiguration: ConfigurationModel,
|
||||
@@ -105,7 +106,7 @@ export class Configuration extends BaseConfiguration {
|
||||
memoryConfiguration: ConfigurationModel,
|
||||
memoryConfigurationByResource: ResourceMap<ConfigurationModel>,
|
||||
private readonly _workspace?: Workspace) {
|
||||
super(defaults, localUser, remoteUser, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource);
|
||||
super(defaults, policy, localUser, remoteUser, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource);
|
||||
}
|
||||
|
||||
override getValue(key: string | undefined, overrides: IConfigurationOverrides = {}): any {
|
||||
|
||||
@@ -155,14 +155,14 @@ suite('Workspace Configuration', () => {
|
||||
|
||||
test('Test compare same configurations', () => {
|
||||
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
|
||||
const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
configuration1.updateDefaultConfiguration(defaultConfigurationModel);
|
||||
configuration1.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } }));
|
||||
configuration1.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' }));
|
||||
configuration1.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'editor.fontSize': 14 }));
|
||||
configuration1.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'editor.wordWrap': 'on' }));
|
||||
|
||||
const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
configuration2.updateDefaultConfiguration(defaultConfigurationModel);
|
||||
configuration2.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } }));
|
||||
configuration2.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' }));
|
||||
@@ -176,14 +176,14 @@ suite('Workspace Configuration', () => {
|
||||
|
||||
test('Test compare different configurations', () => {
|
||||
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
|
||||
const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
configuration1.updateDefaultConfiguration(defaultConfigurationModel);
|
||||
configuration1.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } }));
|
||||
configuration1.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' }));
|
||||
configuration1.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'editor.fontSize': 14 }));
|
||||
configuration1.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'editor.wordWrap': 'on' }));
|
||||
|
||||
const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), workspace);
|
||||
configuration2.updateDefaultConfiguration(defaultConfigurationModel);
|
||||
configuration2.updateLocalUserConfiguration(toConfigurationModel({ 'workbench.enableTabs': true, '[typescript]': { 'editor.insertSpaces': true } }));
|
||||
configuration2.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.fontSize': 11 }));
|
||||
|
||||
Reference in New Issue
Block a user