editorGroup.ts 19.6 KB
Newer Older
B
Benjamin Pasero 已提交
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.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import { Event, Emitter } from 'vs/base/common/event';
B
Benjamin Pasero 已提交
7
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
8
import { URI } from 'vs/base/common/uri';
J
Johannes Rieken 已提交
9
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
10
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
11
import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
12
import { Registry } from 'vs/platform/registry/common/platform';
B
wip  
Benjamin Pasero 已提交
13
import { ResourceMap } from 'vs/base/common/map';
M
Matt Bierner 已提交
14
import { coalesce } from 'vs/base/common/arrays';
B
Benjamin Pasero 已提交
15

B
Benjamin Pasero 已提交
16 17 18 19 20 21 22
const EditorOpenPositioning = {
	LEFT: 'left',
	RIGHT: 'right',
	FIRST: 'first',
	LAST: 'last'
};

B
Benjamin Pasero 已提交
23
export interface EditorCloseEvent extends IEditorCloseEvent {
24 25 26
	editor: EditorInput;
}

27
export interface EditorIdentifier extends IEditorIdentifier {
I
isidor 已提交
28
	groupId: GroupIdentifier;
29
	editor: EditorInput;
30 31
}

B
Benjamin Pasero 已提交
32 33 34
export interface IEditorOpenOptions {
	pinned?: boolean;
	active?: boolean;
B
Benjamin Pasero 已提交
35
	index?: number;
B
Benjamin Pasero 已提交
36 37
}

38
export interface ISerializedEditorInput {
39 40 41 42
	id: string;
	value: string;
}

43
export interface ISerializedEditorGroup {
44
	id: number;
45 46
	editors: ISerializedEditorInput[];
	mru: number[];
M
Matt Bierner 已提交
47
	preview?: number;
48 49
}

50
export function isSerializedEditorGroup(obj?: any): obj is ISerializedEditorGroup {
51
	const group: ISerializedEditorGroup = obj;
52 53 54 55

	return obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru);
}

B
Benjamin Pasero 已提交
56
export class EditorGroup extends Disposable {
57 58 59

	private static IDS = 0;

60 61 62
	//#region events

	private readonly _onDidEditorActivate = this._register(new Emitter<EditorInput>());
63
	readonly onDidEditorActivate = this._onDidEditorActivate.event;
64 65

	private readonly _onDidEditorOpen = this._register(new Emitter<EditorInput>());
66
	readonly onDidEditorOpen = this._onDidEditorOpen.event;
67 68

	private readonly _onDidEditorClose = this._register(new Emitter<EditorCloseEvent>());
69
	readonly onDidEditorClose = this._onDidEditorClose.event;
70 71

	private readonly _onDidEditorDispose = this._register(new Emitter<EditorInput>());
72
	readonly onDidEditorDispose = this._onDidEditorDispose.event;
73 74

	private readonly _onDidEditorBecomeDirty = this._register(new Emitter<EditorInput>());
75
	readonly onDidEditorBecomeDirty = this._onDidEditorBecomeDirty.event;
76 77

	private readonly _onDidEditorLabelChange = this._register(new Emitter<EditorInput>());
78
	readonly onDidEditorLabelChange = this._onDidEditorLabelChange.event;
79 80

	private readonly _onDidEditorMove = this._register(new Emitter<EditorInput>());
81
	readonly onDidEditorMove = this._onDidEditorMove.event;
82 83

	private readonly _onDidEditorPin = this._register(new Emitter<EditorInput>());
84
	readonly onDidEditorPin = this._onDidEditorPin.event;
85 86

	private readonly _onDidEditorUnpin = this._register(new Emitter<EditorInput>());
87
	readonly onDidEditorUnpin = this._onDidEditorUnpin.event;
88 89

	//#endregion
B
Benjamin Pasero 已提交
90

B
Benjamin Pasero 已提交
91
	private _id: GroupIdentifier;
B
Benjamin Pasero 已提交
92
	get id(): GroupIdentifier { return this._id; }
93

94 95
	private editors: EditorInput[] = [];
	private mru: EditorInput[] = [];
96 97 98 99

	private mapResourceToEditorCount = new ResourceMap<number>();
	private mapResourceToMasterEditorCount = new ResourceMap<number>();
	private mapResourceToDetailsEditorCount = new ResourceMap<number>();
100

B
Benjamin Pasero 已提交
101 102
	private preview: EditorInput | null = null; // editor in preview state
	private active: EditorInput | null = null;  // editor in active state
103

B
Benjamin Pasero 已提交
104 105
	private editorOpenPositioning: ('left' | 'right' | 'first' | 'last') | undefined;
	private focusRecentEditorAfterClose: boolean | undefined;
106

107
	constructor(
108
		labelOrSerializedGroup: ISerializedEditorGroup | undefined,
109 110
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IConfigurationService private readonly configurationService: IConfigurationService
111
	) {
112 113
		super();

114
		if (isSerializedEditorGroup(labelOrSerializedGroup)) {
B
Benjamin Pasero 已提交
115
			this._id = this.deserialize(labelOrSerializedGroup);
116
		} else {
117
			this._id = EditorGroup.IDS++;
118
		}
119

120
		this.onConfigurationUpdated();
121 122 123 124
		this.registerListeners();
	}

	private registerListeners(): void {
125
		this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
126 127
	}

128
	private onConfigurationUpdated(event?: IConfigurationChangeEvent): void {
129
		this.editorOpenPositioning = this.configurationService.getValue('workbench.editor.openPositioning');
130
		this.focusRecentEditorAfterClose = this.configurationService.getValue('workbench.editor.focusRecentEditorAfterClose');
B
Benjamin Pasero 已提交
131 132
	}

133
	get count(): number {
134 135 136
		return this.editors.length;
	}

137
	getEditors(mru?: boolean): EditorInput[] {
B
Benjamin Pasero 已提交
138
		return mru ? this.mru.slice(0) : this.editors.slice(0);
B
Benjamin Pasero 已提交
139 140
	}

B
Benjamin Pasero 已提交
141 142
	getEditorByIndex(index: number): EditorInput | undefined {
		return this.editors[index];
143 144
	}

M
Matt Bierner 已提交
145
	get activeEditor(): EditorInput | null {
B
Benjamin Pasero 已提交
146 147 148
		return this.active;
	}

149
	isActive(editor: EditorInput): boolean {
B
Benjamin Pasero 已提交
150
		return this.matches(this.active, editor);
B
Benjamin Pasero 已提交
151 152
	}

M
Matt Bierner 已提交
153
	get previewEditor(): EditorInput | null {
B
Benjamin Pasero 已提交
154 155 156
		return this.preview;
	}

157
	isPreview(editor: EditorInput): boolean {
B
Benjamin Pasero 已提交
158
		return this.matches(this.preview, editor);
B
Benjamin Pasero 已提交
159 160
	}

161
	openEditor(editor: EditorInput, options?: IEditorOpenOptions): void {
B
Benjamin Pasero 已提交
162 163
		const index = this.indexOf(editor);

B
Benjamin Pasero 已提交
164 165
		const makePinned = options?.pinned;
		const makeActive = options?.active || !this.activeEditor || (!makePinned && this.matches(this.preview, this.activeEditor));
B
Benjamin Pasero 已提交
166 167 168

		// New editor
		if (index === -1) {
169 170
			let targetIndex: number;
			const indexOfActive = this.indexOf(this.active);
B
Benjamin Pasero 已提交
171

172 173 174 175
			// Insert into specific position
			if (options && typeof options.index === 'number') {
				targetIndex = options.index;
			}
B
Benjamin Pasero 已提交
176

177
			// Insert to the BEGINNING
178
			else if (this.editorOpenPositioning === EditorOpenPositioning.FIRST) {
179 180 181 182
				targetIndex = 0;
			}

			// Insert to the END
183
			else if (this.editorOpenPositioning === EditorOpenPositioning.LAST) {
184
				targetIndex = this.editors.length;
185
			}
B
Benjamin Pasero 已提交
186

187
			// Insert to the LEFT of active editor
188
			else if (this.editorOpenPositioning === EditorOpenPositioning.LEFT) {
189 190 191
				if (indexOfActive === 0 || !this.editors.length) {
					targetIndex = 0; // to the left becoming first editor in list
				} else {
192
					targetIndex = indexOfActive; // to the left of active editor
B
Benjamin Pasero 已提交
193
				}
194
			}
B
Benjamin Pasero 已提交
195

196 197 198 199 200
			// Insert to the RIGHT of active editor
			else {
				targetIndex = indexOfActive + 1;
			}

201 202 203
			// Insert into our list of editors if pinned or we have no preview editor
			if (makePinned || !this.preview) {
				this.splice(targetIndex, false, editor);
B
Benjamin Pasero 已提交
204 205
			}

206 207
			// Handle preview
			if (!makePinned) {
B
Benjamin Pasero 已提交
208 209

				// Replace existing preview with this editor if we have a preview
210 211
				if (this.preview) {
					const indexOfPreview = this.indexOf(this.preview);
212 213
					if (targetIndex > indexOfPreview) {
						targetIndex--; // accomodate for the fact that the preview editor closes
214 215
					}

216
					this.replaceEditor(this.preview, editor, targetIndex, !makeActive);
217
				}
B
Benjamin Pasero 已提交
218

B
Benjamin Pasero 已提交
219 220 221
				this.preview = editor;
			}

222
			// Listeners
B
Benjamin Pasero 已提交
223
			this.registerEditorListeners(editor);
224

B
Benjamin Pasero 已提交
225
			// Event
226
			this._onDidEditorOpen.fire(editor);
B
Benjamin Pasero 已提交
227

228
			// Handle active
B
Benjamin Pasero 已提交
229
			if (makeActive) {
B
Benjamin Pasero 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
				this.setActive(editor);
			}
		}

		// Existing editor
		else {

			// Pin it
			if (makePinned) {
				this.pin(editor);
			}

			// Activate it
			if (makeActive) {
				this.setActive(editor);
			}
B
Benjamin Pasero 已提交
246 247 248 249 250

			// Respect index
			if (options && typeof options.index === 'number') {
				this.moveEditor(editor, options.index);
			}
B
Benjamin Pasero 已提交
251 252 253
		}
	}

B
Benjamin Pasero 已提交
254
	private registerEditorListeners(editor: EditorInput): void {
255
		const listeners = new DisposableStore();
256 257

		// Re-emit disposal of editor input as our own event
J
Joao Moreno 已提交
258
		const onceDispose = Event.once(editor.onDispose);
259
		listeners.add(onceDispose(() => {
260
			if (this.indexOf(editor) >= 0) {
261
				this._onDidEditorDispose.fire(editor);
262
			}
263 264 265
		}));

		// Re-Emit dirty state changes
266
		listeners.add(editor.onDidChangeDirty(() => {
267
			this._onDidEditorBecomeDirty.fire(editor);
268
		}));
269

B
Benjamin Pasero 已提交
270
		// Re-Emit label changes
271
		listeners.add(editor.onDidChangeLabel(() => {
272
			this._onDidEditorLabelChange.fire(editor);
B
Benjamin Pasero 已提交
273 274
		}));

275
		// Clean up dispose listeners once the editor gets closed
276
		listeners.add(this.onDidEditorClose(event => {
277
			if (event.editor.matches(editor)) {
278
				dispose(listeners);
279
			}
280
		}));
281 282
	}

B
Benjamin Pasero 已提交
283
	private replaceEditor(toReplace: EditorInput, replaceWith: EditorInput, replaceIndex: number, openNext = true): void {
284
		const event = this.doCloseEditor(toReplace, openNext, true); // optimization to prevent multiple setActive() in one call
285 286 287 288

		// We want to first add the new editor into our model before emitting the close event because
		// firing the close event can trigger a dispose on the same editor that is now being added.
		// This can lead into opening a disposed editor which is not what we want.
B
Benjamin Pasero 已提交
289
		this.splice(replaceIndex, false, replaceWith);
290 291

		if (event) {
292
			this._onDidEditorClose.fire(event);
293 294 295
		}
	}

M
Matt Bierner 已提交
296
	closeEditor(editor: EditorInput, openNext = true): number | undefined {
297
		const event = this.doCloseEditor(editor, openNext, false);
298 299

		if (event) {
300
			this._onDidEditorClose.fire(event);
301 302

			return event.index;
303
		}
304

R
Rob Lourens 已提交
305
		return undefined;
306 307
	}

M
Matt Bierner 已提交
308
	private doCloseEditor(editor: EditorInput, openNext: boolean, replaced: boolean): EditorCloseEvent | null {
B
Benjamin Pasero 已提交
309 310
		const index = this.indexOf(editor);
		if (index === -1) {
311
			return null; // not found
B
Benjamin Pasero 已提交
312 313 314
		}

		// Active Editor closed
B
Benjamin Pasero 已提交
315
		if (openNext && this.matches(this.active, editor)) {
B
Benjamin Pasero 已提交
316 317

			// More than one editor
B
Benjamin Pasero 已提交
318
			if (this.mru.length > 1) {
319
				let newActive: EditorInput;
320
				if (this.focusRecentEditorAfterClose) {
321
					newActive = this.mru[1]; // active editor is always first in MRU, so pick second editor after as new active
B
Benjamin Pasero 已提交
322
				} else {
323 324
					if (index === this.editors.length - 1) {
						newActive = this.editors[index - 1]; // last editor is closed, pick previous as new active
B
Benjamin Pasero 已提交
325
					} else {
326 327 328
						newActive = this.editors[index + 1]; // pick next editor as new active
					}
				}
B
Benjamin Pasero 已提交
329

330
				this.setActive(newActive);
B
Benjamin Pasero 已提交
331 332 333 334 335 336 337 338 339
			}

			// One Editor
			else {
				this.active = null;
			}
		}

		// Preview Editor closed
B
Benjamin Pasero 已提交
340
		if (this.matches(this.preview, editor)) {
B
Benjamin Pasero 已提交
341 342 343
			this.preview = null;
		}

B
Benjamin Pasero 已提交
344
		// Remove from arrays
B
Benjamin Pasero 已提交
345
		this.splice(index, true);
B
Benjamin Pasero 已提交
346 347

		// Event
I
isidor 已提交
348
		return { editor, replaced, index, groupId: this.id };
B
Benjamin Pasero 已提交
349 350
	}

B
Benjamin Pasero 已提交
351
	closeEditors(except: EditorInput, direction?: CloseDirection): void {
B
Benjamin Pasero 已提交
352 353 354 355 356 357
		const index = this.indexOf(except);
		if (index === -1) {
			return; // not found
		}

		// Close to the left
B
Benjamin Pasero 已提交
358
		if (direction === CloseDirection.LEFT) {
B
Benjamin Pasero 已提交
359 360 361 362 363 364
			for (let i = index - 1; i >= 0; i--) {
				this.closeEditor(this.editors[i]);
			}
		}

		// Close to the right
B
Benjamin Pasero 已提交
365
		else if (direction === CloseDirection.RIGHT) {
B
Benjamin Pasero 已提交
366 367 368 369 370 371 372
			for (let i = this.editors.length - 1; i > index; i--) {
				this.closeEditor(this.editors[i]);
			}
		}

		// Both directions
		else {
B
Benjamin Pasero 已提交
373
			this.mru.filter(e => !this.matches(e, except)).forEach(e => this.closeEditor(e));
B
Benjamin Pasero 已提交
374 375 376
		}
	}

377
	closeAllEditors(): void {
378 379

		// Optimize: close all non active editors first to produce less upstream work
B
Benjamin Pasero 已提交
380
		this.mru.filter(e => !this.matches(e, this.active)).forEach(e => this.closeEditor(e));
M
Matt Bierner 已提交
381 382 383
		if (this.active) {
			this.closeEditor(this.active);
		}
384 385
	}

386
	moveEditor(editor: EditorInput, toIndex: number): void {
B
Benjamin Pasero 已提交
387 388 389 390 391 392 393 394 395 396
		const index = this.indexOf(editor);
		if (index < 0) {
			return;
		}

		// Move
		this.editors.splice(index, 1);
		this.editors.splice(toIndex, 0, editor);

		// Event
397
		this._onDidEditorMove.fire(editor);
B
Benjamin Pasero 已提交
398 399
	}

400
	setActive(editor: EditorInput): void {
B
Benjamin Pasero 已提交
401 402 403 404 405
		const index = this.indexOf(editor);
		if (index === -1) {
			return; // not found
		}

B
Benjamin Pasero 已提交
406
		if (this.matches(this.active, editor)) {
B
Benjamin Pasero 已提交
407 408 409 410 411
			return; // already active
		}

		this.active = editor;

B
Benjamin Pasero 已提交
412
		// Bring to front in MRU list
B
Benjamin Pasero 已提交
413 414
		this.setMostRecentlyUsed(editor);

B
Benjamin Pasero 已提交
415
		// Event
416
		this._onDidEditorActivate.fire(editor);
B
Benjamin Pasero 已提交
417 418
	}

419
	pin(editor: EditorInput): void {
420 421 422 423 424
		const index = this.indexOf(editor);
		if (index === -1) {
			return; // not found
		}

B
Benjamin Pasero 已提交
425 426
		if (!this.isPreview(editor)) {
			return; // can only pin a preview editor
B
Benjamin Pasero 已提交
427 428 429 430 431 432
		}

		// Convert the preview editor to be a pinned editor
		this.preview = null;

		// Event
433
		this._onDidEditorPin.fire(editor);
B
Benjamin Pasero 已提交
434 435
	}

436
	unpin(editor: EditorInput): void {
437 438 439 440
		const index = this.indexOf(editor);
		if (index === -1) {
			return; // not found
		}
441

B
Benjamin Pasero 已提交
442 443 444 445 446 447 448 449 450
		if (!this.isPinned(editor)) {
			return; // can only unpin a pinned editor
		}

		// Set new
		const oldPreview = this.preview;
		this.preview = editor;

		// Event
451
		this._onDidEditorUnpin.fire(editor);
B
Benjamin Pasero 已提交
452 453

		// Close old preview editor if any
M
Matt Bierner 已提交
454 455 456
		if (oldPreview) {
			this.closeEditor(oldPreview);
		}
B
Benjamin Pasero 已提交
457 458
	}

459 460 461
	isPinned(editor: EditorInput): boolean;
	isPinned(index: number): boolean;
	isPinned(arg1: EditorInput | number): boolean {
462 463 464 465 466 467 468 469 470 471 472
		let editor: EditorInput;
		let index: number;
		if (typeof arg1 === 'number') {
			editor = this.editors[arg1];
			index = arg1;
		} else {
			editor = arg1;
			index = this.indexOf(editor);
		}

		if (index === -1 || !editor) {
B
Benjamin Pasero 已提交
473 474 475 476 477 478 479
			return false; // editor not found
		}

		if (!this.preview) {
			return true; // no preview editor
		}

B
Benjamin Pasero 已提交
480
		return !this.matches(this.preview, editor);
B
Benjamin Pasero 已提交
481 482
	}

B
Benjamin Pasero 已提交
483
	private splice(index: number, del: boolean, editor?: EditorInput): void {
484
		const editorToDeleteOrReplace = this.editors[index];
B
Benjamin Pasero 已提交
485

486
		const args: (number | EditorInput)[] = [index, del ? 1 : 0];
487 488 489
		if (editor) {
			args.push(editor);
		}
B
Benjamin Pasero 已提交
490

491
		// Perform on editors array
492
		this.editors.splice.apply(this.editors, args);
B
Benjamin Pasero 已提交
493

494
		// Add
B
Benjamin Pasero 已提交
495
		if (!del && editor) {
496
			this.mru.push(editor); // make it LRU editor
497
			this.updateResourceCounterMap(editor, false /* add */); // add new to resource map
B
Benjamin Pasero 已提交
498 499
		}

B
Benjamin Pasero 已提交
500
		// Remove / Replace
B
Benjamin Pasero 已提交
501
		else {
B
Benjamin Pasero 已提交
502 503
			const indexInMRU = this.indexOf(editorToDeleteOrReplace, this.mru);

504
			// Remove
B
Benjamin Pasero 已提交
505
			if (del && !editor) {
506
				this.mru.splice(indexInMRU, 1); // remove from MRU
507
				this.updateResourceCounterMap(editorToDeleteOrReplace, true /* delete */); // remove from resource map
B
Benjamin Pasero 已提交
508 509
			}

510
			// Replace
B
Benjamin Pasero 已提交
511
			else if (del && editor) {
512
				this.mru.splice(indexInMRU, 1, editor); // replace MRU at location
513 514
				this.updateResourceCounterMap(editor, false /* add */); // add new to resource map
				this.updateResourceCounterMap(editorToDeleteOrReplace, true /* delete */); // remove replaced from resource map
B
Benjamin Pasero 已提交
515
			}
B
Benjamin Pasero 已提交
516 517 518
		}
	}

519
	private updateResourceCounterMap(editor: EditorInput, remove: boolean): void {
520

521 522 523 524 525
		// Remember editor resource in map for fast lookup
		const resource = toResource(editor);
		if (resource) {
			this.doUpdateResourceCounterMap(resource, this.mapResourceToEditorCount, remove);
		}
B
Benjamin Pasero 已提交
526

527 528 529 530 531 532 533
		// Side by Side editor: store resource information
		// for master and details side in separate maps
		// to be able to lookup properly.
		if (editor instanceof SideBySideEditorInput) {
			const masterResource = toResource(editor.master);
			if (masterResource) {
				this.doUpdateResourceCounterMap(masterResource, this.mapResourceToMasterEditorCount, remove);
534 535
			}

536 537 538
			const detailsResource = toResource(editor.details);
			if (detailsResource) {
				this.doUpdateResourceCounterMap(detailsResource, this.mapResourceToDetailsEditorCount, remove);
B
Benjamin Pasero 已提交
539
			}
540 541
		}
	}
B
Benjamin Pasero 已提交
542

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
	private doUpdateResourceCounterMap(resource: URI, map: ResourceMap<number>, remove: boolean): void {

		// It is possible to have the same resource opened twice (once as normal input and once as diff input)
		// So we need to do ref counting on the resource to provide the correct picture
		const counter = map.get(resource) || 0;

		// Add
		let newCounter: number;
		if (!remove) {
			newCounter = counter + 1;
		}

		// Delete
		else {
			newCounter = counter - 1;
		}

		if (newCounter > 0) {
			map.set(resource, newCounter);
		} else {
			map.delete(resource);
564 565 566
		}
	}

567
	indexOf(candidate: IEditorInput | null, editors = this.editors): number {
B
Benjamin Pasero 已提交
568 569
		if (!candidate) {
			return -1;
B
Benjamin Pasero 已提交
570 571
		}

B
Benjamin Pasero 已提交
572
		for (let i = 0; i < editors.length; i++) {
B
Benjamin Pasero 已提交
573
			if (this.matches(editors[i], candidate)) {
B
Benjamin Pasero 已提交
574 575 576 577 578 579
				return i;
			}
		}

		return -1;
	}
B
Benjamin Pasero 已提交
580

581
	containsEditorByResource(resource: URI, supportSideBySide?: SideBySideEditor): boolean {
582

583 584
		// Check if exact editor match is contained
		let counter = this.mapResourceToEditorCount.get(resource);
585

586 587 588 589 590
		// Optionally search by master/detail resource if instructed
		if (supportSideBySide === SideBySideEditor.MASTER) {
			counter = counter || this.mapResourceToMasterEditorCount.get(resource);
		} else if (supportSideBySide === SideBySideEditor.DETAILS) {
			counter = counter || this.mapResourceToDetailsEditorCount.get(resource);
591 592
		}

593
		return typeof counter === 'number' && counter > 0;
594 595
	}

596
	containsEditorByInstance(editor: EditorInput): boolean {
597 598 599 600 601 602 603 604
		const index = this.indexOf(editor);
		if (index >= 0) {
			return true;
		}

		return false;
	}

B
Benjamin Pasero 已提交
605 606 607 608 609 610 611 612 613
	private setMostRecentlyUsed(editor: EditorInput): void {
		const index = this.indexOf(editor);
		if (index === -1) {
			return; // editor not found
		}

		const mruIndex = this.indexOf(editor, this.mru);

		// Remove old index
B
Benjamin Pasero 已提交
614
		this.mru.splice(mruIndex, 1);
B
Benjamin Pasero 已提交
615

B
Benjamin Pasero 已提交
616
		// Set editor to front
B
Benjamin Pasero 已提交
617 618
		this.mru.unshift(editor);
	}
B
Benjamin Pasero 已提交
619

620
	private matches(editorA: IEditorInput | null, editorB: IEditorInput | null): boolean {
B
Benjamin Pasero 已提交
621 622
		return !!editorA && !!editorB && editorA.matches(editorB);
	}
623

624
	clone(): EditorGroup {
R
Rob Lourens 已提交
625
		const group = this.instantiationService.createInstance(EditorGroup, undefined);
626 627 628
		group.editors = this.editors.slice(0);
		group.mru = this.mru.slice(0);
		group.mapResourceToEditorCount = this.mapResourceToEditorCount.clone();
629 630
		group.mapResourceToMasterEditorCount = this.mapResourceToMasterEditorCount.clone();
		group.mapResourceToDetailsEditorCount = this.mapResourceToDetailsEditorCount.clone();
631 632 633 634 635 636 637 638
		group.preview = this.preview;
		group.active = this.active;
		group.editorOpenPositioning = this.editorOpenPositioning;

		return group;
	}

	serialize(): ISerializedEditorGroup {
639
		const registry = Registry.as<IEditorInputFactoryRegistry>(Extensions.EditorInputFactories);
640 641 642 643 644 645

		// Serialize all editor inputs so that we can store them.
		// Editors that cannot be serialized need to be ignored
		// from mru, active and preview if any.
		let serializableEditors: EditorInput[] = [];
		let serializedEditors: ISerializedEditorInput[] = [];
M
Matt Bierner 已提交
646
		let serializablePreviewIndex: number | undefined;
647
		this.editors.forEach(e => {
648
			const factory = registry.getEditorInputFactory(e.getTypeId());
649
			if (factory) {
650
				const value = factory.serialize(e);
651
				if (typeof value === 'string') {
652
					serializedEditors.push({ id: e.getTypeId(), value });
653
					serializableEditors.push(e);
B
Benjamin Pasero 已提交
654 655 656 657

					if (this.preview === e) {
						serializablePreviewIndex = serializableEditors.length - 1;
					}
658 659 660 661
				}
			}
		});

B
Benjamin Pasero 已提交
662
		const serializableMru = this.mru.map(e => this.indexOf(e, serializableEditors)).filter(i => i >= 0);
663 664

		return {
665
			id: this.id,
666 667
			editors: serializedEditors,
			mru: serializableMru,
B
Benjamin Pasero 已提交
668
			preview: serializablePreviewIndex,
669 670 671
		};
	}

B
Benjamin Pasero 已提交
672
	private deserialize(data: ISerializedEditorGroup): number {
673
		const registry = Registry.as<IEditorInputFactoryRegistry>(Extensions.EditorInputFactories);
674

675 676 677 678 679 680 681 682
		if (typeof data.id === 'number') {
			this._id = data.id;

			EditorGroup.IDS = Math.max(data.id + 1, EditorGroup.IDS); // make sure our ID generator is always larger
		} else {
			this._id = EditorGroup.IDS++; // backwards compatibility
		}

M
Matt Bierner 已提交
683
		this.editors = coalesce(data.editors.map(e => {
B
Benjamin Pasero 已提交
684 685
			const factory = registry.getEditorInputFactory(e.id);
			if (factory) {
B
Benjamin Pasero 已提交
686 687 688
				const editor = factory.deserialize(this.instantiationService, e.value);
				if (editor) {
					this.registerEditorListeners(editor);
689
					this.updateResourceCounterMap(editor, false /* add */);
B
Benjamin Pasero 已提交
690
				}
691

B
Benjamin Pasero 已提交
692 693 694 695
				return editor;
			}

			return null;
M
Matt Bierner 已提交
696
		}));
B
Benjamin Pasero 已提交
697

698
		this.mru = data.mru.map(i => this.editors[i]);
B
Benjamin Pasero 已提交
699

700
		this.active = this.mru[0];
B
Benjamin Pasero 已提交
701

M
Matt Bierner 已提交
702 703 704
		if (typeof data.preview === 'number') {
			this.preview = this.editors[data.preview];
		}
B
Benjamin Pasero 已提交
705 706

		return this._id;
707 708
	}
}