editorService.ts 23.7 KB
Newer Older
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 { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
7
import { IResourceInput, ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor';
8
import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource } from 'vs/workbench/common/editor';
9 10 11 12 13 14 15
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput';
import { Registry } from 'vs/platform/registry/common/platform';
import { ResourceMap } from 'vs/base/common/map';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IFileService } from 'vs/platform/files/common/files';
import { Schemas } from 'vs/base/common/network';
J
Joao Moreno 已提交
16
import { Event, Emitter } from 'vs/base/common/event';
17
import { URI } from 'vs/base/common/uri';
B
Benjamin Pasero 已提交
18
import { basename } from 'vs/base/common/resources';
19 20
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { localize } from 'vs/nls';
21
import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
22
import { IResourceEditor, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService } from 'vs/workbench/services/editor/common/editorService';
23
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
24
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
25
import { coalesce } from 'vs/base/common/arrays';
26
import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
27
import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
I
isidor 已提交
28
import { ILabelService } from 'vs/platform/label/common/label';
29
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
M
Matt Bierner 已提交
30
import { withNullAsUndefined } from 'vs/base/common/types';
31

32
type ICachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput;
33

B
Benjamin Pasero 已提交
34
export class EditorService extends Disposable implements EditorServiceImpl {
35

36
	_serviceBrand: any;
37

38 39
	private static CACHE: ResourceMap<ICachedEditorInput> = new ResourceMap<ICachedEditorInput>();

40 41
	//#region events

M
Matt Bierner 已提交
42
	private readonly _onDidActiveEditorChange: Emitter<void> = this._register(new Emitter<void>());
43 44
	get onDidActiveEditorChange(): Event<void> { return this._onDidActiveEditorChange.event; }

M
Matt Bierner 已提交
45
	private readonly _onDidVisibleEditorsChange: Emitter<void> = this._register(new Emitter<void>());
46 47
	get onDidVisibleEditorsChange(): Event<void> { return this._onDidVisibleEditorsChange.event; }

M
Matt Bierner 已提交
48
	private readonly _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
B
Benjamin Pasero 已提交
49
	get onDidCloseEditor(): Event<IEditorCloseEvent> { return this._onDidCloseEditor.event; }
50

M
Matt Bierner 已提交
51
	private readonly _onDidOpenEditorFail: Emitter<IEditorIdentifier> = this._register(new Emitter<IEditorIdentifier>());
52
	get onDidOpenEditorFail(): Event<IEditorIdentifier> { return this._onDidOpenEditorFail.event; }
53

54 55
	//#endregion

56
	private fileInputFactory: IFileInputFactory;
57
	private openEditorHandlers: IOpenEditorOverrideHandler[] = [];
B
Benjamin Pasero 已提交
58

M
Matt Bierner 已提交
59
	private lastActiveEditor: IEditorInput | null;
B
Benjamin Pasero 已提交
60
	private lastActiveGroupId: GroupIdentifier;
61

62
	constructor(
63
		@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
64 65 66 67 68
		@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@ILabelService private readonly labelService: ILabelService,
		@IFileService private readonly fileService: IFileService,
		@IConfigurationService private readonly configurationService: IConfigurationService
69
	) {
70 71
		super();

72
		this.fileInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getFileInputFactory();
73 74 75 76 77

		this.registerListeners();
	}

	private registerListeners(): void {
78
		this.editorGroupService.whenRestored.then(() => this.onEditorsRestored());
79
		this.editorGroupService.onDidActiveGroupChange(group => this.handleActiveEditorChange(group));
80
		this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView));
81 82
	}

83 84 85 86 87 88 89 90 91 92 93 94
	private onEditorsRestored(): void {

		// Register listeners to each opened group
		this.editorGroupService.groups.forEach(group => this.registerGroupListeners(group as IEditorGroupView));

		// Fire initial set of editor events if there is an active editor
		if (this.activeEditor) {
			this.doEmitActiveEditorChangeEvent();
			this._onDidVisibleEditorsChange.fire();
		}
	}

95
	private handleActiveEditorChange(group: IEditorGroup): void {
96
		if (group !== this.editorGroupService.activeGroup) {
97
			return; // ignore if not the active group
98 99 100 101 102 103
		}

		if (!this.lastActiveEditor && !group.activeEditor) {
			return; // ignore if we still have no active editor
		}

B
Benjamin Pasero 已提交
104 105 106 107
		if (this.lastActiveGroupId === group.id && this.lastActiveEditor === group.activeEditor) {
			return; // ignore if the editor actually did not change
		}

108 109 110 111 112 113 114 115
		this.doEmitActiveEditorChangeEvent();
	}

	private doEmitActiveEditorChangeEvent(): void {
		const activeGroup = this.editorGroupService.activeGroup;

		this.lastActiveGroupId = activeGroup.id;
		this.lastActiveEditor = activeGroup.activeEditor;
116

117 118 119
		this._onDidActiveEditorChange.fire();
	}

120
	private registerGroupListeners(group: IEditorGroupView): void {
121 122
		const groupDisposeables: IDisposable[] = [];

123 124 125 126 127
		groupDisposeables.push(group.onDidGroupChange(e => {
			if (e.kind === GroupChangeKind.EDITOR_ACTIVE) {
				this.handleActiveEditorChange(group);
				this._onDidVisibleEditorsChange.fire();
			}
128 129
		}));

B
Benjamin Pasero 已提交
130 131
		groupDisposeables.push(group.onDidCloseEditor(event => {
			this._onDidCloseEditor.fire(event);
132 133
		}));

134 135
		groupDisposeables.push(group.onWillOpenEditor(event => {
			this.onGroupWillOpenEditor(group, event);
136 137 138
		}));

		groupDisposeables.push(group.onDidOpenEditorFail(editor => {
I
isidor 已提交
139
			this._onDidOpenEditorFail.fire({ editor, groupId: group.id });
140 141
		}));

J
Joao Moreno 已提交
142
		Event.once(group.onWillDispose)(() => {
143 144 145 146
			dispose(groupDisposeables);
		});
	}

147
	private onGroupWillOpenEditor(group: IEditorGroup, event: IEditorOpeningEvent): void {
148
		for (const handler of this.openEditorHandlers) {
149 150 151 152 153 154 155 156
			const result = handler(event.editor, event.options, group);
			if (result && result.override) {
				event.prevent((() => result.override));
				break;
			}
		}
	}

157
	get activeControl(): IVisibleEditor | undefined {
158
		const activeGroup = this.editorGroupService.activeGroup;
159

R
Rob Lourens 已提交
160
		return activeGroup ? activeGroup.activeControl : undefined;
161 162
	}

163
	get activeTextEditorWidget(): ICodeEditor | IDiffEditor | undefined {
164 165 166
		const activeControl = this.activeControl;
		if (activeControl) {
			const activeControlWidget = activeControl.getControl();
167
			if (isCodeEditor(activeControlWidget) || isDiffEditor(activeControlWidget)) {
168 169 170 171
				return activeControlWidget;
			}
		}

R
Rob Lourens 已提交
172
		return undefined;
173 174
	}

175 176
	get editors(): IEditorInput[] {
		const editors: IEditorInput[] = [];
177
		this.editorGroupService.groups.forEach(group => {
178 179 180 181 182 183
			editors.push(...group.editors);
		});

		return editors;
	}

184
	get activeEditor(): IEditorInput | undefined {
185
		const activeGroup = this.editorGroupService.activeGroup;
186

M
Matt Bierner 已提交
187
		return activeGroup ? withNullAsUndefined(activeGroup.activeEditor) : undefined;
188 189
	}

190
	get visibleControls(): IEditor[] {
191
		return coalesce(this.editorGroupService.groups.map(group => group.activeControl));
192 193
	}

194
	get visibleTextEditorWidgets(): Array<ICodeEditor | IDiffEditor> {
195
		return this.visibleControls.map(control => control.getControl() as ICodeEditor | IDiffEditor).filter(widget => isCodeEditor(widget) || isDiffEditor(widget));
196 197
	}

198
	get visibleEditors(): IEditorInput[] {
199
		return coalesce(this.editorGroupService.groups.map(group => group.activeEditor));
200 201
	}

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
	//#region preventOpenEditor()

	overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable {
		this.openEditorHandlers.push(handler);

		return toDisposable(() => {
			const index = this.openEditorHandlers.indexOf(handler);
			if (index >= 0) {
				this.openEditorHandlers.splice(index, 1);
			}
		});
	}

	//#endregion

217 218
	//#region openEditor()

M
Matt Bierner 已提交
219 220 221 222 223
	openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor | null>;
	openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<ITextEditor | null>;
	openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<ITextDiffEditor | null>;
	openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<ITextSideBySideEditor | null>;
	openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise<IEditor | null> {
224 225 226

		// Typed Editor Support
		if (editor instanceof EditorInput) {
227 228 229
			const editorOptions = this.toOptions(optionsOrGroup as IEditorOptions);
			const targetGroup = this.findTargetGroup(editor, editorOptions, group);

230
			return this.doOpenEditor(targetGroup, editor, editorOptions);
231 232 233 234 235 236
		}

		// Untyped Text Editor Support
		const textInput = <IResourceEditor>editor;
		const typedInput = this.createInput(textInput);
		if (typedInput) {
237
			const editorOptions = TextEditorOptions.from(textInput);
238
			const targetGroup = this.findTargetGroup(typedInput, editorOptions, optionsOrGroup as IEditorGroup | GroupIdentifier);
239

240
			return this.doOpenEditor(targetGroup, typedInput, editorOptions);
241 242
		}

B
Benjamin Pasero 已提交
243
		return Promise.resolve(null);
244
	}
245

M
Matt Bierner 已提交
246
	protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise<IEditor | null> {
247
		return group.openEditor(editor, options);
248 249
	}

250
	private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): IEditorGroup {
251
		let targetGroup: IEditorGroup | undefined;
252

B
Benjamin Pasero 已提交
253 254 255 256 257
		// Group: Instance of Group
		if (group && typeof group !== 'number') {
			return group;
		}

258
		// Group: Side by Side
B
Benjamin Pasero 已提交
259
		if (group === SIDE_GROUP) {
260
			targetGroup = this.findSideBySideGroup();
261 262
		}

263
		// Group: Specific Group
B
Benjamin Pasero 已提交
264
		else if (typeof group === 'number' && group >= 0) {
265
			targetGroup = this.editorGroupService.getGroup(group);
266 267
		}

268 269
		// Group: Unspecified without a specific index to open
		else if (!options || typeof options.index !== 'number') {
270
			const groupsByLastActive = this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE);
271 272 273

			// Respect option to reveal an editor if it is already visible in any group
			if (options && options.revealIfVisible) {
274
				for (const group of groupsByLastActive) {
275 276 277 278 279 280 281 282 283
					if (input.matches(group.activeEditor)) {
						targetGroup = group;
						break;
					}
				}
			}

			// Respect option to reveal an editor if it is open (not necessarily visible)
			if ((options && options.revealIfOpened) || this.configurationService.getValue<boolean>('workbench.editor.revealIfOpen')) {
284
				for (const group of groupsByLastActive) {
285 286 287 288 289 290 291 292 293
					if (group.isOpened(input)) {
						targetGroup = group;
						break;
					}
				}
			}
		}

		// Fallback to active group if target not valid
294
		if (!targetGroup) {
295
			targetGroup = this.editorGroupService.activeGroup;
296 297
		}

298 299 300
		return targetGroup;
	}

301
	private findSideBySideGroup(): IEditorGroup {
302
		const direction = preferredSideBySideGroupDirection(this.configurationService);
303

304
		let neighbourGroup = this.editorGroupService.findGroup({ direction });
305
		if (!neighbourGroup) {
306
			neighbourGroup = this.editorGroupService.addGroup(this.editorGroupService.activeGroup, direction);
307 308 309
		}

		return neighbourGroup;
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
	}

	private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions {
		if (!options || options instanceof EditorOptions) {
			return options as EditorOptions;
		}

		const textOptions: ITextEditorOptions = options;
		if (!!textOptions.selection) {
			return TextEditorOptions.create(options);
		}

		return EditorOptions.create(options);
	}

325 326
	//#endregion

327 328
	//#region openEditors()

J
Johannes Rieken 已提交
329 330
	openEditors(editors: IEditorInputWithOptions[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]>;
	openEditors(editors: IResourceEditor[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]>;
331
	openEditors(editors: Array<IEditorInputWithOptions | IResourceEditor>, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]> {
332 333 334 335 336 337 338 339 340 341 342 343

		// Convert to typed editors and options
		const typedEditors: IEditorInputWithOptions[] = [];
		editors.forEach(editor => {
			if (isEditorInputWithOptions(editor)) {
				typedEditors.push(editor);
			} else {
				typedEditors.push({ editor: this.createInput(editor), options: TextEditorOptions.from(editor) });
			}
		});

		// Find target groups to open
344
		const mapGroupToEditors = new Map<IEditorGroup, IEditorInputWithOptions[]>();
345
		if (group === SIDE_GROUP) {
346
			mapGroupToEditors.set(this.findSideBySideGroup(), typedEditors);
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
		} else {
			typedEditors.forEach(typedEditor => {
				const targetGroup = this.findTargetGroup(typedEditor.editor, typedEditor.options, group);

				let targetGroupEditors = mapGroupToEditors.get(targetGroup);
				if (!targetGroupEditors) {
					targetGroupEditors = [];
					mapGroupToEditors.set(targetGroup, targetGroupEditors);
				}

				targetGroupEditors.push(typedEditor);
			});
		}

		// Open in targets
J
Johannes Rieken 已提交
362
		const result: Promise<IEditor>[] = [];
363
		mapGroupToEditors.forEach((editorsWithOptions, group) => {
364
			result.push(group.openEditors(editorsWithOptions));
365
		});
366

B
Benjamin Pasero 已提交
367
		return Promise.all(result);
368 369 370 371
	}

	//#endregion

372 373
	//#region isOpen()

374
	isOpen(editor: IEditorInput | IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): boolean {
375 376 377 378 379 380 381
		return !!this.doGetOpened(editor);
	}

	//#endregion

	//#region getOpend()

M
Matt Bierner 已提交
382
	getOpened(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput | undefined {
383 384 385
		return this.doGetOpened(editor);
	}

386
	private doGetOpened(editor: IEditorInput | IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput | undefined {
387 388 389
		if (!(editor instanceof EditorInput)) {
			const resourceInput = editor as IResourceInput | IUntitledResourceInput;
			if (!resourceInput.resource) {
R
Rob Lourens 已提交
390
				return undefined; // we need a resource at least
391 392 393
			}
		}

394
		let groups: IEditorGroup[] = [];
B
Benjamin Pasero 已提交
395
		if (typeof group === 'number') {
396
			groups.push(this.editorGroupService.getGroup(group));
B
Benjamin Pasero 已提交
397 398 399
		} else if (group) {
			groups.push(group);
		} else {
400
			groups = [...this.editorGroupService.groups];
B
Benjamin Pasero 已提交
401 402
		}

403
		// For each editor group
404
		for (const group of groups) {
405 406

			// Typed editor
407
			if (editor instanceof EditorInput) {
408 409 410
				if (group.isOpened(editor)) {
					return editor;
				}
411 412
			}

413 414
			// Resource editor
			else {
415
				for (const editorInGroup of group.editors) {
416
					const resource = toResource(editorInGroup, { supportSideBySide: true });
B
Benjamin Pasero 已提交
417 418 419
					if (!resource) {
						continue; // need a resource to compare with
					}
420

421
					const resourceInput = editor as IResourceInput | IUntitledResourceInput;
422
					if (resourceInput.resource && resource.toString() === resourceInput.resource.toString()) {
423 424 425 426 427
						return editorInGroup;
					}
				}
			}
		}
428

R
Rob Lourens 已提交
429
		return undefined;
430 431 432 433
	}

	//#endregion

434 435
	//#region replaceEditors()

J
Johannes Rieken 已提交
436 437
	replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise<void>;
	replaceEditors(editors: IEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise<void>;
438
	replaceEditors(editors: Array<IEditorReplacement | IResourceEditorReplacement>, group: IEditorGroup | GroupIdentifier): Promise<void> {
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
		const typedEditors: IEditorReplacement[] = [];

		editors.forEach(replaceEditorArg => {
			if (replaceEditorArg.editor instanceof EditorInput) {
				typedEditors.push(replaceEditorArg as IEditorReplacement);
			} else {
				const editor = replaceEditorArg.editor as IResourceEditor;
				const typedEditor = this.createInput(editor);
				const replacementEditor = this.createInput(replaceEditorArg.replacement as IResourceEditor);

				typedEditors.push({
					editor: typedEditor,
					replacement: replacementEditor,
					options: this.toOptions(editor.options)
				});
			}
		});

457
		const targetGroup = typeof group === 'number' ? this.editorGroupService.getGroup(group) : group;
458 459 460 461 462
		return targetGroup.replaceEditors(typedEditors);
	}

	//#endregion

463 464 465
	//#region invokeWithinEditorContext()

	invokeWithinEditorContext<T>(fn: (accessor: ServicesAccessor) => T): T {
466 467 468
		const activeTextEditorWidget = this.activeTextEditorWidget;
		if (isCodeEditor(activeTextEditorWidget)) {
			return activeTextEditorWidget.invokeWithinContext(fn);
469 470
		}

471
		const activeGroup = this.editorGroupService.activeGroup;
472 473 474 475 476
		if (activeGroup) {
			return activeGroup.invokeWithinContext(fn);
		}

		return this.instantiationService.invokeFunction(fn);
477 478 479 480
	}

	//#endregion

481 482
	//#region createInput()

483
	createInput(input: IEditorInputWithOptions | IEditorInput | IResourceEditor): EditorInput | null {
484

485
		// Typed Editor Input Support (EditorInput)
486 487 488 489
		if (input instanceof EditorInput) {
			return input;
		}

490 491 492 493 494 495
		// Typed Editor Input Support (IEditorInputWithOptions)
		const editorInputWithOptions = input as IEditorInputWithOptions;
		if (editorInputWithOptions.editor instanceof EditorInput) {
			return editorInputWithOptions.editor;
		}

496 497 498
		// Side by Side Support
		const resourceSideBySideInput = <IResourceSideBySideInput>input;
		if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) {
B
Benjamin Pasero 已提交
499 500
			const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource, forceFile: resourceSideBySideInput.forceFile });
			const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource, forceFile: resourceSideBySideInput.forceFile });
501 502 503 504 505 506 507 508 509 510 511 512

			return new SideBySideEditorInput(
				resourceSideBySideInput.label || masterInput.getName(),
				typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(),
				detailInput,
				masterInput
			);
		}

		// Diff Editor Support
		const resourceDiffInput = <IResourceDiffInput>input;
		if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) {
B
Benjamin Pasero 已提交
513 514
			const leftInput = this.createInput({ resource: resourceDiffInput.leftResource, forceFile: resourceDiffInput.forceFile });
			const rightInput = this.createInput({ resource: resourceDiffInput.rightResource, forceFile: resourceDiffInput.forceFile });
515
			const label = resourceDiffInput.label || localize('compareLabels', "{0} ↔ {1}", this.toDiffLabel(leftInput), this.toDiffLabel(rightInput));
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535

			return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput);
		}

		// Untitled file support
		const untitledInput = <IUntitledResourceInput>input;
		if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === Schemas.untitled)) {
			return this.untitledEditorService.createOrGet(
				untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource,
				untitledInput.language,
				untitledInput.contents,
				untitledInput.encoding
			);
		}

		// Resource Editor Support
		const resourceInput = <IResourceInput>input;
		if (resourceInput.resource instanceof URI) {
			let label = resourceInput.label;
			if (!label && resourceInput.resource.scheme !== Schemas.data) {
B
Benjamin Pasero 已提交
536
				label = basename(resourceInput.resource); // derive the label from the path (but not for data URIs)
537 538
			}

B
Benjamin Pasero 已提交
539
			return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, resourceInput.forceFile) as EditorInput;
540 541 542 543 544
		}

		return null;
	}

B
Benjamin Pasero 已提交
545
	private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string, forceFile?: boolean): ICachedEditorInput {
546 547
		if (EditorService.CACHE.has(resource)) {
			const input = EditorService.CACHE.get(resource);
548 549 550 551 552 553 554 555 556 557 558 559 560
			if (input instanceof ResourceEditorInput) {
				input.setName(label);
				input.setDescription(description);
			} else if (!(input instanceof DataUriEditorInput)) {
				input.setPreferredEncoding(encoding);
			}

			return input;
		}

		let input: ICachedEditorInput;

		// File
B
Benjamin Pasero 已提交
561
		if (forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(resource)) {
562 563 564 565 566 567 568 569 570 571 572 573 574
			input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService);
		}

		// Data URI
		else if (resource.scheme === Schemas.data) {
			input = instantiationService.createInstance(DataUriEditorInput, label, description, resource);
		}

		// Resource
		else {
			input = instantiationService.createInstance(ResourceEditorInput, label, description, resource);
		}

575
		EditorService.CACHE.set(resource, input);
J
Joao Moreno 已提交
576
		Event.once(input.onDispose)(() => {
577
			EditorService.CACHE.delete(resource);
578 579 580 581 582
		});

		return input;
	}

583
	private toDiffLabel(input: EditorInput): string | null {
584 585 586
		const res = input.getResource();

		// Do not try to extract any paths from simple untitled editors
587
		if (res && res.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(res)) {
588 589 590 591
			return input.getName();
		}

		// Otherwise: for diff labels prefer to see the path as part of the label
592
		return this.labelService.getUriLabel(res, { relative: true });
593
	}
594 595

	//#endregion
596 597 598
}

export interface IEditorOpenHandler {
J
Johannes Rieken 已提交
599
	(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions): Promise<IEditor>;
600 601 602 603 604 605
}

/**
 * The delegating workbench editor service can be used to override the behaviour of the openEditor()
 * method by providing a IEditorOpenHandler.
 */
B
Benjamin Pasero 已提交
606
export class DelegatingEditorService extends EditorService {
607 608 609
	private editorOpenHandler: IEditorOpenHandler;

	constructor(
610
		@IEditorGroupsService editorGroupService: IEditorGroupsService,
611 612
		@IUntitledEditorService untitledEditorService: IUntitledEditorService,
		@IInstantiationService instantiationService: IInstantiationService,
I
isidor 已提交
613
		@ILabelService labelService: ILabelService,
614 615 616 617
		@IFileService fileService: IFileService,
		@IConfigurationService configurationService: IConfigurationService
	) {
		super(
618
			editorGroupService,
619 620
			untitledEditorService,
			instantiationService,
I
isidor 已提交
621
			labelService,
622 623 624 625 626 627 628 629 630
			fileService,
			configurationService
		);
	}

	setEditorOpenHandler(handler: IEditorOpenHandler): void {
		this.editorOpenHandler = handler;
	}

M
Matt Bierner 已提交
631
	protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise<IEditor | null> {
B
Benjamin Pasero 已提交
632 633 634
		if (!this.editorOpenHandler) {
			return super.doOpenEditor(group, editor, options);
		}
635

B
Benjamin Pasero 已提交
636
		return this.editorOpenHandler(group, editor, options).then(control => {
637
			if (control) {
B
Benjamin Pasero 已提交
638
				return control; // the opening was handled, so return early
639 640 641 642 643
			}

			return super.doOpenEditor(group, editor, options);
		});
	}
I
isidor 已提交
644
}
645 646

registerSingleton(IEditorService, EditorService);