preferencesService.ts 28.1 KB
Newer Older
1 2 3 4
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
S
Sandeep Somavarapu 已提交
5

6
import * as network from 'vs/base/common/network';
7 8
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
9
import { URI } from 'vs/base/common/uri';
10
import * as strings from 'vs/base/common/strings';
S
Sandeep Somavarapu 已提交
11
import { Disposable } from 'vs/base/common/lifecycle';
12
import { Emitter } from 'vs/base/common/event';
13
import { EditorInput, IEditor } from 'vs/workbench/common/editor';
14
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
15
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
B
Benjamin Pasero 已提交
16
import { IEditorOptions } from 'vs/platform/editor/common/editor';
A
Alex Dima 已提交
17
import { ITextModel } from 'vs/editor/common/model';
18
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
19 20
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
S
Sandeep Somavarapu 已提交
21 22
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/services/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettings, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
C
Christof Marti 已提交
23
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
24
import { DefaultPreferencesEditorInput, PreferencesEditorInput, KeybindingsEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
25
import { ITextModelService } from 'vs/editor/common/services/resolverService';
26
import { EditOperation } from 'vs/editor/common/core/editOperation';
A
Alex Dima 已提交
27
import { Position, IPosition } from 'vs/editor/common/core/position';
28 29
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IModelService } from 'vs/editor/common/services/modelService';
B
Benjamin Pasero 已提交
30
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
31
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
32
import { IModeService } from 'vs/editor/common/services/modeService';
S
Sandeep Somavarapu 已提交
33
import { parse } from 'vs/base/common/json';
B
Benjamin Pasero 已提交
34
import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
35
import { INotificationService } from 'vs/platform/notification/common/notification';
36
import { assign } from 'vs/base/common/objects';
B
Benjamin Pasero 已提交
37 38
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroup, IEditorGroupsService, GroupDirection } from 'vs/workbench/services/group/common/editorGroupsService';
I
isidor 已提交
39
import { ILabelService } from 'vs/platform/label/common/label';
40

S
Sandeep Somavarapu 已提交
41 42
const emptyEditableSettingsContent = '{\n}';

43
export class PreferencesService extends Disposable implements IPreferencesService {
44 45

	_serviceBrand: any;
46

47
	private lastOpenedSettingsInput: PreferencesEditorInput = null;
48
	private lastOpenedSettings2Input: SettingsEditor2Input = null;
49

M
Matt Bierner 已提交
50
	private readonly _onDispose: Emitter<void> = new Emitter<void>();
51

S
Sandeep Somavarapu 已提交
52 53 54 55 56 57
	private _defaultUserSettingsUriCounter = 0;
	private _defaultUserSettingsContentModel: DefaultSettings;
	private _defaultWorkspaceSettingsUriCounter = 0;
	private _defaultWorkspaceSettingsContentModel: DefaultSettings;
	private _defaultFolderSettingsUriCounter = 0;
	private _defaultFolderSettingsContentModel: DefaultSettings;
R
Rob Lourens 已提交
58

59
	constructor(
60 61
		@IEditorService private editorService: IEditorService,
		@IEditorGroupsService private editorGroupService: IEditorGroupsService,
62 63
		@IFileService private fileService: IFileService,
		@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
64
		@INotificationService private notificationService: INotificationService,
65 66 67
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IEnvironmentService private environmentService: IEnvironmentService,
68
		@ITelemetryService private telemetryService: ITelemetryService,
69
		@ITextModelService private textModelResolverService: ITextModelService,
70
		@IKeybindingService keybindingService: IKeybindingService,
71
		@IModelService private modelService: IModelService,
72
		@IJSONEditingService private jsonEditingService: IJSONEditingService,
I
isidor 已提交
73
		@IModeService private modeService: IModeService,
I
isidor 已提交
74
		@ILabelService private labelService: ILabelService
75 76
	) {
		super();
77 78 79 80 81 82 83 84
		// The default keybindings.json updates based on keyboard layouts, so here we make sure
		// if a model has been given out we update it accordingly.
		keybindingService.onDidUpdateKeybindings(() => {
			const model = modelService.getModel(this.defaultKeybindingsResource);
			if (!model) {
				// model has not been given out => nothing to do
				return;
			}
85
			modelService.updateModel(model, defaultKeybindingsContents(keybindingService));
86
		});
87 88
	}

S
Sandeep Somavarapu 已提交
89
	readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' });
S
Sandeep Somavarapu 已提交
90
	private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' });
91

92 93 94 95 96 97 98 99
	get userSettingsResource(): URI {
		return this.getEditableSettingsURI(ConfigurationTarget.USER);
	}

	get workspaceSettingsResource(): URI {
		return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
	}

S
Sandeep Somavarapu 已提交
100
	getFolderSettingsResource(resource: URI): URI {
101
		return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource);
S
Sandeep Somavarapu 已提交
102 103
	}

A
Alex Dima 已提交
104
	resolveModel(uri: URI): TPromise<ITextModel> {
S
Sandeep Somavarapu 已提交
105
		if (this.isDefaultSettingsResource(uri)) {
S
Sandeep Somavarapu 已提交
106

S
Sandeep Somavarapu 已提交
107
			const target = this.getConfigurationTargetFromDefaultSettingsResource(uri);
108
			const mode = this.modeService.getOrCreateMode('jsonc');
S
Sandeep Somavarapu 已提交
109 110 111 112 113 114 115 116 117 118
			const model = this._register(this.modelService.createModel('', mode, uri));

			let defaultSettings: DefaultSettings;
			this.configurationService.onDidChangeConfiguration(e => {
				if (e.source === ConfigurationTarget.DEFAULT) {
					const model = this.modelService.getModel(uri);
					if (!model) {
						// model has not been given out => nothing to do
						return;
					}
S
Sandeep Somavarapu 已提交
119
					defaultSettings = this.getDefaultSettings(target);
S
Sandeep Somavarapu 已提交
120
					this.modelService.updateModel(model, defaultSettings.parse());
121
					defaultSettings._onDidChange.fire();
S
Sandeep Somavarapu 已提交
122 123 124 125 126
				}
			});

			// Check if Default settings is already created and updated in above promise
			if (!defaultSettings) {
S
Sandeep Somavarapu 已提交
127
				defaultSettings = this.getDefaultSettings(target);
S
Sandeep Somavarapu 已提交
128 129 130 131
				this.modelService.updateModel(model, defaultSettings.parse());
			}

			return TPromise.as(model);
132 133
		}

S
Sandeep Somavarapu 已提交
134
		if (this.defaultSettingsRawResource.toString() === uri.toString()) {
S
Sandeep Somavarapu 已提交
135
			let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationTarget.USER);
136
			const mode = this.modeService.getOrCreateMode('jsonc');
S
Sandeep Somavarapu 已提交
137 138 139 140
			const model = this._register(this.modelService.createModel(defaultSettings.raw, mode, uri));
			return TPromise.as(model);
		}

141
		if (this.defaultKeybindingsResource.toString() === uri.toString()) {
S
Sandeep Somavarapu 已提交
142
			const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
143
			const mode = this.modeService.getOrCreateMode('jsonc');
S
Sandeep Somavarapu 已提交
144 145
			const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, mode, uri));
			return TPromise.as(model);
146 147 148 149 150 151
		}

		return TPromise.as(null);
	}

	createPreferencesEditorModel(uri: URI): TPromise<IPreferencesEditorModel<any>> {
S
Sandeep Somavarapu 已提交
152
		if (this.isDefaultSettingsResource(uri)) {
153
			return this.createDefaultSettingsEditorModel(uri);
154 155
		}

156
		if (this.getEditableSettingsURI(ConfigurationTarget.USER).toString() === uri.toString()) {
157
			return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri);
158
		}
159

S
Sandeep Somavarapu 已提交
160
		const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
161
		if (workspaceSettingsUri && workspaceSettingsUri.toString() === uri.toString()) {
162
			return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri);
163
		}
164

165
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
166
			return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri);
167
		}
168

169
		return TPromise.wrap<IPreferencesEditorModel<any>>(null);
170 171
	}

B
Benjamin Pasero 已提交
172
	openRawDefaultSettings(): TPromise<IEditor> {
B
Benjamin Pasero 已提交
173
		return this.editorService.openEditor({ resource: this.defaultSettingsRawResource });
S
Sandeep Somavarapu 已提交
174 175
	}

B
Benjamin Pasero 已提交
176
	openRawUserSettings(): TPromise<IEditor> {
177
		return this.editorService.openEditor({ resource: this.userSettingsResource });
S
Sandeep Somavarapu 已提交
178 179
	}

180 181 182 183 184
	openSettings(jsonEditor?: boolean): TPromise<IEditor> {
		if (!jsonEditor) {
			return this.openSettings2();
		}

185 186
		const editorInput = this.getActiveSettingsEditorInput() || this.lastOpenedSettingsInput;
		const resource = editorInput ? editorInput.master.getResource() : this.userSettingsResource;
S
Sandeep Somavarapu 已提交
187
		const target = this.getConfigurationTargetFromSettingsResource(resource);
188
		return this.openOrSwitchSettings(target, resource);
S
Sandeep Somavarapu 已提交
189 190
	}

191 192 193 194 195
	private openSettings2(): TPromise<IEditor> {
		const editorInput = this.getActiveSettingsEditor2Input() || this.lastOpenedSettings2Input;
		const resource = editorInput ? editorInput.getResource() : this.userSettingsResource;
		const target = this.getConfigurationTargetFromSettingsResource(resource);
		return this.openOrSwitchSettings2(target);
196 197
	}

198 199 200
	openGlobalSettings(jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
		return jsonEditor ?
			this.openOrSwitchSettings(ConfigurationTarget.USER, this.userSettingsResource, options, group) :
201
			this.openOrSwitchSettings2(ConfigurationTarget.USER, undefined, options, group);
R
Rob Lourens 已提交
202 203
	}

204
	openWorkspaceSettings(jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
205
		if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
206
			this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
S
Sandeep Somavarapu 已提交
207
			return TPromise.as(null);
208
		}
209 210 211

		return jsonEditor ?
			this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, group) :
212
			this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group);
213 214
	}

215 216 217
	openFolderSettings(folder: URI, jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
		return jsonEditor ?
			this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, group) :
218
			this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group);
219 220
	}

221 222
	switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise<void> {
		if (!jsonEditor) {
223 224
			const folderUri = target === ConfigurationTarget.WORKSPACE_FOLDER && resource;
			return this.switchSettings2(target, folderUri);
225 226
		}

B
Benjamin Pasero 已提交
227 228
		const activeControl = this.editorService.activeControl;
		if (activeControl && activeControl.input instanceof PreferencesEditorInput) {
229
			return this.doSwitchSettings(target, resource, activeControl.input, activeControl.group).then(() => null);
230
		} else {
231
			return this.doOpenSettings(target, resource).then(() => null);
232
		}
233 234
	}

235
	switchSettings2(target: ConfigurationTarget, folderUri?: URI): TPromise<void> {
236 237 238
		const activeControl = this.editorService.activeControl;
		const resource = this.getDefaultSettingsResource(target);
		if (activeControl && activeControl.input instanceof SettingsEditor2Input) {
239
			return this.doSwitchSettings2(resource, target, folderUri, activeControl.input, activeControl.group).then(() => null);
240
		} else {
241
			return this.doOpenSettings2(resource, target, folderUri).then(() => null);
242 243 244
		}
	}

S
Sandeep Somavarapu 已提交
245
	openGlobalKeybindingSettings(textual: boolean): TPromise<void> {
K
kieferrm 已提交
246
		/* __GDPR__
K
kieferrm 已提交
247
			"openKeybindings" : {
K
kieferrm 已提交
248
				"textual" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
K
kieferrm 已提交
249 250
			}
		*/
251
		this.telemetryService.publicLog('openKeybindings', { textual });
S
Sandeep Somavarapu 已提交
252 253 254
		if (textual) {
			const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]';
			const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath);
N
Nilesh 已提交
255
			const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings');
S
Sandeep Somavarapu 已提交
256 257

			// Create as needed and open in editor
S
Sandeep Somavarapu 已提交
258 259
			return this.createIfNotExists(editableKeybindings, emptyContents).then(() => {
				if (openDefaultKeybindings) {
N
Nilesh 已提交
260 261 262 263 264 265
					const activeEditorGroup = this.editorGroupService.activeGroup;
					const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT);
					return TPromise.join([
						this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }),
						this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }, sideEditorGroup.id)
					]).then(editors => void 0);
S
Sandeep Somavarapu 已提交
266 267 268
				} else {
					return this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }).then(() => void 0);
				}
S
Sandeep Somavarapu 已提交
269 270
			});
		}
271

N
Nilesh 已提交
272
		return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null);
273 274
	}

S
Sandeep Somavarapu 已提交
275
	openDefaultKeybindingsFile(): TPromise<IEditor> {
N
Nilesh 已提交
276 277 278
		return this.editorService.openEditor({ resource: this.defaultKeybindingsResource });
	}

279 280
	configureSettingsForLanguage(language: string): void {
		this.openGlobalSettings()
S
Sandeep Somavarapu 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293
			.then(editor => this.createPreferencesEditorModel(this.userSettingsResource)
				.then((settingsModel: IPreferencesEditorModel<ISetting>) => {
					const codeEditor = getCodeEditor(editor.getControl());
					if (codeEditor) {
						this.getPosition(language, settingsModel, codeEditor)
							.then(position => {
								if (codeEditor) {
									codeEditor.setPosition(position);
									codeEditor.focus();
								}
							});
					}
				}));
294 295
	}

296
	private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise<IEditor> {
B
Benjamin Pasero 已提交
297
		const editorInput = this.getActiveSettingsEditorInput(group);
298
		if (editorInput && editorInput.master.getResource().fsPath !== resource.fsPath) {
B
Benjamin Pasero 已提交
299
			return this.doSwitchSettings(configurationTarget, resource, editorInput, group);
300
		}
B
Benjamin Pasero 已提交
301
		return this.doOpenSettings(configurationTarget, resource, options, group);
302 303
	}

304
	private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: IEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise<IEditor> {
305 306 307
		const editorInput = this.getActiveSettingsEditor2Input(group);
		const resource = this.getDefaultSettingsResource(configurationTarget);
		if (editorInput && editorInput.getResource().fsPath !== resource.fsPath) {
308
			return this.doSwitchSettings2(resource, configurationTarget, folderUri, editorInput, group);
309 310
		}

311
		return this.doOpenSettings2(resource, configurationTarget, folderUri, options, group);
312 313
	}

314
	private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
S
Sandeep Somavarapu 已提交
315
		const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING);
316
		return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource)
317
			.then(editableSettingsEditorInput => {
318 319 320
				if (!options) {
					options = { pinned: true };
				} else {
321
					options = assign(options, { pinned: true });
322 323
				}

324
				if (openDefaultSettings) {
S
Sandeep Somavarapu 已提交
325
					const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget));
326
					const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, <EditorInput>editableSettingsEditorInput);
327
					this.lastOpenedSettingsInput = preferencesEditorInput;
B
Benjamin Pasero 已提交
328
					return this.editorService.openEditor(preferencesEditorInput, options, group);
329
				}
B
Benjamin Pasero 已提交
330
				return this.editorService.openEditor(editableSettingsEditorInput, options, group);
331
			});
332 333
	}

334 335
	private doOpenSettings2(resource: URI, target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
		const settingsEditorInput = this.instantiationService.createInstance(SettingsEditor2Input, resource, target, folderUri);
336 337 338 339
		this.lastOpenedSettings2Input = settingsEditorInput;
		return this.editorService.openEditor(settingsEditorInput, options, group);
	}

340
	private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup): TPromise<IEditor> {
341 342
		return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource))
			.then(toInput => {
B
Benjamin Pasero 已提交
343 344 345 346 347 348 349 350 351 352
				return group.openEditor(input).then(() => {
					const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(target)), toInput);

					return group.replaceEditors([{
						editor: input,
						replacement: replaceWith
					}]).then(() => {
						this.lastOpenedSettingsInput = replaceWith;
						return group.activeControl;
					});
353 354 355 356
				});
			});
	}

357
	private doSwitchSettings2(resource: URI, configurationTarget: ConfigurationTarget, folderUri: URI | undefined, input: SettingsEditor2Input, group: IEditorGroup): TPromise<IEditor> {
358
		return group.openEditor(input).then(() => {
359
			const replaceWith = this.instantiationService.createInstance(SettingsEditor2Input, resource, configurationTarget, folderUri);
360 361 362 363 364 365 366 367 368 369 370

			return group.replaceEditors([{
				editor: input,
				replacement: replaceWith
			}]).then(() => {
				this.lastOpenedSettings2Input = replaceWith;
				return group.activeControl;
			});
		});
	}

371
	private getActiveSettingsEditorInput(group: IEditorGroup = this.editorGroupService.activeGroup): PreferencesEditorInput {
B
Benjamin Pasero 已提交
372
		return <PreferencesEditorInput>group.editors.filter(e => e instanceof PreferencesEditorInput)[0];
373 374
	}

375 376 377 378
	private getActiveSettingsEditor2Input(group: IEditorGroup = this.editorGroupService.activeGroup): SettingsEditor2Input {
		return <SettingsEditor2Input>group.editors.filter(e => e instanceof SettingsEditor2Input)[0];
	}

S
Sandeep Somavarapu 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
	private getConfigurationTargetFromSettingsResource(resource: URI): ConfigurationTarget {
		if (this.userSettingsResource.toString() === resource.toString()) {
			return ConfigurationTarget.USER;
		}

		const workspaceSettingsResource = this.workspaceSettingsResource;
		if (workspaceSettingsResource && workspaceSettingsResource.toString() === resource.toString()) {
			return ConfigurationTarget.WORKSPACE;
		}

		const folder = this.contextService.getWorkspaceFolder(resource);
		if (folder) {
			return ConfigurationTarget.WORKSPACE_FOLDER;
		}

		return ConfigurationTarget.USER;
	}

S
Sandeep Somavarapu 已提交
397 398 399 400
	private getConfigurationTargetFromDefaultSettingsResource(uri: URI) {
		return this.isDefaultWorkspaceSettingsResource(uri) ? ConfigurationTarget.WORKSPACE : this.isDefaultFolderSettingsResource(uri) ? ConfigurationTarget.WORKSPACE_FOLDER : ConfigurationTarget.USER;
	}

R
Rob Lourens 已提交
401
	private isDefaultSettingsResource(uri: URI): boolean {
S
Sandeep Somavarapu 已提交
402 403 404 405
		return this.isDefaultUserSettingsResource(uri) || this.isDefaultWorkspaceSettingsResource(uri) || this.isDefaultFolderSettingsResource(uri);
	}

	private isDefaultUserSettingsResource(uri: URI): boolean {
406
		return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?settings\.json$/);
R
Rob Lourens 已提交
407 408
	}

S
Sandeep Somavarapu 已提交
409 410 411 412 413
	private isDefaultWorkspaceSettingsResource(uri: URI): boolean {
		return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?workspaceSettings\.json$/);
	}

	private isDefaultFolderSettingsResource(uri: URI): boolean {
414
		return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?resourceSettings\.json$/);
R
Rob Lourens 已提交
415 416
	}

S
Sandeep Somavarapu 已提交
417
	private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI {
S
Sandeep Somavarapu 已提交
418 419 420 421 422
		switch (configurationTarget) {
			case ConfigurationTarget.WORKSPACE:
				return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultWorkspaceSettingsUriCounter++}/workspaceSettings.json` });
			case ConfigurationTarget.WORKSPACE_FOLDER:
				return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultFolderSettingsUriCounter++}/resourceSettings.json` });
S
Sandeep Somavarapu 已提交
423
		}
S
Sandeep Somavarapu 已提交
424
		return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultUserSettingsUriCounter++}/settings.json` });
S
Sandeep Somavarapu 已提交
425 426
	}

427 428
	private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string {
		const name = getSettingsTargetName(target, resource, this.contextService);
429
		return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name;
430 431
	}

432 433 434
	private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise<EditorInput> {
		return this.createSettingsIfNotExists(target, resource)
			.then(() => <EditorInput>this.editorService.createInput({ resource }));
435 436
	}

437 438
	private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise<SettingsEditorModel> {
		const settingsUri = this.getEditableSettingsURI(configurationTarget, resource);
439
		if (settingsUri) {
S
Sandeep Somavarapu 已提交
440 441 442 443
			const workspace = this.contextService.getWorkspace();
			if (workspace.configuration && workspace.configuration.toString() === settingsUri.toString()) {
				return this.textModelResolverService.createModelReference(settingsUri)
					.then(reference => this.instantiationService.createInstance(WorkspaceConfigurationEditorModel, reference, configurationTarget));
444
			}
445
			return this.textModelResolverService.createModelReference(settingsUri)
S
Sandeep Somavarapu 已提交
446
				.then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget));
447
		}
448
		return TPromise.wrap<SettingsEditorModel>(null);
449 450
	}

451 452 453
	private createDefaultSettingsEditorModel(defaultSettingsUri: URI): TPromise<DefaultSettingsEditorModel> {
		return this.textModelResolverService.createModelReference(defaultSettingsUri)
			.then(reference => {
S
Sandeep Somavarapu 已提交
454 455
				const target = this.getConfigurationTargetFromDefaultSettingsResource(defaultSettingsUri);
				return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, this.getDefaultSettings(target));
456 457 458
			});
	}

S
Sandeep Somavarapu 已提交
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
	private getDefaultSettings(target: ConfigurationTarget): DefaultSettings {
		if (target === ConfigurationTarget.WORKSPACE) {
			if (!this._defaultWorkspaceSettingsContentModel) {
				this._defaultWorkspaceSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), target);
			}
			return this._defaultWorkspaceSettingsContentModel;
		}
		if (target === ConfigurationTarget.WORKSPACE_FOLDER) {
			if (!this._defaultFolderSettingsContentModel) {
				this._defaultFolderSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), target);
			}
			return this._defaultFolderSettingsContentModel;
		}
		if (!this._defaultUserSettingsContentModel) {
			this._defaultUserSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), target);
474
		}
S
Sandeep Somavarapu 已提交
475
		return this._defaultUserSettingsContentModel;
476 477
	}

478
	private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI {
479 480 481 482
		switch (configurationTarget) {
			case ConfigurationTarget.USER:
				return URI.file(this.environmentService.appSettingsPath);
			case ConfigurationTarget.WORKSPACE:
483
				if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
484 485
					return null;
				}
486
				const workspace = this.contextService.getWorkspace();
487
				return workspace.configuration || workspace.folders[0].toResource(FOLDER_SETTINGS_PATH);
488
			case ConfigurationTarget.WORKSPACE_FOLDER:
S
Sandeep Somavarapu 已提交
489
				const folder = this.contextService.getWorkspaceFolder(resource);
490
				return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null;
491
		}
492
		return null;
493 494
	}

495
	private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise<void> {
496
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) {
S
Sandeep Somavarapu 已提交
497 498 499 500 501 502 503
			return this.fileService.resolveContent(this.contextService.getWorkspace().configuration)
				.then(content => {
					if (Object.keys(parse(content.value)).indexOf('settings') === -1) {
						return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { });
					}
					return null;
				});
504
		}
S
Sandeep Somavarapu 已提交
505
		return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { });
506 507
	}

R
Ron Buckton 已提交
508
	private createIfNotExists(resource: URI, contents: string): TPromise<any> {
509
		return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => {
510
			if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
511
				return this.fileService.updateContent(resource, contents).then(null, error => {
I
isidor 已提交
512
					return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(resource, true), error)));
513 514 515
				});
			}

R
Ron Buckton 已提交
516
			return TPromise.wrapError(error);
517 518 519
		});
	}

520 521
	private getMostCommonlyUsedSettings(): string[] {
		return [
S
Sandeep Somavarapu 已提交
522
			'files.autoSave',
523
			'editor.fontSize',
S
Sandeep Somavarapu 已提交
524 525 526
			'editor.fontFamily',
			'editor.tabSize',
			'editor.renderWhitespace',
S
Sandeep Somavarapu 已提交
527
			'editor.cursorStyle',
528
			'editor.multiCursorModifier',
S
Sandeep Somavarapu 已提交
529
			'editor.insertSpaces',
530
			'editor.wordWrap',
531
			'files.exclude',
S
Sandeep Somavarapu 已提交
532
			'files.associations'
533
		];
S
Sandeep Somavarapu 已提交
534
	}
535

S
Sandeep Somavarapu 已提交
536 537 538 539 540 541 542 543 544 545 546 547 548 549
	private getPosition(language: string, settingsModel: IPreferencesEditorModel<ISetting>, codeEditor: ICodeEditor): TPromise<IPosition> {
		const languageKey = `[${language}]`;
		let setting = settingsModel.getPreference(languageKey);
		const model = codeEditor.getModel();
		const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean }, files: { eol: string } }>();
		const eol = configuration.files && configuration.files.eol;
		if (setting) {
			if (setting.overrides.length) {
				const lastSetting = setting.overrides[setting.overrides.length - 1];
				let content;
				if (lastSetting.valueRange.endLineNumber === setting.range.endLineNumber) {
					content = ',' + eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor);
				} else {
					content = ',' + eol + this.spaces(2, configuration.editor);
550
				}
S
Sandeep Somavarapu 已提交
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
				const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content);
				model.pushEditOperations([], [editOperation], () => []);
				return TPromise.as({ lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) });
			}
			return TPromise.as({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 });
		}
		return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER)
			.then(() => {
				setting = settingsModel.getPreference(languageKey);
				let content = eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor);
				let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content);
				model.pushEditOperations([], [editOperation], () => []);
				let lineNumber = setting.valueRange.endLineNumber + 1;
				settingsModel.dispose();
				return { lineNumber, column: model.getLineMaxColumn(lineNumber) };
566 567 568
			});
	}

A
Alex Dima 已提交
569
	private spaces(count: number, { tabSize, insertSpaces }: { tabSize: number; insertSpaces: boolean }): string {
570 571 572
		return insertSpaces ? strings.repeat(' ', tabSize * count) : strings.repeat('\t', count);
	}

573
	public dispose(): void {
574
		this._onDispose.fire();
575 576
		super.dispose();
	}
577
}