未验证 提交 5e8d276f 编写于 作者: S Sandeep Somavarapu 提交者: GitHub

Merge pull request #86854 from microsoft/sandy081/wip

Enhance Resource configuration service
......@@ -6,11 +6,16 @@
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IPosition } from 'vs/editor/common/core/position';
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ITextResourceConfigurationService = createDecorator<ITextResourceConfigurationService>('textResourceConfigurationService');
export interface IResourceConfigurationChangeEvent {
readonly affectedKeys: string[];
affectsConfiguration(resource: URI, configuration: string): boolean;
}
export interface ITextResourceConfigurationService {
_serviceBrand: undefined;
......@@ -18,7 +23,7 @@ export interface ITextResourceConfigurationService {
/**
* Event that fires when the configuration changes.
*/
onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
onDidChangeConfiguration: Event<IResourceConfigurationChangeEvent>;
/**
* Fetches the value of the section for the given resource by applying language overrides.
......@@ -32,6 +37,8 @@ export interface ITextResourceConfigurationService {
getValue<T>(resource: URI | undefined, section?: string): T;
getValue<T>(resource: URI | undefined, position?: IPosition, section?: string): T;
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void>;
}
export const ITextResourcePropertiesService = createDecorator<ITextResourcePropertiesService>('textResourcePropertiesService');
......
......@@ -9,15 +9,15 @@ import { URI } from 'vs/base/common/uri';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITextResourceConfigurationService, IResourceConfigurationChangeEvent } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationService, ConfigurationTarget, IConfigurationValue, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService {
public _serviceBrand: undefined;
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
private readonly _onDidChangeConfiguration: Emitter<IResourceConfigurationChangeEvent> = this._register(new Emitter<IResourceConfigurationChangeEvent>());
public readonly onDidChangeConfiguration: Event<IResourceConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
......@@ -25,7 +25,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex
@IModeService private readonly modeService: IModeService,
) {
super();
this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(e)));
this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(this.toResourceConfigurationChangeEvent(e))));
}
getValue<T>(resource: URI, section?: string): T;
......@@ -37,6 +37,67 @@ export class TextResourceConfigurationService extends Disposable implements ITex
return this._getValue(resource, null, typeof arg2 === 'string' ? arg2 : undefined);
}
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void> {
const language = this.getLanguage(resource, null);
const configurationValue = this.configurationService.inspect(key, { resource, overrideIdentifier: language });
if (configurationTarget === undefined) {
configurationTarget = this.deriveConfigurationTarget(configurationValue, language);
}
switch (configurationTarget) {
case ConfigurationTarget.MEMORY:
return this._updateValue(key, value, configurationTarget, configurationValue.memoryTarget?.override, resource, language);
case ConfigurationTarget.WORKSPACE_FOLDER:
return this._updateValue(key, value, configurationTarget, configurationValue.workspaceFolderTarget?.override, resource, language);
case ConfigurationTarget.WORKSPACE:
return this._updateValue(key, value, configurationTarget, configurationValue.workspaceTarget?.override, resource, language);
case ConfigurationTarget.USER_REMOTE:
return this._updateValue(key, value, configurationTarget, configurationValue.userRemoteTarget?.override, resource, language);
default:
return this._updateValue(key, value, configurationTarget, configurationValue.userLocalTarget?.override, resource, language);
}
}
private _updateValue(key: string, value: any, configurationTarget: ConfigurationTarget, overriddenValue: any | undefined, resource: URI, language: string | null): Promise<void> {
if (language && overriddenValue !== undefined) {
return this.configurationService.updateValue(key, value, { resource, overrideIdentifier: language }, configurationTarget);
} else {
return this.configurationService.updateValue(key, value, { resource }, configurationTarget);
}
}
private deriveConfigurationTarget(configurationValue: IConfigurationValue<any>, language: string | null): ConfigurationTarget {
if (language) {
if (configurationValue.memoryTarget?.override !== undefined) {
return ConfigurationTarget.MEMORY;
}
if (configurationValue.workspaceFolderTarget?.override !== undefined) {
return ConfigurationTarget.WORKSPACE_FOLDER;
}
if (configurationValue.workspaceTarget?.override !== undefined) {
return ConfigurationTarget.WORKSPACE;
}
if (configurationValue.userRemoteTarget?.override !== undefined) {
return ConfigurationTarget.USER_REMOTE;
}
if (configurationValue.userLocalTarget?.override !== undefined) {
return ConfigurationTarget.USER_LOCAL;
}
}
if (configurationValue.memoryTarget?.value !== undefined) {
return ConfigurationTarget.MEMORY;
}
if (configurationValue.workspaceFolderTarget?.value !== undefined) {
return ConfigurationTarget.WORKSPACE_FOLDER;
}
if (configurationValue.workspaceTarget?.value !== undefined) {
return ConfigurationTarget.WORKSPACE;
}
if (configurationValue.userRemoteTarget?.value !== undefined) {
return ConfigurationTarget.USER_REMOTE;
}
return ConfigurationTarget.USER_LOCAL;
}
private _getValue<T>(resource: URI, position: IPosition | null, section: string | undefined): T {
const language = resource ? this.getLanguage(resource, position) : undefined;
if (typeof section === 'undefined') {
......@@ -51,6 +112,15 @@ export class TextResourceConfigurationService extends Disposable implements ITex
return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language;
}
return this.modeService.getModeIdByFilepathOrFirstLine(resource);
}
private toResourceConfigurationChangeEvent(configurationChangeEvent: IConfigurationChangeEvent): IResourceConfigurationChangeEvent {
return {
affectedKeys: configurationChangeEvent.affectedKeys,
affectsConfiguration: (resource: URI, configuration: string) => {
const overrideIdentifier = this.getLanguage(resource, null);
return configurationChangeEvent.affectsConfiguration(configuration, { resource, overrideIdentifier });
}
};
}
}
......@@ -23,9 +23,9 @@ import { ITextModel, ITextSnapshot } from 'vs/editor/common/model';
import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { ITextResourceConfigurationService, ITextResourcePropertiesService, IResourceConfigurationChangeEvent } from 'vs/editor/common/services/resourceConfiguration';
import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel, IConfigurationValue, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IShowResult } from 'vs/platform/dialogs/common/dialogs';
......@@ -460,13 +460,7 @@ export class SimpleConfigurationService implements IConfigurationService {
return Promise.resolve();
}
public inspect<C>(key: string, options: IConfigurationOverrides = {}): {
default: C,
user: C,
workspace?: C,
workspaceFolder?: C
value: C,
} {
public inspect<C>(key: string, options: IConfigurationOverrides = {}): IConfigurationValue<C> {
return this.configuration().inspect<C>(key, options, undefined);
}
......@@ -497,12 +491,12 @@ export class SimpleResourceConfigurationService implements ITextResourceConfigur
_serviceBrand: undefined;
private readonly _onDidChangeConfiguration = new Emitter<IConfigurationChangeEvent>();
private readonly _onDidChangeConfiguration = new Emitter<IResourceConfigurationChangeEvent>();
public readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event;
constructor(private readonly configurationService: SimpleConfigurationService) {
this.configurationService.onDidChangeConfiguration((e) => {
this._onDidChangeConfiguration.fire(e);
this._onDidChangeConfiguration.fire({ affectedKeys: e.affectedKeys, affectsConfiguration: (resource: URI, configuration: string) => e.affectsConfiguration(configuration) });
});
}
......@@ -516,6 +510,10 @@ export class SimpleResourceConfigurationService implements ITextResourceConfigur
}
return this.configurationService.getValue<T>(section);
}
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void> {
return this.configurationService.updateValue(key, value, { resource }, configurationTarget);
}
}
export class SimpleResourcePropertiesService implements ITextResourcePropertiesService {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IConfigurationValue, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl';
import { URI } from 'vs/base/common/uri';
suite('TextResourceConfigurationService - Update', () => {
let configurationValue: IConfigurationValue<any> = {};
let updateArgs: any[];
let configurationService = new class extends TestConfigurationService {
inspect() {
return configurationValue;
}
updateValue() {
updateArgs = [...arguments];
return Promise.resolve();
}
}();
let language: string | null = null;
let testObject: TextResourceConfigurationService;
setup(() => {
const instantiationService = new TestInstantiationService();
instantiationService.stub(IModelService, <Partial<IModelService>>{ getModel() { return null; } });
instantiationService.stub(IModeService, <Partial<IModeService>>{ getModeIdByFilepathOrFirstLine() { return language; } });
instantiationService.stub(IConfigurationService, configurationService);
testObject = instantiationService.createInstance(TextResourceConfigurationService);
});
test('updateValue writes without target and overrides when no language is defined', async () => {
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes with target and without overrides when no language is defined', async () => {
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.USER_LOCAL);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into given memory target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '1' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.MEMORY);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.MEMORY]);
});
test('updateValue writes into given workspace target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.WORKSPACE);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into given user target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.USER);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER]);
});
test('updateValue writes into given workspace folder target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2', override: '1' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.WORKSPACE_FOLDER);
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE_FOLDER]);
});
test('updateValue writes into derived workspace folder target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.WORKSPACE_FOLDER]);
});
test('updateValue writes into derived workspace folder target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2', override: '1' },
workspaceFolderTarget: { value: '2', override: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE_FOLDER]);
});
test('updateValue writes into derived workspace target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into derived workspace target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2', override: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into derived workspace target with overrides and value defined in folder', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1', override: '3' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2', override: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into derived user remote target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
userRemoteTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user remote target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
userRemoteTarget: { value: '2', override: '3' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user remote target with overrides and value defined in workspace', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
userRemoteTarget: { value: '2', override: '3' },
workspaceTarget: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user remote target with overrides and value defined in workspace folder', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '1' },
userRemoteTarget: { value: '2', override: '3' },
workspaceTarget: { value: '3' },
workspaceFolderTarget: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '3' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides and value is defined in remote', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '3' },
userRemoteTarget: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides and value is defined in workspace', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '3' },
workspace: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides and value is defined in workspace folder', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1', override: '3' },
userLocalTarget: { value: '2', override: '3' },
userRemoteTarget: { value: '3' },
workspaceFolder: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue when not changed', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
});
......@@ -11,7 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { ResourceMap } from 'vs/base/common/map';
import { IStringDictionary } from 'vs/base/common/collections';
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
......@@ -48,18 +48,41 @@ export function ConfigurationTargetToString(configurationTarget: ConfigurationTa
}
}
export interface IConfigurationChange {
keys: string[];
overrides: [string, string[]][];
}
export interface IConfigurationChangeEvent {
source: ConfigurationTarget;
affectedKeys: string[];
affectsConfiguration(configuration: string, resource?: URI): boolean;
readonly source: ConfigurationTarget;
readonly affectedKeys: string[];
readonly change: IConfigurationChange;
affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean;
// Following data is used for telemetry
sourceConfig: any;
readonly sourceConfig: any;
}
// Following data is used for Extension host configuration event
changedConfiguration: IConfigurationModel;
changedConfigurationByResource: ResourceMap<IConfigurationModel>;
export interface IConfigurationValue<T> {
readonly default?: T;
readonly user?: T;
readonly userLocal?: T;
readonly userRemote?: T;
readonly workspace?: T;
readonly workspaceFolder?: T;
readonly memory?: T;
readonly value?: T;
readonly defaultTarget?: { value?: T, override?: T };
readonly userTarget?: { value?: T, override?: T };
readonly userLocalTarget?: { value?: T, override?: T };
readonly userRemoteTarget?: { value?: T, override?: T };
readonly workspaceTarget?: { value?: T, override?: T };
readonly workspaceFolderTarget?: { value?: T, override?: T };
readonly memoryTarget?: { value?: T, override?: T };
}
export interface IConfigurationService {
......@@ -87,18 +110,9 @@ export interface IConfigurationService {
updateValue(key: string, value: any, target: ConfigurationTarget): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise<void>;
reloadConfiguration(folder?: IWorkspaceFolder): Promise<void>;
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T>;
inspect<T>(key: string, overrides?: IConfigurationOverrides): {
default: T,
user: T,
userLocal?: T,
userRemote?: T,
workspace?: T,
workspaceFolder?: T,
memory?: T,
value: T,
};
reloadConfiguration(folder?: IWorkspaceFolder): Promise<void>;
keys(): {
default: string[];
......@@ -116,6 +130,7 @@ export interface IConfigurationModel {
}
export interface IOverrides {
keys: string[];
contents: any;
identifiers: string[];
}
......@@ -127,20 +142,76 @@ export interface IConfigurationData {
folders: [UriComponents, IConfigurationModel][];
}
export function compare(from: IConfigurationModel, to: IConfigurationModel): { added: string[], removed: string[], updated: string[] } {
const added = to.keys.filter(key => from.keys.indexOf(key) === -1);
const removed = from.keys.filter(key => to.keys.indexOf(key) === -1);
export interface IConfigurationCompareResult {
added: string[];
removed: string[];
updated: string[];
overrides: [string, string[]][];
}
export function compare(from: IConfigurationModel | undefined, to: IConfigurationModel | undefined): IConfigurationCompareResult {
const added = to
? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]
: [];
const removed = from
? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]
: [];
const updated: string[] = [];
if (to && from) {
for (const key of from.keys) {
if (to.keys.indexOf(key) !== -1) {
const value1 = getConfigurationValue(from.contents, key);
const value2 = getConfigurationValue(to.contents, key);
if (!objects.equals(value1, value2)) {
updated.push(key);
}
}
}
}
const overrides: [string, string[]][] = [];
const byOverrideIdentifier = (overrides: IOverrides[]): IStringDictionary<IOverrides> => {
const result: IStringDictionary<IOverrides> = {};
for (const override of overrides) {
for (const identifier of override.identifiers) {
result[keyFromOverrideIdentifier(identifier)] = override;
}
}
return result;
};
const toOverridesByIdentifier: IStringDictionary<IOverrides> = to ? byOverrideIdentifier(to.overrides) : {};
const fromOverridesByIdentifier: IStringDictionary<IOverrides> = from ? byOverrideIdentifier(from.overrides) : {};
if (Object.keys(toOverridesByIdentifier).length) {
for (const key of added) {
const override = toOverridesByIdentifier[key];
if (override) {
overrides.push([overrideIdentifierFromKey(key), override.keys]);
}
}
}
if (Object.keys(fromOverridesByIdentifier).length) {
for (const key of removed) {
const override = fromOverridesByIdentifier[key];
if (override) {
overrides.push([overrideIdentifierFromKey(key), override.keys]);
}
}
}
if (Object.keys(toOverridesByIdentifier).length && Object.keys(fromOverridesByIdentifier).length) {
for (const key of updated) {
const fromOverride = fromOverridesByIdentifier[key];
const toOverride = toOverridesByIdentifier[key];
if (fromOverride && toOverride) {
const result = compare({ contents: fromOverride.contents, keys: fromOverride.keys, overrides: [] }, { contents: toOverride.contents, keys: toOverride.keys, overrides: [] });
overrides.push([overrideIdentifierFromKey(key), [...result.added, ...result.removed, ...result.updated]]);
}
}
}
return { added, removed, updated };
return { added, removed, updated, overrides };
}
export function toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] {
......@@ -156,6 +227,7 @@ export function toOverrides(raw: any, conflictReporter: (message: string) => voi
}
overrides.push({
identifiers: [overrideIdentifierFromKey(key).trim()],
keys: Object.keys(overrideRaw),
contents: toValuesTree(overrideRaw, conflictReporter)
});
}
......@@ -290,10 +362,10 @@ export function getMigratedSettingValue<T>(configurationService: IConfigurationS
const legacySetting = configurationService.inspect<T>(legacySettingName);
if (typeof setting.user !== 'undefined' || typeof setting.workspace !== 'undefined' || typeof setting.workspaceFolder !== 'undefined') {
return setting.value;
return setting.value!;
} else if (typeof legacySetting.user !== 'undefined' || typeof legacySetting.workspace !== 'undefined' || typeof legacySetting.workspaceFolder !== 'undefined') {
return legacySetting.value;
return legacySetting.value!;
} else {
return setting.default;
return setting.default!;
}
}
......@@ -6,8 +6,8 @@
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationModelParser, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { Event, Emitter } from 'vs/base/common/event';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigWatcher } from 'vs/base/node/config';
......@@ -79,13 +79,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
return Promise.reject(new Error('not supported'));
}
inspect<T>(key: string): {
default: T,
user: T,
workspace?: T,
workspaceFolder?: T
value: T
} {
inspect<T>(key: string): IConfigurationValue<T> {
return this.configuration.inspect<T>(key, {}, undefined);
}
......@@ -109,21 +103,22 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
}
private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {
const { added, updated, removed } = compare(this.configuration.localUserConfiguration, userConfigurationModel);
const changedKeys = [...added, ...updated, ...removed];
if (changedKeys.length) {
this.configuration.updateLocalUserConfiguration(userConfigurationModel);
this.trigger(changedKeys, ConfigurationTarget.USER);
}
const previous = this.configuration.toData();
const change = this.configuration.compareAndUpdateLocalUserConfiguration(userConfigurationModel);
this.trigger(change, previous, ConfigurationTarget.USER);
}
private onDidDefaultConfigurationChange(keys: string[]): void {
this.configuration.updateDefaultConfiguration(new DefaultConfigurationModel());
this.trigger(keys, ConfigurationTarget.DEFAULT);
const previous = this.configuration.toData();
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), keys);
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
}
private trigger(keys: string[], source: ConfigurationTarget): void {
this._onDidChangeConfiguration.fire(new ConfigurationChangeEvent().change(keys).telemetryData(source, this.getTargetConfiguration(source)));
private trigger(configurationChange: IConfigurationChange, previous: IConfigurationData, source: ConfigurationTarget): void {
const event = new ConfigurationChangeEvent(configurationChange, { data: previous }, this.configuration);
event.source = source;
event.sourceConfig = this.getTargetConfiguration(source);
this._onDidChangeConfiguration.fire(event);
}
private getTargetConfiguration(target: ConfigurationTarget): any {
......
......@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { merge, removeFromValueTree } from 'vs/platform/configuration/common/configuration';
import { mergeChanges } from 'vs/platform/configuration/common/configurationModels';
suite('Configuration', () => {
......@@ -105,3 +106,42 @@ suite('Configuration', () => {
});
});
suite('Configuration Changes: Merge', () => {
test('merge only keys', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] });
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd'], overrides: [] });
});
test('merge only keys with duplicates', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] }, { keys: ['a', 'd', 'e'], overrides: [] });
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd', 'e'], overrides: [] });
});
test('merge only overrides', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']]] }, { keys: [], overrides: [['b', ['3', '4']]] });
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2']], ['b', ['3', '4']]] });
});
test('merge only overrides with duplicates', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: [], overrides: [['b', ['3', '4']]] }, { keys: [], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
});
test('merge', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: ['b'], overrides: [['b', ['3', '4']]] }, { keys: ['c', 'a'], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
assert.deepEqual(actual, { keys: ['b', 'c', 'a'], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
});
test('merge single change', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
assert.deepEqual(actual, { keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
});
test('merge no changes', () => {
const actual = mergeChanges();
assert.deepEqual(actual, { keys: [], overrides: [] });
});
});
......@@ -5,12 +5,18 @@
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides, IConfigurationValue } from 'vs/platform/configuration/common/configuration';
import { Emitter } from 'vs/base/common/event';
export class TestConfigurationService implements IConfigurationService {
public _serviceBrand: undefined;
private configuration = Object.create(null);
private configuration: any;
readonly onDidChangeConfiguration = new Emitter<any>().event;
constructor(configuration?: any) {
this.configuration = configuration || Object.create(null);
}
private configurationByRoot: TernarySearchTree<any> = TernarySearchTree.forPaths<any>();
......@@ -33,7 +39,7 @@ export class TestConfigurationService implements IConfigurationService {
return configuration;
}
public updateValue(key: string, overrides?: IConfigurationOverrides): Promise<void> {
public updateValue(key: string, value: any): Promise<void> {
return Promise.resolve(undefined);
}
......@@ -49,27 +55,13 @@ export class TestConfigurationService implements IConfigurationService {
return Promise.resolve(undefined);
}
public onDidChangeConfiguration() {
return { dispose() { } };
}
public inspect<T>(key: string, overrides?: IConfigurationOverrides): {
default: T,
user: T,
userLocal?: T,
userRemote?: T,
workspace?: T,
workspaceFolder?: T
value: T,
} {
public inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
const config = this.getValue(undefined, overrides);
return {
value: getConfigurationValue<T>(config, key),
default: getConfigurationValue<T>(config, key),
user: getConfigurationValue<T>(config, key),
workspace: undefined,
workspaceFolder: undefined
user: getConfigurationValue<T>(config, key)
};
}
......
......@@ -9,8 +9,8 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import * as Errors from 'vs/base/common/errors';
import * as sinon from 'sinon';
import { getConfigurationValue } from 'vs/platform/configuration/common/configuration';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
class TestTelemetryAppender implements ITelemetryAppender {
......@@ -767,30 +767,14 @@ suite('TelemetryService', () => {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({
appender: testAppender
}, {
_serviceBrand: undefined,
}, new class extends TestConfigurationService {
onDidChangeConfiguration = emitter.event;
getValue() {
return {
enableTelemetry: enableTelemetry
} as any;
},
updateValue(): Promise<void> {
return null!;
},
inspect(key: string) {
return {
value: getConfigurationValue(this.getValue(), key),
default: getConfigurationValue(this.getValue(), key),
user: getConfigurationValue(this.getValue(), key),
workspace: null!,
workspaceFolder: null!
};
},
keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; },
onDidChangeConfiguration: emitter.event,
reloadConfiguration(): Promise<void> { return null!; },
getConfigurationData() { return null; }
});
}
}());
assert.equal(service.isOptedIn, false);
......
......@@ -8,9 +8,9 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from '../common/extHost.protocol';
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IConfigurationInitData } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@extHostNamedCustomer(MainContext.MainThreadConfiguration)
......@@ -28,7 +28,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
proxy.$initializeConfiguration(this._getConfigurationData());
this._configurationListener = configurationService.onDidChangeConfiguration(e => {
proxy.$acceptConfigurationChanged(this._getConfigurationData(), this.toConfigurationChangeEventData(e));
proxy.$acceptConfigurationChanged(this._getConfigurationData(), e.change);
});
}
......@@ -69,22 +69,4 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
}
return ConfigurationTarget.WORKSPACE;
}
private toConfigurationChangeEventData(event: IConfigurationChangeEvent): IWorkspaceConfigurationChangeEventData {
return {
changedConfiguration: this.toJSONConfiguration(event.changedConfiguration),
changedConfigurationByResource: event.changedConfigurationByResource.keys().reduce((result, resource) => {
result[resource.toString()] = this.toJSONConfiguration(event.changedConfigurationByResource.get(resource));
return result;
}, Object.create({}))
};
}
private toJSONConfiguration({ contents, keys, overrides }: IConfigurationModel = { contents: {}, keys: [], overrides: [] }): IConfigurationModel {
return {
contents,
keys,
overrides
};
}
}
......@@ -22,7 +22,7 @@ import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationData, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as files from 'vs/platform/files/common/files';
......@@ -97,11 +97,6 @@ export interface IConfigurationInitData extends IConfigurationData {
configurationScopes: [string, ConfigurationScope | undefined][];
}
export interface IWorkspaceConfigurationChangeEventData {
changedConfiguration: IConfigurationModel;
changedConfigurationByResource: { [folder: string]: IConfigurationModel; };
}
export interface IExtHostContext extends IRPCProtocol {
remoteAuthority: string;
}
......@@ -794,7 +789,7 @@ export interface ExtHostCommandsShape {
export interface ExtHostConfigurationShape {
$initializeConfiguration(data: IConfigurationInitData): void;
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void;
$acceptConfigurationChanged(data: IConfigurationInitData, change: IConfigurationChange): void;
}
export interface ExtHostDiagnosticsShape {
......
......@@ -8,12 +8,10 @@ import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import * as vscode from 'vscode';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData, MainContext } from './extHost.protocol';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IConfigurationInitData, MainContext } from './extHost.protocol';
import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { ResourceMap } from 'vs/base/common/map';
import { ConfigurationTarget, IConfigurationChange, IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { isObject } from 'vs/base/common/types';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
......@@ -21,6 +19,7 @@ import { Barrier } from 'vs/base/common/async';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ILogService } from 'vs/platform/log/common/log';
import { Workspace } from 'vs/platform/workspace/common/workspace';
function lookUp(tree: any, key: string) {
if (key) {
......@@ -72,8 +71,8 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
this._barrier.open();
}
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void {
this.getConfigProvider().then(provider => provider.$acceptConfigurationChanged(data, eventData));
$acceptConfigurationChanged(data: IConfigurationInitData, change: IConfigurationChange): void {
this.getConfigProvider().then(provider => provider.$acceptConfigurationChanged(data, change));
}
}
......@@ -90,7 +89,7 @@ export class ExtHostConfigProvider {
this._proxy = proxy;
this._logService = logService;
this._extHostWorkspace = extHostWorkspace;
this._configuration = ExtHostConfigProvider.parse(data);
this._configuration = Configuration.parse(data);
this._configurationScopes = this._toMap(data.configurationScopes);
}
......@@ -98,10 +97,11 @@ export class ExtHostConfigProvider {
return this._onDidChangeConfiguration && this._onDidChangeConfiguration.event;
}
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData) {
this._configuration = ExtHostConfigProvider.parse(data);
$acceptConfigurationChanged(data: IConfigurationInitData, change: IConfigurationChange) {
const previous = { data: this._configuration.toData(), workspace: this._extHostWorkspace.workspace };
this._configuration = Configuration.parse(data);
this._configurationScopes = this._toMap(data.configurationScopes);
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData));
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(change, previous));
}
getConfiguration(section?: string, resource?: URI, extensionId?: ExtensionIdentifier): vscode.WorkspaceConfiguration {
......@@ -254,17 +254,10 @@ export class ExtHostConfigProvider {
}
}
private _toConfigurationChangeEvent(data: IWorkspaceConfigurationChangeEventData): vscode.ConfigurationChangeEvent {
const changedConfiguration = new ConfigurationModel(data.changedConfiguration.contents, data.changedConfiguration.keys, data.changedConfiguration.overrides);
const changedConfigurationByResource: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>();
for (const key of Object.keys(data.changedConfigurationByResource)) {
const resource = URI.parse(key);
const model = data.changedConfigurationByResource[key];
changedConfigurationByResource.set(resource, new ConfigurationModel(model.contents, model.keys, model.overrides));
}
const event = new WorkspaceConfigurationChangeEvent(new ConfigurationChangeEvent(changedConfiguration, changedConfigurationByResource), this._extHostWorkspace.workspace);
private _toConfigurationChangeEvent(change: IConfigurationChange, previous: { data: IConfigurationData, workspace: Workspace | undefined }): vscode.ConfigurationChangeEvent {
const event = new ConfigurationChangeEvent(change, previous, this._configuration, this._extHostWorkspace.workspace);
return Object.freeze({
affectsConfiguration: (section: string, resource?: URI) => event.affectsConfiguration(section, resource)
affectsConfiguration: (section: string, resource?: URI) => event.affectsConfiguration(section, resource ? { resource } : undefined)
});
}
......@@ -272,20 +265,6 @@ export class ExtHostConfigProvider {
return scopes.reduce((result, scope) => { result.set(scope[0], scope[1]); return result; }, new Map<string, ConfigurationScope | undefined>());
}
private static parse(data: IConfigurationData): Configuration {
const defaultConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.defaults);
const userConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.user);
const workspaceConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.workspace);
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
result.set(URI.revive(value[0]), ExtHostConfigProvider.parseConfigurationModel(value[1]));
return result;
}, new ResourceMap<ConfigurationModel>());
return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
}
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}
export const IExtHostConfiguration = createDecorator<IExtHostConfiguration>('IExtHostConfiguration');
......
......@@ -542,7 +542,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
// Empty workbench
else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') {
else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.getValue('workbench.startupEditor') === 'newUntitledFile') {
if (this.editorGroupService.willRestoreEditors) {
return []; // do not open any empty untitled file if we restored editors from previous session
}
......
......@@ -8,7 +8,7 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { isArray, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration';
import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
import { ITOCEntry, knownAcronyms, knownTermMappings } from 'vs/workbench/contrib/preferences/browser/settingsLayout';
import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences';
......@@ -374,16 +374,7 @@ export class SettingsTreeModel {
interface IInspectResult {
isConfigured: boolean;
inspected: {
default: any,
user: any,
userLocal?: any,
userRemote?: any,
workspace?: any,
workspaceFolder?: any,
memory?: any,
value: any,
};
inspected: IConfigurationValue<any>;
targetSelector: 'userLocal' | 'userRemote' | 'workspace' | 'workspaceFolder';
}
......
......@@ -246,7 +246,7 @@ export function escapeNonWindowsPath(path: string): string {
}
export function getDefaultShell(
fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
fetchSetting: (key: string) => { user?: string | string[], value?: string | string[], default?: string | string[] },
isWorkspaceShellAllowed: boolean,
defaultShell: string,
isWoW64: boolean,
......@@ -294,7 +294,7 @@ export function getDefaultShell(
}
export function getDefaultShellArgs(
fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
fetchSetting: (key: string) => { user?: string | string[], value?: string | string[], default?: string | string[] },
isWorkspaceShellAllowed: boolean,
useAutomationShell: boolean,
lastActiveWorkspace: IWorkspaceFolder | undefined,
......@@ -330,7 +330,7 @@ export function getDefaultShellArgs(
}
function getShellSetting(
fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
fetchSetting: (key: string) => { user?: string | string[], value?: string | string[], default?: string | string[] },
isWorkspaceShellAllowed: boolean,
type: 'automationShell' | 'shell',
platformOverride: platform.Platform = platform.platform,
......@@ -344,7 +344,7 @@ function getShellSetting(
export function createTerminalEnvironment(
shellLaunchConfig: IShellLaunchConfig,
lastActiveWorkspace: IWorkspaceFolder | null,
envFromConfig: { user: ITerminalEnvironment | undefined, value: ITerminalEnvironment | undefined, default: ITerminalEnvironment | undefined },
envFromConfig: { user?: ITerminalEnvironment, value?: ITerminalEnvironment, default?: ITerminalEnvironment },
configurationResolverService: IConfigurationResolverService | undefined,
isWorkspaceShellAllowed: boolean,
version: string | undefined,
......
......@@ -11,9 +11,9 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { Queue, Barrier } from 'vs/base/common/async';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels';
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings } from 'vs/platform/configuration/common/configurationRegistry';
......@@ -271,16 +271,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
.then(() => this.loadConfiguration(local, remote)));
}
inspect<T>(key: string, overrides?: IConfigurationOverrides): {
default: T,
user: T,
userLocal?: T,
userRemote?: T,
workspace?: T,
workspaceFolder?: T,
memory?: T,
value: T
} {
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
return this._configuration.inspect<T>(key, overrides);
}
......@@ -465,10 +456,10 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
if (this.initialized) {
const changedKeys = this._configuration.compare(currentConfiguration);
this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE);
const change = this._configuration.compare(currentConfiguration);
this.triggerConfigurationChange(change, { data: currentConfiguration.toData(), workspace: this.workspace }, ConfigurationTarget.WORKSPACE);
} else {
this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE)));
this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, this.workspace, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE)));
this.initialized = true;
}
});
......@@ -489,7 +480,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this.defaultConfiguration = new DefaultConfigurationModel();
this.registerConfigurationSchemas();
if (this.workspace) {
this._configuration.updateDefaultConfiguration(this.defaultConfiguration);
const previousData = this._configuration.toData();
const change = this._configuration.compareAndUpdateDefaultConfiguration(this.defaultConfiguration, keys);
if (this.remoteUserConfiguration) {
this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reprocess());
this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reprocess());
......@@ -500,7 +492,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reprocessWorkspaceSettings());
this.workspace.folders.forEach(folder => this._configuration.updateFolderConfiguration(folder.uri, this.cachedFolderConfigs.get(folder.uri)!.reprocess()));
}
this.triggerConfigurationChange(new ConfigurationChangeEvent().change(keys), ConfigurationTarget.DEFAULT);
this.triggerConfigurationChange(change, { data: previousData, workspace: this.workspace }, ConfigurationTarget.DEFAULT);
}
}
......@@ -528,29 +520,32 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
}
private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void {
const keys = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration);
this.triggerConfigurationChange(keys, ConfigurationTarget.USER);
const previous = { data: this._configuration.toData(), workspace: this.workspace };
const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration);
this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);
}
private onRemoteUserConfigurationChanged(userConfiguration: ConfigurationModel): void {
const keys = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration);
this.triggerConfigurationChange(keys, ConfigurationTarget.USER);
const previous = { data: this._configuration.toData(), workspace: this.workspace };
const change = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration);
this.triggerConfigurationChange(change, previous, ConfigurationTarget.USER);
}
private onWorkspaceConfigurationChanged(): Promise<void> {
if (this.workspace && this.workspace.configuration) {
const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
const previous = { data: this._configuration.toData(), workspace: this.workspace };
const change = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration);
const changes = this.compareFolders(this.workspace.folders, configuredFolders);
if (changes.added.length || changes.removed.length || changes.changed.length) {
this.workspace.folders = configuredFolders;
return this.onFoldersChanged()
.then(foldersConfigurationChangeEvent => {
this.triggerConfigurationChange(foldersConfigurationChangeEvent.change(workspaceConfigurationChangeEvent), ConfigurationTarget.WORKSPACE_FOLDER);
.then(change => {
this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE_FOLDER);
this._onDidChangeWorkspaceFolders.fire(changes);
});
} else {
this.triggerConfigurationChange(workspaceConfigurationChangeEvent, ConfigurationTarget.WORKSPACE);
this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE);
}
}
return Promise.resolve(undefined);
......@@ -559,18 +554,19 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): Promise<void> {
return this.loadFolderConfigurations([folder])
.then(([folderConfiguration]) => {
const folderChangedKeys = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);
const previous = { data: this._configuration.toData(), workspace: this.workspace };
const folderConfiguraitonChange = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);
if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
const workspaceChangedKeys = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);
this.triggerConfigurationChange(workspaceChangedKeys, ConfigurationTarget.WORKSPACE);
const workspaceConfigurationChange = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);
this.triggerConfigurationChange(mergeChanges(folderConfiguraitonChange, workspaceConfigurationChange), previous, ConfigurationTarget.WORKSPACE);
} else {
this.triggerConfigurationChange(folderChangedKeys, ConfigurationTarget.WORKSPACE_FOLDER);
this.triggerConfigurationChange(folderConfiguraitonChange, previous, ConfigurationTarget.WORKSPACE_FOLDER);
}
});
}
private onFoldersChanged(): Promise<ConfigurationChangeEvent> {
let changeEvent = new ConfigurationChangeEvent();
private async onFoldersChanged(): Promise<IConfigurationChange> {
const changes: IConfigurationChange[] = [];
// Remove the configurations of deleted folders
for (const key of this.cachedFolderConfigs.keys()) {
......@@ -578,21 +574,18 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
const folderConfiguration = this.cachedFolderConfigs.get(key);
folderConfiguration!.dispose();
this.cachedFolderConfigs.delete(key);
changeEvent = changeEvent.change(this._configuration.compareAndDeleteFolderConfiguration(key));
changes.push(this._configuration.compareAndDeleteFolderConfiguration(key));
}
}
const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri));
if (toInitialize.length) {
return this.loadFolderConfigurations(toInitialize)
.then(folderConfigurations => {
const folderConfigurations = await this.loadFolderConfigurations(toInitialize);
folderConfigurations.forEach((folderConfiguration, index) => {
changeEvent = changeEvent.change(this._configuration.compareAndUpdateFolderConfiguration(toInitialize[index].uri, folderConfiguration));
});
return changeEvent;
changes.push(this._configuration.compareAndUpdateFolderConfiguration(toInitialize[index].uri, folderConfiguration));
});
}
return Promise.resolve(changeEvent);
return mergeChanges(...changes);
}
private loadFolderConfigurations(folders: IWorkspaceFolder[]): Promise<ConfigurationModel[]> {
......@@ -613,8 +606,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
}
if (target === ConfigurationTarget.MEMORY) {
const previous = { data: this._configuration.toData(), workspace: this.workspace };
this._configuration.updateValue(key, value, overrides);
this.triggerConfigurationChange(new ConfigurationChangeEvent().change(overrides && overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier)] : [key], overrides && overrides.resource || undefined), target);
this.triggerConfigurationChange({ keys: overrides?.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier), key] : [key], overrides: overrides?.overrideIdentifier ? [[overrides?.overrideIdentifier, [key]]] : [] }, previous, target);
return Promise.resolve(undefined);
}
......@@ -673,10 +667,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
return ConfigurationTarget.USER;
}
private triggerConfigurationChange(configurationEvent: ConfigurationChangeEvent, target: ConfigurationTarget): void {
if (configurationEvent.affectedKeys.length) {
configurationEvent.telemetryData(target, this.getTargetConfiguration(target));
this._onDidChangeConfiguration.fire(new WorkspaceConfigurationChangeEvent(configurationEvent, this.workspace));
private triggerConfigurationChange(change: IConfigurationChange, previous: { data: IConfigurationData, workspace?: Workspace } | undefined, target: ConfigurationTarget): void {
if (change.keys.length) {
const configurationChangeEvent = new ConfigurationChangeEvent(change, previous, this._configuration, this.workspace);
configurationChangeEvent.source = target;
configurationChangeEvent.sourceConfig = this.getTargetConfiguration(target);
this._onDidChangeConfiguration.fire(configurationChangeEvent);
}
}
......
......@@ -4,13 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { equals } from 'vs/base/common/objects';
import { compare, toValuesTree, IConfigurationChangeEvent, ConfigurationTarget, IConfigurationModel, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { Configuration as BaseConfiguration, ConfigurationModelParser, ConfigurationChangeEvent, ConfigurationModel, AbstractConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { toValuesTree, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IConfigurationChange, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration';
import { Configuration as BaseConfiguration, ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { ResourceMap } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
export class WorkspaceConfigurationModelParser extends ConfigurationModelParser {
......@@ -101,16 +102,7 @@ export class Configuration extends BaseConfiguration {
return super.getValue(key, overrides, this._workspace);
}
inspect<C>(key: string, overrides: IConfigurationOverrides = {}): {
default: C,
user: C,
userLocal?: C,
userRemote?: C,
workspace?: C,
workspaceFolder?: C
memory?: C
value: C,
} {
inspect<C>(key: string, overrides: IConfigurationOverrides = {}): IConfigurationValue<C> {
return super.inspect(key, overrides, this._workspace);
}
......@@ -123,141 +115,42 @@ export class Configuration extends BaseConfiguration {
return super.keys(this._workspace);
}
compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): ConfigurationChangeEvent {
const { added, updated, removed } = compare(this.localUserConfiguration, user);
let changedKeys = [...added, ...updated, ...removed];
if (changedKeys.length) {
super.updateLocalUserConfiguration(user);
}
return new ConfigurationChangeEvent().change(changedKeys);
}
compareAndUpdateRemoteUserConfiguration(user: ConfigurationModel): ConfigurationChangeEvent {
const { added, updated, removed } = compare(this.remoteUserConfiguration, user);
let changedKeys = [...added, ...updated, ...removed];
if (changedKeys.length) {
super.updateRemoteUserConfiguration(user);
}
return new ConfigurationChangeEvent().change(changedKeys);
}
compareAndUpdateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): ConfigurationChangeEvent {
const { added, updated, removed } = compare(this.workspaceConfiguration, workspaceConfiguration);
let changedKeys = [...added, ...updated, ...removed];
if (changedKeys.length) {
super.updateWorkspaceConfiguration(workspaceConfiguration);
}
return new ConfigurationChangeEvent().change(changedKeys);
}
compareAndUpdateFolderConfiguration(resource: URI, folderConfiguration: ConfigurationModel): ConfigurationChangeEvent {
const currentFolderConfiguration = this.folderConfigurations.get(resource);
if (currentFolderConfiguration) {
const { added, updated, removed } = compare(currentFolderConfiguration, folderConfiguration);
let changedKeys = [...added, ...updated, ...removed];
if (changedKeys.length) {
super.updateFolderConfiguration(resource, folderConfiguration);
}
return new ConfigurationChangeEvent().change(changedKeys, resource);
} else {
super.updateFolderConfiguration(resource, folderConfiguration);
return new ConfigurationChangeEvent().change(folderConfiguration.keys, resource);
}
}
compareAndDeleteFolderConfiguration(folder: URI): ConfigurationChangeEvent {
compareAndDeleteFolderConfiguration(folder: URI): IConfigurationChange {
if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.toString() === folder.toString()) {
// Do not remove workspace configuration
return new ConfigurationChangeEvent();
}
const folderConfig = this.folderConfigurations.get(folder);
if (!folderConfig) {
throw new Error('Unknown folder');
return { keys: [], overrides: [] };
}
const keys = folderConfig.keys;
super.deleteFolderConfiguration(folder);
return new ConfigurationChangeEvent().change(keys, folder);
return super.compareAndDeleteFolderConfiguration(folder);
}
compare(other: Configuration): string[] {
const result: string[] = [];
for (const key of this.allKeys()) {
if (!equals(this.getValue(key), other.getValue(key))
|| (this._workspace && this._workspace.folders.some(folder => !equals(this.getValue(key, { resource: folder.uri }), other.getValue(key, { resource: folder.uri }))))) {
result.push(key);
}
}
return result;
}
allKeys(): string[] {
return super.allKeys(this._workspace);
}
}
export class AllKeysConfigurationChangeEvent extends AbstractConfigurationChangeEvent implements IConfigurationChangeEvent {
private _changedConfiguration: ConfigurationModel | null = null;
constructor(private _configuration: Configuration, readonly source: ConfigurationTarget, readonly sourceConfig: any) { super(); }
get changedConfiguration(): ConfigurationModel {
if (!this._changedConfiguration) {
this._changedConfiguration = new ConfigurationModel();
this.updateKeys(this._changedConfiguration, this.affectedKeys);
}
return this._changedConfiguration;
}
get changedConfigurationByResource(): ResourceMap<IConfigurationModel> {
return new ResourceMap();
}
get affectedKeys(): string[] {
return this._configuration.allKeys();
}
affectsConfiguration(config: string, resource?: URI): boolean {
return this.doesConfigurationContains(this.changedConfiguration, config);
}
}
export class WorkspaceConfigurationChangeEvent implements IConfigurationChangeEvent {
constructor(private configurationChangeEvent: IConfigurationChangeEvent, private workspace: Workspace | undefined) { }
get changedConfiguration(): IConfigurationModel {
return this.configurationChangeEvent.changedConfiguration;
}
get changedConfigurationByResource(): ResourceMap<IConfigurationModel> {
return this.configurationChangeEvent.changedConfigurationByResource;
}
get affectedKeys(): string[] {
return this.configurationChangeEvent.affectedKeys;
}
get source(): ConfigurationTarget {
return this.configurationChangeEvent.source;
}
get sourceConfig(): any {
return this.configurationChangeEvent.sourceConfig;
compare(other: Configuration): IConfigurationChange {
const compare = (fromKeys: string[], toKeys: string[], overrideIdentifier?: string): string[] => {
const keys: string[] = [];
keys.push(...toKeys.filter(key => fromKeys.indexOf(key) === -1));
keys.push(...fromKeys.filter(key => toKeys.indexOf(key) === -1));
keys.push(...fromKeys.filter(key => {
// Ignore if the key does not exist in both models
if (toKeys.indexOf(key) === -1) {
return false;
}
affectsConfiguration(config: string, resource?: URI): boolean {
if (this.configurationChangeEvent.affectsConfiguration(config, resource)) {
// Compare workspace value
if (!equals(this.getValue(key, { overrideIdentifier }), other.getValue(key, { overrideIdentifier }))) {
return true;
}
if (resource && this.workspace) {
let workspaceFolder = this.workspace.getFolder(resource);
if (workspaceFolder) {
return this.configurationChangeEvent.affectsConfiguration(config, workspaceFolder.uri);
// Compare workspace folder value
return this._workspace && this._workspace.folders.some(folder => !equals(this.getValue(key, { resource: folder.uri, overrideIdentifier }), other.getValue(key, { resource: folder.uri, overrideIdentifier })));
}));
return keys;
};
const keys = compare(this.allKeys(), other.allKeys());
const overrides: [string, string[]][] = [];
for (const key of keys) {
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
const overrideIdentifier = overrideIdentifierFromKey(key);
overrides.push([overrideIdentifier, compare(this.getAllKeysForOverrideIdentifier(overrideIdentifier), other.getAllKeysForOverrideIdentifier(overrideIdentifier), overrideIdentifier)]);
}
}
return false;
return { keys, overrides };
}
}
......@@ -3,15 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { WorkspaceConfigurationChangeEvent, StandaloneConfigurationModelParser, AllKeysConfigurationChangeEvent, Configuration } from 'vs/workbench/services/configuration/common/configurationModels';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
import { ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { StandaloneConfigurationModelParser, Configuration } from 'vs/workbench/services/configuration/common/configurationModels';
import { ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ResourceMap } from 'vs/base/common/map';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
suite('FolderSettingsModelParser', () => {
......@@ -66,7 +64,7 @@ suite('FolderSettingsModelParser', () => {
testObject.parseContent(JSON.stringify({ '[json]': { 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'application', 'FolderSettingsModelParser.machine': 'executable' } }));
assert.deepEqual(testObject.configurationModel.overrides, [{ 'contents': { 'FolderSettingsModelParser': { 'resource': 'resource' } }, 'identifiers': ['json'] }]);
assert.deepEqual(testObject.configurationModel.overrides, [{ 'contents': { 'FolderSettingsModelParser': { 'resource': 'resource' } }, 'identifiers': ['json'], 'keys': ['FolderSettingsModelParser.resource'] }]);
});
test('reprocess folder settings excludes application and machine setting', () => {
......@@ -112,143 +110,72 @@ suite('StandaloneConfigurationModelParser', () => {
});
suite('WorkspaceConfigurationChangeEvent', () => {
test('changeEvent affecting workspace folders', () => {
let configurationChangeEvent = new ConfigurationChangeEvent();
configurationChangeEvent.change(['window.title']);
configurationChangeEvent.change(['window.zoomLevel'], URI.file('folder1'));
configurationChangeEvent.change(['workbench.editor.enablePreview'], URI.file('folder2'));
configurationChangeEvent.change(['window.restoreFullscreen'], URI.file('folder1'));
configurationChangeEvent.change(['window.restoreWindows'], URI.file('folder2'));
configurationChangeEvent.telemetryData(ConfigurationTarget.WORKSPACE, {});
let testObject = new WorkspaceConfigurationChangeEvent(configurationChangeEvent, new Workspace('id',
[new WorkspaceFolder({ index: 0, name: '1', uri: URI.file('folder1') }),
new WorkspaceFolder({ index: 1, name: '2', uri: URI.file('folder2') }),
new WorkspaceFolder({ index: 2, name: '3', uri: URI.file('folder3') })]));
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.equal(testObject.source, ConfigurationTarget.WORKSPACE);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('folder1')));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file(join('folder1', 'file1'))));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file(join('folder3', 'file3'))));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file(join('folder1', 'file1'))));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('folder1')));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file(join('folder3', 'file3'))));
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('folder2')));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file(join('folder1', 'file1'))));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file(join('folder3', 'file3'))));
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('folder1')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file(join('folder1', 'file1'))));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('folder2')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file(join('folder2', 'file2'))));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('folder3')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file(join('folder3', 'file3'))));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file3')));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window', URI.file('folder1')));
assert.ok(testObject.affectsConfiguration('window', URI.file(join('folder1', 'file1'))));
assert.ok(testObject.affectsConfiguration('window', URI.file('folder2')));
assert.ok(testObject.affectsConfiguration('window', URI.file(join('folder2', 'file2'))));
assert.ok(testObject.affectsConfiguration('window', URI.file('folder3')));
assert.ok(testObject.affectsConfiguration('window', URI.file(join('folder3', 'file3'))));
assert.ok(testObject.affectsConfiguration('window', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window', URI.file('file3')));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('folder2')));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('folder1')));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file(join('folder1', 'file1'))));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('folder3')));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('folder2')));
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('folder1')));
assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file(join('folder1', 'file1'))));
assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('folder3')));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench', URI.file('folder2')));
assert.ok(testObject.affectsConfiguration('workbench', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('workbench', URI.file('folder1')));
assert.ok(!testObject.affectsConfiguration('workbench', URI.file('folder3')));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', URI.file('folder1')));
assert.ok(!testObject.affectsConfiguration('files', URI.file(join('folder1', 'file1'))));
assert.ok(!testObject.affectsConfiguration('files', URI.file('folder2')));
assert.ok(!testObject.affectsConfiguration('files', URI.file(join('folder2', 'file2'))));
assert.ok(!testObject.affectsConfiguration('files', URI.file('folder3')));
assert.ok(!testObject.affectsConfiguration('files', URI.file(join('folder3', 'file3'))));
});
});
suite('AllKeysConfigurationChangeEvent', () => {
test('changeEvent affects keys for any resource', () => {
const configuraiton = new Configuration(new ConfigurationModel({}, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']),
new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), null!);
let testObject = new AllKeysConfigurationChangeEvent(configuraiton, ConfigurationTarget.USER, null);
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
suite('Workspace Configuration', () => {
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file1')));
const defaultConfigurationModel = toConfigurationModel({
'editor.lineNumbers': 'on',
'editor.fontSize': 12,
'window.zoomLevel': 1,
'[markdown]': {
'editor.wordWrap': 'off'
},
'window.title': 'custom',
'workbench.enableTabs': false,
'editor.insertSpaces': true
});
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2')));
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);
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);
configuration2.updateDefaultConfiguration(defaultConfigurationModel);
configuration2.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } }));
configuration2.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' }));
configuration2.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'editor.fontSize': 14 }));
configuration2.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'editor.wordWrap': 'on' }));
const actual = configuration2.compare(configuration1);
assert.deepEqual(actual, { keys: [], overrides: [] });
});
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window', URI.file('file2')));
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);
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);
configuration2.updateDefaultConfiguration(defaultConfigurationModel);
configuration2.updateLocalUserConfiguration(toConfigurationModel({ 'workbench.enableTabs': true, '[typescript]': { 'editor.insertSpaces': true } }));
configuration2.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.fontSize': 11 }));
configuration2.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'editor.insertSpaces': true }));
configuration2.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({
'[markdown]': {
'editor.wordWrap': 'on',
'editor.lineNumbers': 'relative'
},
}));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file1')));
const actual = configuration2.compare(configuration1);
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file1')));
assert.deepEqual(actual, { keys: ['editor.wordWrap', 'editor.fontSize', '[markdown]', 'window.title', 'workbench.enableTabs', '[typescript]'], overrides: [['markdown', ['editor.lineNumbers', 'editor.wordWrap']], ['typescript', ['editor.insertSpaces']]] });
});
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('workbench', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', URI.file('file1')));
});
});
function toConfigurationModel(obj: any): ConfigurationModel {
const parser = new ConfigurationModelParser('test');
parser.parseContent(JSON.stringify(obj));
return parser.configurationModel;
}
......@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { URI as uri } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import { IConfigurationService, getConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
......@@ -125,7 +125,7 @@ suite('Configuration Resolver Service', () => {
});
test('substitute one configuration variable', () => {
let configurationService: IConfigurationService = new MockConfigurationService({
let configurationService: IConfigurationService = new TestConfigurationService({
editor: {
fontFamily: 'foo'
},
......@@ -142,7 +142,7 @@ suite('Configuration Resolver Service', () => {
test('substitute many configuration variables', () => {
let configurationService: IConfigurationService;
configurationService = new MockConfigurationService({
configurationService = new TestConfigurationService({
editor: {
fontFamily: 'foo'
},
......@@ -159,7 +159,7 @@ suite('Configuration Resolver Service', () => {
test('substitute one env variable and a configuration variable', () => {
let configurationService: IConfigurationService;
configurationService = new MockConfigurationService({
configurationService = new TestConfigurationService({
editor: {
fontFamily: 'foo'
},
......@@ -180,7 +180,7 @@ suite('Configuration Resolver Service', () => {
test('substitute many env variable and a configuration variable', () => {
let configurationService: IConfigurationService;
configurationService = new MockConfigurationService({
configurationService = new TestConfigurationService({
editor: {
fontFamily: 'foo'
},
......@@ -201,7 +201,7 @@ suite('Configuration Resolver Service', () => {
test('mixed types of configuration variables', () => {
let configurationService: IConfigurationService;
configurationService = new MockConfigurationService({
configurationService = new TestConfigurationService({
editor: {
fontFamily: 'foo',
lineNumbers: 123,
......@@ -231,7 +231,7 @@ suite('Configuration Resolver Service', () => {
test('uses original variable as fallback', () => {
let configurationService: IConfigurationService;
configurationService = new MockConfigurationService({
configurationService = new TestConfigurationService({
editor: {}
});
......@@ -242,7 +242,7 @@ suite('Configuration Resolver Service', () => {
test('configuration variables with invalid accessor', () => {
let configurationService: IConfigurationService;
configurationService = new MockConfigurationService({
configurationService = new TestConfigurationService({
editor: {
fontFamily: 'foo'
}
......@@ -503,32 +503,6 @@ suite('Configuration Resolver Service', () => {
});
class MockConfigurationService implements IConfigurationService {
public _serviceBrand: undefined;
public serviceId = IConfigurationService;
public constructor(private configuration: any = {}) { }
public inspect<T>(key: string, overrides?: IConfigurationOverrides): any { return { value: getConfigurationValue<T>(this.getValue(), key), default: getConfigurationValue<T>(this.getValue(), key), user: getConfigurationValue<T>(this.getValue(), key), workspaceFolder: undefined, folder: undefined }; }
public keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; }
public getValue(): any;
public getValue(value: string): any;
public getValue(value?: any): any {
if (!value) {
return this.configuration;
}
const valuePath = (<string>value).split('.');
let object = this.configuration;
while (valuePath.length && object) {
object = object[valuePath.shift()!];
}
return object;
}
public updateValue(): Promise<void> { return Promise.resolve(); }
public getConfigurationData(): any { return null; }
public onDidChangeConfiguration() { return { dispose() { } }; }
public reloadConfiguration() { return Promise.resolve(); }
}
class MockCommandService implements ICommandService {
public _serviceBrand: undefined;
......
......@@ -25,6 +25,9 @@ suite('Breadcrumb Model', function () {
}
return super.getValue(...args);
}
updateValue() {
return Promise.resolve();
}
};
test('only uri, inside workspace', function () {
......
......@@ -12,7 +12,7 @@ import { ConfigurationModel } from 'vs/platform/configuration/common/configurati
import { TestRPCProtocol } from './testRPCProtocol';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationModel, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { NullLogService } from 'vs/platform/log/common/log';
import { assign } from 'vs/base/common/objects';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
......@@ -608,7 +608,7 @@ suite('ExtHostConfiguration', function () {
createConfigurationData({
'farboo': {
'config': false,
'updatedconfig': false
'updatedConfig': false
}
}),
new NullLogService()
......@@ -617,29 +617,16 @@ suite('ExtHostConfiguration', function () {
const newConfigData = createConfigurationData({
'farboo': {
'config': false,
'updatedconfig': true,
'updatedConfig': true,
'newConfig': true,
}
});
const changedConfigurationByResource = Object.create({});
changedConfigurationByResource[workspaceFolder.uri.toString()] = new ConfigurationModel({
'farboo': {
'newConfig': true,
}
}, ['farboo.newConfig']);
const configEventData = {
changedConfiguration: new ConfigurationModel({
'farboo': {
'updatedConfig': true,
}
}, ['farboo.updatedConfig']),
changedConfigurationByResource
};
const configEventData: IConfigurationChange = { keys: ['farboo.updatedConfig', 'farboo.newConfig'], overrides: [] };
testObject.onDidChangeConfiguration(e => {
assert.deepEqual(testObject.getConfiguration().get('farboo'), {
'config': false,
'updatedconfig': true,
'updatedConfig': true,
'newConfig': true,
});
......@@ -653,7 +640,7 @@ suite('ExtHostConfiguration', function () {
assert.ok(e.affectsConfiguration('farboo.newConfig'));
assert.ok(e.affectsConfiguration('farboo.newConfig', workspaceFolder.uri));
assert.ok(!e.affectsConfiguration('farboo.newConfig', URI.file('any')));
assert.ok(e.affectsConfiguration('farboo.newConfig', URI.file('any')));
assert.ok(!e.affectsConfiguration('farboo.config'));
assert.ok(!e.affectsConfiguration('farboo.config', workspaceFolder.uri));
......
......@@ -16,7 +16,7 @@ import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView } from 'vs/wor
import { Event, Emitter } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService';
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
......@@ -1263,6 +1263,10 @@ export class TestTextResourceConfigurationService implements ITextResourceConfig
const section: string | undefined = position ? (typeof arg3 === 'string' ? arg3 : undefined) : (typeof arg2 === 'string' ? arg2 : undefined);
return this.configurationService.getValue(section, { resource });
}
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void> {
return this.configurationService.updateValue(key, value);
}
}
export class TestTextResourcePropertiesService implements ITextResourcePropertiesService {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册