- Define Machine Settings: Schema, Environment

- Scope machine settings
- Make remote settings scoped to machine settings
This commit is contained in:
Sandeep Somavarapu 2019-04-10 18:25:00 +02:00
parent dc234da073
commit f329277979
22 changed files with 159 additions and 146 deletions

View file

@ -57,6 +57,10 @@
"fileMatch": "%APP_SETTINGS_HOME%/settings.json",
"url": "vscode://schemas/settings/user"
},
{
"fileMatch": "%MACHINE_SETTINGS_HOME%/settings.json",
"url": "vscode://schemas/settings/machine"
},
{
"fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json",
"url": "vscode://schemas/workspaceConfig"

View file

@ -265,6 +265,7 @@ function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations {
}
if (fileMatch[0] === '%') {
fileMatch = fileMatch.replace(/%APP_SETTINGS_HOME%/, '/User');
fileMatch = fileMatch.replace(/%MACHINE_SETTINGS_HOME%/, '/Machine');
fileMatch = fileMatch.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces');
} else if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:\/\//)) {
fileMatch = '/' + fileMatch;

View file

@ -101,7 +101,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
services.set(IEnvironmentService, environmentService);
services.set(ILogService, logService);
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IDownloadService, new SyncDescriptor(DownloadService));

View file

@ -307,7 +307,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I
services.set(ILogService, logService);
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));

View file

@ -308,7 +308,7 @@ export function main(argv: ParsedArgs): Promise<void> {
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService;
const services = new ServiceCollection();
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService, [false]));
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));

View file

@ -9,9 +9,10 @@ import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as objects from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree, toOverrides } from 'vs/platform/configuration/common/configuration';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { Registry } from 'vs/platform/registry/common/platform';
export class ConfigurationModel implements IConfigurationModel {
@ -194,10 +195,11 @@ export class DefaultConfigurationModel extends ConfigurationModel {
export class ConfigurationModelParser {
private _raw: any = null;
private _configurationModel: ConfigurationModel | null = null;
private _parseErrors: any[] = [];
constructor(protected readonly _name: string) { }
constructor(protected readonly _name: string, private _scopes?: ConfigurationScope[]) { }
get configurationModel(): ConfigurationModel {
return this._configurationModel || new ConfigurationModel();
@ -207,15 +209,26 @@ export class ConfigurationModelParser {
return this._parseErrors;
}
public parse(content: string | null | undefined): void {
public parseContent(content: string | null | undefined): void {
if (content) {
const raw = this.parseContent(content);
const configurationModel = this.parseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
const raw = this.doParseContent(content);
this.parseRaw(raw);
}
}
protected parseContent(content: string): any {
public parseRaw(raw: any): void {
this._raw = raw;
const configurationModel = this.doParseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
}
public parse(): void {
if (this._raw) {
this.parseRaw(this._raw);
}
}
protected doParseContent(content: string): any {
let raw: any = {};
let currentProperty: string | null = null;
let currentParent: any = [];
@ -272,12 +285,36 @@ export class ConfigurationModelParser {
return raw;
}
protected parseRaw(raw: any): IConfigurationModel {
protected doParseRaw(raw: any): IConfigurationModel {
if (this._scopes) {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
raw = this.filterByScope(raw, configurationProperties, true, this._scopes);
}
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const keys = Object.keys(raw);
const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
return { contents, keys, overrides };
}
private filterByScope(properties: {}, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }, filterOverriddenProperties: boolean, scopes: ConfigurationScope[]): {} {
const result = {};
for (let key in properties) {
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
result[key] = this.filterByScope(properties[key], configurationProperties, false, scopes);
} else {
const scope = this.getScope(key, configurationProperties);
if (scopes.indexOf(scope) !== -1) {
result[key] = properties[key];
}
}
}
return result;
}
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope {
const propertySchema = configurationProperties[key];
return propertySchema && typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW;
}
}
export class Configuration {

View file

@ -27,7 +27,7 @@ export class NodeBasedUserConfiguration extends Disposable {
this.userConfigModelWatcher = new ConfigWatcher(this.settingsPath, {
changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsPath), parse: (content: string, parseErrors: any[]) => {
const userConfigModelParser = new ConfigurationModelParser(this.settingsPath);
userConfigModelParser.parse(content);
userConfigModelParser.parseContent(content);
parseErrors = [...userConfigModelParser.errors];
return userConfigModelParser;
}, initCallback: () => c(undefined)

View file

@ -9,7 +9,6 @@ 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 } from 'vs/platform/configuration/common/configurationModels';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration';
@ -24,11 +23,11 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
constructor(
@IEnvironmentService environmentService: IEnvironmentService
configurationPath: string
) {
super();
this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath));
this.userConfiguration = this._register(new NodeBasedUserConfiguration(configurationPath));
// Initialize
const defaults = new DefaultConfigurationModel();

View file

@ -250,10 +250,10 @@ suite('CustomConfigurationModel', () => {
test('simple merge using models', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': 3, 'c': 4 }));
add.parseContent(JSON.stringify({ 'a': 3, 'c': 4 }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
@ -261,14 +261,14 @@ suite('CustomConfigurationModel', () => {
test('simple merge with an undefined contents', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
base = new ConfigurationModelParser('base');
add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
add.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
@ -280,25 +280,25 @@ suite('CustomConfigurationModel', () => {
test('Recursive merge using config models', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': { 'b': 1 } }));
base.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
let add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': { 'b': 2 } }));
add.parseContent(JSON.stringify({ 'a': { 'b': 2 } }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
});
test('Test contents while getting an existing property', () => {
let testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({ 'a': 1 }));
testObject.parseContent(JSON.stringify({ 'a': 1 }));
assert.deepEqual(testObject.configurationModel.getValue('a'), 1);
testObject.parse(JSON.stringify({ 'a': { 'b': 1 } }));
testObject.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
assert.deepEqual(testObject.configurationModel.getValue('a'), { 'b': 1 });
});
test('Test contents are undefined for non existing properties', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({
testObject.parseContent(JSON.stringify({
awesome: true
}));
@ -313,7 +313,7 @@ suite('CustomConfigurationModel', () => {
test('Test configWithOverrides gives all content merged with overrides', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
testObject.parseContent(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
assert.deepEqual(testObject.configurationModel.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
});
@ -326,17 +326,17 @@ suite('CustomConfigurationModel', () => {
test('Test update with empty data', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse('');
testObject.parseContent('');
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
testObject.parse(null!);
testObject.parseContent(null!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
testObject.parse(undefined!);
testObject.parseContent(undefined!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
@ -472,7 +472,7 @@ suite('Configuration', () => {
test('Test update value', () => {
const parser = new ConfigurationModelParser('test');
parser.parse(JSON.stringify({ 'a': 1 }));
parser.parseContent(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.updateValue('a', 2);
@ -482,7 +482,7 @@ suite('Configuration', () => {
test('Test update value after inspect', () => {
const parser = new ConfigurationModelParser('test');
parser.parse(JSON.stringify({ 'a': 1 }));
parser.parseContent(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.inspect('a', {}, undefined);

View file

@ -10,29 +10,17 @@ import * as fs from 'fs';
import { Registry } from 'vs/platform/registry/common/platform';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { testFile } from 'vs/base/test/node/utils';
class SettingsTestEnvironmentService extends EnvironmentService {
constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) {
super(args, _execPath);
}
get appSettingsPath(): string { return this.customAppSettingsHome; }
}
suite('ConfigurationService - Node', () => {
test('simple', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
foo: string;
}>();
@ -49,7 +37,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
testworkbench: {
editor: {
@ -71,7 +59,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, ',,,,');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
foo: string;
}>();
@ -87,7 +75,7 @@ suite('ConfigurationService - Node', () => {
const newDir = path.join(parentDir, 'config', id);
const testFile = path.join(newDir, 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile));
const service = new ConfigurationService(testFile);
const config = service.getValue<{ foo: string }>();
assert.ok(config);
@ -98,7 +86,7 @@ suite('ConfigurationService - Node', () => {
test('trigger configuration change event', async () => {
const res = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
return new Promise((c, e) => {
service.onDidChangeConfiguration(() => {
assert.equal(service.getValue('foo'), 'bar');
@ -115,7 +103,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
let config = service.getValue<{
foo: string;
}>();
@ -163,7 +151,7 @@ suite('ConfigurationService - Node', () => {
}
});
let serviceWithoutFile = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, '__testFile'));
let serviceWithoutFile = new ConfigurationService('__testFile');
let setting = serviceWithoutFile.getValue<ITestSetting>();
assert.ok(setting);
@ -172,7 +160,7 @@ suite('ConfigurationService - Node', () => {
return testFile('config', 'config.json').then(async res => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
let setting = service.getValue<ITestSetting>();
@ -205,7 +193,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, r.testFile));
const service = new ConfigurationService(r.testFile);
let res = service.inspect('something.missing');
assert.strictEqual(res.value, undefined);
assert.strictEqual(res.default, undefined);
@ -241,7 +229,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, r.testFile));
const service = new ConfigurationService(r.testFile);
let res = service.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.default, null);
assert.strictEqual(res.value, null);

View file

@ -102,6 +102,9 @@ export interface IEnvironmentService {
appSettingsPath: string;
appKeybindingsPath: string;
machineSettingsHome: string;
machineSettingsPath: string;
settingsSearchBuildId?: number;
settingsSearchUrl?: string;

View file

@ -110,6 +110,12 @@ export class EnvironmentService implements IEnvironmentService {
@memoize
get appSettingsPath(): string { return path.join(this.appSettingsHome, 'settings.json'); }
@memoize
get machineSettingsHome(): string { return path.join(this.userDataPath, 'Machine'); }
@memoize
get machineSettingsPath(): string { return path.join(this.machineSettingsHome, 'settings.json'); }
@memoize
get globalStorageHome(): string { return path.join(this.appSettingsHome, 'globalStorage'); }

View file

@ -11,7 +11,7 @@ export interface IRemoteAgentEnvironment {
pid: number;
appRoot: URI;
appSettingsHome: URI;
appSettingsPath: URI;
settingsPath: URI;
logsPath: URI;
extensionsPath: URI;
extensionHostLogsPath: URI;

View file

@ -243,6 +243,8 @@ export class SimpleWorkbenchEnvironmentService implements IWorkbenchEnvironmentS
appSettingsHome: string = '/nodeless/settings';
appSettingsPath: string = '/nodeless/settings/settings.json';
appKeybindingsPath: string = '/nodeless/settings/keybindings.json';
machineSettingsHome: string;
machineSettingsPath: string;
settingsSearchBuildId?: number;
settingsSearchUrl?: string;
globalStorageHome: string;

View file

@ -11,8 +11,8 @@ import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/l
import { RunOnceScheduler } from 'vs/base/common/async';
import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, IConfigurationFileService, MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
@ -47,7 +47,7 @@ export class RemoteUserConfiguration extends Disposable {
if (environment) {
this._userConfiguration.dispose();
this._userConfigurationDisposable.dispose();
this._userConfiguration = this._register(new UserConfiguration(environment.appSettingsPath, this._configurationFileService));
this._userConfiguration = this._register(new UserConfiguration(environment.settingsPath, MACHINE_SCOPES, this._configurationFileService));
this._userConfigurationDisposable = this._register(this._userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
this._userConfiguration.initialize().then(configurationModel => this.onDidUserConfigurationChange(configurationModel));
}
@ -62,6 +62,10 @@ export class RemoteUserConfiguration extends Disposable {
return this._userConfiguration.reload();
}
reprocess(): ConfigurationModel {
return this._userConfiguration.reprocess();
}
private onDidUserConfigurationChange(configurationModel: ConfigurationModel): void {
this.updateCache(configurationModel);
this._onDidChangeConfiguration.fire(configurationModel);
@ -74,6 +78,7 @@ export class RemoteUserConfiguration extends Disposable {
export class UserConfiguration extends Disposable {
private readonly parser: ConfigurationModelParser;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
protected readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
@ -83,10 +88,12 @@ export class UserConfiguration extends Disposable {
constructor(
private readonly configurationResource: URI,
private readonly scopes: ConfigurationScope[] | undefined,
private readonly configurationFileService: IConfigurationFileService
) {
super();
this.parser = new ConfigurationModelParser(this.configurationResource.toString(), this.scopes);
this._register(configurationFileService.onFileChanges(e => this.handleFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
this._register(toDisposable(() => {
@ -127,14 +134,18 @@ export class UserConfiguration extends Disposable {
async reload(): Promise<ConfigurationModel> {
try {
const content = await this.configurationFileService.resolveContent(this.configurationResource);
const parser = new ConfigurationModelParser(this.configurationResource.toString());
parser.parse(content);
return parser.configurationModel;
this.parser.parseContent(content);
return this.parser.configurationModel;
} catch (e) {
return new ConfigurationModel();
}
}
reprocess(): ConfigurationModel {
this.parser.parse();
return this.parser.configurationModel;
}
private async onWatchStarted(currentModel: ConfigurationModel): Promise<void> {
const configuraitonModel = await this.reload();
const { added, removed, updated } = compare(currentModel, configuraitonModel);
@ -202,6 +213,10 @@ class CachedUserConfiguration extends Disposable {
return this.reload();
}
reprocess(): ConfigurationModel {
return this.configurationModel;
}
async reload(): Promise<ConfigurationModel> {
const content = await this.configurationCache.read(this.key);
try {
@ -371,7 +386,7 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork
errors.onUnexpectedError(error);
}
}
this.workspaceConfigurationModelParser.parse(contents);
this.workspaceConfigurationModelParser.parseContent(contents);
this.consolidate();
}
@ -449,7 +464,7 @@ class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfi
const key = this.getKey(workspaceIdentifier);
const contents = await this.configurationCache.read(key);
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(key.key);
this.workspaceConfigurationModelParser.parse(contents);
this.workspaceConfigurationModelParser.parseContent(contents);
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel);
} catch (e) {
}
@ -503,7 +518,7 @@ export interface IFolderConfiguration extends IDisposable {
class FileServiceBasedFolderConfiguration extends Disposable implements IFolderConfiguration {
private _folderSettingsModelParser: FolderSettingsModelParser;
private _folderSettingsModelParser: ConfigurationModelParser;
private _standAloneConfigurations: ConfigurationModel[];
private _cache: ConfigurationModel;
@ -518,7 +533,7 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC
this.configurationNames = [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY];
this.configurationResources = this.configurationNames.map(name => resources.joinPath(this.configurationFolder, `${name}.json`));
this._folderSettingsModelParser = new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? [ConfigurationScope.RESOURCE] : [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._folderSettingsModelParser = new ConfigurationModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? FOLDER_SCOPES : WORKSPACE_SCOPES);
this._standAloneConfigurations = [];
this._cache = new ConfigurationModel();
@ -544,17 +559,17 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC
// reset
this._standAloneConfigurations = [];
this._folderSettingsModelParser.parse('');
this._folderSettingsModelParser.parseContent('');
// parse
if (configurationContents[0]) {
this._folderSettingsModelParser.parse(configurationContents[0]);
this._folderSettingsModelParser.parseContent(configurationContents[0]);
}
for (let index = 1; index < configurationContents.length; index++) {
const contents = configurationContents[index];
if (contents) {
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(this.configurationResources[index].toString(), this.configurationNames[index]);
standAloneConfigurationModelParser.parse(contents);
standAloneConfigurationModelParser.parseContent(contents);
this._standAloneConfigurations.push(standAloneConfigurationModelParser.configurationModel);
}
}
@ -567,7 +582,7 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC
reprocess(): ConfigurationModel {
const oldContents = this._folderSettingsModelParser.configurationModel.contents;
this._folderSettingsModelParser.reprocess();
this._folderSettingsModelParser.parse();
if (!equals(oldContents, this._folderSettingsModelParser.configurationModel.contents)) {
this.consolidate();
}

View file

@ -16,7 +16,7 @@ import { isLinux } from 'vs/base/common/platform';
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 { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, IConfigurationFileService, machineSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
@ -72,7 +72,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this.defaultConfiguration = new DefaultConfigurationModel();
this.configurationCache = configurationCache;
if (userSettingsResource) {
this.localUserConfiguration = this._register(new UserConfiguration(userSettingsResource, configurationFileService));
this.localUserConfiguration = this._register(new UserConfiguration(userSettingsResource, undefined, configurationFileService));
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
}
if (remoteAuthority) {
@ -478,6 +478,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this.registerConfigurationSchemas();
if (this.workspace && this._configuration) {
this._configuration.updateDefaultConfiguration(this.defaultConfiguration);
if (this.remoteUserConfiguration) {
this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reprocess());
}
if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
this._configuration.updateWorkspaceConfiguration(this.cachedFolderConfigs.get(this.workspace.folders[0].uri)!.reprocess());
} else {
@ -505,6 +508,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema);
jsonRegistry.registerSchema(userSettingsSchemaId, allSettingsSchema);
jsonRegistry.registerSchema(machineSettingsSchemaId, workspaceSettingsSchema);
if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) {
const unsupportedWindowSettings = convertToNotSuggestedProperties(windowSettings.properties, localize('unsupportedWindowSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."));

View file

@ -7,6 +7,7 @@ import { URI } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
export const FOLDER_CONFIG_FOLDER_NAME = '.vscode';
export const FOLDER_SETTINGS_NAME = 'settings';
@ -14,10 +15,15 @@ export const FOLDER_SETTINGS_PATH = `${FOLDER_CONFIG_FOLDER_NAME}/${FOLDER_SETTI
export const defaultSettingsSchemaId = 'vscode://schemas/settings/default';
export const userSettingsSchemaId = 'vscode://schemas/settings/user';
export const machineSettingsSchemaId = 'vscode://schemas/settings/machine';
export const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace';
export const folderSettingsSchemaId = 'vscode://schemas/settings/folder';
export const launchSchemaId = 'vscode://schemas/launch';
export const MACHINE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE];
export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE];
export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE];
export const TASKS_CONFIGURATION_KEY = 'tasks';
export const LAUNCH_CONFIGURATION_KEY = 'launch';
@ -25,7 +31,6 @@ export const WORKSPACE_STANDALONE_CONFIGURATIONS = Object.create(null);
WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${TASKS_CONFIGURATION_KEY}.json`;
WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${LAUNCH_CONFIGURATION_KEY}.json`;
export type ConfigurationKey = { type: 'user' | 'workspaces' | 'folder', key: string };
export interface IConfigurationCache {

View file

@ -138,7 +138,7 @@ export class ConfigurationEditingService {
this.queue = new Queue<void>();
remoteAgentService.getEnvironment().then(environment => {
if (environment) {
this.remoteSettingsResource = environment.appSettingsPath;
this.remoteSettingsResource = environment.settingsPath;
}
});
}

View file

@ -6,22 +6,21 @@
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 { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, IConfigurationPropertySchema, Extensions, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
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';
export class WorkspaceConfigurationModelParser extends ConfigurationModelParser {
private _folders: IStoredWorkspaceFolder[] = [];
private _settingsModelParser: FolderSettingsModelParser;
private _settingsModelParser: ConfigurationModelParser;
private _launchModel: ConfigurationModel;
constructor(name: string) {
super(name);
this._settingsModelParser = new FolderSettingsModelParser(name, [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._settingsModelParser = new ConfigurationModelParser(name, WORKSPACE_SCOPES);
this._launchModel = new ConfigurationModel();
}
@ -38,14 +37,14 @@ export class WorkspaceConfigurationModelParser extends ConfigurationModelParser
}
reprocessWorkspaceSettings(): void {
this._settingsModelParser.reprocess();
this._settingsModelParser.parse();
}
protected parseRaw(raw: any): IConfigurationModel {
protected doParseRaw(raw: any): IConfigurationModel {
this._folders = (raw['folders'] || []) as IStoredWorkspaceFolder[];
this._settingsModelParser.parse(raw['settings']);
this._settingsModelParser.parseRaw(raw['settings']);
this._launchModel = this.createConfigurationModelFrom(raw, 'launch');
return super.parseRaw(raw);
return super.doParseRaw(raw);
}
private createConfigurationModelFrom(raw: any, key: string): ConfigurationModel {
@ -67,7 +66,7 @@ export class StandaloneConfigurationModelParser extends ConfigurationModelParser
super(name);
}
protected parseRaw(raw: any): IConfigurationModel {
protected doParseRaw(raw: any): IConfigurationModel {
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const scopedContents = Object.create(null);
scopedContents[this.scope] = contents;
@ -77,56 +76,6 @@ export class StandaloneConfigurationModelParser extends ConfigurationModelParser
}
export class FolderSettingsModelParser extends ConfigurationModelParser {
private _raw: any;
private _settingsModel: ConfigurationModel;
constructor(name: string, private scopes: ConfigurationScope[]) {
super(name);
}
parse(content: string | any): void {
this._raw = typeof content === 'string' ? this.parseContent(content) : content;
this.parseWorkspaceSettings(this._raw);
}
get configurationModel(): ConfigurationModel {
return this._settingsModel || new ConfigurationModel();
}
reprocess(): void {
this.parse(this._raw);
}
private parseWorkspaceSettings(rawSettings: any): void {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
const rawWorkspaceSettings = this.filterByScope(rawSettings, configurationProperties, true);
const configurationModel = this.parseRaw(rawWorkspaceSettings);
this._settingsModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
}
private filterByScope(properties: {}, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }, filterOverriddenProperties: boolean): {} {
const result = {};
for (let key in properties) {
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
result[key] = this.filterByScope(properties[key], configurationProperties, false);
} else {
const scope = this.getScope(key, configurationProperties);
if (this.scopes.indexOf(scope) !== -1) {
result[key] = properties[key];
}
}
}
return result;
}
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope {
const propertySchema = configurationProperties[key];
return propertySchema && typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW;
}
}
export class Configuration extends BaseConfiguration {
constructor(

View file

@ -5,10 +5,10 @@
import * as assert from 'assert';
import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { FolderSettingsModelParser, WorkspaceConfigurationChangeEvent, StandaloneConfigurationModelParser, AllKeysConfigurationChangeEvent, Configuration } from 'vs/workbench/services/configuration/common/configurationModels';
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 } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ResourceMap } from 'vs/base/common/map';
@ -41,33 +41,33 @@ suite('FolderSettingsModelParser', () => {
});
test('parse all folder settings', () => {
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
const testObject = new ConfigurationModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' }));
testObject.parseContent(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' }));
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'window': 'window', 'resource': 'resource' } });
});
test('parse resource folder settings', () => {
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE]);
const testObject = new ConfigurationModelParser('settings', [ConfigurationScope.RESOURCE]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' }));
testObject.parseContent(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' }));
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'resource': 'resource' } });
});
test('parse overridable resource settings', () => {
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE]);
const testObject = new ConfigurationModelParser('settings', [ConfigurationScope.RESOURCE]);
testObject.parse(JSON.stringify({ '[json]': { 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' } }));
testObject.parseContent(JSON.stringify({ '[json]': { 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' } }));
assert.deepEqual(testObject.configurationModel.overrides, [{ 'contents': { 'FolderSettingsModelParser': { 'resource': 'resource' } }, 'identifiers': ['json'] }]);
});
test('reprocess folder settings excludes application setting', () => {
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
const testObject = new ConfigurationModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.anotherApplicationSetting': 'executable' }));
testObject.parseContent(JSON.stringify({ 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.anotherApplicationSetting': 'executable' }));
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'resource': 'resource', 'anotherApplicationSetting': 'executable' } });
@ -84,7 +84,7 @@ suite('FolderSettingsModelParser', () => {
}
});
testObject.reprocess();
testObject.parse();
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'resource': 'resource' } });
});
@ -95,7 +95,7 @@ suite('StandaloneConfigurationModelParser', () => {
test('parse tasks stand alone configuration model', () => {
const testObject = new StandaloneConfigurationModelParser('tasks', 'tasks');
testObject.parse(JSON.stringify({ 'version': '1.1.1', 'tasks': [] }));
testObject.parseContent(JSON.stringify({ 'version': '1.1.1', 'tasks': [] }));
assert.deepEqual(testObject.configurationModel.contents, { 'tasks': { 'version': '1.1.1', 'tasks': [] } });
});

View file

@ -217,8 +217,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic
async openRemoteSettings(): Promise<IEditor | null> {
const environemnt = await this.remoteAgentService.getEnvironment();
if (environemnt) {
await this.createIfNotExists(environemnt.appSettingsPath, emptyEditableSettingsContent);
return this.editorService.openEditor({ resource: environemnt.appSettingsPath, options: { pinned: true, revealIfOpened: true } });
await this.createIfNotExists(environemnt.settingsPath, emptyEditableSettingsContent);
return this.editorService.openEditor({ resource: environemnt.settingsPath, options: { pinned: true, revealIfOpened: true } });
}
return null;
}

View file

@ -20,7 +20,7 @@ export interface IRemoteAgentEnvironmentDTO {
pid: number;
appRoot: UriComponents;
appSettingsHome: UriComponents;
appSettingsPath: UriComponents;
settingsPath: UriComponents;
logsPath: UriComponents;
extensionsPath: UriComponents;
extensionHostLogsPath: UriComponents;
@ -47,7 +47,7 @@ export class RemoteExtensionEnvironmentChannelClient {
pid: data.pid,
appRoot: URI.revive(data.appRoot),
appSettingsHome: URI.revive(data.appSettingsHome),
appSettingsPath: URI.revive(data.appSettingsPath),
settingsPath: URI.revive(data.settingsPath),
logsPath: URI.revive(data.logsPath),
extensionsPath: URI.revive(data.extensionsPath),
extensionHostLogsPath: URI.revive(data.extensionHostLogsPath),