nextEditorControl.ts 6.4 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 6 7

'use strict';

8
import { Disposable } from 'vs/base/common/lifecycle';
9
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
10
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
11 12 13
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
import { TPromise } from 'vs/base/common/winjs.base';
14
import { IPartService } from 'vs/workbench/services/part/common/partService';
15 16
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
17
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
18
import { toWinJsPromise } from 'vs/base/common/async';
19
import { INextEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
20

21 22 23 24 25
export interface IOpenEditorResult {
	readonly control: BaseEditor;
	readonly editorChanged: boolean;
}

26 27
export class NextEditorControl extends Disposable {
	private dimension: Dimension;
28
	private editorOperation: LongRunningOperation;
29

30 31
	private _activeControl: BaseEditor;
	private controls: BaseEditor[] = [];
32 33 34

	constructor(
		private parent: HTMLElement,
35
		private groupView: INextEditorGroupView,
36 37
		@IPartService private partService: IPartService,
		@IInstantiationService private instantiationService: IInstantiationService,
38
		@IProgressService progressService: IProgressService
39 40
	) {
		super();
41 42

		this.editorOperation = new LongRunningOperation(progressService);
43 44
	}

45 46
	get activeControl(): BaseEditor {
		return this._activeControl;
47 48
	}

49
	openEditor(editor: EditorInput, options?: EditorOptions): TPromise<IOpenEditorResult> {
50

51
		// Editor control
52 53 54
		const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editor);
		const control = this.doShowEditorControl(descriptor, options);

55
		// Set input
56
		return this.doSetInput(control, editor, options).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
57 58
	}

59
	private doShowEditorControl(descriptor: IEditorDescriptor, options: EditorOptions): BaseEditor {
60

61 62 63
		// Return early if the currently active editor control can handle the input
		if (this._activeControl && descriptor.describes(this._activeControl)) {
			return this._activeControl;
64 65 66
		}

		// Hide active one first
67
		this.doHideActiveEditorControl();
68

69
		// Create editor
70
		const control = this.doCreateEditorControl(descriptor);
71

72
		// Remember editor as active
73
		this._activeControl = control;
74

75
		// Show editor
76 77
		this.parent.appendChild(control.getContainer());
		show(control.getContainer());
78 79

		// Indicate to editor that it is now visible
80
		control.setVisible(true, this.groupView);
81 82

		// Layout
83 84 85
		if (this.dimension) {
			control.layout(this.dimension);
		}
86

87
		return control;
88 89
	}

90
	private doCreateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
91 92

		// Instantiate editor
93
		const control = this.doInstantiateEditorControl(descriptor);
94

95
		// Create editor container as needed
96 97
		if (!control.getContainer()) {
			const controlInstanceContainer = document.createElement('div');
98
			addClass(controlInstanceContainer, 'editor-instance');
99
			controlInstanceContainer.id = descriptor.getId();
100

101
			control.create(controlInstanceContainer);
102 103
		}

104
		return control;
105 106
	}

107
	private doInstantiateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
108 109

		// Return early if already instantiated
B
Benjamin Pasero 已提交
110
		const existingControl = this.controls.filter(control => descriptor.describes(control))[0];
111 112
		if (existingControl) {
			return existingControl;
113 114 115
		}

		// Otherwise instantiate new
116
		const control = this._register(descriptor.instantiate(this.instantiationService));
117
		this.controls.push(control);
118

119
		return control;
120 121
	}

122
	private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): TPromise<boolean> {
123

124 125 126 127 128
		// 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
		const forceOpen = options && options.forceOpen;
		const inputMatches = control.input && control.input.matches(editor);
		if (inputMatches && !forceOpen) {
129 130

			// Forward options
131 132
			control.setOptions(options);

133 134 135 136 137 138
			// Still focus as needed
			const focus = !options || !options.preserveFocus;
			if (focus) {
				control.focus();
			}

139 140 141
			return TPromise.as(false);
		}

142 143
		// 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.
144
		const operation = this.editorOperation.start(this.partService.isCreated() ? 800 : 3200);
145

146
		// Call into editor control
147
		const editorWillChange = !inputMatches || forceOpen;
148
		return toWinJsPromise(control.setInput(editor, options, operation.token)).then(() => {
149

150
			// Focus (unless prevented or another operation is running)
151
			if (operation.isCurrent()) {
152 153 154 155
				const focus = !options || !options.preserveFocus;
				if (focus) {
					control.focus();
				}
156
			}
157

158 159 160
			// Operation done
			operation.stop();

161
			return editorWillChange;
162 163
		}, e => {

164
			// Operation done
165
			operation.stop();
166

167
			return TPromise.wrapError(e);
168 169 170
		});
	}

171
	private doHideActiveEditorControl(): void {
172
		if (!this._activeControl) {
173 174
			return;
		}
175

176 177 178
		// Stop any running operation
		this.editorOperation.stop();

179
		// Remove control from parent and hide
180 181 182
		const controlInstanceContainer = this._activeControl.getContainer();
		this.parent.removeChild(controlInstanceContainer);
		hide(controlInstanceContainer);
183

184 185
		// Indicate to editor control
		this._activeControl.clearInput();
186
		this._activeControl.setVisible(false, this.groupView);
187

188 189
		// Clear active control
		this._activeControl = null;
190 191
	}

192 193 194 195 196 197
	closeEditor(editor: EditorInput): void {
		if (this._activeControl && editor.matches(this._activeControl.input)) {
			this.doHideActiveEditorControl();
		}
	}

198 199
	layout(dimension: Dimension): void {
		this.dimension = dimension;
200

201
		if (this._activeControl && this.dimension) {
202
			this._activeControl.layout(this.dimension);
203 204 205 206 207
		}
	}

	shutdown(): void {

208 209
		// Forward to all editor controls
		this.controls.forEach(editor => editor.shutdown());
210
	}
211
}