configurationService.ts 33.8 KB
Newer Older
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
9
import { dirname, basename } from 'path';
10
import * as assert from 'vs/base/common/assert';
M
Matt Bierner 已提交
11
import { Event, Emitter } from 'vs/base/common/event';
12
import { StrictResourceMap } from 'vs/base/common/map';
S
Sandeep Somavarapu 已提交
13
import { equals, deepClone } from 'vs/base/common/objects';
S
Sandeep Somavarapu 已提交
14 15 16
import { Disposable } from 'vs/base/common/lifecycle';
import { Queue } from 'vs/base/common/async';
import { stat, writeFile } from 'vs/base/node/pfs';
17
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
18
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
S
#47154  
Sandeep Somavarapu 已提交
19
import { IFileService } from 'vs/platform/files/common/files';
20
import { isLinux } from 'vs/base/common/platform';
21
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
S
Sandeep Somavarapu 已提交
22
import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
23
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
S
Sandeep Somavarapu 已提交
24 25
import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { IWorkspaceConfigurationService, FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
26
import { Registry } from 'vs/platform/registry/common/platform';
S
Sandeep Somavarapu 已提交
27
import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry';
28
import { createHash } from 'crypto';
S
Sandeep Somavarapu 已提交
29
import { getWorkspaceLabel, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
30
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
31
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
32 33
import { ICommandService } from 'vs/platform/commands/common/commands';
import product from 'vs/platform/node/product';
34 35
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
S
#47154  
Sandeep Somavarapu 已提交
36
import { WorkspaceConfiguration, FolderConfiguration, FileServiceBasedFolderConfiguration, NodeBasedFolderConfiguration, VoidFolderConfiguration } from 'vs/workbench/services/configuration/node/configuration';
S
Sandeep Somavarapu 已提交
37 38 39
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
import { Schemas } from 'vs/base/common/network';
import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces';
S
Sandeep Somavarapu 已提交
40
import { distinct } from 'vs/base/common/arrays';
S
Sandeep Somavarapu 已提交
41
import { UserConfiguration } from 'vs/platform/configuration/node/configuration';
B
Benjamin Pasero 已提交
42
import { getBaseLabel } from 'vs/base/common/labels';
S
Sandeep Somavarapu 已提交
43 44
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
45

S
Sandeep Somavarapu 已提交
46
export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService {
47 48 49

	public _serviceBrand: any;

50
	private workspace: Workspace;
51
	private _configuration: Configuration;
S
Sandeep Somavarapu 已提交
52 53
	private defaultConfiguration: DefaultConfigurationModel;
	private userConfiguration: UserConfiguration;
54
	private workspaceConfiguration: WorkspaceConfiguration;
55
	private cachedFolderConfigs: StrictResourceMap<FolderConfiguration>;
56

S
Sandeep Somavarapu 已提交
57 58
	private workspaceEditingQueue: Queue<void>;

59 60
	protected readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
	public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
61

62 63
	protected readonly _onDidChangeWorkspaceFolders: Emitter<IWorkspaceFoldersChangeEvent> = this._register(new Emitter<IWorkspaceFoldersChangeEvent>());
	public readonly onDidChangeWorkspaceFolders: Event<IWorkspaceFoldersChangeEvent> = this._onDidChangeWorkspaceFolders.event;
64

S
Sandeep Somavarapu 已提交
65 66 67
	protected readonly _onDidChangeWorkspaceName: Emitter<void> = this._register(new Emitter<void>());
	public readonly onDidChangeWorkspaceName: Event<void> = this._onDidChangeWorkspaceName.event;

68 69 70
	protected readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState> = this._register(new Emitter<WorkbenchState>());
	public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;

S
#47154  
Sandeep Somavarapu 已提交
71
	private fileService: IFileService;
S
Sandeep Somavarapu 已提交
72
	private configurationEditingService: ConfigurationEditingService;
S
Sandeep Somavarapu 已提交
73
	private jsonEditingService: JSONEditingService;
74

S
Sandeep Somavarapu 已提交
75
	constructor(private environmentService: IEnvironmentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) {
76
		super();
77

S
Sandeep Somavarapu 已提交
78 79
		this.defaultConfiguration = new DefaultConfigurationModel();
		this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath));
80
		this.workspaceConfiguration = this._register(new WorkspaceConfiguration());
S
Sandeep Somavarapu 已提交
81
		this._register(this.userConfiguration.onDidChangeConfiguration(() => this.onUserConfigurationChanged()));
82 83
		this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged()));

84
		this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(e => this.registerConfigurationSchemas()));
S
Sandeep Somavarapu 已提交
85
		this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties)));
S
Sandeep Somavarapu 已提交
86 87

		this.workspaceEditingQueue = new Queue<void>();
88 89
	}

90 91
	// Workspace Context Service Impl

92
	public getWorkspace(): Workspace {
93 94 95
		return this.workspace;
	}

96
	public getWorkbenchState(): WorkbenchState {
97 98 99 100 101 102
		// Workspace has configuration file
		if (this.workspace.configuration) {
			return WorkbenchState.WORKSPACE;
		}

		// Folder has single root
S
Sandeep Somavarapu 已提交
103
		if (this.workspace.folders.length === 1) {
104
			return WorkbenchState.FOLDER;
105
		}
106 107

		// Empty
108
		return WorkbenchState.EMPTY;
109 110
	}

S
Sandeep Somavarapu 已提交
111
	public getWorkspaceFolder(resource: URI): IWorkspaceFolder {
S
Sandeep Somavarapu 已提交
112
		return this.workspace.getFolder(resource);
113 114
	}

B
Benjamin Pasero 已提交
115
	public addFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number): TPromise<void> {
116
		return this.updateFolders(foldersToAdd, [], index);
S
Sandeep Somavarapu 已提交
117 118 119
	}

	public removeFolders(foldersToRemove: URI[]): TPromise<void> {
120 121 122 123
		return this.updateFolders([], foldersToRemove);
	}

	public updateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): TPromise<void> {
S
Sandeep Somavarapu 已提交
124
		assert.ok(this.jsonEditingService, 'Workbench is not initialized yet');
125
		return this.workspaceEditingQueue.queue(() => this.doUpdateFolders(foldersToAdd, foldersToRemove, index));
S
Sandeep Somavarapu 已提交
126 127
	}

128
	public isInsideWorkspace(resource: URI): boolean {
S
Sandeep Somavarapu 已提交
129
		return !!this.getWorkspaceFolder(resource);
130 131
	}

132
	public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean {
133 134
		switch (this.getWorkbenchState()) {
			case WorkbenchState.FOLDER:
135
				return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier);
136 137
			case WorkbenchState.WORKSPACE:
				return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id;
138
		}
139
		return false;
140 141
	}

142
	private doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): TPromise<void> {
S
Sandeep Somavarapu 已提交
143 144 145 146
		if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {
			return TPromise.as(void 0); // we need a workspace to begin with
		}

147 148 149
		if (foldersToAdd.length + foldersToRemove.length === 0) {
			return TPromise.as(void 0); // nothing to do
		}
S
Sandeep Somavarapu 已提交
150

151
		let foldersHaveChanged = false;
S
Sandeep Somavarapu 已提交
152

153 154 155 156 157
		// Remove first (if any)
		let currentWorkspaceFolders = this.getWorkspace().folders;
		let newStoredFolders: IStoredWorkspaceFolder[] = currentWorkspaceFolders.map(f => f.raw).filter((folder, index) => {
			if (!isStoredWorkspaceFolder(folder)) {
				return true; // keep entries which are unrelated
S
Sandeep Somavarapu 已提交
158 159
			}

160 161
			return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated
		});
162

163
		foldersHaveChanged = currentWorkspaceFolders.length !== newStoredFolders.length;
S
Sandeep Somavarapu 已提交
164

165 166
		// Add afterwards (if any)
		if (foldersToAdd.length) {
167

168 169 170 171
			// Recompute current workspace folders if we have folders to add
			const workspaceConfigFolder = dirname(this.getWorkspace().configuration.fsPath);
			currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, URI.file(workspaceConfigFolder));
			const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
172

173
			const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];
S
Sandeep Somavarapu 已提交
174

175 176 177 178
			foldersToAdd.forEach(folderToAdd => {
				if (this.contains(currentWorkspaceFolderUris, folderToAdd.uri)) {
					return; // already existing
				}
B
Benjamin Pasero 已提交
179

180
				let storedFolder: IStoredWorkspaceFolder;
B
Benjamin Pasero 已提交
181

182 183 184 185 186 187
				// File resource: use "path" property
				if (folderToAdd.uri.scheme === Schemas.file) {
					storedFolder = {
						path: massageFolderPathForWorkspace(folderToAdd.uri.fsPath, workspaceConfigFolder, newStoredFolders)
					};
				}
S
Sandeep Somavarapu 已提交
188

189 190 191 192 193 194
				// Any other resource: use "uri" property
				else {
					storedFolder = {
						uri: folderToAdd.uri.toString(true)
					};
				}
S
Sandeep Somavarapu 已提交
195

196 197 198
				if (folderToAdd.name) {
					storedFolder.name = folderToAdd.name;
				}
S
Sandeep Somavarapu 已提交
199

200 201
				storedFoldersToAdd.push(storedFolder);
			});
S
Sandeep Somavarapu 已提交
202

203 204 205
			// Apply to array of newStoredFolders
			if (storedFoldersToAdd.length > 0) {
				foldersHaveChanged = true;
S
Sandeep Somavarapu 已提交
206

207 208 209 210 211 212 213 214
				if (typeof index === 'number' && index >= 0 && index < newStoredFolders.length) {
					newStoredFolders = newStoredFolders.slice(0);
					newStoredFolders.splice(index, 0, ...storedFoldersToAdd);
				} else {
					newStoredFolders = [...newStoredFolders, ...storedFoldersToAdd];
				}
			}
		}
S
Sandeep Somavarapu 已提交
215

216 217
		// Set folders if we recorded a change
		if (foldersHaveChanged) {
S
Sandeep Somavarapu 已提交
218
			return this.setFolders(newStoredFolders);
S
Sandeep Somavarapu 已提交
219 220 221 222 223
		}

		return TPromise.as(void 0);
	}

S
Sandeep Somavarapu 已提交
224 225 226 227 228
	private setFolders(folders: IStoredWorkspaceFolder[]): TPromise<void> {
		return this.workspaceConfiguration.setFolders(folders, this.jsonEditingService)
			.then(() => this.onWorkspaceConfigurationChanged());
	}

S
Sandeep Somavarapu 已提交
229 230 231 232 233 234 235 236 237 238
	private contains(resources: URI[], toCheck: URI): boolean {
		return resources.some(resource => {
			if (isLinux) {
				return resource.toString() === toCheck.toString();
			}

			return resource.toString().toLowerCase() === toCheck.toString().toLowerCase();
		});
	}

239 240
	// Workspace Configuration Service Impl

241 242 243 244
	getConfigurationData(): IConfigurationData {
		return this._configuration.toData();
	}

245 246 247 248 249 250 251 252
	getValue<T>(): T;
	getValue<T>(section: string): T;
	getValue<T>(overrides: IConfigurationOverrides): T;
	getValue<T>(section: string, overrides: IConfigurationOverrides): T;
	getValue(arg1?: any, arg2?: any): any {
		const section = typeof arg1 === 'string' ? arg1 : void 0;
		const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : void 0;
		return this._configuration.getValue(section, overrides);
253 254
	}

255 256 257 258 259
	updateValue(key: string, value: any): TPromise<void>;
	updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise<void>;
	updateValue(key: string, value: any, target: ConfigurationTarget): TPromise<void>;
	updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): TPromise<void>;
	updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError: boolean): TPromise<void>;
260
	updateValue(key: string, value: any, arg3?: any, arg4?: any, donotNotifyError?: any): TPromise<void> {
261 262 263
		assert.ok(this.configurationEditingService, 'Workbench is not initialized yet');
		const overrides = isConfigurationOverrides(arg3) ? arg3 : void 0;
		const target = this.deriveConfigurationTarget(key, value, overrides, overrides ? arg4 : arg3);
264
		return target ? this.writeConfigurationValue(key, value, target, overrides, donotNotifyError)
265
			: TPromise.as(null);
266 267
	}

268 269 270 271
	reloadConfiguration(folder?: IWorkspaceFolder, key?: string): TPromise<void> {
		if (folder) {
			return this.reloadWorkspaceFolderConfiguration(folder, key);
		}
S
Sandeep Somavarapu 已提交
272
		return this.reloadUserConfiguration()
S
Sandeep Somavarapu 已提交
273
			.then(() => this.reloadWorkspaceConfiguration())
S
Sandeep Somavarapu 已提交
274
			.then(() => this.loadConfiguration());
275
	}
276

277 278 279 280 281
	inspect<T>(key: string, overrides?: IConfigurationOverrides): {
		default: T,
		user: T,
		workspace: T,
		workspaceFolder: T,
282
		memory?: T,
283 284
		value: T
	} {
285
		return this._configuration.inspect<T>(key, overrides);
286 287
	}

288 289 290 291 292 293 294
	keys(): {
		default: string[];
		user: string[];
		workspace: string[];
		workspaceFolder: string[];
	} {
		return this._configuration.keys();
295 296
	}

297
	getUnsupportedWorkspaceKeys(): string[] {
298
		const unsupportedWorkspaceKeys = [...this.workspaceConfiguration.getUnsupportedKeys()];
S
Sandeep Somavarapu 已提交
299
		for (const folder of this.workspace.folders) {
S
Sandeep Somavarapu 已提交
300
			unsupportedWorkspaceKeys.push(...this.cachedFolderConfigs.get(folder.uri).getUnsupportedKeys());
S
Sandeep Somavarapu 已提交
301 302
		}
		return distinct(unsupportedWorkspaceKeys);
303 304
	}

305
	initialize(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise<any> {
306
		return this.createWorkspace(arg)
S
Sandeep Somavarapu 已提交
307
			.then(workspace => this.updateWorkspaceAndInitializeConfiguration(workspace));
308 309
	}

S
#47154  
Sandeep Somavarapu 已提交
310 311 312
	acquireFileService(fileService: IFileService): void {
		this.fileService = fileService;
		this.reloadConfiguration();
313 314
	}

S
#47154  
Sandeep Somavarapu 已提交
315 316 317
	acquireInstantiationService(instantiationService: IInstantiationService): void {
		this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
		this.jsonEditingService = instantiationService.createInstance(JSONEditingService);
318 319
	}

320
	private createWorkspace(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise<Workspace> {
321
		if (isWorkspaceIdentifier(arg)) {
322
			return this.createMulitFolderWorkspace(arg);
323 324 325
		}

		if (isSingleFolderWorkspaceIdentifier(arg)) {
326
			return this.createSingleFolderWorkspace(arg);
327 328
		}

329
		return this.createEmptyWorkspace(arg);
330 331
	}

332
	private createMulitFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): TPromise<Workspace> {
333
		const workspaceConfigPath = URI.file(workspaceIdentifier.configPath);
334
		return this.workspaceConfiguration.load(workspaceConfigPath)
335
			.then(() => {
336
				const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(workspaceConfigPath.fsPath)));
337
				const workspaceId = workspaceIdentifier.id;
338
				const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService);
339
				return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath);
340 341 342
			});
	}

343
	private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): TPromise<Workspace> {
344
		const folderPath = URI.file(singleFolderWorkspaceIdentifier);
345
		return stat(folderPath.fsPath)
346 347
			.then(workspaceStat => {
				const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead!
348 349
				const id = createHash('md5').update(folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex');
				const folder = URI.file(folderPath.fsPath);
B
Benjamin Pasero 已提交
350
				return new Workspace(id, getBaseLabel(folder), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime);
351 352 353
			});
	}

354
	private createEmptyWorkspace(configuration: IWindowConfiguration): TPromise<Workspace> {
355
		let id = configuration.backupPath ? URI.from({ path: basename(configuration.backupPath), scheme: 'empty' }).toString() : '';
356 357 358
		return TPromise.as(new Workspace(id));
	}

S
Sandeep Somavarapu 已提交
359
	private updateWorkspaceAndInitializeConfiguration(workspace: Workspace): TPromise<void> {
S
Sandeep Somavarapu 已提交
360
		const hasWorkspaceBefore = !!this.workspace;
361 362 363
		let previousState: WorkbenchState;
		let previousWorkspacePath: string;
		let previousFolders: WorkspaceFolder[];
S
Sandeep Somavarapu 已提交
364 365 366 367 368

		if (hasWorkspaceBefore) {
			previousState = this.getWorkbenchState();
			previousWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0;
			previousFolders = this.workspace.folders;
S
Sandeep Somavarapu 已提交
369 370 371
			this.workspace.update(workspace);
		} else {
			this.workspace = workspace;
372
		}
S
Sandeep Somavarapu 已提交
373 374

		return this.initializeConfiguration().then(() => {
S
Sandeep Somavarapu 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
			// Trigger changes after configuration initialization so that configuration is up to date.
			if (hasWorkspaceBefore) {
				const newState = this.getWorkbenchState();
				if (previousState && newState !== previousState) {
					this._onDidChangeWorkbenchState.fire(newState);
				}

				const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0;
				if (previousWorkspacePath && newWorkspacePath !== previousWorkspacePath || newState !== previousState) {
					this._onDidChangeWorkspaceName.fire();
				}

				const folderChanges = this.compareFolders(previousFolders, this.workspace.folders);
				if (folderChanges && (folderChanges.added.length || folderChanges.removed.length || folderChanges.changed.length)) {
					this._onDidChangeWorkspaceFolders.fire(folderChanges);
				}
S
Sandeep Somavarapu 已提交
391 392
			}
		});
393 394
	}

S
Sandeep Somavarapu 已提交
395
	private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent {
396
		const result = { added: [], removed: [], changed: [] } as IWorkspaceFoldersChangeEvent;
397
		result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString()));
S
Sandeep Somavarapu 已提交
398 399 400 401 402 403 404 405 406 407 408 409
		for (let currentIndex = 0; currentIndex < currentFolders.length; currentIndex++) {
			let currentFolder = currentFolders[currentIndex];
			let newIndex = 0;
			for (newIndex = 0; newIndex < newFolders.length && currentFolder.uri.toString() !== newFolders[newIndex].uri.toString(); newIndex++) { }
			if (newIndex < newFolders.length) {
				if (currentIndex !== newIndex || currentFolder.name !== newFolders[newIndex].name) {
					result.changed.push(currentFolder);
				}
			} else {
				result.removed.push(currentFolder);
			}
		}
410 411 412
		return result;
	}

413
	private initializeConfiguration(): TPromise<void> {
S
Sandeep Somavarapu 已提交
414
		this.registerConfigurationSchemas();
415
		return this.loadConfiguration();
416 417
	}

S
Sandeep Somavarapu 已提交
418
	private reloadUserConfiguration(key?: string): TPromise<void> {
S
Sandeep Somavarapu 已提交
419
		return this.userConfiguration.reload();
S
Sandeep Somavarapu 已提交
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	}

	private reloadWorkspaceConfiguration(key?: string): TPromise<void> {
		const workbenchState = this.getWorkbenchState();
		if (workbenchState === WorkbenchState.FOLDER) {
			return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0], key);
		}
		if (workbenchState === WorkbenchState.WORKSPACE) {
			return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged());
		}
		return TPromise.as(null);
	}

	private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder, key?: string): TPromise<void> {
		return this.onWorkspaceFolderConfigurationChanged(folder, key);
	}

437 438
	private loadConfiguration(): TPromise<void> {
		// reset caches
439
		this.cachedFolderConfigs = new StrictResourceMap<FolderConfiguration>();
440

441 442 443 444 445
		const folders = this.workspace.folders;
		return this.loadFolderConfigurations(folders)
			.then((folderConfigurations) => {

				let workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations);
S
Sandeep Somavarapu 已提交
446
				const folderConfigurationModels = new StrictResourceMap<ConfigurationModel>();
447 448
				folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));

449
				const currentConfiguration = this._configuration;
S
Sandeep Somavarapu 已提交
450
				this._configuration = new Configuration(this.defaultConfiguration, this.userConfiguration.configurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new StrictResourceMap<ConfigurationModel>(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null
451

452 453 454 455
				if (currentConfiguration) {
					const changedKeys = this._configuration.compare(currentConfiguration);
					this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE);
				} else {
S
Sandeep Somavarapu 已提交
456
					this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE)));
457
				}
458
			});
459 460
	}

S
Sandeep Somavarapu 已提交
461
	private getWorkspaceConfigurationModel(folderConfigurations: ConfigurationModel[]): ConfigurationModel {
462 463 464 465
		switch (this.getWorkbenchState()) {
			case WorkbenchState.FOLDER:
				return folderConfigurations[0];
			case WorkbenchState.WORKSPACE:
S
Sandeep Somavarapu 已提交
466
				return this.workspaceConfiguration.getConfiguration();
467
			default:
468
				return new ConfigurationModel();
469
		}
470 471
	}

S
Sandeep Somavarapu 已提交
472 473 474 475 476
	private onDefaultConfigurationChanged(keys: string[]): void {
		this.defaultConfiguration = new DefaultConfigurationModel();
		this.registerConfigurationSchemas();
		if (this.workspace && this._configuration) {
			this._configuration.updateDefaultConfiguration(this.defaultConfiguration);
S
Sandeep Somavarapu 已提交
477 478 479 480 481 482
			if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
				this._configuration.updateWorkspaceConfiguration(this.cachedFolderConfigs.get(this.workspace.folders[0].uri).reprocess());
			} else {
				this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reprocessWorkspaceSettings());
				this.workspace.folders.forEach(folder => this._configuration.updateFolderConfiguration(folder.uri, this.cachedFolderConfigs.get(folder.uri).reprocess()));
			}
S
Sandeep Somavarapu 已提交
483 484 485 486
			this.triggerConfigurationChange(new ConfigurationChangeEvent().change(keys), ConfigurationTarget.DEFAULT);
		}
	}

S
Sandeep Somavarapu 已提交
487 488
	private registerConfigurationSchemas(): void {
		if (this.workspace) {
489
			const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
S
Sandeep Somavarapu 已提交
490 491 492 493 494 495 496 497 498 499 500 501 502 503
			const convertToNotSuggestedProperties = (properties: IJSONSchemaMap, errorMessage: string): IJSONSchemaMap => {
				return Object.keys(properties).reduce((result: IJSONSchemaMap, property) => {
					result[property] = deepClone(properties[property]);
					result[property].deprecationMessage = errorMessage;
					return result;
				}, {});
			};

			const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
			const unsupportedApplicationSettings = convertToNotSuggestedProperties(applicationSettings.properties, localize('unsupportedApplicationSetting', "This setting can be applied only in User Settings"));
			const workspaceSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };

			jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema);
			jsonRegistry.registerSchema(userSettingsSchemaId, allSettingsSchema);
S
Sandeep Somavarapu 已提交
504 505

			if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) {
S
Sandeep Somavarapu 已提交
506 507 508 509
				const unsupportedWindowSettings = convertToNotSuggestedProperties(windowSettings.properties, localize('unsupportedWindowSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."));
				const folderSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...unsupportedWindowSettings, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
				jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
				jsonRegistry.registerSchema(folderSettingsSchemaId, folderSettingsSchema);
S
Sandeep Somavarapu 已提交
510
			} else {
S
Sandeep Somavarapu 已提交
511 512
				jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
				jsonRegistry.registerSchema(folderSettingsSchemaId, workspaceSettingsSchema);
S
Sandeep Somavarapu 已提交
513 514 515 516
			}
		}
	}

S
Sandeep Somavarapu 已提交
517 518 519
	private onUserConfigurationChanged(): void {
		let keys = this._configuration.compareAndUpdateUserConfiguration(this.userConfiguration.configurationModel);
		this.triggerConfigurationChange(keys, ConfigurationTarget.USER);
520 521
	}

522 523
	private onWorkspaceConfigurationChanged(): TPromise<void> {
		if (this.workspace && this.workspace.configuration && this._configuration) {
S
Sandeep Somavarapu 已提交
524
			const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
525
			let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(this.workspace.configuration.fsPath)));
526
			const changes = this.compareFolders(this.workspace.folders, configuredFolders);
527
			if (changes.added.length || changes.removed.length || changes.changed.length) {
528
				this.workspace.folders = configuredFolders;
529
				return this.onFoldersChanged()
530 531
					.then(foldersConfigurationChangeEvent => {
						this.triggerConfigurationChange(foldersConfigurationChangeEvent.change(workspaceConfigurationChangeEvent), ConfigurationTarget.WORKSPACE_FOLDER);
S
Sandeep Somavarapu 已提交
532
						this._onDidChangeWorkspaceFolders.fire(changes);
533 534
					});
			} else {
535
				this.triggerConfigurationChange(workspaceConfigurationChangeEvent, ConfigurationTarget.WORKSPACE);
S
Sandeep Somavarapu 已提交
536
			}
537
		}
538 539 540 541 542 543
		return TPromise.as(null);
	}

	private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): TPromise<void> {
		return this.loadFolderConfigurations([folder])
			.then(([folderConfiguration]) => {
S
Sandeep Somavarapu 已提交
544
				const folderChangedKeys = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);
545
				if (this.getWorkbenchState() === WorkbenchState.FOLDER) {
S
Sandeep Somavarapu 已提交
546
					const workspaceChangedKeys = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);
547 548 549 550 551 552 553
					this.triggerConfigurationChange(workspaceChangedKeys, ConfigurationTarget.WORKSPACE);
				} else {
					this.triggerConfigurationChange(folderChangedKeys, ConfigurationTarget.WORKSPACE_FOLDER);
				}
			});
	}

554 555
	private onFoldersChanged(): TPromise<ConfigurationChangeEvent> {
		let changeEvent = new ConfigurationChangeEvent();
556

557 558
		// Remove the configurations of deleted folders
		for (const key of this.cachedFolderConfigs.keys()) {
559
			if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) {
560
				this.cachedFolderConfigs.delete(key);
S
Sandeep Somavarapu 已提交
561
				changeEvent = changeEvent.change(this._configuration.compareAndDeleteFolderConfiguration(key));
562 563 564
			}
		}

565
		const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri));
566
		if (toInitialize.length) {
567 568 569
			return this.loadFolderConfigurations(toInitialize)
				.then(folderConfigurations => {
					folderConfigurations.forEach((folderConfiguration, index) => {
S
Sandeep Somavarapu 已提交
570
						changeEvent = changeEvent.change(this._configuration.compareAndUpdateFolderConfiguration(toInitialize[index].uri, folderConfiguration));
571
					});
572
					return changeEvent;
573
				});
574
		}
575
		return TPromise.as(changeEvent);
576 577
	}

S
Sandeep Somavarapu 已提交
578
	private loadFolderConfigurations(folders: IWorkspaceFolder[]): TPromise<ConfigurationModel[]> {
579
		return TPromise.join([...folders.map(folder => {
S
#47154  
Sandeep Somavarapu 已提交
580 581 582
			this.disposeFolderConfiguration(folder);
			const folderConfiguration = this.createFolderConfiguration(folder);
			this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));
583 584 585
			this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));
			return folderConfiguration.loadConfiguration();
		})]);
586 587
	}

S
#47154  
Sandeep Somavarapu 已提交
588 589 590 591 592 593 594 595 596 597
	private createFolderConfiguration(folder: IWorkspaceFolder): FolderConfiguration {
		if (this.fileService) {
			return new FileServiceBasedFolderConfiguration(folder.uri, this.workspaceSettingsRootFolder, this.getWorkbenchState(), this.fileService);
		}
		if (folder.uri.scheme === Schemas.file) {
			return new NodeBasedFolderConfiguration(folder.uri, this.workspaceSettingsRootFolder, this.getWorkbenchState());
		}
		return new VoidFolderConfiguration(folder.uri, this.getWorkbenchState());
	}

598
	private writeConfigurationValue(key: string, value: any, target: ConfigurationTarget, overrides: IConfigurationOverrides, donotNotifyError: boolean): TPromise<void> {
599 600 601 602 603 604
		if (target === ConfigurationTarget.DEFAULT) {
			return TPromise.wrapError(new Error('Invalid configuration target'));
		}

		if (target === ConfigurationTarget.MEMORY) {
			this._configuration.updateValue(key, value, overrides);
605
			this.triggerConfigurationChange(new ConfigurationChangeEvent().change(overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier)] : [key], overrides.resource), target);
606 607 608
			return TPromise.as(null);
		}

S
Sandeep Somavarapu 已提交
609
		return this.configurationEditingService.writeConfiguration(target, { key, value }, { scopes: overrides, donotNotifyError })
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
			.then(() => {
				switch (target) {
					case ConfigurationTarget.USER:
						return this.reloadUserConfiguration();
					case ConfigurationTarget.WORKSPACE:
						return this.reloadWorkspaceConfiguration();
					case ConfigurationTarget.WORKSPACE_FOLDER:
						const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null;
						if (workspaceFolder) {
							return this.reloadWorkspaceFolderConfiguration(this.workspace.getFolder(overrides.resource), key);
						}
				}
				return null;
			});
	}

	private deriveConfigurationTarget(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): ConfigurationTarget {
		if (target) {
			return target;
		}

		if (value === void 0) {
			// Ignore. But expected is to remove the value from all targets
			return void 0;
		}

		const inspect = this.inspect(key, overrides);
		if (equals(value, inspect.value)) {
			// No change. So ignore.
			return void 0;
640
		}
641 642 643 644 645 646 647 648 649 650

		if (inspect.workspaceFolder !== void 0) {
			return ConfigurationTarget.WORKSPACE_FOLDER;
		}

		if (inspect.workspace !== void 0) {
			return ConfigurationTarget.WORKSPACE;
		}

		return ConfigurationTarget.USER;
651 652
	}

653 654 655
	private triggerConfigurationChange(configurationEvent: ConfigurationChangeEvent, target: ConfigurationTarget): void {
		if (configurationEvent.affectedKeys.length) {
			configurationEvent.telemetryData(target, this.getTargetConfiguration(target));
656
			this._onDidChangeConfiguration.fire(new WorkspaceConfigurationChangeEvent(configurationEvent, this.workspace));
657
		}
658
	}
659

660 661 662 663 664 665 666 667
	private getTargetConfiguration(target: ConfigurationTarget): any {
		switch (target) {
			case ConfigurationTarget.DEFAULT:
				return this._configuration.defaults.contents;
			case ConfigurationTarget.USER:
				return this._configuration.user.contents;
			case ConfigurationTarget.WORKSPACE:
				return this._configuration.workspace.contents;
668
		}
669
		return {};
670
	}
671 672 673 674 675 676 677 678 679

	private pathEquals(path1: string, path2: string): boolean {
		if (!isLinux) {
			path1 = path1.toLowerCase();
			path2 = path2.toLowerCase();
		}

		return path1 === path2;
	}
680 681 682 683 684 685 686

	private disposeFolderConfiguration(folder: IWorkspaceFolder): void {
		const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
		if (folderConfiguration) {
			folderConfiguration.dispose();
		}
	}
687
}
688

689 690 691 692 693 694 695 696 697 698 699 700 701
interface IExportedConfigurationNode {
	name: string;
	description: string;
	default: any;
	type: string | string[];
	enum?: any[];
	enumDescriptions?: string[];
}

interface IConfigurationExport {
	settings: IExportedConfigurationNode[];
	buildTime: number;
	commit: string;
702
	buildNumber: number;
703 704 705
}

export class DefaultConfigurationExportHelper {
706 707 708 709 710

	constructor(
		@IEnvironmentService environmentService: IEnvironmentService,
		@IExtensionService private extensionService: IExtensionService,
		@ICommandService private commandService: ICommandService) {
711 712
		if (environmentService.args['export-default-configuration']) {
			this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']);
713 714 715 716
		}
	}

	private writeConfigModelAndQuit(targetPath: string): TPromise<void> {
717
		return this.extensionService.whenInstalledExtensionsRegistered()
718 719 720 721 722 723
			.then(() => this.writeConfigModel(targetPath))
			.then(() => this.commandService.executeCommand('workbench.action.quit'))
			.then(() => { });
	}

	private writeConfigModel(targetPath: string): TPromise<void> {
724 725 726 727 728 729 730
		const config = this.getConfigModel();

		const resultString = JSON.stringify(config, undefined, '  ');
		return writeFile(targetPath, resultString);
	}

	private getConfigModel(): IConfigurationExport {
731 732
		const configRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
		const configurations = configRegistry.getConfigurations().slice();
733
		const settings: IExportedConfigurationNode[] = [];
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753

		const processProperty = (name: string, prop: IConfigurationPropertySchema) => {
			const propDetails: IExportedConfigurationNode = {
				name,
				description: prop.description,
				default: prop.default,
				type: prop.type
			};

			if (prop.enum) {
				propDetails.enum = prop.enum;
			}

			if (prop.enumDescriptions) {
				propDetails.enumDescriptions = prop.enumDescriptions;
			}

			settings.push(propDetails);
		};

754 755 756
		const processConfig = (config: IConfigurationNode) => {
			if (config.properties) {
				for (let name in config.properties) {
757
					processProperty(name, config.properties[name]);
758 759 760 761 762 763 764 765
				}
			}

			if (config.allOf) {
				config.allOf.forEach(processConfig);
			}
		};

766
		configurations.forEach(processConfig);
767

768 769 770 771 772
		const excludedProps = configRegistry.getExcludedConfigurationProperties();
		for (let name in excludedProps) {
			processProperty(name, excludedProps[name]);
		}

773 774 775 776
		const result: IConfigurationExport = {
			settings: settings.sort((a, b) => a.name.localeCompare(b.name)),
			buildTime: Date.now(),
			commit: product.commit,
777
			buildNumber: product.settingsSearchBuildId
778
		};
779

780
		return result;
781 782
	}
}