mainThreadEditors.ts 11.6 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

7
import URI from 'vs/base/common/uri';
J
Johannes Rieken 已提交
8
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
9
import { disposed } from 'vs/base/common/errors';
J
Johannes Rieken 已提交
10
import { TPromise } from 'vs/base/common/winjs.base';
11
import { ISingleEditOperation, IDecorationRenderOptions, IDecorationOptions, ILineChange, ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
12 13 14
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
15
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
16 17
import { MainThreadTextEditor } from './mainThreadEditor';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
J
Johannes Rieken 已提交
18
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
J
Johannes Rieken 已提交
19 20
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { equals as objectEquals } from 'vs/base/common/objects';
21
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext, IWorkspaceResourceEdit } from '../node/extHost.protocol';
22 23
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
24 25 26 27
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IFileService } from 'vs/platform/files/common/files';
import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { IModelService } from 'vs/editor/common/services/modelService';
28

29
export class MainThreadEditors implements MainThreadEditorsShape {
30

31
	private _proxy: ExtHostEditorsShape;
J
Johannes Rieken 已提交
32
	private _documentsAndEditors: MainThreadDocumentsAndEditors;
33 34 35 36 37
	private _workbenchEditorService: IWorkbenchEditorService;
	private _telemetryService: ITelemetryService;
	private _toDispose: IDisposable[];
	private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
	private _editorPositionData: ITextEditorPositionData;
38
	private _registeredDecorationTypes: { [decorationType: string]: boolean; };
E
Erich Gamma 已提交
39

40
	constructor(
J
Johannes Rieken 已提交
41
		documentsAndEditors: MainThreadDocumentsAndEditors,
42
		extHostContext: IExtHostContext,
J
Johannes Rieken 已提交
43
		@ICodeEditorService private _codeEditorService: ICodeEditorService,
44 45
		@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
		@IEditorGroupService editorGroupService: IEditorGroupService,
46 47 48 49
		@ITelemetryService telemetryService: ITelemetryService,
		@ITextModelService private readonly _textModelResolverService: ITextModelService,
		@IFileService private readonly _fileService: IFileService,
		@IModelService private readonly _modelService: IModelService,
50
	) {
51
		this._proxy = extHostContext.get(ExtHostContext.ExtHostEditors);
J
Johannes Rieken 已提交
52
		this._documentsAndEditors = documentsAndEditors;
53 54 55 56 57
		this._workbenchEditorService = workbenchEditorService;
		this._telemetryService = telemetryService;
		this._toDispose = [];
		this._textEditorsListenersMap = Object.create(null);
		this._editorPositionData = null;
E
Erich Gamma 已提交
58

J
Johannes Rieken 已提交
59 60
		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 已提交
61

62 63
		this._toDispose.push(editorGroupService.onEditorsChanged(() => this._updateActiveAndVisibleTextEditors()));
		this._toDispose.push(editorGroupService.onEditorsMoved(() => this._updateActiveAndVisibleTextEditors()));
64 65

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

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

80 81 82 83
	private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
		let id = textEditor.getId();
		let toDispose: IDisposable[] = [];
		toDispose.push(textEditor.onConfigurationChanged((opts) => {
84
			this._proxy.$acceptOptionsChanged(id, opts);
85
		}));
86 87
		toDispose.push(textEditor.onSelectionChanged((event) => {
			this._proxy.$acceptSelectionsChanged(id, event);
88
		}));
E
Erich Gamma 已提交
89

90
		this._textEditorsListenersMap[id] = toDispose;
E
Erich Gamma 已提交
91 92
	}

J
Johannes Rieken 已提交
93
	private _onTextEditorRemove(id: string): void {
94 95
		dispose(this._textEditorsListenersMap[id]);
		delete this._textEditorsListenersMap[id];
E
Erich Gamma 已提交
96 97
	}

98
	private _updateActiveAndVisibleTextEditors(): void {
E
Erich Gamma 已提交
99

100 101 102 103
		// editor columns
		let editorPositionData = this._getTextEditorPositionData();
		if (!objectEquals(this._editorPositionData, editorPositionData)) {
			this._editorPositionData = editorPositionData;
104
			this._proxy.$acceptEditorPositionData(this._editorPositionData);
E
Erich Gamma 已提交
105 106 107
		}
	}

108 109 110
	private _getTextEditorPositionData(): ITextEditorPositionData {
		let result: ITextEditorPositionData = Object.create(null);
		for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
J
Johannes Rieken 已提交
111 112 113
			const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
			if (id) {
				result[id] = workbenchEditor.position;
114
			}
E
Erich Gamma 已提交
115
		}
116
		return result;
E
Erich Gamma 已提交
117 118
	}

119
	// --- from extension host process
E
Erich Gamma 已提交
120

121
	$tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise<string> {
122
		const editorOptions: ITextEditorOptions = {
123
			preserveFocus: options.preserveFocus,
124 125
			pinned: options.pinned,
			selection: options.selection
126
		};
E
Erich Gamma 已提交
127

128 129
		const input = {
			resource,
130
			options: editorOptions
E
Erich Gamma 已提交
131 132
		};

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

141
	$tryShowEditor(id: string, position: EditorPosition): TPromise<void> {
142 143
		// check how often this is used
		this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.show' });
E
Erich Gamma 已提交
144

J
Johannes Rieken 已提交
145
		let mainThreadEditor = this._documentsAndEditors.getEditor(id);
146 147 148 149 150 151
		if (mainThreadEditor) {
			let model = mainThreadEditor.getModel();
			return this._workbenchEditorService.openEditor({
				resource: model.uri,
				options: { preserveFocus: false }
			}, position).then(() => { return; });
E
Erich Gamma 已提交
152
		}
M
Matt Bierner 已提交
153
		return undefined;
E
Erich Gamma 已提交
154 155
	}

156
	$tryHideEditor(id: string): TPromise<void> {
157 158 159
		// check how often this is used
		this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.hide' });

J
Johannes Rieken 已提交
160
		let mainThreadEditor = this._documentsAndEditors.getEditor(id);
161 162 163 164 165
		if (mainThreadEditor) {
			let editors = this._workbenchEditorService.getVisibleEditors();
			for (let editor of editors) {
				if (mainThreadEditor.matches(editor)) {
					return this._workbenchEditorService.closeEditor(editor.position, editor.input).then(() => { return; });
E
Erich Gamma 已提交
166 167 168
				}
			}
		}
M
Matt Bierner 已提交
169
		return undefined;
E
Erich Gamma 已提交
170 171
	}

172
	$trySetSelections(id: string, selections: ISelection[]): TPromise<any> {
J
Johannes Rieken 已提交
173
		if (!this._documentsAndEditors.getEditor(id)) {
174
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
E
Erich Gamma 已提交
175
		}
J
Johannes Rieken 已提交
176
		this._documentsAndEditors.getEditor(id).setSelections(selections);
177
		return TPromise.as(null);
E
Erich Gamma 已提交
178 179
	}

180
	$trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): TPromise<any> {
J
Johannes Rieken 已提交
181
		if (!this._documentsAndEditors.getEditor(id)) {
182
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
183
		}
J
Johannes Rieken 已提交
184
		this._documentsAndEditors.getEditor(id).setDecorations(key, ranges);
185
		return TPromise.as(null);
E
Erich Gamma 已提交
186 187
	}

188
	$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any> {
J
Johannes Rieken 已提交
189
		if (!this._documentsAndEditors.getEditor(id)) {
190
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
191
		}
J
Johannes Rieken 已提交
192
		this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
M
Matt Bierner 已提交
193
		return undefined;
E
Erich Gamma 已提交
194 195
	}

196
	$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any> {
J
Johannes Rieken 已提交
197
		if (!this._documentsAndEditors.getEditor(id)) {
198
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
E
Erich Gamma 已提交
199
		}
J
Johannes Rieken 已提交
200
		this._documentsAndEditors.getEditor(id).setConfiguration(options);
201
		return TPromise.as(null);
E
Erich Gamma 已提交
202 203
	}

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

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {

		// First check if loaded models were not changed in the meantime
		for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
			const workspaceResourceEdit = workspaceResourceEdits[i];
			if (workspaceResourceEdit.modelVersionId) {
				let model = this._modelService.getModel(workspaceResourceEdit.resource);
				if (model && model.getVersionId() !== workspaceResourceEdit.modelVersionId) {
					// model changed in the meantime
					return TPromise.as(false);
				}
			}
		}

		// Convert to shape expected by bulkEdit below
		let resourceEdits: IResourceEdit[] = [];
		for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
			const workspaceResourceEdit = workspaceResourceEdits[i];
			const uri = workspaceResourceEdit.resource;
			const edits = workspaceResourceEdit.edits;

			for (let j = 0, lenJ = edits.length; j < lenJ; j++) {
				const edit = edits[j];

				resourceEdits.push({
					resource: <URI>uri,
					newText: edit.newText,
					newEol: edit.newEol,
					range: edit.range
				});
			}
		}

		let codeEditor: ICommonCodeEditor;
		let editor = this._workbenchEditorService.getActiveEditor();
		if (editor) {
			let candidate = editor.getControl();
			if (isCommonCodeEditor(candidate)) {
				codeEditor = candidate;
			}
		}

		return bulkEdit(this._textModelResolverService, codeEditor, resourceEdits, this._fileService)
			.then(() => true);
	}

257
	$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
J
Johannes Rieken 已提交
258
		if (!this._documentsAndEditors.getEditor(id)) {
259
			return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
260
		}
J
Johannes Rieken 已提交
261
		return TPromise.as(this._documentsAndEditors.getEditor(id).insertSnippet(template, ranges, opts));
262 263
	}

264
	$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
265
		this._registeredDecorationTypes[key] = true;
J
Johannes Rieken 已提交
266
		this._codeEditorService.registerDecorationType(key, options);
E
Erich Gamma 已提交
267 268
	}

269
	$removeTextEditorDecorationType(key: string): void {
270
		delete this._registeredDecorationTypes[key];
J
Johannes Rieken 已提交
271
		this._codeEditorService.removeDecorationType(key);
272 273 274 275 276 277
	}

	$getDiffInformation(id: string): TPromise<ILineChange[]> {
		const editor = this._documentsAndEditors.getEditor(id);

		if (!editor) {
278
			return TPromise.wrapError<ILineChange[]>(new Error('No such TextEditor'));
279 280 281 282 283 284 285 286 287 288
		}

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

		if (!diffEditor) {
			return TPromise.as([]);
		}
J
Johannes Rieken 已提交
289

290
		return TPromise.as(diffEditor.getLineChanges());
E
Erich Gamma 已提交
291
	}
A
Alex Dima 已提交
292
}