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

import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
S
Sandeep Somavarapu 已提交
9
import * as DOM from 'vs/base/browser/dom';
10
import { Delayer } from 'vs/base/common/async';
S
Sandeep Somavarapu 已提交
11
import { Dimension, Builder } from 'vs/base/browser/builder';
12
import { ArrayNavigator, INavigator } from 'vs/base/common/iterator';
S
Sandeep Somavarapu 已提交
13
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
14
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
15 16
import { toResource, SideBySideEditorInput, EditorOptions, EditorInput } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
17
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
A
Alex Dima 已提交
18
import { IEditorControl, Position, Verbosity } from 'vs/platform/editor/common/editor';
19
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
S
Sandeep Somavarapu 已提交
20
import * as editorCommon from 'vs/editor/common/editorCommon';
S
Sandeep Somavarapu 已提交
21
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
S
Sandeep Somavarapu 已提交
22
import { CodeEditor } from 'vs/editor/browser/codeEditor';
23
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
S
Sandeep Somavarapu 已提交
24
import {
25
	IPreferencesService, ISettingsGroup, ISetting, IFilterResult,
S
Sandeep Somavarapu 已提交
26
	CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel
S
Sandeep Somavarapu 已提交
27
} from 'vs/workbench/parts/preferences/common/preferences';
28
import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
S
Sandeep Somavarapu 已提交
29
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
30
import { ICodeEditor, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser';
31
import { SearchWidget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
S
Sandeep Somavarapu 已提交
32
import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
A
Alex Dima 已提交
33
import { Command } from 'vs/editor/common/editorCommonExtensions';
S
Sandeep Somavarapu 已提交
34
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
35
import { IThemeService } from 'vs/platform/theme/common/themeService';
S
Sandeep Somavarapu 已提交
36 37
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
S
Sandeep Somavarapu 已提交
38
import { IStorageService } from 'vs/platform/storage/common/storage';
39
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
S
Sandeep Somavarapu 已提交
40
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
41
import { ITextModelService } from 'vs/editor/common/services/resolverService';
42
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
43 44 45
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { VSash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
46
import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers';
B
Benjamin Pasero 已提交
47 48
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
49
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
50 51

// Ignore following contributions
S
Sandeep Somavarapu 已提交
52
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
S
Sandeep Somavarapu 已提交
53 54
import { FindController } from 'vs/editor/contrib/find/browser/find';
import { SelectionHighlighter } from 'vs/editor/contrib/find/common/findController';
55 56
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
57 58
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
59
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
S
Sandeep Somavarapu 已提交
60
import Event, { Emitter } from "vs/base/common/event";
S
Sandeep Somavarapu 已提交
61

62 63
export class PreferencesEditorInput extends SideBySideEditorInput {
	public static ID: string = 'workbench.editorinputs.preferencesEditorInput';
S
Sandeep Somavarapu 已提交
64

65 66 67
	getTypeId(): string {
		return PreferencesEditorInput.ID;
	}
S
Sandeep Somavarapu 已提交
68 69 70 71

	public getTitle(verbosity: Verbosity): string {
		return this.master.getTitle(verbosity);
	}
72 73 74 75

	public isDirty(): boolean {
		return false;
	}
76
}
77

78
export class DefaultPreferencesEditorInput extends ResourceEditorInput {
79
	public static ID = 'workbench.editorinputs.defaultpreferences';
80
	constructor(defaultSettingsResource: URI,
81
		@ITextModelService textModelResolverService: ITextModelService
82 83
	) {
		super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService);
84 85
	}

S
Sandeep Somavarapu 已提交
86
	getTypeId(): string {
87
		return DefaultPreferencesEditorInput.ID;
S
Sandeep Somavarapu 已提交
88
	}
89

S
Sandeep Somavarapu 已提交
90
	matches(other: any): boolean {
91
		if (!super.matches(other)) {
S
Sandeep Somavarapu 已提交
92
			return false;
93
		}
94
		if (!(other instanceof DefaultPreferencesEditorInput)) {
S
Sandeep Somavarapu 已提交
95 96
			return false;
		}
S
Sandeep Somavarapu 已提交
97 98
		return true;
	}
99
}
100

101 102 103 104
export class PreferencesEditor extends BaseEditor {

	public static ID: string = 'workbench.editor.preferencesEditor';

S
Sandeep Somavarapu 已提交
105
	private defaultSettingsEditorContextKey: IContextKey<boolean>;
S
Sandeep Somavarapu 已提交
106
	private focusSettingsContextKey: IContextKey<boolean>;
107 108
	private headerContainer: HTMLElement;
	private searchWidget: SearchWidget;
109
	private settingsTargetsWidget: SettingsTargetsWidget;
110
	private sideBySidePreferencesWidget: SideBySidePreferencesWidget;
111
	private preferencesRenderers: PreferencesRenderers;
112 113 114

	private delayedFilterLogging: Delayer<void>;

115
	private latestEmptyFilters: string[] = [];
S
Sandeep Somavarapu 已提交
116
	private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
117

118 119 120 121 122
	constructor(
		@IPreferencesService private preferencesService: IPreferencesService,
		@IEnvironmentService private environmentService: IEnvironmentService,
		@ITelemetryService telemetryService: ITelemetryService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
S
Sandeep Somavarapu 已提交
123
		@IContextKeyService private contextKeyService: IContextKeyService,
B
Benjamin Pasero 已提交
124
		@IInstantiationService private instantiationService: IInstantiationService,
125 126
		@IThemeService themeService: IThemeService,
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
127
	) {
B
Benjamin Pasero 已提交
128
		super(PreferencesEditor.ID, telemetryService, themeService);
S
Sandeep Somavarapu 已提交
129
		this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService);
S
Sandeep Somavarapu 已提交
130
		this.focusSettingsContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
131 132 133 134 135 136 137 138 139
		this.delayedFilterLogging = new Delayer<void>(1000);
	}

	public createEditor(parent: Builder): void {
		const parentElement = parent.getHTMLElement();
		DOM.addClass(parentElement, 'preferences-editor');

		this.headerContainer = DOM.append(parentElement, DOM.$('.preferences-header'));

140 141
		this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.headerContainer, {
			ariaLabel: nls.localize('SearchSettingsWidget.AriaLabel', "Search settings"),
S
Sandeep Somavarapu 已提交
142
			placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"),
S
Sandeep Somavarapu 已提交
143
			focusKey: this.focusSettingsContextKey
144
		}));
145
		this._register(this.searchWidget.onDidChange(value => this.filterPreferences(value.trim())));
S
Sandeep Somavarapu 已提交
146
		this._register(this.searchWidget.onNavigate(shift => this.preferencesRenderers.focusNextPreference(!shift)));
S
Sandeep Somavarapu 已提交
147 148
		this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget));
		this.lastFocusedWidget = this.searchWidget;
149

150 151
		this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, this.headerContainer, this.preferencesService.userSettingsResource));
		this._register(this.settingsTargetsWidget.onDidTargetChange(target => this.switchSettings(target)));
152 153 154

		const editorsContainer = DOM.append(parentElement, DOM.$('.preferences-editors-container'));
		this.sideBySidePreferencesWidget = this._register(this.instantiationService.createInstance(SideBySidePreferencesWidget, editorsContainer));
S
Sandeep Somavarapu 已提交
155 156
		this._register(this.sideBySidePreferencesWidget.onFocus(() => this.lastFocusedWidget = this.sideBySidePreferencesWidget));

157
		this.preferencesRenderers = this._register(new PreferencesRenderers());
158 159 160
	}

	public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
S
Sandeep Somavarapu 已提交
161
		this.defaultSettingsEditorContextKey.set(true);
162
		const oldInput = <PreferencesEditorInput>this.input;
163
		return super.setInput(newInput, options).then(() => this.updateInput(oldInput, newInput, options));
164 165 166
	}

	public layout(dimension: Dimension): void {
S
Sandeep Somavarapu 已提交
167 168
		DOM.toggleClass(this.headerContainer, 'vertical-layout', dimension.width < 700);
		this.searchWidget.layout(dimension);
169 170 171 172 173
		const headerHeight = DOM.getTotalHeight(this.headerContainer);
		this.sideBySidePreferencesWidget.layout(new Dimension(dimension.width, dimension.height - headerHeight));
	}

	public getControl(): IEditorControl {
174
		return this.sideBySidePreferencesWidget.getControl();
175 176 177
	}

	public focus(): void {
S
Sandeep Somavarapu 已提交
178 179 180
		if (this.lastFocusedWidget) {
			this.lastFocusedWidget.focus();
		}
181 182
	}

183
	public focusSearch(filter?: string): void {
R
Rob Lourens 已提交
184
		if (filter) {
185 186
			this.searchWidget.setValue(filter);
		}
R
Rob Lourens 已提交
187

S
Sandeep Somavarapu 已提交
188 189 190
		this.searchWidget.focus();
	}

S
Sandeep Somavarapu 已提交
191 192 193 194 195 196
	public focusSettingsFileEditor(): void {
		if (this.sideBySidePreferencesWidget) {
			this.sideBySidePreferencesWidget.focus();
		}
	}

197
	public clearInput(): void {
S
Sandeep Somavarapu 已提交
198
		this.defaultSettingsEditorContextKey.set(false);
199 200 201 202
		this.sideBySidePreferencesWidget.clearInput();
		super.clearInput();
	}

S
Sandeep Somavarapu 已提交
203 204 205 206 207 208 209 210 211 212
	protected setEditorVisible(visible: boolean, position: Position): void {
		this.sideBySidePreferencesWidget.setEditorVisible(visible, position);
		super.setEditorVisible(visible, position);
	}

	public changePosition(position: Position): void {
		this.sideBySidePreferencesWidget.changePosition(position);
		super.changePosition(position);
	}

213
	private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
214
		this.settingsTargetsWidget.setTarget(this.getSettingsConfigurationTarget(newInput));
215

216
		return this.sideBySidePreferencesWidget.setInput(<DefaultPreferencesEditorInput>newInput.details, <EditorInput>newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => {
217 218
			this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer;
			this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer;
S
Sandeep Somavarapu 已提交
219
			this.filterPreferences(this.searchWidget.getValue());
220 221 222
		});
	}

223 224 225 226 227 228 229 230 231 232 233 234
	private getSettingsConfigurationTarget(preferencesEditorInput: PreferencesEditorInput): ConfigurationTarget | URI {
		const resource = toResource(preferencesEditorInput.master);
		if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) {
			return ConfigurationTarget.USER;
		}
		if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) {
			return ConfigurationTarget.WORKSPACE;
		}
		return this.workspaceContextService.getRoot(resource);
	}

	private switchSettings(target: ConfigurationTarget | URI): void {
235 236 237 238
		// Focus the editor if this editor is not active editor
		if (this.editorService.getActiveEditor() !== this) {
			this.focus();
		}
239
		const promise = this.input.isDirty() ? this.input.save() : TPromise.as(true);
240
		promise.done(value => this.preferencesService.switchSettings(target));
241 242 243
	}

	private filterPreferences(filter: string) {
244 245 246 247 248
		const count = this.preferencesRenderers.filterPreferences(filter);
		const message = filter ? this.showSearchResultsMessage(count) : nls.localize('totalSettingsMessage', "Total {0} Settings", count);
		this.searchWidget.showMessage(message, count);
		if (count === 0) {
			this.latestEmptyFilters.push(filter);
249
		}
250
		this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter));
251 252 253
	}

	private showSearchResultsMessage(count: number): string {
S
Sandeep Somavarapu 已提交
254
		return count === 0 ? nls.localize('noSettingsFound', "No Results") :
255 256 257 258 259
			count === 1 ? nls.localize('oneSettingFound', "1 Setting matched") :
				nls.localize('settingsFound', "{0} Settings matched", count);
	}

	private reportFilteringUsed(filter: string): void {
260 261 262 263 264 265 266 267
		if (filter) {
			let data = {
				filter,
				emptyFilters: this.getLatestEmptyFiltersForTelemetry()
			};
			this.latestEmptyFilters = [];
			this.telemetryService.publicLog('defaultSettings.filter', data);
		}
268 269
	}

270 271 272 273 274 275 276 277
	/**
	 * Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount
	 * of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc.
	 */
	private getLatestEmptyFiltersForTelemetry(): string[] {
		let cumulativeSize = 0;
		return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192);
	}
278 279
}

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
class SettingsNavigator implements INavigator<ISetting> {

	private iterator: ArrayNavigator<ISetting>;

	constructor(settings: ISetting[]) {
		this.iterator = new ArrayNavigator<ISetting>(settings);
	}

	public next(): ISetting {
		return this.iterator.next() || this.iterator.first();
	}

	public previous(): ISetting {
		return this.iterator.previous() || this.iterator.last();
	}

	public parent(): ISetting {
		return this.iterator.parent();
	}

	public first(): ISetting {
		return this.iterator.first();
	}

	public last(): ISetting {
		return this.iterator.last();
	}

	public current(): ISetting {
		return this.iterator.current();
	}
}

313
class PreferencesRenderers extends Disposable {
314

315 316
	private _defaultPreferencesRenderer: IPreferencesRenderer<ISetting>;
	private _editablePreferencesRenderer: IPreferencesRenderer<ISetting>;
317
	private _settingsNavigator: SettingsNavigator;
318 319 320 321 322 323 324 325

	private _disposables: IDisposable[] = [];

	public get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
		return this._defaultPreferencesRenderer;
	}

	public set defaultPreferencesRenderer(defaultPreferencesRenderer: IPreferencesRenderer<ISetting>) {
S
Sandeep Somavarapu 已提交
326 327
		if (this._defaultPreferencesRenderer !== defaultPreferencesRenderer) {
			this._defaultPreferencesRenderer = defaultPreferencesRenderer;
328

S
Sandeep Somavarapu 已提交
329
			this._disposables = dispose(this._disposables);
A
Alex Dima 已提交
330
			this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source }) => this._updatePreference(key, value, source, this._editablePreferencesRenderer), this, this._disposables);
S
Sandeep Somavarapu 已提交
331 332 333
			this._defaultPreferencesRenderer.onFocusPreference(preference => this._focusPreference(preference, this._editablePreferencesRenderer), this, this._disposables);
			this._defaultPreferencesRenderer.onClearFocusPreference(preference => this._clearFocus(preference, this._editablePreferencesRenderer), this, this._disposables);
		}
334 335 336 337 338 339 340
	}

	public set editablePreferencesRenderer(editableSettingsRenderer: IPreferencesRenderer<ISetting>) {
		this._editablePreferencesRenderer = editableSettingsRenderer;
	}

	public filterPreferences(filter: string): number {
S
Sandeep Somavarapu 已提交
341 342 343 344 345 346
		const defaultPreferencesFilterResult = this._filterPreferences(filter, this._defaultPreferencesRenderer);
		const editablePreferencesFilterResult = this._filterPreferences(filter, this._editablePreferencesRenderer);

		const defaultPreferencesFilteredGroups = defaultPreferencesFilterResult ? defaultPreferencesFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
		const editablePreferencesFilteredGroups = editablePreferencesFilterResult ? editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
		const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
347
		this._settingsNavigator = new SettingsNavigator(filter ? consolidatedSettings : []);
S
Sandeep Somavarapu 已提交
348

349
		return consolidatedSettings.length;
350 351
	}

352
	public focusNextPreference(forward: boolean = true) {
353
		const setting = forward ? this._settingsNavigator.next() : this._settingsNavigator.previous();
354 355 356 357
		this._focusPreference(setting, this._defaultPreferencesRenderer);
		this._focusPreference(setting, this._editablePreferencesRenderer);
	}

S
Sandeep Somavarapu 已提交
358 359 360 361 362 363
	private _getAllPreferences(preferencesRenderer: IPreferencesRenderer<ISetting>): ISettingsGroup[] {
		return preferencesRenderer ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).settingsGroups : [];
	}

	private _filterPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>): IFilterResult {
		let filterResult = null;
364
		if (preferencesRenderer) {
S
Sandeep Somavarapu 已提交
365
			filterResult = filter ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).filterSettings(filter) : null;
366 367
			preferencesRenderer.filterPreferences(filterResult);
		}
S
Sandeep Somavarapu 已提交
368
		return filterResult;
369 370 371 372 373 374 375 376 377 378 379 380 381 382
	}

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

S
Sandeep Somavarapu 已提交
383 384 385 386 387 388
	private _updatePreference(key: string, value: any, source: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
		if (preferencesRenderer) {
			preferencesRenderer.updatePreference(key, value, source);
		}
	}

389 390 391 392 393 394 395 396
	private _consolidateSettings(editableSettingsGroups: ISettingsGroup[], defaultSettingsGroups: ISettingsGroup[]): ISetting[] {
		const editableSettings = this._flatten(editableSettingsGroups);
		const defaultSettings = this._flatten(defaultSettingsGroups).filter(secondarySetting => !editableSettings.some(primarySetting => primarySetting.key === secondarySetting.key));
		return [...editableSettings, ...defaultSettings];
	}

	private _flatten(settingsGroups: ISettingsGroup[]): ISetting[] {
		const settings: ISetting[] = [];
397 398
		for (const group of settingsGroups) {
			for (const section of group.sections) {
399
				settings.push(...section.settings);
400 401
			}
		}
402
		return settings;
403
	}
404 405 406 407 408

	public dispose(): void {
		dispose(this._disposables);
		super.dispose();
	}
409 410
}

411
class SideBySidePreferencesWidget extends Widget {
412 413 414 415

	private dimension: Dimension;

	private defaultPreferencesEditor: DefaultPreferencesEditor;
S
Sandeep Somavarapu 已提交
416
	private editablePreferencesEditor: EditableSettingsEditor;
417 418 419
	private defaultPreferencesEditorContainer: HTMLElement;
	private editablePreferencesEditorContainer: HTMLElement;

S
Sandeep Somavarapu 已提交
420 421 422 423 424
	private _onFocus: Emitter<void> = new Emitter<void>();
	readonly onFocus: Event<void> = this._onFocus.event;

	private lastFocusedEditor: BaseEditor;

425 426
	private sash: VSash;

427
	constructor(parent: HTMLElement, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService) {
428 429 430 431 432 433 434 435 436 437
		super();
		this.create(parent);
	}

	private create(parentElement: HTMLElement): void {
		DOM.addClass(parentElement, 'side-by-side-preferences-editor');
		this.createSash(parentElement);

		this.defaultPreferencesEditorContainer = DOM.append(parentElement, DOM.$('.default-preferences-editor-container'));
		this.defaultPreferencesEditorContainer.style.position = 'absolute';
438
		this.defaultPreferencesEditor = this._register(this.instantiationService.createInstance(DefaultPreferencesEditor));
439
		this.defaultPreferencesEditor.create(new Builder(this.defaultPreferencesEditorContainer));
S
Sandeep Somavarapu 已提交
440
		this.defaultPreferencesEditor.setVisible(true);
S
Sandeep Somavarapu 已提交
441
		(<CodeEditor>this.defaultPreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.defaultPreferencesEditor);
442 443 444

		this.editablePreferencesEditorContainer = DOM.append(parentElement, DOM.$('.editable-preferences-editor-container'));
		this.editablePreferencesEditorContainer.style.position = 'absolute';
445
		this.editablePreferencesEditor = this._register(this.instantiationService.createInstance(EditableSettingsEditor));
446 447
		this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer));
		this.editablePreferencesEditor.setVisible(true);
S
Sandeep Somavarapu 已提交
448 449
		(<CodeEditor>this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor);
		this.lastFocusedEditor = this.editablePreferencesEditor;
450

451 452 453 454 455 456 457 458 459
		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 已提交
460 461 462

		const focusTracker = this._register(DOM.trackFocus(parentElement));
		this._register(focusTracker.addFocusListener(() => this._onFocus.fire()));
463 464
	}

465
	public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer<ISetting>, editablePreferencesRenderer: IPreferencesRenderer<ISetting> }> {
466 467 468 469
		this.dolayout(this.sash.getVerticalSashLeft());
		return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, toResource(editablePreferencesEditorInput), options),
		this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)])
			.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => ({ defaultPreferencesRenderer, editablePreferencesRenderer }));
470 471 472 473 474 475 476
	}

	public layout(dimension: Dimension): void {
		this.dimension = dimension;
		this.sash.setDimenesion(this.dimension);
	}

S
Sandeep Somavarapu 已提交
477
	public focus(): void {
S
Sandeep Somavarapu 已提交
478 479
		if (this.lastFocusedEditor) {
			this.lastFocusedEditor.focus();
S
Sandeep Somavarapu 已提交
480 481 482
		}
	}

483 484
	public getControl(): IEditorControl {
		return this.editablePreferencesEditor ? this.editablePreferencesEditor.getControl() : null;
485 486
	}

487 488 489 490 491 492
	public clearInput(): void {
		if (this.editablePreferencesEditor) {
			this.editablePreferencesEditor.clearInput();
		}
	}

S
Sandeep Somavarapu 已提交
493 494 495 496 497 498 499 500 501 502 503 504
	public setEditorVisible(visible: boolean, position: Position): void {
		if (this.editablePreferencesEditor) {
			this.editablePreferencesEditor.setVisible(visible, position);
		}
	}

	public changePosition(position: Position): void {
		if (this.editablePreferencesEditor) {
			this.editablePreferencesEditor.changePosition(position);
		}
	}

505
	private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions): TPromise<IPreferencesRenderer<ISetting>> {
S
Sandeep Somavarapu 已提交
506 507 508 509
		return editor.setInput(input, options)
			.then(() => (<CodeEditor>editor.getControl()).getContribution<ISettingsEditorContribution>(editorContributionId).createPreferencesRenderer(associatedPreferencesModelUri));
	}

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
	private createSash(parentElement: HTMLElement): void {
		this.sash = this._register(new VSash(parentElement, 220));
		this._register(this.sash.onPositionChange(position => this.dolayout(position)));
	}

	private dolayout(splitPoint: number): void {
		if (!this.editablePreferencesEditor || !this.dimension) {
			return;
		}
		const masterEditorWidth = this.dimension.width - splitPoint;
		const detailsEditorWidth = this.dimension.width - masterEditorWidth;

		this.defaultPreferencesEditorContainer.style.width = `${detailsEditorWidth}px`;
		this.defaultPreferencesEditorContainer.style.height = `${this.dimension.height}px`;
		this.defaultPreferencesEditorContainer.style.left = '0px';

		this.editablePreferencesEditorContainer.style.width = `${masterEditorWidth}px`;
		this.editablePreferencesEditorContainer.style.height = `${this.dimension.height}px`;
		this.editablePreferencesEditorContainer.style.left = `${splitPoint}px`;

		this.defaultPreferencesEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height));
		this.editablePreferencesEditor.layout(new Dimension(masterEditorWidth, this.dimension.height));
	}

	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 已提交
547
		super.dispose();
548 549 550
	}
}

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
export class EditableSettingsEditor extends BaseTextEditor {

	public static ID: string = 'workbench.editor.settingsEditor';

	private modelDisposables: IDisposable[] = [];

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IInstantiationService instantiationService: IInstantiationService,
		@IStorageService storageService: IStorageService,
		@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
		@IThemeService themeService: IThemeService,
		@IPreferencesService private preferencesService: IPreferencesService,
		@IModelService private modelService: IModelService,
		@IModeService modeService: IModeService,
		@ITextFileService textFileService: ITextFileService,
		@IEditorGroupService editorGroupService: IEditorGroupService
	) {
		super(EditableSettingsEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService);
		this._register({ dispose: () => dispose(this.modelDisposables) });
	}

	protected createEditor(parent: Builder): void {
		super.createEditor(parent);

		const codeEditor = getCodeEditor(this);
		if (codeEditor) {
			this._register(codeEditor.onDidChangeModel(() => this.onDidModelChange()));
		}
	}

	protected getAriaLabel(): string {
		const input = this.input;
		const inputName = input && input.getName();

		let ariaLabel: string;
		if (inputName) {
			ariaLabel = nls.localize('fileEditorWithInputAriaLabel', "{0}. Text file editor.", inputName);
		} else {
			ariaLabel = nls.localize('fileEditorAriaLabel', "Text file editor.");
		}

		return ariaLabel;
	}

	setInput(input: EditorInput, options: EditorOptions): TPromise<void> {
		return super.setInput(input, options)
			.then(() => this.input.resolve()
				.then(editorModel => editorModel.load())
				.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
	}

604
	clearInput(): void {
605
		this.modelDisposables = dispose(this.modelDisposables);
606
		super.clearInput();
607 608
	}

609 610 611 612 613 614 615 616 617 618 619
	private onDidModelChange(): void {
		this.modelDisposables = dispose(this.modelDisposables);
		const model = getCodeEditor(this).getModel();
		if (model) {
			this.preferencesService.createPreferencesEditorModel(model.uri)
				.then(preferencesEditorModel => {
					const settingsEditorModel = <SettingsEditorModel>preferencesEditorModel;
					this.modelDisposables.push(settingsEditorModel);
					this.modelDisposables.push(model.onDidChangeContent(() => settingsEditorModel.save()));
				});
		}
620 621 622
	}
}

S
Sandeep Somavarapu 已提交
623
export class DefaultPreferencesEditor extends BaseTextEditor {
624

S
Sandeep Somavarapu 已提交
625
	public static ID: string = 'workbench.editor.defaultPreferences';
626

S
Sandeep Somavarapu 已提交
627 628
	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
S
Sandeep Somavarapu 已提交
629
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
S
Sandeep Somavarapu 已提交
630 631
		@IInstantiationService instantiationService: IInstantiationService,
		@IStorageService storageService: IStorageService,
632
		@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
633
		@IThemeService themeService: IThemeService,
S
Sandeep Somavarapu 已提交
634 635
		@IPreferencesService private preferencesService: IPreferencesService,
		@IModelService private modelService: IModelService,
636
		@IModeService modeService: IModeService,
B
Benjamin Pasero 已提交
637 638
		@ITextFileService textFileService: ITextFileService,
		@IEditorGroupService editorGroupService: IEditorGroupService
S
Sandeep Somavarapu 已提交
639
	) {
B
Benjamin Pasero 已提交
640
		super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService);
S
Sandeep Somavarapu 已提交
641 642
	}

643
	public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor {
644
		return this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration);
645 646
	}

647
	protected getConfigurationOverrides(): IEditorOptions {
648
		const options = super.getConfigurationOverrides();
649
		options.readOnly = true;
650
		if (this.input) {
S
Sandeep Somavarapu 已提交
651 652 653 654 655
			options.lineNumbers = 'off';
			options.renderLineHighlight = 'none';
			options.scrollBeyondLastLine = false;
			options.folding = false;
			options.renderWhitespace = 'none';
656
			options.wordWrap = 'on';
S
Sandeep Somavarapu 已提交
657
			options.renderIndentGuides = false;
S
Sandeep Somavarapu 已提交
658
			options.rulers = [];
S
Sandeep Somavarapu 已提交
659
			options.glyphMargin = true;
S
Sandeep Somavarapu 已提交
660 661 662
			options.minimap = {
				enabled: false
			};
S
Sandeep Somavarapu 已提交
663
		}
S
Sandeep Somavarapu 已提交
664 665 666
		return options;
	}

S
Sandeep Somavarapu 已提交
667 668
	setInput(input: DefaultPreferencesEditorInput, options: EditorOptions): TPromise<void> {
		return super.setInput(input, options)
669
			.then(() => this.input.resolve()
S
Sandeep Somavarapu 已提交
670 671
				.then(editorModel => editorModel.load())
				.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
S
Sandeep Somavarapu 已提交
672 673 674
	}

	public layout(dimension: Dimension) {
675
		this.getControl().layout(dimension);
S
Sandeep Somavarapu 已提交
676 677
	}

678 679 680
	protected getAriaLabel(): string {
		return nls.localize('preferencesAriaLabel', "Default preferences. Readonly text editor.");
	}
S
Sandeep Somavarapu 已提交
681 682
}

S
Sandeep Somavarapu 已提交
683
class DefaultPreferencesCodeEditor extends CodeEditor {
S
Sandeep Somavarapu 已提交
684 685 686

	protected _getContributions(): IEditorContributionCtor[] {
		let contributions = super._getContributions();
S
Sandeep Somavarapu 已提交
687
		let skipContributions = [FoldingController.prototype, SelectionHighlighter.prototype, FindController.prototype];
S
Sandeep Somavarapu 已提交
688
		contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
689 690
		contributions.push(DefaultSettingsEditorContribution);
		return contributions;
S
Sandeep Somavarapu 已提交
691
	}
692

S
Sandeep Somavarapu 已提交
693 694 695 696 697
}

interface ISettingsEditorContribution extends editorCommon.IEditorContribution {

	createPreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>>;
S
Sandeep Somavarapu 已提交
698

S
Sandeep Somavarapu 已提交
699 700
}

S
Sandeep Somavarapu 已提交
701
class DefaultSettingsEditorContribution extends Disposable implements ISettingsEditorContribution {
S
Sandeep Somavarapu 已提交
702

S
Sandeep Somavarapu 已提交
703
	static ID: string = 'editor.contrib.defaultsettings';
S
Sandeep Somavarapu 已提交
704

S
Sandeep Somavarapu 已提交
705 706 707 708 709
	private preferencesRenderer: TPromise<IPreferencesRenderer<ISetting>>;

	constructor(private editor: ICodeEditor,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IPreferencesService private preferencesService: IPreferencesService
S
Sandeep Somavarapu 已提交
710 711
	) {
		super();
712
		this._register(this.editor.onDidChangeModel(() => this._onModelChanged()));
S
Sandeep Somavarapu 已提交
713 714
	}

S
Sandeep Somavarapu 已提交
715 716
	getId(): string {
		return DefaultSettingsEditorContribution.ID;
717
	}
S
Sandeep Somavarapu 已提交
718

S
Sandeep Somavarapu 已提交
719
	createPreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
720 721 722 723
		if (!this.preferencesRenderer) {
			return TPromise.as(null);
		}

S
Sandeep Somavarapu 已提交
724
		return this._hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri)
725
			.then(changed => changed ? this._updatePreferencesRenderer(associatedPreferencesModelUri) : this.preferencesRenderer);
726 727
	}

728 729 730 731
	private _onModelChanged(): void {
		const model = this.editor.getModel();
		if (model) {
			this.preferencesRenderer = this._createPreferencesRenderer();
732 733
		} else {
			this.disposePreferencesRenderer();
734 735 736
		}
	}

737 738 739 740 741 742 743 744
	private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise<boolean> {
		return this.preferencesRenderer.then(preferencesRenderer => {
			return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.fsPath === associatedPreferencesModelUri.fsPath);
		});
	}

	private _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>> {
		return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)
745 746
			.then(editorModel => {
				if (editorModel instanceof DefaultSettingsEditorModel) {
747
					const preferencesRenderer = this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel);
S
Sandeep Somavarapu 已提交
748
					preferencesRenderer.render();
749
					return preferencesRenderer;
S
Sandeep Somavarapu 已提交
750
				}
751
				return null;
752
			});
753
	}
754

755 756 757 758 759 760 761 762 763 764 765 766 767
	private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
		return this.preferencesService.createPreferencesEditorModel<ISetting>(associatedPreferencesModelUri)
			.then(associatedPreferencesEditorModel => {
				return this.preferencesRenderer.then(preferencesRenderer => {
					if (preferencesRenderer) {
						if (preferencesRenderer.associatedPreferencesModel) {
							preferencesRenderer.associatedPreferencesModel.dispose();
						}
						preferencesRenderer.associatedPreferencesModel = associatedPreferencesEditorModel;
					}
					return preferencesRenderer;
				});
			});
S
Sandeep Somavarapu 已提交
768 769
	}

770
	private disposePreferencesRenderer(): void {
S
Sandeep Somavarapu 已提交
771 772 773 774 775 776 777 778 779 780
		if (this.preferencesRenderer) {
			this.preferencesRenderer.then(preferencesRenderer => {
				if (preferencesRenderer) {
					if (preferencesRenderer.associatedPreferencesModel) {
						preferencesRenderer.associatedPreferencesModel.dispose();
					}
					preferencesRenderer.dispose();
				}
			});
		}
781 782 783 784
	}

	dispose() {
		this.disposePreferencesRenderer();
S
Sandeep Somavarapu 已提交
785
		super.dispose();
786
	}
787 788 789
}

@editorContribution
S
Sandeep Somavarapu 已提交
790
class SettingsEditorContribution extends Disposable implements ISettingsEditorContribution {
791 792 793

	static ID: string = 'editor.contrib.settings';

S
Sandeep Somavarapu 已提交
794 795 796 797 798 799 800 801 802
	private preferencesRenderer: TPromise<IPreferencesRenderer<ISetting>>;

	constructor(private editor: ICodeEditor,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IPreferencesService private preferencesService: IPreferencesService
	) {
		super();
	}

803 804 805 806
	getId(): string {
		return SettingsEditorContribution.ID;
	}

S
Sandeep Somavarapu 已提交
807 808 809
	createPreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
		this.disposePreferencesRenderer();
		this.preferencesRenderer = TPromise.join<any>([this.preferencesService.createPreferencesEditorModel(this.preferencesService.defaultSettingsResource), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)])
810 811 812 813 814 815 816 817
			.then(([defaultSettingsModel, settingsModel]) => {
				if (settingsModel instanceof SettingsEditorModel) {
					if (ConfigurationTarget.USER === settingsModel.configurationTarget) {
						return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
					}
					return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
				}
				return null;
S
Sandeep Somavarapu 已提交
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
			})
			.then(preferencesRenderer => {
				if (preferencesRenderer) {
					preferencesRenderer.render();
				}
				return preferencesRenderer;
			});
		return this.preferencesRenderer;
	}

	private disposePreferencesRenderer(): void {
		if (this.preferencesRenderer) {
			this.preferencesRenderer.then(preferencesRenderer => {
				if (preferencesRenderer) {
					preferencesRenderer.dispose();
				}
834
			});
S
Sandeep Somavarapu 已提交
835 836 837 838 839 840
		}
	}

	dispose() {
		this.disposePreferencesRenderer();
		super.dispose();
841 842 843
	}
}

S
Sandeep Somavarapu 已提交
844
abstract class SettingsCommand extends Command {
845

S
Sandeep Somavarapu 已提交
846
	protected getPreferencesEditor(accessor: ServicesAccessor): PreferencesEditor {
847
		const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
S
Sandeep Somavarapu 已提交
848 849
		if (activeEditor instanceof PreferencesEditor) {
			return activeEditor;
850 851
		}
		return null;
S
Sandeep Somavarapu 已提交
852

853
	}
S
Sandeep Somavarapu 已提交
854

855
}
S
Sandeep Somavarapu 已提交
856 857 858 859 860 861 862 863
class StartSearchDefaultSettingsCommand extends SettingsCommand {

	public runCommand(accessor: ServicesAccessor, args: any): void {
		const preferencesEditor = this.getPreferencesEditor(accessor);
		if (preferencesEditor) {
			preferencesEditor.focusSearch();
		}
	}
864

S
Sandeep Somavarapu 已提交
865
}
A
Alex Dima 已提交
866
const command = new StartSearchDefaultSettingsCommand({
S
Sandeep Somavarapu 已提交
867 868
	id: SETTINGS_EDITOR_COMMAND_SEARCH,
	precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR),
869
	kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F }
A
Alex Dima 已提交
870 871
});
KeybindingsRegistry.registerCommandAndKeybindingRule(command.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
S
Sandeep Somavarapu 已提交
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888

class FocusSettingsFileEditorCommand extends SettingsCommand {

	public runCommand(accessor: ServicesAccessor, args: any): void {
		const preferencesEditor = this.getPreferencesEditor(accessor);
		if (preferencesEditor) {
			preferencesEditor.focusSettingsFileEditor();
		}
	}

}
const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({
	id: SETTINGS_EDITOR_COMMAND_FOCUS_FILE,
	precondition: CONTEXT_SETTINGS_SEARCH_FOCUS,
	kbOpts: { primary: KeyCode.DownArrow }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFileEditorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));