preferencesService.ts 31.0 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 7 8
import { Emitter } from 'vs/base/common/event';
import { parse } from 'vs/base/common/json';
import { Disposable } from 'vs/base/common/lifecycle';
9
import * as network from 'vs/base/common/network';
10 11 12 13 14 15 16 17 18 19
import { assign } from 'vs/base/common/objects';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
20
import * as nls from 'vs/nls';
21
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
B
Benjamin Pasero 已提交
22
import { IEditorOptions } from 'vs/platform/editor/common/editor';
S
Sandeep Somavarapu 已提交
23
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
B
Benjamin Pasero 已提交
24
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
25
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
26
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
27
import { ILabelService } from 'vs/platform/label/common/label';
28
import { INotificationService } from 'vs/platform/notification/common/notification';
29 30 31 32
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { EditorInput, IEditor } from 'vs/workbench/common/editor';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
B
Benjamin Pasero 已提交
33
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
34
import { GroupDirection, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
35
import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, getSettingsTargetName, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, SettingsEditorOptions, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences';
36
import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
S
Sandeep Somavarapu 已提交
37
import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSettings, DefaultSettingsEditorModel, Settings2EditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel, DefaultRawSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
38
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
39
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
40
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
41
import { withNullAsUndefined } from 'vs/base/common/types';
42

S
Sandeep Somavarapu 已提交
43 44
const emptyEditableSettingsContent = '{\n}';

45
export class PreferencesService extends Disposable implements IPreferencesService {
46

47
	_serviceBrand: undefined;
48

49
	private lastOpenedSettingsInput: PreferencesEditorInput | null = null;
50

51
	private readonly _onDispose = this._register(new Emitter<void>());
52

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

60
	constructor(
61 62
		@IEditorService private readonly editorService: IEditorService,
		@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
63
		@ITextFileService private readonly textFileService: ITextFileService,
64
		@IConfigurationService private readonly configurationService: IConfigurationService,
65 66 67
		@INotificationService private readonly notificationService: INotificationService,
		@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
68
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
69 70
		@ITelemetryService private readonly telemetryService: ITelemetryService,
		@ITextModelService private readonly textModelResolverService: ITextModelService,
71
		@IKeybindingService keybindingService: IKeybindingService,
72 73 74
		@IModelService private readonly modelService: IModelService,
		@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
		@IModeService private readonly modeService: IModeService,
75 76
		@ILabelService private readonly labelService: ILabelService,
		@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService
77 78
	) {
		super();
79 80
		// 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.
81
		this._register(keybindingService.onDidUpdateKeybindings(() => {
82 83 84 85 86
			const model = modelService.getModel(this.defaultKeybindingsResource);
			if (!model) {
				// model has not been given out => nothing to do
				return;
			}
87
			modelService.updateModel(model, defaultKeybindingsContents(keybindingService));
88
		}));
89 90
	}

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

94
	get userSettingsResource(): URI {
95
		return this.environmentService.settingsResource;
96 97
	}

S
Sandeep Somavarapu 已提交
98
	get workspaceSettingsResource(): URI | null {
99 100 101 102 103
		if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
			return null;
		}
		const workspace = this.contextService.getWorkspace();
		return workspace.configuration || workspace.folders[0].toResource(FOLDER_SETTINGS_PATH);
104 105
	}

106 107 108 109
	get settingsEditor2Input(): SettingsEditor2Input {
		return this.instantiationService.createInstance(SettingsEditor2Input);
	}

S
Sandeep Somavarapu 已提交
110
	getFolderSettingsResource(resource: URI): URI | null {
111 112
		const folder = this.contextService.getWorkspaceFolder(resource);
		return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null;
S
Sandeep Somavarapu 已提交
113 114
	}

S
Sandeep Somavarapu 已提交
115
	resolveModel(uri: URI): Promise<ITextModel | null> {
S
Sandeep Somavarapu 已提交
116
		if (this.isDefaultSettingsResource(uri)) {
S
Sandeep Somavarapu 已提交
117

S
Sandeep Somavarapu 已提交
118
			const target = this.getConfigurationTargetFromDefaultSettingsResource(uri);
A
Alex Dima 已提交
119 120
			const languageSelection = this.modeService.create('jsonc');
			const model = this._register(this.modelService.createModel('', languageSelection, uri));
S
Sandeep Somavarapu 已提交
121

122
			let defaultSettings: DefaultSettings | undefined;
S
Sandeep Somavarapu 已提交
123 124 125 126 127 128 129
			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 已提交
130
					defaultSettings = this.getDefaultSettings(target);
131
					this.modelService.updateModel(model, defaultSettings.getContent(true));
132
					defaultSettings._onDidChange.fire();
S
Sandeep Somavarapu 已提交
133 134 135 136 137
				}
			});

			// Check if Default settings is already created and updated in above promise
			if (!defaultSettings) {
S
Sandeep Somavarapu 已提交
138
				defaultSettings = this.getDefaultSettings(target);
139
				this.modelService.updateModel(model, defaultSettings.getContent(true));
S
Sandeep Somavarapu 已提交
140 141
			}

R
Rob Lourens 已提交
142
			return Promise.resolve(model);
143 144
		}

S
Sandeep Somavarapu 已提交
145
		if (this.defaultSettingsRawResource.toString() === uri.toString()) {
R
Rob Lourens 已提交
146
			const defaultRawSettingsEditorModel = this.instantiationService.createInstance(DefaultRawSettingsEditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL));
A
Alex Dima 已提交
147
			const languageSelection = this.modeService.create('jsonc');
S
Sandeep Somavarapu 已提交
148
			const model = this._register(this.modelService.createModel(defaultRawSettingsEditorModel.content, languageSelection, uri));
R
Rob Lourens 已提交
149
			return Promise.resolve(model);
S
Sandeep Somavarapu 已提交
150 151
		}

152
		if (this.defaultKeybindingsResource.toString() === uri.toString()) {
S
Sandeep Somavarapu 已提交
153
			const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
A
Alex Dima 已提交
154 155
			const languageSelection = this.modeService.create('jsonc');
			const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, languageSelection, uri));
R
Rob Lourens 已提交
156
			return Promise.resolve(model);
157 158
		}

R
Rob Lourens 已提交
159
		return Promise.resolve(null);
160 161
	}

162
	async createPreferencesEditorModel(uri: URI): Promise<IPreferencesEditorModel<any> | null> {
S
Sandeep Somavarapu 已提交
163
		if (this.isDefaultSettingsResource(uri)) {
164
			return this.createDefaultSettingsEditorModel(uri);
165 166
		}

S
Sandeep Somavarapu 已提交
167
		if (this.userSettingsResource.toString() === uri.toString()) {
R
Rob Lourens 已提交
168
			return this.createEditableSettingsEditorModel(ConfigurationTarget.USER_LOCAL, uri);
169
		}
170

171
		const workspaceSettingsUri = await this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
172
		if (workspaceSettingsUri && workspaceSettingsUri.toString() === uri.toString()) {
173
			return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri);
174
		}
175

176
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
177
			const settingsUri = await this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, uri);
178
			if (settingsUri && settingsUri.toString() === uri.toString()) {
179 180 181 182 183 184 185 186
				return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri);
			}
		}

		const remoteEnvironment = await this.remoteAgentService.getEnvironment();
		const remoteSettingsUri = remoteEnvironment ? remoteEnvironment.settingsPath : null;
		if (remoteSettingsUri && remoteSettingsUri.toString() === uri.toString()) {
			return this.createEditableSettingsEditorModel(ConfigurationTarget.USER_REMOTE, uri);
187
		}
188

189
		return null;
190 191
	}

192
	openRawDefaultSettings(): Promise<IEditor | undefined> {
B
Benjamin Pasero 已提交
193
		return this.editorService.openEditor({ resource: this.defaultSettingsRawResource });
S
Sandeep Somavarapu 已提交
194 195
	}

196
	openRawUserSettings(): Promise<IEditor | undefined> {
197
		return this.editorService.openEditor({ resource: this.userSettingsResource });
S
Sandeep Somavarapu 已提交
198 199
	}

200
	openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise<IEditor | undefined> {
201 202 203 204
		jsonEditor = typeof jsonEditor === 'undefined' ?
			this.configurationService.getValue('workbench.settings.editor') === 'json' :
			jsonEditor;

205
		if (!jsonEditor) {
P
Peng Lyu 已提交
206
			return this.openSettings2({ query: query });
207 208
		}

209
		const editorInput = this.getActiveSettingsEditorInput() || this.lastOpenedSettingsInput;
S
Sandeep Somavarapu 已提交
210
		const resource = editorInput ? editorInput.master.getResource()! : this.userSettingsResource;
S
Sandeep Somavarapu 已提交
211
		const target = this.getConfigurationTargetFromSettingsResource(resource);
P
Peng Lyu 已提交
212
		return this.openOrSwitchSettings(target, resource, { query: query });
S
Sandeep Somavarapu 已提交
213 214
	}

P
Peng Lyu 已提交
215
	private openSettings2(options?: ISettingsEditorOptions): Promise<IEditor> {
216
		const input = this.settingsEditor2Input;
217
		return this.editorService.openEditor(input, options ? SettingsEditorOptions.create(options) : undefined)
S
Sandeep Somavarapu 已提交
218
			.then(() => this.editorGroupService.activeGroup.activeControl!);
219 220
	}

221
	openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined> {
222 223 224 225
		jsonEditor = typeof jsonEditor === 'undefined' ?
			this.configurationService.getValue('workbench.settings.editor') === 'json' :
			jsonEditor;

226
		return jsonEditor ?
R
Rob Lourens 已提交
227 228
			this.openOrSwitchSettings(ConfigurationTarget.USER_LOCAL, this.userSettingsResource, options, group) :
			this.openOrSwitchSettings2(ConfigurationTarget.USER_LOCAL, undefined, options, group);
R
Rob Lourens 已提交
229 230
	}

231
	async openRemoteSettings(): Promise<IEditor | undefined> {
R
Rob Lourens 已提交
232 233 234
		const environment = await this.remoteAgentService.getEnvironment();
		if (environment) {
			await this.createIfNotExists(environment.settingsPath, emptyEditableSettingsContent);
235
			return this.editorService.openEditor({ resource: environment.settingsPath, options: { pinned: true, revealIfOpened: true } });
236
		}
237
		return undefined;
238 239
	}

240
	openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined> {
241 242 243 244
		jsonEditor = typeof jsonEditor === 'undefined' ?
			this.configurationService.getValue('workbench.settings.editor') === 'json' :
			jsonEditor;

S
Sandeep Somavarapu 已提交
245
		if (!this.workspaceSettingsResource) {
246
			this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
S
Sandeep Somavarapu 已提交
247
			return Promise.reject(null);
248
		}
249 250 251

		return jsonEditor ?
			this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, group) :
252
			this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group);
253 254
	}

255
	async openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined> {
256 257 258
		jsonEditor = typeof jsonEditor === 'undefined' ?
			this.configurationService.getValue('workbench.settings.editor') === 'json' :
			jsonEditor;
259
		const folderSettingsUri = await this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder);
S
Sandeep Somavarapu 已提交
260 261 262 263 264 265 266
		if (jsonEditor) {
			if (folderSettingsUri) {
				return this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, folderSettingsUri, options, group);
			}
			return Promise.reject(`Invalid folder URI - ${folder.toString()}`);
		}
		return this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group);
267 268
	}

J
Johannes Rieken 已提交
269
	switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise<void> {
270
		if (!jsonEditor) {
S
Sandeep Somavarapu 已提交
271
			return this.doOpenSettings2(target, resource).then(() => undefined);
272 273
		}

B
Benjamin Pasero 已提交
274 275
		const activeControl = this.editorService.activeControl;
		if (activeControl && activeControl.input instanceof PreferencesEditorInput) {
S
Sandeep Somavarapu 已提交
276
			return this.doSwitchSettings(target, resource, activeControl.input, activeControl.group).then(() => undefined);
277
		} else {
S
Sandeep Somavarapu 已提交
278
			return this.doOpenSettings(target, resource).then(() => undefined);
279
		}
280 281
	}

J
Johannes Rieken 已提交
282
	openGlobalKeybindingSettings(textual: boolean): Promise<void> {
283 284 285 286
		type OpenKeybindingsClassification = {
			textual: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
		};
		this.telemetryService.publicLog2<{ textual: boolean }, OpenKeybindingsClassification>('openKeybindings', { textual });
S
Sandeep Somavarapu 已提交
287
		if (textual) {
288
			const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]';
S
Sandeep Somavarapu 已提交
289
			const editableKeybindings = this.environmentService.keybindingsResource;
N
Nilesh 已提交
290
			const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings');
S
Sandeep Somavarapu 已提交
291 292

			// Create as needed and open in editor
S
Sandeep Somavarapu 已提交
293 294
			return this.createIfNotExists(editableKeybindings, emptyContents).then(() => {
				if (openDefaultKeybindings) {
N
Nilesh 已提交
295 296
					const activeEditorGroup = this.editorGroupService.activeGroup;
					const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT);
R
Rob Lourens 已提交
297
					return Promise.all([
S
Sandeep Somavarapu 已提交
298 299
						this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }),
						this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true, revealIfOpened: true } }, sideEditorGroup.id)
R
Rob Lourens 已提交
300
					]).then(editors => undefined);
S
Sandeep Somavarapu 已提交
301
				} else {
R
Rob Lourens 已提交
302
					return this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true, revealIfOpened: true } }).then(() => undefined);
S
Sandeep Somavarapu 已提交
303
				}
S
Sandeep Somavarapu 已提交
304 305
			});
		}
306

S
Sandeep Somavarapu 已提交
307
		return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true, revealIfOpened: true }).then(() => undefined);
308 309
	}

310
	openDefaultKeybindingsFile(): Promise<IEditor | undefined> {
311
		return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") });
N
Nilesh 已提交
312 313
	}

314
	configureSettingsForLanguage(language: string): void {
315
		this.openGlobalSettings(true)
S
Sandeep Somavarapu 已提交
316
			.then(editor => this.createPreferencesEditorModel(this.userSettingsResource)
M
Matt Bierner 已提交
317
				.then((settingsModel: IPreferencesEditorModel<ISetting> | null) => {
318
					const codeEditor = editor ? getCodeEditor(editor.getControl()) : null;
M
Matt Bierner 已提交
319
					if (codeEditor && settingsModel) {
S
Sandeep Somavarapu 已提交
320
						this.addLanguageOverrideEntry(language, settingsModel, codeEditor)
S
Sandeep Somavarapu 已提交
321
							.then(position => {
S
Sandeep Somavarapu 已提交
322
								if (codeEditor && position) {
S
Sandeep Somavarapu 已提交
323
									codeEditor.setPosition(position);
324
									codeEditor.revealLine(position.lineNumber);
S
Sandeep Somavarapu 已提交
325 326 327 328 329
									codeEditor.focus();
								}
							});
					}
				}));
330 331
	}

332
	private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise<IEditor | undefined> {
B
Benjamin Pasero 已提交
333
		const editorInput = this.getActiveSettingsEditorInput(group);
S
Sandeep Somavarapu 已提交
334 335 336 337 338
		if (editorInput) {
			const editorInputResource = editorInput.master.getResource();
			if (editorInputResource && editorInputResource.fsPath !== resource.fsPath) {
				return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options);
			}
339
		}
B
Benjamin Pasero 已提交
340
		return this.doOpenSettings(configurationTarget, resource, options, group);
341 342
	}

343
	private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise<IEditor | undefined> {
344
		return this.doOpenSettings2(configurationTarget, folderUri, options, group);
345 346
	}

347
	private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined> {
348 349 350 351 352
		const openSplitJSON = !!this.configurationService.getValue(USE_SPLIT_JSON_SETTING);
		if (openSplitJSON) {
			return this.doOpenSplitJSON(configurationTarget, resource, options, group);
		}

S
Sandeep Somavarapu 已提交
353
		const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING);
354

355
		return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource)
356
			.then(editableSettingsEditorInput => {
357 358 359
				if (!options) {
					options = { pinned: true };
				} else {
360
					options = assign(options, { pinned: true });
361 362
				}

363
				if (openDefaultSettings) {
364 365 366
					const activeEditorGroup = this.editorGroupService.activeGroup;
					const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT);
					return Promise.all([
367
						this.editorService.openEditor({ resource: this.defaultSettingsRawResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true }, label: nls.localize('defaultSettings', "Default Settings"), description: '' }),
368
						this.editorService.openEditor(editableSettingsEditorInput, { pinned: true, revealIfOpened: true }, sideEditorGroup.id)
369
					]).then(([defaultEditor, editor]) => withNullAsUndefined(editor));
370
				} else {
371
					return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group);
372
				}
373
			});
374 375
	}

376
	private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined> {
377 378 379 380 381 382 383 384 385 386 387
		return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource)
			.then(editableSettingsEditorInput => {
				if (!options) {
					options = { pinned: true };
				} else {
					options = assign(options, { pinned: true });
				}

				const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget));
				const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, <EditorInput>editableSettingsEditorInput);
				this.lastOpenedSettingsInput = preferencesEditorInput;
388
				return this.editorService.openEditor(preferencesEditorInput, SettingsEditorOptions.create(options), group);
389 390 391
			});
	}

392
	public createSettings2EditorModel(): Settings2EditorModel {
R
Rob Lourens 已提交
393
		return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL));
394 395
	}

396
	private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise<IEditor | undefined> {
397 398 399 400 401 402 403
		const input = this.settingsEditor2Input;
		const settingsOptions: ISettingsEditorOptions = {
			...options,
			target,
			folderUri
		};

404
		return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group);
405 406
	}

407 408
	private async doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Promise<IEditor> {
		const settingsURI = await this.getEditableSettingsURI(target, resource);
S
Sandeep Somavarapu 已提交
409 410 411 412
		if (!settingsURI) {
			return Promise.reject(`Invalid settings URI - ${resource.toString()}`);
		}
		return this.getOrCreateEditableSettingsEditorInput(target, settingsURI)
413
			.then(toInput => {
B
Benjamin Pasero 已提交
414 415 416 417 418
				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,
419
						replacement: replaceWith,
S
Sandeep Somavarapu 已提交
420
						options: options ? SettingsEditorOptions.create(options) : undefined
B
Benjamin Pasero 已提交
421 422
					}]).then(() => {
						this.lastOpenedSettingsInput = replaceWith;
S
Sandeep Somavarapu 已提交
423
						return group.activeControl!;
B
Benjamin Pasero 已提交
424
					});
425 426 427 428
				});
			});
	}

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

S
Sandeep Somavarapu 已提交
433 434
	private getConfigurationTargetFromSettingsResource(resource: URI): ConfigurationTarget {
		if (this.userSettingsResource.toString() === resource.toString()) {
R
Rob Lourens 已提交
435
			return ConfigurationTarget.USER_LOCAL;
S
Sandeep Somavarapu 已提交
436 437 438 439 440 441 442 443 444 445 446 447
		}

		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;
		}

R
Rob Lourens 已提交
448
		return ConfigurationTarget.USER_LOCAL;
S
Sandeep Somavarapu 已提交
449 450
	}

S
Sandeep Somavarapu 已提交
451
	private getConfigurationTargetFromDefaultSettingsResource(uri: URI) {
R
Rob Lourens 已提交
452 453 454 455 456
		return this.isDefaultWorkspaceSettingsResource(uri) ?
			ConfigurationTarget.WORKSPACE :
			this.isDefaultFolderSettingsResource(uri) ?
				ConfigurationTarget.WORKSPACE_FOLDER :
				ConfigurationTarget.USER_LOCAL;
S
Sandeep Somavarapu 已提交
457 458
	}

R
Rob Lourens 已提交
459
	private isDefaultSettingsResource(uri: URI): boolean {
S
Sandeep Somavarapu 已提交
460 461 462 463
		return this.isDefaultUserSettingsResource(uri) || this.isDefaultWorkspaceSettingsResource(uri) || this.isDefaultFolderSettingsResource(uri);
	}

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

S
Sandeep Somavarapu 已提交
467 468 469 470 471
	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 {
472
		return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?resourceSettings\.json$/);
R
Rob Lourens 已提交
473 474
	}

S
Sandeep Somavarapu 已提交
475
	private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI {
S
Sandeep Somavarapu 已提交
476 477 478 479 480
		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 已提交
481
		}
S
Sandeep Somavarapu 已提交
482
		return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultUserSettingsUriCounter++}/settings.json` });
S
Sandeep Somavarapu 已提交
483 484
	}

485 486
	private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string {
		const name = getSettingsTargetName(target, resource, this.contextService);
487
		return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name;
488 489
	}

J
Johannes Rieken 已提交
490
	private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): Promise<EditorInput> {
491 492
		return this.createSettingsIfNotExists(target, resource)
			.then(() => <EditorInput>this.editorService.createInput({ resource }));
493 494
	}

495 496 497
	private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, settingsUri: URI): Promise<SettingsEditorModel> {
		const workspace = this.contextService.getWorkspace();
		if (workspace.configuration && workspace.configuration.toString() === settingsUri.toString()) {
498
			return this.textModelResolverService.createModelReference(settingsUri)
499
				.then(reference => this.instantiationService.createInstance(WorkspaceConfigurationEditorModel, reference, configurationTarget));
500
		}
501 502
		return this.textModelResolverService.createModelReference(settingsUri)
			.then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget));
503 504
	}

J
Johannes Rieken 已提交
505
	private createDefaultSettingsEditorModel(defaultSettingsUri: URI): Promise<DefaultSettingsEditorModel> {
506 507
		return this.textModelResolverService.createModelReference(defaultSettingsUri)
			.then(reference => {
S
Sandeep Somavarapu 已提交
508 509
				const target = this.getConfigurationTargetFromDefaultSettingsResource(defaultSettingsUri);
				return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, this.getDefaultSettings(target));
510 511 512
			});
	}

S
Sandeep Somavarapu 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
	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);
528
		}
S
Sandeep Somavarapu 已提交
529
		return this._defaultUserSettingsContentModel;
530 531
	}

532
	private async getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): Promise<URI | null> {
533 534
		switch (configurationTarget) {
			case ConfigurationTarget.USER:
R
Rob Lourens 已提交
535
			case ConfigurationTarget.USER_LOCAL:
536
				return this.userSettingsResource;
R
Rob Lourens 已提交
537
			case ConfigurationTarget.USER_REMOTE:
538 539
				const remoteEnvironment = await this.remoteAgentService.getEnvironment();
				return remoteEnvironment ? remoteEnvironment.settingsPath : null;
540
			case ConfigurationTarget.WORKSPACE:
541
				return this.workspaceSettingsResource;
542
			case ConfigurationTarget.WORKSPACE_FOLDER:
S
Sandeep Somavarapu 已提交
543
				if (resource) {
544
					return this.getFolderSettingsResource(resource);
S
Sandeep Somavarapu 已提交
545
				}
546
		}
547
		return null;
548 549
	}

J
Johannes Rieken 已提交
550
	private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): Promise<void> {
551
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) {
R
Rob Lourens 已提交
552 553
			const workspaceConfig = this.contextService.getWorkspace().configuration;
			if (!workspaceConfig) {
R
Rob Lourens 已提交
554
				return Promise.resolve(undefined);
R
Rob Lourens 已提交
555 556
			}

B
Benjamin Pasero 已提交
557
			return this.textFileService.read(workspaceConfig)
S
Sandeep Somavarapu 已提交
558 559
				.then(content => {
					if (Object.keys(parse(content.value)).indexOf('settings') === -1) {
560
						return this.jsonEditingService.write(resource, [{ key: 'settings', value: {} }], true).then(undefined, () => { });
S
Sandeep Somavarapu 已提交
561
					}
S
Sandeep Somavarapu 已提交
562
					return undefined;
S
Sandeep Somavarapu 已提交
563
				});
564
		}
S
Sandeep Somavarapu 已提交
565
		return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { });
566 567
	}

J
Johannes Rieken 已提交
568
	private createIfNotExists(resource: URI, contents: string): Promise<any> {
B
Benjamin Pasero 已提交
569
		return this.textFileService.read(resource, { acceptTextOnly: true }).then(undefined, error => {
570
			if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
B
Benjamin Pasero 已提交
571
				return this.textFileService.write(resource, contents).then(undefined, error => {
R
Rob Lourens 已提交
572
					return Promise.reject(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(resource, { relative: true }), error)));
573 574 575
				});
			}

R
Rob Lourens 已提交
576
			return Promise.reject(error);
577 578 579
		});
	}

580 581
	private getMostCommonlyUsedSettings(): string[] {
		return [
S
Sandeep Somavarapu 已提交
582
			'files.autoSave',
583
			'editor.fontSize',
S
Sandeep Somavarapu 已提交
584 585 586
			'editor.fontFamily',
			'editor.tabSize',
			'editor.renderWhitespace',
S
Sandeep Somavarapu 已提交
587
			'editor.cursorStyle',
588
			'editor.multiCursorModifier',
S
Sandeep Somavarapu 已提交
589
			'editor.insertSpaces',
590
			'editor.wordWrap',
591
			'files.exclude',
S
Sandeep Somavarapu 已提交
592
			'files.associations'
593
		];
S
Sandeep Somavarapu 已提交
594
	}
595

S
Sandeep Somavarapu 已提交
596
	private addLanguageOverrideEntry(language: string, settingsModel: IPreferencesEditorModel<ISetting>, codeEditor: ICodeEditor): Promise<IPosition | null> {
S
Sandeep Somavarapu 已提交
597 598 599
		const languageKey = `[${language}]`;
		let setting = settingsModel.getPreference(languageKey);
		const model = codeEditor.getModel();
S
Sandeep Somavarapu 已提交
600 601 602 603 604 605 606 607 608
		if (model) {
			const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean } }>();
			const eol = model.getEOL();
			if (setting) {
				if (setting.overrides && setting.overrides.length) {
					const lastSetting = setting.overrides[setting.overrides.length - 1];
					return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber) });
				}
				return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 });
S
Sandeep Somavarapu 已提交
609
			}
S
Sandeep Somavarapu 已提交
610 611 612 613 614 615 616 617 618 619 620 621 622
			return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER)
				.then(() => {
					setting = settingsModel.getPreference(languageKey);
					if (setting) {
						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) };
					}
					return null;
				});
S
Sandeep Somavarapu 已提交
623
		}
S
Sandeep Somavarapu 已提交
624
		return Promise.resolve(null);
625 626
	}

A
Alex Dima 已提交
627
	private spaces(count: number, { tabSize, insertSpaces }: { tabSize: number; insertSpaces: boolean }): string {
628 629 630
		return insertSpaces ? strings.repeat(' ', tabSize * count) : strings.repeat('\t', count);
	}

631
	public dispose(): void {
632
		this._onDispose.fire();
633 634
		super.dispose();
	}
635
}
636

637
registerSingleton(IPreferencesService, PreferencesService);