editorControl.ts 7.9 KB
Newer Older
1
/*---------------------------------------------------------------------------------------------
J
Joao Moreno 已提交
2 3 4
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
5

B
Benjamin Pasero 已提交
6
import { dispose, Disposable, IDisposable } from 'vs/base/common/lifecycle';
7
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
8
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
9 10
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
11
import { IPartService } from 'vs/workbench/services/part/common/partService';
12 13
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
14
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
15
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
16
import { Event, Emitter } from 'vs/base/common/event';
17

18 19 20 21 22
export interface IOpenEditorResult {
	readonly control: BaseEditor;
	readonly editorChanged: boolean;
}

23
export class EditorControl extends Disposable {
24

B
Benjamin Pasero 已提交
25 26 27 28 29
	get minimumWidth() { return this._activeControl ? this._activeControl.minimumWidth : DEFAULT_EDITOR_MIN_DIMENSIONS.width; }
	get minimumHeight() { return this._activeControl ? this._activeControl.minimumHeight : DEFAULT_EDITOR_MIN_DIMENSIONS.height; }
	get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
	get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }

30 31 32
	private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
	get onDidFocus(): Event<void> { return this._onDidFocus.event; }

B
Benjamin Pasero 已提交
33 34
	private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; }>());
	get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return this._onDidSizeConstraintsChange.event; }
35

36 37
	private _activeControl: BaseEditor;
	private controls: BaseEditor[] = [];
38

B
Benjamin Pasero 已提交
39 40 41 42
	private activeControlDisposeables: IDisposable[] = [];
	private dimension: Dimension;
	private editorOperation: LongRunningOperation;

43 44
	constructor(
		private parent: HTMLElement,
45
		private groupView: IEditorGroupView,
46 47
		@IPartService private partService: IPartService,
		@IInstantiationService private instantiationService: IInstantiationService,
48
		@IProgressService progressService: IProgressService
49 50
	) {
		super();
51

52
		this.editorOperation = this._register(new LongRunningOperation(progressService));
53 54
	}

55 56
	get activeControl(): BaseEditor {
		return this._activeControl;
57 58
	}

59
	openEditor(editor: EditorInput, options?: EditorOptions): Thenable<IOpenEditorResult> {
60

61
		// Editor control
62
		const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editor);
B
Benjamin Pasero 已提交
63
		const control = this.doShowEditorControl(descriptor);
64

65
		// Set input
66
		return this.doSetInput(control, editor, options).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
67 68
	}

B
Benjamin Pasero 已提交
69
	private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor {
70

71 72 73
		// Return early if the currently active editor control can handle the input
		if (this._activeControl && descriptor.describes(this._activeControl)) {
			return this._activeControl;
74 75 76
		}

		// Hide active one first
77
		this.doHideActiveEditorControl();
78

79
		// Create editor
80
		const control = this.doCreateEditorControl(descriptor);
81

B
Benjamin Pasero 已提交
82
		// Set editor as active
B
Benjamin Pasero 已提交
83
		this.doSetActiveControl(control);
84

85
		// Show editor
86 87
		this.parent.appendChild(control.getContainer());
		show(control.getContainer());
88 89

		// Indicate to editor that it is now visible
90
		control.setVisible(true, this.groupView);
91 92

		// Layout
93 94 95
		if (this.dimension) {
			control.layout(this.dimension);
		}
96

97
		return control;
98 99
	}

100
	private doCreateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
101 102

		// Instantiate editor
103
		const control = this.doInstantiateEditorControl(descriptor);
104

105
		// Create editor container as needed
106 107
		if (!control.getContainer()) {
			const controlInstanceContainer = document.createElement('div');
108
			addClass(controlInstanceContainer, 'editor-instance');
109
			controlInstanceContainer.id = descriptor.getId();
110

111
			control.create(controlInstanceContainer);
112 113
		}

114
		return control;
115 116
	}

117
	private doInstantiateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
118 119

		// Return early if already instantiated
B
Benjamin Pasero 已提交
120
		const existingControl = this.controls.filter(control => descriptor.describes(control))[0];
121 122
		if (existingControl) {
			return existingControl;
123 124 125
		}

		// Otherwise instantiate new
126
		const control = this._register(descriptor.instantiate(this.instantiationService));
127
		this.controls.push(control);
128

129
		return control;
130 131
	}

B
Benjamin Pasero 已提交
132 133 134
	private doSetActiveControl(control: BaseEditor) {
		this._activeControl = control;

B
Benjamin Pasero 已提交
135 136 137 138
		// Clear out previous active control listeners
		this.activeControlDisposeables = dispose(this.activeControlDisposeables);

		// Listen to control changes
B
Benjamin Pasero 已提交
139
		if (control) {
B
Benjamin Pasero 已提交
140 141
			this.activeControlDisposeables.push(control.onDidSizeConstraintsChange(e => this._onDidSizeConstraintsChange.fire(e)));
			this.activeControlDisposeables.push(control.onDidFocus(() => this._onDidFocus.fire()));
B
Benjamin Pasero 已提交
142 143
		}

B
Benjamin Pasero 已提交
144
		// Indicate that size constraints could have changed due to new editor
B
Benjamin Pasero 已提交
145 146 147
		this._onDidSizeConstraintsChange.fire();
	}

148
	private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): Thenable<boolean> {
149

150 151
		// If the input did not change, return early and only apply the options
		// unless the options instruct us to force open it even if it is the same
152
		const forceReload = options && options.forceReload;
153
		const inputMatches = control.input && control.input.matches(editor);
154
		if (inputMatches && !forceReload) {
155 156

			// Forward options
157 158
			control.setOptions(options);

159 160 161 162 163 164
			// Still focus as needed
			const focus = !options || !options.preserveFocus;
			if (focus) {
				control.focus();
			}

B
Benjamin Pasero 已提交
165
			return Promise.resolve(false);
166 167
		}

168 169
		// Show progress while setting input after a certain timeout. If the workbench is opening
		// be more relaxed about progress showing by increasing the delay a little bit to reduce flicker.
B
Benjamin Pasero 已提交
170
		const operation = this.editorOperation.start(this.partService.isRestored() ? 800 : 3200);
171

172
		// Call into editor control
B
Benjamin Pasero 已提交
173
		const editorWillChange = !inputMatches;
B
Benjamin Pasero 已提交
174
		return control.setInput(editor, options, operation.token).then(() => {
175

176
			// Focus (unless prevented or another operation is running)
177
			if (operation.isCurrent()) {
178 179 180 181
				const focus = !options || !options.preserveFocus;
				if (focus) {
					control.focus();
				}
182
			}
183

184 185 186
			// Operation done
			operation.stop();

187
			return editorWillChange;
188 189
		}, e => {

190
			// Operation done
191
			operation.stop();
192

B
Benjamin Pasero 已提交
193
			return Promise.reject(e);
194 195 196
		});
	}

197
	private doHideActiveEditorControl(): void {
198
		if (!this._activeControl) {
199 200
			return;
		}
201

202 203 204
		// Stop any running operation
		this.editorOperation.stop();

205
		// Remove control from parent and hide
206 207 208
		const controlInstanceContainer = this._activeControl.getContainer();
		this.parent.removeChild(controlInstanceContainer);
		hide(controlInstanceContainer);
209

210 211
		// Indicate to editor control
		this._activeControl.clearInput();
212
		this._activeControl.setVisible(false, this.groupView);
213

214
		// Clear active control
B
Benjamin Pasero 已提交
215
		this.doSetActiveControl(null);
216 217
	}

218 219 220 221 222 223
	closeEditor(editor: EditorInput): void {
		if (this._activeControl && editor.matches(this._activeControl.input)) {
			this.doHideActiveEditorControl();
		}
	}

224 225
	layout(dimension: Dimension): void {
		this.dimension = dimension;
226

227
		if (this._activeControl && this.dimension) {
228
			this._activeControl.layout(this.dimension);
229 230 231
		}
	}

232
	dispose(): void {
B
Benjamin Pasero 已提交
233
		this.activeControlDisposeables = dispose(this.activeControlDisposeables);
234 235 236

		super.dispose();
	}
237
}