editorControl.ts 8.2 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
import { IActiveEditor } from 'vs/workbench/services/editor/common/editorService';
18

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

24
export class EditorControl extends Disposable {
25

B
Benjamin Pasero 已提交
26 27 28 29 30
	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; }

M
Matt Bierner 已提交
31
	private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
32 33
	get onDidFocus(): Event<void> { return this._onDidFocus.event; }

M
Matt Bierner 已提交
34 35
	private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; } | undefined>());
	get onDidSizeConstraintsChange(): Event<{ width: number; height: number; } | undefined> { return this._onDidSizeConstraintsChange.event; }
36

M
Matt Bierner 已提交
37
	private _activeControl: BaseEditor | null;
38
	private controls: BaseEditor[] = [];
39

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

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

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

56 57
	get activeControl(): IActiveEditor | null {
		return this._activeControl as IActiveEditor | null;
58 59
	}

J
Johannes Rieken 已提交
60
	openEditor(editor: EditorInput, options?: EditorOptions): Promise<IOpenEditorResult> {
61

62
		// Editor control
63
		const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editor);
M
Matt Bierner 已提交
64 65 66
		if (!descriptor) {
			throw new Error('No editor descriptor found');
		}
B
Benjamin Pasero 已提交
67
		const control = this.doShowEditorControl(descriptor);
68

69
		// Set input
M
Matt Bierner 已提交
70
		return this.doSetInput(control, editor, options || null).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
71 72
	}

B
Benjamin Pasero 已提交
73
	private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor {
74

75 76 77
		// Return early if the currently active editor control can handle the input
		if (this._activeControl && descriptor.describes(this._activeControl)) {
			return this._activeControl;
78 79 80
		}

		// Hide active one first
81
		this.doHideActiveEditorControl();
82

83
		// Create editor
84
		const control = this.doCreateEditorControl(descriptor);
85

B
Benjamin Pasero 已提交
86
		// Set editor as active
B
Benjamin Pasero 已提交
87
		this.doSetActiveControl(control);
88

89
		// Show editor
90 91
		this.parent.appendChild(control.getContainer());
		show(control.getContainer());
92 93

		// Indicate to editor that it is now visible
94
		control.setVisible(true, this.groupView);
95 96

		// Layout
97 98 99
		if (this.dimension) {
			control.layout(this.dimension);
		}
100

101
		return control;
102 103
	}

104
	private doCreateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
105 106

		// Instantiate editor
107
		const control = this.doInstantiateEditorControl(descriptor);
108

109
		// Create editor container as needed
110 111
		if (!control.getContainer()) {
			const controlInstanceContainer = document.createElement('div');
112
			addClass(controlInstanceContainer, 'editor-instance');
113
			controlInstanceContainer.id = descriptor.getId();
114

115
			control.create(controlInstanceContainer);
116 117
		}

118
		return control;
119 120
	}

121
	private doInstantiateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
122 123

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

		// Otherwise instantiate new
130
		const control = this._register(descriptor.instantiate(this.instantiationService));
131
		this.controls.push(control);
132

133
		return control;
134 135
	}

M
Matt Bierner 已提交
136
	private doSetActiveControl(control: BaseEditor | null) {
B
Benjamin Pasero 已提交
137 138
		this._activeControl = control;

B
Benjamin Pasero 已提交
139 140 141 142
		// Clear out previous active control listeners
		this.activeControlDisposeables = dispose(this.activeControlDisposeables);

		// Listen to control changes
B
Benjamin Pasero 已提交
143
		if (control) {
B
Benjamin Pasero 已提交
144 145
			this.activeControlDisposeables.push(control.onDidSizeConstraintsChange(e => this._onDidSizeConstraintsChange.fire(e)));
			this.activeControlDisposeables.push(control.onDidFocus(() => this._onDidFocus.fire()));
B
Benjamin Pasero 已提交
146 147
		}

B
Benjamin Pasero 已提交
148
		// Indicate that size constraints could have changed due to new editor
149
		this._onDidSizeConstraintsChange.fire(undefined);
B
Benjamin Pasero 已提交
150 151
	}

M
Matt Bierner 已提交
152
	private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions | null): Promise<boolean> {
153

154 155
		// 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
156
		const forceReload = options && options.forceReload;
157
		const inputMatches = control.input && control.input.matches(editor);
158
		if (inputMatches && !forceReload) {
159 160

			// Forward options
161 162
			control.setOptions(options);

163 164 165 166 167 168
			// Still focus as needed
			const focus = !options || !options.preserveFocus;
			if (focus) {
				control.focus();
			}

B
Benjamin Pasero 已提交
169
			return Promise.resolve(false);
170 171
		}

172 173
		// 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 已提交
174
		const operation = this.editorOperation.start(this.partService.isRestored() ? 800 : 3200);
175

176
		// Call into editor control
B
Benjamin Pasero 已提交
177
		const editorWillChange = !inputMatches;
B
Benjamin Pasero 已提交
178
		return control.setInput(editor, options, operation.token).then(() => {
179

180
			// Focus (unless prevented or another operation is running)
181
			if (operation.isCurrent()) {
182 183 184 185
				const focus = !options || !options.preserveFocus;
				if (focus) {
					control.focus();
				}
186
			}
187

188 189 190
			// Operation done
			operation.stop();

191
			return editorWillChange;
192 193
		}, e => {

194
			// Operation done
195
			operation.stop();
196

B
Benjamin Pasero 已提交
197
			return Promise.reject(e);
198 199 200
		});
	}

201
	private doHideActiveEditorControl(): void {
202
		if (!this._activeControl) {
203 204
			return;
		}
205

206 207 208
		// Stop any running operation
		this.editorOperation.stop();

209
		// Remove control from parent and hide
210 211 212
		const controlInstanceContainer = this._activeControl.getContainer();
		this.parent.removeChild(controlInstanceContainer);
		hide(controlInstanceContainer);
213

214 215
		// Indicate to editor control
		this._activeControl.clearInput();
216
		this._activeControl.setVisible(false, this.groupView);
217

218
		// Clear active control
B
Benjamin Pasero 已提交
219
		this.doSetActiveControl(null);
220 221
	}

222 223 224 225 226 227
	closeEditor(editor: EditorInput): void {
		if (this._activeControl && editor.matches(this._activeControl.input)) {
			this.doHideActiveEditorControl();
		}
	}

228 229
	layout(dimension: Dimension): void {
		this.dimension = dimension;
230

231
		if (this._activeControl && this.dimension) {
232
			this._activeControl.layout(this.dimension);
233 234 235
		}
	}

236
	dispose(): void {
B
Benjamin Pasero 已提交
237
		this.activeControlDisposeables = dispose(this.activeControlDisposeables);
238 239 240

		super.dispose();
	}
241
}