preferencesEditor.ts 51.6 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  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 已提交
6
import * as DOM from 'vs/base/browser/dom';
7 8
import { Button } from 'vs/base/browser/ui/button/button';
import { Widget } from 'vs/base/browser/ui/widget';
9
import * as arrays from 'vs/base/common/arrays';
10
import { Delayer, ThrottledDelayer } from 'vs/base/common/async';
11
import { CancellationToken } from 'vs/base/common/cancellation';
12 13 14
import { IStringDictionary } from 'vs/base/common/collections';
import { getErrorMessage, isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
15
import { ArrayNavigator } from 'vs/base/common/iterator';
16 17 18 19
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
20
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
21
import { EditorExtensionsRegistry, IEditorContributionCtor, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
22 23 24
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import * as editorCommon from 'vs/editor/common/editorCommon';
25
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
26
import { FindController } from 'vs/editor/contrib/find/findController';
27 28
import { FoldingController } from 'vs/editor/contrib/folding/folding';
import { MessageController } from 'vs/editor/contrib/message/messageController';
29
import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/multicursor';
30 31
import * as nls from 'vs/nls';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
32 33
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
34 35 36 37 38
import { ILogService } from 'vs/platform/log/common/log';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
39
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
40 41
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
42
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
43 44 45
import { Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
46
import { EditorInput, EditorOptions, IEditorControl } from 'vs/workbench/common/editor';
47
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
I
isidor 已提交
48
import { PREFERENCES_EDITOR_ID } from 'vs/workbench/parts/files/common/files';
49 50
import { DefaultSettingsRenderer, FolderSettingsRenderer, IPreferencesRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers';
import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
51 52 53
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
54 55 56 57
import { IFilterResult, IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences';
import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
58
import { SplitView, Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview';
59

60 61
export class PreferencesEditor extends BaseEditor {

62
	public static readonly ID: string = PREFERENCES_EDITOR_ID;
63

S
Sandeep Somavarapu 已提交
64
	private defaultSettingsEditorContextKey: IContextKey<boolean>;
65
	private searchFocusContextKey: IContextKey<boolean>;
66 67 68
	private headerContainer: HTMLElement;
	private searchWidget: SearchWidget;
	private sideBySidePreferencesWidget: SideBySidePreferencesWidget;
69
	private preferencesRenderers: PreferencesRenderersController;
70 71

	private delayedFilterLogging: Delayer<void>;
72
	private localSearchDelayer: Delayer<void>;
73
	private remoteSearchThrottle: ThrottledDelayer<void>;
74
	private _lastReportedFilter: string;
75

S
Sandeep Somavarapu 已提交
76
	private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
77

78 79 80 81 82 83 84
	get minimumWidth(): number { return this.sideBySidePreferencesWidget ? this.sideBySidePreferencesWidget.minimumWidth : 0; }
	get maximumWidth(): number { return this.sideBySidePreferencesWidget ? this.sideBySidePreferencesWidget.maximumWidth : Number.POSITIVE_INFINITY; }

	// these setters need to exist because this extends from BaseEditor
	set minimumWidth(value: number) { /*noop*/ }
	set maximumWidth(value: number) { /*noop*/ }

85 86
	readonly minimumHeight = 260;

87
	private _onDidCreateWidget = new Emitter<{ width: number; height: number; }>();
B
Benjamin Pasero 已提交
88
	readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = this._onDidCreateWidget.event;
89

90 91 92
	constructor(
		@IPreferencesService private preferencesService: IPreferencesService,
		@ITelemetryService telemetryService: ITelemetryService,
93
		@IEditorService private editorService: IEditorService,
S
Sandeep Somavarapu 已提交
94
		@IContextKeyService private contextKeyService: IContextKeyService,
B
Benjamin Pasero 已提交
95
		@IInstantiationService private instantiationService: IInstantiationService,
96 97
		@IThemeService themeService: IThemeService,
		@IProgressService private progressService: IProgressService
98
	) {
B
Benjamin Pasero 已提交
99
		super(PreferencesEditor.ID, telemetryService, themeService);
S
Sandeep Somavarapu 已提交
100
		this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService);
101
		this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
102
		this.delayedFilterLogging = new Delayer<void>(1000);
103
		this.localSearchDelayer = new Delayer(100);
104
		this.remoteSearchThrottle = new ThrottledDelayer(200);
105 106
	}

107 108
	public createEditor(parent: HTMLElement): void {
		DOM.addClass(parent, 'preferences-editor');
109

110
		this.headerContainer = DOM.append(parent, DOM.$('.preferences-header'));
111 112 113 114 115 116 117 118 119 120 121 122
		const advertisement = DOM.append(this.headerContainer, DOM.$('.new-settings-ad'));
		const advertisementLabel = DOM.append(advertisement, DOM.$('span.new-settings-ad-label'));
		advertisementLabel.textContent = nls.localize('advertisementLabel', "Try a preview of our ") + ' ';
		const openSettings2Button = this._register(new Button(advertisement, { title: true, buttonBackground: null, buttonHoverBackground: null }));
		openSettings2Button.style({
			buttonBackground: null,
			buttonForeground: null,
			buttonBorder: null,
			buttonHoverBackground: null
		});
		openSettings2Button.label = nls.localize('openSettings2Label', "new settings editor");
		openSettings2Button.element.classList.add('open-settings2-button');
123
		this._register(openSettings2Button.onDidClick(() => this.preferencesService.openSettings(false)));
124

125 126
		this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.headerContainer, {
			ariaLabel: nls.localize('SearchSettingsWidget.AriaLabel', "Search settings"),
S
Sandeep Somavarapu 已提交
127
			placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"),
128
			focusKey: this.searchFocusContextKey,
S
Sandeep Somavarapu 已提交
129 130
			showResultCount: true,
			ariaLive: 'assertive'
131
		}));
132
		this._register(this.searchWidget.onDidChange(value => this.onInputChanged()));
S
Sandeep Somavarapu 已提交
133 134
		this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget));
		this.lastFocusedWidget = this.searchWidget;
135

136
		const editorsContainer = DOM.append(parent, DOM.$('.preferences-editors-container'));
137
		this.sideBySidePreferencesWidget = this._register(this.instantiationService.createInstance(SideBySidePreferencesWidget, editorsContainer));
138
		this._onDidCreateWidget.fire();
S
Sandeep Somavarapu 已提交
139
		this._register(this.sideBySidePreferencesWidget.onFocus(() => this.lastFocusedWidget = this.sideBySidePreferencesWidget));
S
Sandeep Somavarapu 已提交
140
		this._register(this.sideBySidePreferencesWidget.onDidSettingsTargetChange(target => this.switchSettings(target)));
S
Sandeep Somavarapu 已提交
141

142
		this.preferencesRenderers = this._register(this.instantiationService.createInstance(PreferencesRenderersController));
S
Sandeep Somavarapu 已提交
143 144

		this._register(this.preferencesRenderers.onDidFilterResultsCountChange(count => this.showSearchResultsMessage(count)));
145 146
	}

S
Sandeep Somavarapu 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	public clearSearchResults(): void {
		if (this.searchWidget) {
			this.searchWidget.clear();
		}
	}

	public focusNextResult(): void {
		if (this.preferencesRenderers) {
			this.preferencesRenderers.focusNextPreference(true);
		}
	}

	public focusPreviousResult(): void {
		if (this.preferencesRenderers) {
			this.preferencesRenderers.focusNextPreference(false);
		}
	}

165 166 167 168
	public editFocusedPreference(): void {
		this.preferencesRenderers.editFocusedPreference();
	}

169
	public setInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
S
Sandeep Somavarapu 已提交
170
		this.defaultSettingsEditorContextKey.set(true);
171
		return super.setInput(newInput, options, token).then(() => this.updateInput(newInput, options, token));
172 173
	}

174
	public layout(dimension: DOM.Dimension): void {
S
Sandeep Somavarapu 已提交
175
		this.searchWidget.layout(dimension);
176
		const headerHeight = DOM.getTotalHeight(this.headerContainer);
177
		this.sideBySidePreferencesWidget.layout(new DOM.Dimension(dimension.width, dimension.height - headerHeight));
178 179 180
	}

	public getControl(): IEditorControl {
181
		return this.sideBySidePreferencesWidget.getControl();
182 183 184
	}

	public focus(): void {
S
Sandeep Somavarapu 已提交
185 186 187
		if (this.lastFocusedWidget) {
			this.lastFocusedWidget.focus();
		}
188 189
	}

190
	public focusSearch(filter?: string): void {
R
Rob Lourens 已提交
191
		if (filter) {
192 193
			this.searchWidget.setValue(filter);
		}
R
Rob Lourens 已提交
194

S
Sandeep Somavarapu 已提交
195 196 197
		this.searchWidget.focus();
	}

S
Sandeep Somavarapu 已提交
198 199 200 201 202 203
	public focusSettingsFileEditor(): void {
		if (this.sideBySidePreferencesWidget) {
			this.sideBySidePreferencesWidget.focus();
		}
	}

204
	public clearInput(): void {
S
Sandeep Somavarapu 已提交
205
		this.defaultSettingsEditorContextKey.set(false);
206
		this.sideBySidePreferencesWidget.clearInput();
207
		this.preferencesRenderers.onHidden();
208 209 210
		super.clearInput();
	}

211
	protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
212 213
		this.sideBySidePreferencesWidget.setEditorVisible(visible, group);
		super.setEditorVisible(visible, group);
S
Sandeep Somavarapu 已提交
214 215
	}

216 217 218 219 220 221
	private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
		return this.sideBySidePreferencesWidget.setInput(<DefaultPreferencesEditorInput>newInput.details, <EditorInput>newInput.master, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => {
			if (token.isCancellationRequested) {
				return void 0;
			}

222 223
			this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer;
			this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer;
224
			this.onInputChanged();
225 226 227
		});
	}

228
	private onInputChanged(): void {
229 230
		const query = this.searchWidget.getValue().trim();
		this.delayedFilterLogging.cancel();
231
		this.triggerSearch(query)
S
Sandeep Somavarapu 已提交
232 233 234 235 236
			.then(() => {
				const result = this.preferencesRenderers.lastFilterResult;
				if (result) {
					this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(
						query,
237
						this.preferencesRenderers.lastFilterResult));
S
Sandeep Somavarapu 已提交
238 239
				}
			});
240 241
	}

242
	private triggerSearch(query: string): TPromise<void> {
243
		if (query) {
244
			return TPromise.join([
245
				this.localSearchDelayer.trigger(() => this.preferencesRenderers.localFilterPreferences(query).then(() => { })),
246
				this.remoteSearchThrottle.trigger(() => TPromise.wrap(this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500)))
247
			]) as TPromise;
248 249
		} else {
			// When clearing the input, update immediately to clear it
250 251 252
			this.localSearchDelayer.cancel();
			this.preferencesRenderers.localFilterPreferences(query);

253
			this.remoteSearchThrottle.cancel();
254
			return this.preferencesRenderers.remoteSearchPreferences(query);
255
		}
256 257
	}

S
Sandeep Somavarapu 已提交
258
	private switchSettings(target: SettingsTarget): void {
259
		// Focus the editor if this editor is not active editor
B
Benjamin Pasero 已提交
260
		if (this.editorService.activeControl !== this) {
261 262
			this.focus();
		}
S
Sandeep Somavarapu 已提交
263
		const promise = this.input && this.input.isDirty() ? this.input.save() : TPromise.as(true);
S
Sandeep Somavarapu 已提交
264 265
		promise.done(value => {
			if (target === ConfigurationTarget.USER) {
266
				this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource, true);
S
Sandeep Somavarapu 已提交
267
			} else if (target === ConfigurationTarget.WORKSPACE) {
268
				this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource, true);
S
Sandeep Somavarapu 已提交
269
			} else if (target instanceof URI) {
270
				this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target, true);
S
Sandeep Somavarapu 已提交
271 272
			}
		});
273 274
	}

275 276 277 278 279 280 281 282 283
	private showSearchResultsMessage(count: IPreferencesCount): void {
		const countValue = count.count;
		if (count.target) {
			this.sideBySidePreferencesWidget.setResultCount(count.target, count.count);
		} else if (this.searchWidget.getValue()) {
			if (countValue === 0) {
				this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Results"), countValue);
			} else if (countValue === 1) {
				this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found"), countValue);
S
Sandeep Somavarapu 已提交
284
			} else {
285
				this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", countValue), countValue);
S
Sandeep Somavarapu 已提交
286 287
			}
		} else {
288
			this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", countValue), countValue);
S
Sandeep Somavarapu 已提交
289
		}
290 291
	}

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	private _countById(settingsGroups: ISettingsGroup[]): IStringDictionary<number> {
		const result = {};

		for (const group of settingsGroups) {
			let i = 0;
			for (const section of group.sections) {
				i += section.settings.length;
			}

			result[group.id] = i;
		}

		return result;
	}

	private reportFilteringUsed(filter: string, filterResult: IFilterResult): void {
308
		if (filter && filter !== this._lastReportedFilter) {
309 310 311
			const metadata = filterResult && filterResult.metadata;
			const counts = filterResult && this._countById(filterResult.filteredGroups);

312 313 314 315 316 317
			let durations: any;
			if (metadata) {
				durations = Object.create(null);
				Object.keys(metadata).forEach(key => durations[key] = metadata[key].duration);
			}

318 319
			let data = {
				filter,
320
				durations,
321 322
				counts,
				requestCount: metadata && metadata['nlpResult'] && metadata['nlpResult'].requestCount
323
			};
324

K
kieferrm 已提交
325
			/* __GDPR__
K
kieferrm 已提交
326
				"defaultSettings.filter" : {
327
					"filter": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
R
Ramya Achutha Rao 已提交
328 329 330 331
					"durations.nlpresult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
					"counts.nlpresult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
					"durations.filterresult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
					"counts.filterresult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
R
Ramya Achutha Rao 已提交
332
					"requestCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
K
kieferrm 已提交
333 334
				}
			*/
335
			this.telemetryService.publicLog('defaultSettings.filter', data);
336
			this._lastReportedFilter = filter;
337
		}
338
	}
339 340 341 342 343

	dispose(): void {
		this._onDidCreateWidget.dispose();
		super.dispose();
	}
344 345
}

346
class SettingsNavigator extends ArrayNavigator<ISetting> {
347 348

	public next(): ISetting {
349
		return super.next() || super.first();
350 351 352
	}

	public previous(): ISetting {
353
		return super.previous() || super.last();
354 355
	}

356 357
	public reset(): void {
		this.index = this.start - 1;
358 359 360
	}
}

361 362 363 364 365
interface IPreferencesCount {
	target?: SettingsTarget;
	count: number;
}

366
class PreferencesRenderersController extends Disposable {
367

368
	private _defaultPreferencesRenderer: IPreferencesRenderer<ISetting>;
S
Sandeep Somavarapu 已提交
369 370
	private _defaultPreferencesRendererDisposables: IDisposable[] = [];

371
	private _editablePreferencesRenderer: IPreferencesRenderer<ISetting>;
S
Sandeep Somavarapu 已提交
372 373
	private _editablePreferencesRendererDisposables: IDisposable[] = [];

374
	private _settingsNavigator: SettingsNavigator;
375
	private _remoteFilterInProgress: TPromise<any>;
376
	private _prefsModelsForSearch = new Map<string, ISettingsEditorModel>();
377

378 379 380
	private _currentLocalSearchProvider: ISearchProvider;
	private _currentRemoteSearchProvider: ISearchProvider;
	private _lastQuery: string;
381
	private _lastFilterResult: IFilterResult;
382

M
Matt Bierner 已提交
383
	private readonly _onDidFilterResultsCountChange: Emitter<IPreferencesCount> = this._register(new Emitter<IPreferencesCount>());
384
	public readonly onDidFilterResultsCountChange: Event<IPreferencesCount> = this._onDidFilterResultsCountChange.event;
S
Sandeep Somavarapu 已提交
385 386

	constructor(
387 388 389
		@IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService,
		@ITelemetryService private telemetryService: ITelemetryService,
		@IPreferencesService private preferencesService: IPreferencesService,
390 391
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
		@ILogService private logService: ILogService
S
Sandeep Somavarapu 已提交
392 393 394 395
	) {
		super();
	}

396
	get lastFilterResult(): IFilterResult {
397 398 399
		return this._lastFilterResult;
	}

S
Sandeep Somavarapu 已提交
400
	get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
401 402 403
		return this._defaultPreferencesRenderer;
	}

404 405 406 407
	get editablePreferencesRenderer(): IPreferencesRenderer<ISetting> {
		return this._editablePreferencesRenderer;
	}

S
Sandeep Somavarapu 已提交
408
	set defaultPreferencesRenderer(defaultPreferencesRenderer: IPreferencesRenderer<ISetting>) {
S
Sandeep Somavarapu 已提交
409 410
		if (this._defaultPreferencesRenderer !== defaultPreferencesRenderer) {
			this._defaultPreferencesRenderer = defaultPreferencesRenderer;
411

S
Sandeep Somavarapu 已提交
412
			this._defaultPreferencesRendererDisposables = dispose(this._defaultPreferencesRendererDisposables);
S
Sandeep Somavarapu 已提交
413 414

			if (this._defaultPreferencesRenderer) {
415 416 417 418
				this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source }) => {
					this._editablePreferencesRenderer.updatePreference(key, value, source);
					this._updatePreference(key, value, source);
				}, this, this._defaultPreferencesRendererDisposables);
S
Sandeep Somavarapu 已提交
419 420
				this._defaultPreferencesRenderer.onFocusPreference(preference => this._focusPreference(preference, this._editablePreferencesRenderer), this, this._defaultPreferencesRendererDisposables);
				this._defaultPreferencesRenderer.onClearFocusPreference(preference => this._clearFocus(preference, this._editablePreferencesRenderer), this, this._defaultPreferencesRendererDisposables);
S
Sandeep Somavarapu 已提交
421
			}
S
Sandeep Somavarapu 已提交
422
		}
423 424
	}

S
Sandeep Somavarapu 已提交
425 426 427 428 429
	set editablePreferencesRenderer(editableSettingsRenderer: IPreferencesRenderer<ISetting>) {
		if (this._editablePreferencesRenderer !== editableSettingsRenderer) {
			this._editablePreferencesRenderer = editableSettingsRenderer;
			this._editablePreferencesRendererDisposables = dispose(this._editablePreferencesRendererDisposables);
			if (this._editablePreferencesRenderer) {
430 431
				(<ISettingsEditorModel>this._editablePreferencesRenderer.preferencesModel)
					.onDidChangeGroups(this._onEditableContentDidChange, this, this._editablePreferencesRendererDisposables);
432 433

				this._editablePreferencesRenderer.onUpdatePreference(({ key, value, source }) => this._updatePreference(key, value, source, true), this, this._defaultPreferencesRendererDisposables);
S
Sandeep Somavarapu 已提交
434 435
			}
		}
436 437
	}

438
	private async _onEditableContentDidChange(): Promise<void> {
439 440 441 442
		const foundExactMatch = await this.localFilterPreferences(this._lastQuery, true);
		if (!foundExactMatch) {
			await this.remoteSearchPreferences(this._lastQuery, true);
		}
443 444
	}

445 446 447 448 449
	onHidden(): void {
		this._prefsModelsForSearch.forEach(model => model.dispose());
		this._prefsModelsForSearch = new Map<string, ISettingsEditorModel>();
	}

450
	remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise<void> {
451 452 453 454 455
		if (this.lastFilterResult && this.lastFilterResult.exactMatch) {
			// Skip and clear remote search
			query = '';
		}

456
		if (this._remoteFilterInProgress && this._remoteFilterInProgress.cancel) {
457
			// Resolved/rejected promises have no .cancel()
458
			this._remoteFilterInProgress.cancel();
459 460
		}

461
		this._currentRemoteSearchProvider = (updateCurrentResults && this._currentRemoteSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query);
462

463
		this._remoteFilterInProgress = this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1, updateCurrentResults);
464

465 466
		return this._remoteFilterInProgress.then(() => {
			this._remoteFilterInProgress = null;
467 468 469 470 471 472 473
		}, err => {
			if (isPromiseCanceledError(err)) {
				return null;
			} else {
				onUnexpectedError(err);
			}
		});
474 475
	}

476
	localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise<boolean> {
477 478 479 480
		if (this._settingsNavigator) {
			this._settingsNavigator.reset();
		}

481
		this._currentLocalSearchProvider = (updateCurrentResults && this._currentLocalSearchProvider) || this.preferencesSearchService.getLocalSearchProvider(query);
482
		return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, updateCurrentResults);
483
	}
S
Sandeep Somavarapu 已提交
484

485
	private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, editableContentOnly?: boolean): TPromise<boolean> {
486
		this._lastQuery = query;
R
Rob Lourens 已提交
487

488
		const filterPs: TPromise<IFilterResult>[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)];
489
		if (!editableContentOnly) {
490 491
			filterPs.push(
				this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder));
492 493
			filterPs.push(
				this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder).then(() => null));
494 495
		}

496
		return TPromise.join(filterPs).then(results => {
497 498 499 500 501
			let [editableFilterResult, defaultFilterResult] = results;

			if (!defaultFilterResult && editableContentOnly) {
				defaultFilterResult = this.lastFilterResult;
			}
S
Sandeep Somavarapu 已提交
502

503
			this.consolidateAndUpdate(defaultFilterResult, editableFilterResult);
504
			this._lastFilterResult = defaultFilterResult;
505 506

			return defaultFilterResult && defaultFilterResult.exactMatch;
507
		});
S
Sandeep Somavarapu 已提交
508 509
	}

510
	private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<void> {
511
		const searchPs = [
512 513
			this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder),
			this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder)
514 515 516 517
		];

		for (const folder of this.workspaceContextService.getWorkspace().folders) {
			const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri);
518
			searchPs.push(this.searchSettingsTarget(query, searchProvider, folderSettingsResource, groupId, groupLabel, groupOrder));
519 520 521 522 523 524
		}


		return TPromise.join(searchPs).then(() => { });
	}

525
	private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number): Promise<void> {
526 527 528
		if (!query) {
			// Don't open the other settings targets when query is empty
			this._onDidFilterResultsCountChange.fire({ target, count: 0 });
529
			return Promise.resolve(null);
530 531
		}

532
		return this.getPreferencesEditorModel(target).then(model => {
533
			return model && this._filterOrSearchPreferencesModel('', <ISettingsEditorModel>model, provider, groupId, groupLabel, groupOrder);
534 535 536 537 538 539 540 541 542 543 544 545
		}).then(result => {
			const count = result ? this._flatten(result.filteredGroups).length : 0;
			this._onDidFilterResultsCountChange.fire({ target, count });
		}, err => {
			if (!isPromiseCanceledError(err)) {
				return TPromise.wrapError(err);
			}

			return null;
		});
	}

546
	private async getPreferencesEditorModel(target: SettingsTarget): Promise<ISettingsEditorModel | null> {
547 548 549 550
		const resource = target === ConfigurationTarget.USER ? this.preferencesService.userSettingsResource :
			target === ConfigurationTarget.WORKSPACE ? this.preferencesService.workspaceSettingsResource :
				target;

551 552 553 554
		if (!resource) {
			return null;
		}

555 556
		const targetKey = resource.toString();
		if (!this._prefsModelsForSearch.has(targetKey)) {
557 558 559 560 561 562 563
			try {
				const model = this._register(await this.preferencesService.createPreferencesEditorModel(resource));
				this._prefsModelsForSearch.set(targetKey, <ISettingsEditorModel>model);
			} catch (e) {
				// Will throw when the settings file doesn't exist.
				return null;
			}
564 565
		}

566
		return this._prefsModelsForSearch.get(targetKey);
567 568
	}

S
Sandeep Somavarapu 已提交
569
	focusNextPreference(forward: boolean = true) {
R
Rob Lourens 已提交
570 571 572 573
		if (!this._settingsNavigator) {
			return;
		}

574
		const setting = forward ? this._settingsNavigator.next() : this._settingsNavigator.previous();
575 576 577 578
		this._focusPreference(setting, this._defaultPreferencesRenderer);
		this._focusPreference(setting, this._editablePreferencesRenderer);
	}

579 580 581 582 583 584 585 586 587 588 589 590
	editFocusedPreference(): void {
		if (!this._settingsNavigator || !this._settingsNavigator.current()) {
			return;
		}

		const setting = this._settingsNavigator.current();
		const shownInEditableRenderer = this._editablePreferencesRenderer.editPreference(setting);
		if (!shownInEditableRenderer) {
			this.defaultPreferencesRenderer.editPreference(setting);
		}
	}

591
	private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<IFilterResult> {
592 593
		if (!preferencesRenderer) {
			return TPromise.wrap(null);
S
Sandeep Somavarapu 已提交
594
		}
595

596 597 598 599 600
		const model = <ISettingsEditorModel>preferencesRenderer.preferencesModel;
		return this._filterOrSearchPreferencesModel(filter, model, provider, groupId, groupLabel, groupOrder).then(filterResult => {
			preferencesRenderer.filterPreferences(filterResult);
			return filterResult;
		});
S
Sandeep Somavarapu 已提交
601 602
	}

603 604 605 606 607 608 609 610 611
	private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<IFilterResult> {
		const searchP = provider ? provider.searchModel(model) : TPromise.wrap(null);
		return searchP
			.then<ISearchResult>(null, err => {
				if (isPromiseCanceledError(err)) {
					return TPromise.wrapError(err);
				} else {
					/* __GDPR__
						"defaultSettings.searchError" : {
612
							"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" },
613
							"filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
614 615 616
						}
					*/
					const message = getErrorMessage(err).trim();
617 618
					if (message && message !== 'Error') {
						// "Error" = any generic network error
619
						this.telemetryService.publicLog('defaultSettings.searchError', { message, filter });
620
						this.logService.info('Setting search error: ' + message);
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
					}
					return null;
				}
			})
			.then(searchResult => {
				const filterResult = searchResult ?
					model.updateResultGroup(groupId, {
						id: groupId,
						label: groupLabel,
						result: searchResult,
						order: groupOrder
					}) :
					model.updateResultGroup(groupId, null);

				if (filterResult) {
					filterResult.query = filter;
637
					filterResult.exactMatch = searchResult && searchResult.exactMatch;
638 639
				}

640

641 642 643 644
				return filterResult;
			});
	}

645
	private consolidateAndUpdate(defaultFilterResult: IFilterResult, editableFilterResult: IFilterResult): void {
646 647 648 649
		const defaultPreferencesFilteredGroups = defaultFilterResult ? defaultFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
		const editablePreferencesFilteredGroups = editableFilterResult ? editableFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
		const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
		// Maintain the current navigation position when updating SettingsNavigator
		const current = this._settingsNavigator && this._settingsNavigator.current();
		const navigatorSettings = this._lastQuery ? consolidatedSettings : [];
		const currentIndex = current ?
			arrays.firstIndex(navigatorSettings, s => s.key === current.key) :
			-1;

		this._settingsNavigator = new SettingsNavigator(navigatorSettings, Math.max(currentIndex, 0));

		if (currentIndex >= 0) {
			this._settingsNavigator.next();
			const newCurrent = this._settingsNavigator.current();
			this._focusPreference(newCurrent, this._defaultPreferencesRenderer);
			this._focusPreference(newCurrent, this._editablePreferencesRenderer);
		}

666
		const totalCount = consolidatedSettings.length;
667
		this._onDidFilterResultsCountChange.fire({ count: totalCount });
S
Sandeep Somavarapu 已提交
668 669
	}

S
Sandeep Somavarapu 已提交
670 671 672 673
	private _getAllPreferences(preferencesRenderer: IPreferencesRenderer<ISetting>): ISettingsGroup[] {
		return preferencesRenderer ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).settingsGroups : [];
	}

674 675 676 677 678 679 680 681 682 683 684 685
	private _focusPreference(preference: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
		if (preference && preferencesRenderer) {
			preferencesRenderer.focusPreference(preference);
		}
	}

	private _clearFocus(preference: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
		if (preference && preferencesRenderer) {
			preferencesRenderer.clearFocus(preference);
		}
	}

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
	private _updatePreference(key: string, value: any, source: ISetting, fromEditableSettings?: boolean): void {
		const data = {
			userConfigurationKeys: [key]
		};

		if (this.lastFilterResult) {
			data['query'] = this.lastFilterResult.query;
			data['editableSide'] = !!fromEditableSettings;

			const nlpMetadata = this.lastFilterResult.metadata && this.lastFilterResult.metadata['nlpResult'];
			if (nlpMetadata) {
				const sortedKeys = Object.keys(nlpMetadata.scoredResults).sort((a, b) => nlpMetadata.scoredResults[b].score - nlpMetadata.scoredResults[a].score);
				const suffix = '##' + key;
				data['nlpIndex'] = arrays.firstIndex(sortedKeys, key => strings.endsWith(key, suffix));
			}

			const settingLocation = this._findSetting(this.lastFilterResult, key);
			if (settingLocation) {
				data['groupId'] = this.lastFilterResult.filteredGroups[settingLocation.groupIdx].id;
				data['displayIdx'] = settingLocation.overallSettingIdx;
			}
		}

		/* __GDPR__
			"defaultSettingsActions.copySetting" : {
				"userConfigurationKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
R
Rob Lourens 已提交
712
				"query" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
R
Ramya Achutha Rao 已提交
713
				"nlpIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
714
				"groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
R
Ramya Achutha Rao 已提交
715 716
				"displayIdx" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
				"editableSide" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
			}
		*/
		this.telemetryService.publicLog('defaultSettingsActions.copySetting', data);
	}

	private _findSetting(filterResult: IFilterResult, key: string): { groupIdx: number, settingIdx: number, overallSettingIdx: number } {
		let overallSettingIdx = 0;

		for (let groupIdx = 0; groupIdx < filterResult.filteredGroups.length; groupIdx++) {
			const group = filterResult.filteredGroups[groupIdx];
			for (let settingIdx = 0; settingIdx < group.sections[0].settings.length; settingIdx++) {
				const setting = group.sections[0].settings[settingIdx];
				if (key === setting.key) {
					return { groupIdx, settingIdx, overallSettingIdx };
				}

				overallSettingIdx++;
			}
S
Sandeep Somavarapu 已提交
735
		}
736 737

		return null;
S
Sandeep Somavarapu 已提交
738 739
	}

740
	private _consolidateSettings(editableSettingsGroups: ISettingsGroup[], defaultSettingsGroups: ISettingsGroup[]): ISetting[] {
741 742
		const defaultSettings = this._flatten(defaultSettingsGroups);
		const editableSettings = this._flatten(editableSettingsGroups).filter(secondarySetting => defaultSettings.every(primarySetting => primarySetting.key !== secondarySetting.key));
S
Sandeep Somavarapu 已提交
743
		return [...defaultSettings, ...editableSettings];
744 745 746 747
	}

	private _flatten(settingsGroups: ISettingsGroup[]): ISetting[] {
		const settings: ISetting[] = [];
748 749
		for (const group of settingsGroups) {
			for (const section of group.sections) {
750
				settings.push(...section.settings);
751 752
			}
		}
753

754
		return settings;
755
	}
756 757

	public dispose(): void {
S
Sandeep Somavarapu 已提交
758 759
		dispose(this._defaultPreferencesRendererDisposables);
		dispose(this._editablePreferencesRendererDisposables);
760 761
		super.dispose();
	}
762 763
}

764
class SideBySidePreferencesWidget extends Widget {
765

766
	private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
767

S
Sandeep Somavarapu 已提交
768
	private defaultPreferencesHeader: HTMLElement;
769
	private defaultPreferencesEditor: DefaultPreferencesEditor;
S
Sandeep Somavarapu 已提交
770
	private editablePreferencesEditor: BaseEditor;
771 772 773
	private defaultPreferencesEditorContainer: HTMLElement;
	private editablePreferencesEditorContainer: HTMLElement;

S
Sandeep Somavarapu 已提交
774 775
	private settingsTargetsWidget: SettingsTargetsWidget;

M
Matt Bierner 已提交
776
	private readonly _onFocus: Emitter<void> = new Emitter<void>();
S
Sandeep Somavarapu 已提交
777 778
	readonly onFocus: Event<void> = this._onFocus.event;

M
Matt Bierner 已提交
779
	private readonly _onDidSettingsTargetChange: Emitter<SettingsTarget> = new Emitter<SettingsTarget>();
S
Sandeep Somavarapu 已提交
780 781
	readonly onDidSettingsTargetChange: Event<SettingsTarget> = this._onDidSettingsTargetChange.event;

S
Sandeep Somavarapu 已提交
782
	private lastFocusedEditor: BaseEditor;
783
	private splitview: SplitView;
784

785 786 787
	get minimumWidth(): number { return this.splitview.minimumSize; }
	get maximumWidth(): number { return this.splitview.maximumSize; }

S
Sandeep Somavarapu 已提交
788
	constructor(
789
		parentElement: HTMLElement,
S
Sandeep Somavarapu 已提交
790 791 792 793 794
		@IInstantiationService private instantiationService: IInstantiationService,
		@IThemeService private themeService: IThemeService,
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
		@IPreferencesService private preferencesService: IPreferencesService,
	) {
795 796 797 798
		super();

		DOM.addClass(parentElement, 'side-by-side-preferences-editor');

799
		this.splitview = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL });
800 801
		this._register(this.splitview);
		this._register(this.splitview.onDidSashReset(() => this.splitview.distributeViewSizes()));
802 803

		this.defaultPreferencesEditorContainer = DOM.$('.default-preferences-editor-container');
S
Sandeep Somavarapu 已提交
804 805 806 807 808

		const defaultPreferencesHeaderContainer = DOM.append(this.defaultPreferencesEditorContainer, DOM.$('.preferences-header-container'));
		this.defaultPreferencesHeader = DOM.append(defaultPreferencesHeaderContainer, DOM.$('div.default-preferences-header'));
		this.defaultPreferencesHeader.textContent = nls.localize('defaultSettings', "Default Settings");

809
		this.defaultPreferencesEditor = this._register(this.instantiationService.createInstance(DefaultPreferencesEditor));
810
		this.defaultPreferencesEditor.create(this.defaultPreferencesEditorContainer);
A
Alex Dima 已提交
811
		(<CodeEditorWidget>this.defaultPreferencesEditor.getControl()).onDidFocusEditorWidget(() => this.lastFocusedEditor = this.defaultPreferencesEditor);
812

813 814 815 816 817 818 819 820 821
		this.splitview.addView({
			element: this.defaultPreferencesEditorContainer,
			layout: size => this.defaultPreferencesEditor.layout(new DOM.Dimension(size, this.dimension.height - 34 /* height of header container */)),
			minimumSize: 220,
			maximumSize: Number.POSITIVE_INFINITY,
			onDidChange: Event.None
		}, Sizing.Distribute);

		this.editablePreferencesEditorContainer = DOM.$('.editable-preferences-editor-container');
S
Sandeep Somavarapu 已提交
822 823 824
		const editablePreferencesHeaderContainer = DOM.append(this.editablePreferencesEditorContainer, DOM.$('.preferences-header-container'));
		this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, editablePreferencesHeaderContainer));
		this._register(this.settingsTargetsWidget.onDidTargetChange(target => this._onDidSettingsTargetChange.fire(target)));
825

826 827 828 829 830 831 832 833 834
		this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => {
			const shadow = colors.scrollbarShadow ? colors.scrollbarShadow.toString() : null;

			if (shadow) {
				this.editablePreferencesEditorContainer.style.boxShadow = `-6px 0 5px -5px ${shadow}`;
			} else {
				this.editablePreferencesEditorContainer.style.boxShadow = null;
			}
		}));
S
Sandeep Somavarapu 已提交
835

836 837 838 839 840 841 842 843
		this.splitview.addView({
			element: this.editablePreferencesEditorContainer,
			layout: size => this.editablePreferencesEditor && this.editablePreferencesEditor.layout(new DOM.Dimension(size, this.dimension.height - 34 /* height of header container */)),
			minimumSize: 220,
			maximumSize: Number.POSITIVE_INFINITY,
			onDidChange: Event.None
		}, Sizing.Distribute);

S
Sandeep Somavarapu 已提交
844
		const focusTracker = this._register(DOM.trackFocus(parentElement));
845
		this._register(focusTracker.onDidFocus(() => this._onFocus.fire()));
846 847
	}

848
	public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<{ defaultPreferencesRenderer?: IPreferencesRenderer<ISetting>, editablePreferencesRenderer?: IPreferencesRenderer<ISetting> }> {
849
		this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput);
S
Sandeep Somavarapu 已提交
850
		this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource());
851 852 853 854
		return TPromise.join([
			this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options, token),
			this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options, token)
		])
S
Sandeep Somavarapu 已提交
855
			.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => {
856
				if (token.isCancellationRequested) {
857
					return {};
858 859
				}

S
Sandeep Somavarapu 已提交
860
				this.defaultPreferencesHeader.textContent = defaultPreferencesRenderer && this.getDefaultPreferencesHeaderText((<DefaultSettingsEditorModel>defaultPreferencesRenderer.preferencesModel).target);
S
Sandeep Somavarapu 已提交
861 862
				return { defaultPreferencesRenderer, editablePreferencesRenderer };
			});
863 864
	}

S
Sandeep Somavarapu 已提交
865 866 867 868 869 870 871 872 873 874 875 876
	private getDefaultPreferencesHeaderText(target: ConfigurationTarget): string {
		switch (target) {
			case ConfigurationTarget.USER:
				return nls.localize('defaultUserSettings', "Default User Settings");
			case ConfigurationTarget.WORKSPACE:
				return nls.localize('defaultWorkspaceSettings', "Default Workspace Settings");
			case ConfigurationTarget.WORKSPACE_FOLDER:
				return nls.localize('defaultFolderSettings', "Default Folder Settings");
		}
		return '';
	}

877 878 879 880
	public setResultCount(settingsTarget: SettingsTarget, count: number): void {
		this.settingsTargetsWidget.setResultCount(settingsTarget, count);
	}

881
	public layout(dimension: DOM.Dimension = this.dimension): void {
882
		this.dimension = dimension;
883
		this.splitview.layout(dimension.width);
884 885
	}

S
Sandeep Somavarapu 已提交
886
	public focus(): void {
S
Sandeep Somavarapu 已提交
887 888
		if (this.lastFocusedEditor) {
			this.lastFocusedEditor.focus();
S
Sandeep Somavarapu 已提交
889 890 891
		}
	}

892 893
	public getControl(): IEditorControl {
		return this.editablePreferencesEditor ? this.editablePreferencesEditor.getControl() : null;
894 895
	}

896
	public clearInput(): void {
897 898 899
		if (this.defaultPreferencesEditor) {
			this.defaultPreferencesEditor.clearInput();
		}
900 901 902 903 904
		if (this.editablePreferencesEditor) {
			this.editablePreferencesEditor.clearInput();
		}
	}

905
	public setEditorVisible(visible: boolean, group: IEditorGroup): void {
906 907 908
		if (this.defaultPreferencesEditor) {
			this.defaultPreferencesEditor.setVisible(visible, group);
		}
S
Sandeep Somavarapu 已提交
909
		if (this.editablePreferencesEditor) {
910
			this.editablePreferencesEditor.setVisible(visible, group);
S
Sandeep Somavarapu 已提交
911 912 913
		}
	}

914
	private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): BaseEditor {
S
Sandeep Somavarapu 已提交
915
		if (this.editablePreferencesEditor) {
916
			return this.editablePreferencesEditor;
S
Sandeep Somavarapu 已提交
917 918
		}
		const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
919 920
		const editor = descriptor.instantiate(this.instantiationService);
		this.editablePreferencesEditor = editor;
921
		this.editablePreferencesEditor.create(this.editablePreferencesEditorContainer);
A
Alex Dima 已提交
922
		(<CodeEditorWidget>this.editablePreferencesEditor.getControl()).onDidFocusEditorWidget(() => this.lastFocusedEditor = this.editablePreferencesEditor);
923
		this.lastFocusedEditor = this.editablePreferencesEditor;
924
		this.layout();
925 926

		return editor;
S
Sandeep Somavarapu 已提交
927 928
	}

929 930 931 932 933 934 935 936 937
	private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions, token: CancellationToken): Thenable<IPreferencesRenderer<ISetting>> {
		return editor.setInput(input, options, token)
			.then(() => {
				if (token.isCancellationRequested) {
					return void 0;
				}

				return (<CodeEditorWidget>editor.getControl()).getContribution<ISettingsEditorContribution>(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri);
			});
S
Sandeep Somavarapu 已提交
938 939
	}

S
Sandeep Somavarapu 已提交
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
	private getSettingsTarget(resource: URI): SettingsTarget {
		if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
			return ConfigurationTarget.USER;
		}

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

		const folder = this.workspaceContextService.getWorkspaceFolder(resource);
		if (folder) {
			return folder.uri;
		}

		return ConfigurationTarget.USER;
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
	}

	private disposeEditors(): void {
		if (this.defaultPreferencesEditor) {
			this.defaultPreferencesEditor.dispose();
			this.defaultPreferencesEditor = null;
		}
		if (this.editablePreferencesEditor) {
			this.editablePreferencesEditor.dispose();
			this.editablePreferencesEditor = null;
		}
	}

	public dispose(): void {
		this.disposeEditors();
S
Sandeep Somavarapu 已提交
971
		super.dispose();
972 973 974
	}
}

S
Sandeep Somavarapu 已提交
975
export class DefaultPreferencesEditor extends BaseTextEditor {
976

M
Matt Bierner 已提交
977
	public static readonly ID: string = 'workbench.editor.defaultPreferences';
978

S
Sandeep Somavarapu 已提交
979 980
	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
S
Sandeep Somavarapu 已提交
981 982
		@IInstantiationService instantiationService: IInstantiationService,
		@IStorageService storageService: IStorageService,
983
		@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
984
		@IThemeService themeService: IThemeService,
B
Benjamin Pasero 已提交
985
		@ITextFileService textFileService: ITextFileService,
986 987
		@IEditorGroupsService editorGroupService: IEditorGroupsService,
		@IEditorService editorService: IEditorService
S
Sandeep Somavarapu 已提交
988
	) {
989
		super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
S
Sandeep Somavarapu 已提交
990 991
	}

992 993 994 995 996 997 998
	private static _getContributions(): IEditorContributionCtor[] {
		let skipContributions = [FoldingController.prototype, SelectionHighlighter.prototype, FindController.prototype];
		let contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.prototype) === -1);
		contributions.push(DefaultSettingsEditorContribution);
		return contributions;
	}

999
	public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
1000
		const editor = this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, { contributions: DefaultPreferencesEditor._getContributions() });
B
Benjamin Pasero 已提交
1001 1002

		// Inform user about editor being readonly if user starts type
B
Benjamin Pasero 已提交
1003 1004
		this._register(editor.onDidType(() => this.showReadonlyHint(editor)));
		this._register(editor.onDidPaste(() => this.showReadonlyHint(editor)));
B
Benjamin Pasero 已提交
1005 1006 1007 1008

		return editor;
	}

1009
	private showReadonlyHint(editor: ICodeEditor): void {
B
Benjamin Pasero 已提交
1010 1011 1012 1013
		const messageController = MessageController.get(editor);
		if (!messageController.isVisible()) {
			messageController.showMessage(nls.localize('defaultEditorReadonly', "Edit in the right hand side editor to override defaults."), editor.getSelection().getPosition());
		}
1014 1015
	}

1016
	protected getConfigurationOverrides(): IEditorOptions {
1017
		const options = super.getConfigurationOverrides();
1018
		options.readOnly = true;
1019
		if (this.input) {
S
Sandeep Somavarapu 已提交
1020 1021 1022 1023 1024
			options.lineNumbers = 'off';
			options.renderLineHighlight = 'none';
			options.scrollBeyondLastLine = false;
			options.folding = false;
			options.renderWhitespace = 'none';
1025
			options.wordWrap = 'on';
S
Sandeep Somavarapu 已提交
1026
			options.renderIndentGuides = false;
S
Sandeep Somavarapu 已提交
1027
			options.rulers = [];
S
Sandeep Somavarapu 已提交
1028
			options.glyphMargin = true;
S
Sandeep Somavarapu 已提交
1029 1030 1031
			options.minimap = {
				enabled: false
			};
S
Sandeep Somavarapu 已提交
1032
		}
S
Sandeep Somavarapu 已提交
1033 1034 1035
		return options;
	}

1036 1037
	setInput(input: DefaultPreferencesEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
		return super.setInput(input, options, token)
1038
			.then(() => this.input.resolve()
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
				.then(editorModel => {
					if (token.isCancellationRequested) {
						return void 0;
					}

					return editorModel.load();
				})
				.then(editorModel => {
					if (token.isCancellationRequested) {
						return void 0;
					}

					this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel);
				}));
S
Sandeep Somavarapu 已提交
1053 1054
	}

1055 1056 1057 1058 1059 1060 1061 1062
	public clearInput(): void {
		// Clear Model
		this.getControl().setModel(null);

		// Pass to super
		super.clearInput();
	}

1063
	public layout(dimension: DOM.Dimension) {
1064
		this.getControl().layout(dimension);
S
Sandeep Somavarapu 已提交
1065 1066
	}

1067 1068 1069
	protected getAriaLabel(): string {
		return nls.localize('preferencesAriaLabel', "Default preferences. Readonly text editor.");
	}
S
Sandeep Somavarapu 已提交
1070 1071
}

S
Sandeep Somavarapu 已提交
1072 1073
interface ISettingsEditorContribution extends editorCommon.IEditorContribution {

S
Sandeep Somavarapu 已提交
1074
	updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>>;
S
Sandeep Somavarapu 已提交
1075

S
Sandeep Somavarapu 已提交
1076 1077
}

S
Sandeep Somavarapu 已提交
1078
abstract class AbstractSettingsEditorContribution extends Disposable implements ISettingsEditorContribution {
S
Sandeep Somavarapu 已提交
1079

S
Sandeep Somavarapu 已提交
1080
	private preferencesRendererCreationPromise: TPromise<IPreferencesRenderer<ISetting>>;
S
Sandeep Somavarapu 已提交
1081

S
Sandeep Somavarapu 已提交
1082 1083
	constructor(protected editor: ICodeEditor,
		@IInstantiationService protected instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
1084 1085
		@IPreferencesService protected preferencesService: IPreferencesService,
		@IWorkspaceContextService protected workspaceContextService: IWorkspaceContextService
S
Sandeep Somavarapu 已提交
1086 1087
	) {
		super();
1088
		this._register(this.editor.onDidChangeModel(() => this._onModelChanged()));
S
Sandeep Somavarapu 已提交
1089 1090
	}

S
Sandeep Somavarapu 已提交
1091
	updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
S
Sandeep Somavarapu 已提交
1092 1093
		if (!this.preferencesRendererCreationPromise) {
			this.preferencesRendererCreationPromise = this._createPreferencesRenderer();
1094 1095
		}

S
Sandeep Somavarapu 已提交
1096 1097 1098 1099 1100 1101
		if (this.preferencesRendererCreationPromise) {
			return this._hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri)
				.then(changed => changed ? this._updatePreferencesRenderer(associatedPreferencesModelUri) : this.preferencesRendererCreationPromise);
		}

		return TPromise.as(null);
1102 1103
	}

S
Sandeep Somavarapu 已提交
1104
	protected _onModelChanged(): void {
1105
		const model = this.editor.getModel();
S
Sandeep Somavarapu 已提交
1106
		this.disposePreferencesRenderer();
1107
		if (model) {
S
Sandeep Somavarapu 已提交
1108
			this.preferencesRendererCreationPromise = this._createPreferencesRenderer();
1109 1110 1111
		}
	}

1112
	private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise<boolean> {
S
Sandeep Somavarapu 已提交
1113
		return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
S
Sandeep Somavarapu 已提交
1114
			return !(preferencesRenderer && preferencesRenderer.getAssociatedPreferencesModel() && preferencesRenderer.getAssociatedPreferencesModel().uri.toString() === associatedPreferencesModelUri.toString());
1115 1116 1117 1118 1119 1120
		});
	}

	private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
		return this.preferencesService.createPreferencesEditorModel<ISetting>(associatedPreferencesModelUri)
			.then(associatedPreferencesEditorModel => {
S
Sandeep Somavarapu 已提交
1121
				return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
1122
					if (preferencesRenderer) {
S
Sandeep Somavarapu 已提交
1123 1124 1125
						const associatedPreferencesModel = preferencesRenderer.getAssociatedPreferencesModel();
						if (associatedPreferencesModel) {
							associatedPreferencesModel.dispose();
1126
						}
S
Sandeep Somavarapu 已提交
1127
						preferencesRenderer.setAssociatedPreferencesModel(associatedPreferencesEditorModel);
1128 1129 1130 1131
					}
					return preferencesRenderer;
				});
			});
S
Sandeep Somavarapu 已提交
1132 1133
	}

1134
	private disposePreferencesRenderer(): void {
S
Sandeep Somavarapu 已提交
1135 1136
		if (this.preferencesRendererCreationPromise) {
			this.preferencesRendererCreationPromise.then(preferencesRenderer => {
S
Sandeep Somavarapu 已提交
1137
				if (preferencesRenderer) {
S
Sandeep Somavarapu 已提交
1138 1139 1140
					const associatedPreferencesModel = preferencesRenderer.getAssociatedPreferencesModel();
					if (associatedPreferencesModel) {
						associatedPreferencesModel.dispose();
S
Sandeep Somavarapu 已提交
1141
					}
1142
					preferencesRenderer.preferencesModel.dispose();
S
Sandeep Somavarapu 已提交
1143 1144 1145
					preferencesRenderer.dispose();
				}
			});
S
Sandeep Somavarapu 已提交
1146
			this.preferencesRendererCreationPromise = TPromise.as(null);
S
Sandeep Somavarapu 已提交
1147
		}
1148 1149 1150 1151
	}

	dispose() {
		this.disposePreferencesRenderer();
S
Sandeep Somavarapu 已提交
1152
		super.dispose();
1153
	}
S
Sandeep Somavarapu 已提交
1154 1155

	protected abstract _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>>;
S
Sandeep Somavarapu 已提交
1156
	abstract getId(): string;
1157 1158
}

S
Sandeep Somavarapu 已提交
1159
class DefaultSettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
1160

M
Matt Bierner 已提交
1161
	static readonly ID: string = 'editor.contrib.defaultsettings';
1162

S
Sandeep Somavarapu 已提交
1163 1164 1165
	getId(): string {
		return DefaultSettingsEditorContribution.ID;
	}
S
Sandeep Somavarapu 已提交
1166

S
Sandeep Somavarapu 已提交
1167 1168 1169
	protected _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>> {
		return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)
			.then(editorModel => {
S
Sandeep Somavarapu 已提交
1170
				if (editorModel instanceof DefaultSettingsEditorModel && this.editor.getModel()) {
S
Sandeep Somavarapu 已提交
1171 1172 1173 1174 1175 1176
					const preferencesRenderer = this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel);
					preferencesRenderer.render();
					return preferencesRenderer;
				}
				return null;
			});
S
Sandeep Somavarapu 已提交
1177
	}
S
Sandeep Somavarapu 已提交
1178 1179 1180 1181
}

class SettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {

M
Matt Bierner 已提交
1182
	static readonly ID: string = 'editor.contrib.settings';
S
Sandeep Somavarapu 已提交
1183

S
Sandeep Somavarapu 已提交
1184 1185 1186 1187 1188 1189 1190 1191 1192
	constructor(editor: ICodeEditor,
		@IInstantiationService instantiationService: IInstantiationService,
		@IPreferencesService preferencesService: IPreferencesService,
		@IWorkspaceContextService workspaceContextService: IWorkspaceContextService
	) {
		super(editor, instantiationService, preferencesService, workspaceContextService);
		this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this._onModelChanged()));
	}

1193 1194 1195 1196
	getId(): string {
		return SettingsEditorContribution.ID;
	}

S
Sandeep Somavarapu 已提交
1197
	protected _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>> {
S
Sandeep Somavarapu 已提交
1198
		if (this.isSettingsModel()) {
1199 1200
			return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)
				.then(settingsModel => {
S
Sandeep Somavarapu 已提交
1201
					if (settingsModel instanceof SettingsEditorModel && this.editor.getModel()) {
S
Sandeep Somavarapu 已提交
1202 1203
						switch (settingsModel.configurationTarget) {
							case ConfigurationTarget.USER:
1204
								return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel);
S
Sandeep Somavarapu 已提交
1205
							case ConfigurationTarget.WORKSPACE:
1206
								return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel);
1207
							case ConfigurationTarget.WORKSPACE_FOLDER:
1208
								return this.instantiationService.createInstance(FolderSettingsRenderer, this.editor, settingsModel);
S
Sandeep Somavarapu 已提交
1209
						}
1210
					}
S
Sandeep Somavarapu 已提交
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
					return null;
				})
				.then(preferencesRenderer => {
					if (preferencesRenderer) {
						preferencesRenderer.render();
					}
					return preferencesRenderer;
				});
		}
		return null;
	}

	private isSettingsModel(): boolean {
		const model = this.editor.getModel();
		if (!model) {
			return false;
		}

S
Sandeep Somavarapu 已提交
1229
		if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.toString() === model.uri.toString()) {
S
Sandeep Somavarapu 已提交
1230 1231 1232
			return true;
		}

S
Sandeep Somavarapu 已提交
1233
		if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.toString() === model.uri.toString()) {
S
Sandeep Somavarapu 已提交
1234 1235 1236
			return true;
		}

S
Sandeep Somavarapu 已提交
1237
		for (const folder of this.workspaceContextService.getWorkspace().folders) {
1238
			const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri);
S
Sandeep Somavarapu 已提交
1239
			if (folderSettingsResource && folderSettingsResource.toString() === model.uri.toString()) {
1240
				return true;
S
Sandeep Somavarapu 已提交
1241 1242 1243 1244
			}
		}

		return false;
S
Sandeep Somavarapu 已提交
1245 1246
	}

1247 1248
}

1249
registerEditorContribution(SettingsEditorContribution);