mainThreadEditors.ts 11.1 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;
E
Erich Gamma 已提交
38

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

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

61 62
		this._toDispose.push(editorGroupService.onEditorsChanged(() => this._updateActiveAndVisibleTextEditors()));
		this._toDispose.push(editorGroupService.onEditorsMoved(() => this._updateActiveAndVisibleTextEditors()));
E
Erich Gamma 已提交
63 64
	}

65 66 67 68 69 70
	public dispose(): void {
		Object.keys(this._textEditorsListenersMap).forEach((editorId) => {
			dispose(this._textEditorsListenersMap[editorId]);
		});
		this._textEditorsListenersMap = Object.create(null);
		this._toDispose = dispose(this._toDispose);
E
Erich Gamma 已提交
71 72
	}

73 74 75 76
	private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
		let id = textEditor.getId();
		let toDispose: IDisposable[] = [];
		toDispose.push(textEditor.onConfigurationChanged((opts) => {
77
			this._proxy.$acceptOptionsChanged(id, opts);
78
		}));
79 80
		toDispose.push(textEditor.onSelectionChanged((event) => {
			this._proxy.$acceptSelectionsChanged(id, event);
81
		}));
E
Erich Gamma 已提交
82

83
		this._textEditorsListenersMap[id] = toDispose;
E
Erich Gamma 已提交
84 85
	}

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

91
	private _updateActiveAndVisibleTextEditors(): void {
E
Erich Gamma 已提交
92

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

101 102 103
	private _getTextEditorPositionData(): ITextEditorPositionData {
		let result: ITextEditorPositionData = Object.create(null);
		for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
J
Johannes Rieken 已提交
104 105 106
			const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
			if (id) {
				result[id] = workbenchEditor.position;
107
			}
E
Erich Gamma 已提交
108
		}
109
		return result;
E
Erich Gamma 已提交
110 111
	}

112
	// --- from extension host process
E
Erich Gamma 已提交
113

114
	$tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise<string> {
115
		const editorOptions: ITextEditorOptions = {
116
			preserveFocus: options.preserveFocus,
117 118
			pinned: options.pinned,
			selection: options.selection
119
		};
E
Erich Gamma 已提交
120

121 122
		const input = {
			resource,
123
			options: editorOptions
E
Erich Gamma 已提交
124 125
		};

126
		return this._workbenchEditorService.openEditor(input, options.position).then(editor => {
127
			if (!editor) {
M
Matt Bierner 已提交
128
				return undefined;
E
Erich Gamma 已提交
129
			}
J
Johannes Rieken 已提交
130
			return this._documentsAndEditors.findTextEditorIdFor(editor);
E
Erich Gamma 已提交
131 132 133
		});
	}

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

J
Johannes Rieken 已提交
138
		let mainThreadEditor = this._documentsAndEditors.getEditor(id);
139 140 141 142 143 144
		if (mainThreadEditor) {
			let model = mainThreadEditor.getModel();
			return this._workbenchEditorService.openEditor({
				resource: model.uri,
				options: { preserveFocus: false }
			}, position).then(() => { return; });
E
Erich Gamma 已提交
145
		}
M
Matt Bierner 已提交
146
		return undefined;
E
Erich Gamma 已提交
147 148
	}

149
	$tryHideEditor(id: string): TPromise<void> {
150 151 152
		// check how often this is used
		this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.hide' });

J
Johannes Rieken 已提交
153
		let mainThreadEditor = this._documentsAndEditors.getEditor(id);
154 155 156 157 158
		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 已提交
159 160 161
				}
			}
		}
M
Matt Bierner 已提交
162
		return undefined;
E
Erich Gamma 已提交
163 164
	}

165
	$trySetSelections(id: string, selections: ISelection[]): TPromise<any> {
J
Johannes Rieken 已提交
166
		if (!this._documentsAndEditors.getEditor(id)) {
167
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
E
Erich Gamma 已提交
168
		}
J
Johannes Rieken 已提交
169
		this._documentsAndEditors.getEditor(id).setSelections(selections);
170
		return TPromise.as(null);
E
Erich Gamma 已提交
171 172
	}

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

181
	$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any> {
J
Johannes Rieken 已提交
182
		if (!this._documentsAndEditors.getEditor(id)) {
183
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
184
		}
J
Johannes Rieken 已提交
185
		this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
M
Matt Bierner 已提交
186
		return undefined;
E
Erich Gamma 已提交
187 188
	}

189
	$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any> {
J
Johannes Rieken 已提交
190
		if (!this._documentsAndEditors.getEditor(id)) {
191
			return TPromise.wrapError(disposed(`TextEditor(${id})`));
E
Erich Gamma 已提交
192
		}
J
Johannes Rieken 已提交
193
		this._documentsAndEditors.getEditor(id).setConfiguration(options);
194
		return TPromise.as(null);
E
Erich Gamma 已提交
195 196
	}

J
Johannes Rieken 已提交
197
	$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean> {
J
Johannes Rieken 已提交
198
		if (!this._documentsAndEditors.getEditor(id)) {
199
			return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
200
		}
J
Johannes Rieken 已提交
201
		return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
E
Erich Gamma 已提交
202 203
	}

204 205 206 207 208 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
	$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);
	}

250
	$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
J
Johannes Rieken 已提交
251
		if (!this._documentsAndEditors.getEditor(id)) {
252
			return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
253
		}
J
Johannes Rieken 已提交
254
		return TPromise.as(this._documentsAndEditors.getEditor(id).insertSnippet(template, ranges, opts));
255 256
	}

257
	$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
J
Johannes Rieken 已提交
258
		this._codeEditorService.registerDecorationType(key, options);
E
Erich Gamma 已提交
259 260
	}

261
	$removeTextEditorDecorationType(key: string): void {
J
Johannes Rieken 已提交
262
		this._codeEditorService.removeDecorationType(key);
263 264 265 266 267 268
	}

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

		if (!editor) {
269
			return TPromise.wrapError<ILineChange[]>(new Error('No such TextEditor'));
270 271 272 273 274 275 276 277 278 279
		}

		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 已提交
280

281
		return TPromise.as(diffEditor.getLineChanges());
E
Erich Gamma 已提交
282
	}
A
Alex Dima 已提交
283
}