mainThreadEditors.ts 12.7 KB
Newer Older
E
Erich Gamma 已提交
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.
 *--------------------------------------------------------------------------------------------*/

6
import { localize } from 'vs/nls';
7
import { disposed } from 'vs/base/common/errors';
8 9
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { equals as objectEquals } from 'vs/base/common/objects';
10
import { URI, UriComponents } from 'vs/base/common/uri';
11
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
12
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
13 14 15 16
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
17
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
A
Alex Dima 已提交
18 19 20 21 22 23 24
import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors';
import { MainThreadTextEditor } from 'vs/workbench/api/electron-browser/mainThreadEditor';
import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor';
25
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
26
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
J
Joao Moreno 已提交
27
import { IURLService } from 'vs/platform/url/common/url';
28
import product from 'vs/platform/product/node/product';
29

30
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
31

A
Alex Dima 已提交
32 33
	private static INSTANCE_COUNT: number = 0;

34 35 36
	private readonly _instanceId: string;
	private readonly _proxy: ExtHostEditorsShape;
	private readonly _documentsAndEditors: MainThreadDocumentsAndEditors;
37 38
	private _toDispose: IDisposable[];
	private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
M
Matt Bierner 已提交
39
	private _editorPositionData: ITextEditorPositionData | null;
40
	private _registeredDecorationTypes: { [decorationType: string]: boolean; };
E
Erich Gamma 已提交
41

42
	constructor(
J
Johannes Rieken 已提交
43
		documentsAndEditors: MainThreadDocumentsAndEditors,
44
		extHostContext: IExtHostContext,
45
		@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
46
		@IBulkEditService private readonly _bulkEditService: IBulkEditService,
47 48
		@IEditorService private readonly _editorService: IEditorService,
		@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService
49
	) {
A
Alex Dima 已提交
50
		this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT);
51
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors);
J
Johannes Rieken 已提交
52
		this._documentsAndEditors = documentsAndEditors;
53 54 55
		this._toDispose = [];
		this._textEditorsListenersMap = Object.create(null);
		this._editorPositionData = null;
E
Erich Gamma 已提交
56

J
Johannes Rieken 已提交
57 58
		this._toDispose.push(documentsAndEditors.onTextEditorAdd(editors => editors.forEach(this._onTextEditorAdd, this)));
		this._toDispose.push(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
E
Erich Gamma 已提交
59

60
		this._toDispose.push(this._editorService.onDidVisibleEditorsChange(() => this._updateActiveAndVisibleTextEditors()));
61
		this._toDispose.push(this._editorGroupService.onDidRemoveGroup(() => this._updateActiveAndVisibleTextEditors()));
62
		this._toDispose.push(this._editorGroupService.onDidMoveGroup(() => this._updateActiveAndVisibleTextEditors()));
63 64

		this._registeredDecorationTypes = Object.create(null);
E
Erich Gamma 已提交
65 66
	}

67 68 69 70 71 72
	public dispose(): void {
		Object.keys(this._textEditorsListenersMap).forEach((editorId) => {
			dispose(this._textEditorsListenersMap[editorId]);
		});
		this._textEditorsListenersMap = Object.create(null);
		this._toDispose = dispose(this._toDispose);
73 74 75 76
		for (let decorationType in this._registeredDecorationTypes) {
			this._codeEditorService.removeDecorationType(decorationType);
		}
		this._registeredDecorationTypes = Object.create(null);
E
Erich Gamma 已提交
77 78
	}

79
	private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
80 81
		const id = textEditor.getId();
		const toDispose: IDisposable[] = [];
82 83
		toDispose.push(textEditor.onPropertiesChanged((data) => {
			this._proxy.$acceptEditorPropertiesChanged(id, data);
84
		}));
E
Erich Gamma 已提交
85

86
		this._textEditorsListenersMap[id] = toDispose;
E
Erich Gamma 已提交
87 88
	}

J
Johannes Rieken 已提交
89
	private _onTextEditorRemove(id: string): void {
90 91
		dispose(this._textEditorsListenersMap[id]);
		delete this._textEditorsListenersMap[id];
E
Erich Gamma 已提交
92 93
	}

94
	private _updateActiveAndVisibleTextEditors(): void {
E
Erich Gamma 已提交
95

96
		// editor columns
97
		const editorPositionData = this._getTextEditorPositionData();
98 99
		if (!objectEquals(this._editorPositionData, editorPositionData)) {
			this._editorPositionData = editorPositionData;
100
			this._proxy.$acceptEditorPositionData(this._editorPositionData);
E
Erich Gamma 已提交
101 102 103
		}
	}

104
	private _getTextEditorPositionData(): ITextEditorPositionData {
105
		const result: ITextEditorPositionData = Object.create(null);
106
		for (let workbenchEditor of this._editorService.visibleControls) {
J
Johannes Rieken 已提交
107 108
			const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
			if (id) {
109
				result[id] = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group);
110
			}
E
Erich Gamma 已提交
111
		}
112
		return result;
E
Erich Gamma 已提交
113 114
	}

115
	// --- from extension host process
E
Erich Gamma 已提交
116

J
Johannes Rieken 已提交
117
	$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string> {
A
Alex Dima 已提交
118 119
		const uri = URI.revive(resource);

120
		const editorOptions: ITextEditorOptions = {
121
			preserveFocus: options.preserveFocus,
122 123
			pinned: options.pinned,
			selection: options.selection
124
		};
E
Erich Gamma 已提交
125

126
		const input = {
A
Alex Dima 已提交
127
			resource: uri,
128
			options: editorOptions
E
Erich Gamma 已提交
129 130
		};

131
		return this._editorService.openEditor(input, viewColumnToEditorGroup(this._editorGroupService, options.position)).then(editor => {
132
			if (!editor) {
M
Matt Bierner 已提交
133
				return undefined;
E
Erich Gamma 已提交
134
			}
J
Johannes Rieken 已提交
135
			return this._documentsAndEditors.findTextEditorIdFor(editor);
E
Erich Gamma 已提交
136 137 138
		});
	}

J
Johannes Rieken 已提交
139
	$tryShowEditor(id: string, position?: EditorViewColumn): Promise<void> {
140
		const mainThreadEditor = this._documentsAndEditors.getEditor(id);
141
		if (mainThreadEditor) {
142
			const model = mainThreadEditor.getModel();
143
			return this._editorService.openEditor({
144 145
				resource: model.uri,
				options: { preserveFocus: false }
146
			}, viewColumnToEditorGroup(this._editorGroupService, position)).then(() => { return; });
E
Erich Gamma 已提交
147
		}
M
Matt Bierner 已提交
148
		return Promise.resolve();
E
Erich Gamma 已提交
149 150
	}

J
Johannes Rieken 已提交
151
	$tryHideEditor(id: string): Promise<void> {
152
		const mainThreadEditor = this._documentsAndEditors.getEditor(id);
153
		if (mainThreadEditor) {
154
			const editors = this._editorService.visibleControls;
155 156
			for (let editor of editors) {
				if (mainThreadEditor.matches(editor)) {
157
					return editor.group.closeEditor(editor.input).then(() => { return; });
E
Erich Gamma 已提交
158 159 160
				}
			}
		}
M
Matt Bierner 已提交
161
		return Promise.resolve();
E
Erich Gamma 已提交
162 163
	}

J
Johannes Rieken 已提交
164
	$trySetSelections(id: string, selections: ISelection[]): Promise<void> {
J
Johannes Rieken 已提交
165
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
166
			return Promise.reject(disposed(`TextEditor(${id})`));
E
Erich Gamma 已提交
167
		}
J
Johannes Rieken 已提交
168
		this._documentsAndEditors.getEditor(id).setSelections(selections);
R
Rob Lourens 已提交
169
		return Promise.resolve(undefined);
E
Erich Gamma 已提交
170 171
	}

J
Johannes Rieken 已提交
172
	$trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): Promise<void> {
A
Alex Dima 已提交
173
		key = `${this._instanceId}-${key}`;
J
Johannes Rieken 已提交
174
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
175
			return Promise.reject(disposed(`TextEditor(${id})`));
176
		}
J
Johannes Rieken 已提交
177
		this._documentsAndEditors.getEditor(id).setDecorations(key, ranges);
R
Rob Lourens 已提交
178
		return Promise.resolve(undefined);
E
Erich Gamma 已提交
179 180
	}

J
Johannes Rieken 已提交
181
	$trySetDecorationsFast(id: string, key: string, ranges: number[]): Promise<void> {
A
Alex Dima 已提交
182
		key = `${this._instanceId}-${key}`;
183
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
184
			return Promise.reject(disposed(`TextEditor(${id})`));
185
		}
186
		this._documentsAndEditors.getEditor(id).setDecorationsFast(key, ranges);
R
Rob Lourens 已提交
187
		return Promise.resolve(undefined);
188 189
	}

J
Johannes Rieken 已提交
190
	$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise<void> {
J
Johannes Rieken 已提交
191
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
192
			return Promise.reject(disposed(`TextEditor(${id})`));
193
		}
J
Johannes Rieken 已提交
194
		this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
M
Matt Bierner 已提交
195
		return Promise.resolve();
E
Erich Gamma 已提交
196 197
	}

J
Johannes Rieken 已提交
198
	$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise<void> {
J
Johannes Rieken 已提交
199
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
200
			return Promise.reject(disposed(`TextEditor(${id})`));
E
Erich Gamma 已提交
201
		}
J
Johannes Rieken 已提交
202
		this._documentsAndEditors.getEditor(id).setConfiguration(options);
R
Rob Lourens 已提交
203
		return Promise.resolve(undefined);
E
Erich Gamma 已提交
204 205
	}

J
Johannes Rieken 已提交
206
	$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise<boolean> {
J
Johannes Rieken 已提交
207
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
208
			return Promise.reject(disposed(`TextEditor(${id})`));
209
		}
J
Johannes Rieken 已提交
210
		return Promise.resolve(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
E
Erich Gamma 已提交
211 212
	}

J
Johannes Rieken 已提交
213
	$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): Promise<boolean> {
214
		const { edits } = reviveWorkspaceEditDto(dto);
215
		return this._bulkEditService.apply({ edits }, undefined).then(() => true, err => false);
216 217
	}

J
Johannes Rieken 已提交
218
	$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): Promise<boolean> {
J
Johannes Rieken 已提交
219
		if (!this._documentsAndEditors.getEditor(id)) {
J
Johannes Rieken 已提交
220
			return Promise.reject(disposed(`TextEditor(${id})`));
221
		}
J
Johannes Rieken 已提交
222
		return Promise.resolve(this._documentsAndEditors.getEditor(id).insertSnippet(template, ranges, opts));
223 224
	}

225
	$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
A
Alex Dima 已提交
226
		key = `${this._instanceId}-${key}`;
227
		this._registeredDecorationTypes[key] = true;
J
Johannes Rieken 已提交
228
		this._codeEditorService.registerDecorationType(key, options);
E
Erich Gamma 已提交
229 230
	}

231
	$removeTextEditorDecorationType(key: string): void {
A
Alex Dima 已提交
232
		key = `${this._instanceId}-${key}`;
233
		delete this._registeredDecorationTypes[key];
J
Johannes Rieken 已提交
234
		this._codeEditorService.removeDecorationType(key);
235 236
	}

J
Johannes Rieken 已提交
237
	$getDiffInformation(id: string): Promise<ILineChange[]> {
238 239 240
		const editor = this._documentsAndEditors.getEditor(id);

		if (!editor) {
J
Johannes Rieken 已提交
241
			return Promise.reject(new Error('No such TextEditor'));
242 243 244 245 246 247 248
		}

		const codeEditor = editor.getCodeEditor();
		const codeEditorId = codeEditor.getId();
		const diffEditors = this._codeEditorService.listDiffEditors();
		const [diffEditor] = diffEditors.filter(d => d.getOriginalEditor().getId() === codeEditorId || d.getModifiedEditor().getId() === codeEditorId);

J
Joao Moreno 已提交
249
		if (diffEditor) {
J
Johannes Rieken 已提交
250
			return Promise.resolve(diffEditor.getLineChanges());
251
		}
J
Johannes Rieken 已提交
252

J
Joao Moreno 已提交
253 254 255
		const dirtyDiffContribution = codeEditor.getContribution('editor.contrib.dirtydiff');

		if (dirtyDiffContribution) {
J
Johannes Rieken 已提交
256
			return Promise.resolve((dirtyDiffContribution as any).getChanges());
J
Joao Moreno 已提交
257 258
		}

J
Johannes Rieken 已提交
259
		return Promise.resolve([]);
E
Erich Gamma 已提交
260
	}
A
Alex Dima 已提交
261
}
262

263 264
// --- commands

J
Joao Moreno 已提交
265
CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) {
266 267
	const editorService = accessor.get(IEditorService);
	const editorGroupService = accessor.get(IEditorGroupsService);
J
Johannes Rieken 已提交
268
	const openerService = accessor.get(IOpenerService);
J
Joao Moreno 已提交
269
	const urlService = accessor.get(IURLService);
270

J
Joao Moreno 已提交
271
	const [resource, options, position, label] = args;
272

B
Benjamin Pasero 已提交
273 274
	if (options || typeof position === 'number') {
		// use editor options or editor view column as a hint to use the editor service for opening
R
Rob Lourens 已提交
275
		return editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)).then(_ => undefined);
J
Johannes Rieken 已提交
276
	}
B
Benjamin Pasero 已提交
277 278 279

	if (resource && resource.scheme === 'command') {
		// do not allow to execute commands from here
R
Rob Lourens 已提交
280
		return Promise.resolve(undefined);
B
Benjamin Pasero 已提交
281 282
	}

J
Joao Moreno 已提交
283
	if (resource && (resource.scheme === product.urlProtocol || /^vscode/.test(resource.scheme))) {
R
Rob Lourens 已提交
284
		return urlService.open(resource).then(_ => undefined);
J
Joao Moreno 已提交
285 286
	}

B
Benjamin Pasero 已提交
287
	// finally, delegate to opener service
R
Rob Lourens 已提交
288
	return openerService.open(resource).then(_ => undefined);
289 290
});

B
Benjamin Pasero 已提交
291

292
CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	const editorService = accessor.get(IEditorService);
	const editorGroupService = accessor.get(IEditorGroupsService);

	let [leftResource, rightResource, label, description, options, position] = args;

	if (!options || typeof options !== 'object') {
		options = {
			preserveFocus: false
		};
	}

	if (!label) {
		label = localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true));
	}

R
Rob Lourens 已提交
308
	return editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)).then(() => undefined);
J
Johannes Rieken 已提交
309
});