openEditorsView.ts 25.3 KB
Newer Older
I
isidor 已提交
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.
 *--------------------------------------------------------------------------------------------*/

I
isidor 已提交
6 7
import * as nls from 'vs/nls';
import * as errors from 'vs/base/common/errors';
J
Johannes Rieken 已提交
8
import { RunOnceScheduler } from 'vs/base/common/async';
9
import { IAction } from 'vs/base/common/actions';
I
isidor 已提交
10
import * as dom from 'vs/base/browser/dom';
J
Johannes Rieken 已提交
11 12 13
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
14
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
J
Johannes Rieken 已提交
15
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
I
isidor 已提交
16
import { Position, IEditorInput } from 'vs/platform/editor/common/editor';
I
isidor 已提交
17
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup } from 'vs/workbench/common/editor';
18
import { SaveAllAction, SaveAllInGroupAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
I
isidor 已提交
19 20
import { IViewletViewOptions, IViewOptions, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { OpenEditorsFocusedContext, ExplorerFocusedContext, IFilesConfiguration } from 'vs/workbench/parts/files/common/files';
J
Johannes Rieken 已提交
21
import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
22
import { OpenEditor } from 'vs/workbench/parts/files/common/explorerModel';
J
Johannes Rieken 已提交
23
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
24
import { CloseAllEditorsAction, CloseEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
25
import { ToggleEditorLayoutAction } from 'vs/workbench/browser/actions/toggleEditorLayout';
26
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
B
Benjamin Pasero 已提交
27
import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
28
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
29
import { IThemeService } from 'vs/platform/theme/common/themeService';
30
import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
31
import { WorkbenchList } from 'vs/platform/list/browser/listService';
32
import { IDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
I
isidor 已提交
33
import { EditorLabel } from 'vs/workbench/browser/labels';
I
isidor 已提交
34
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
I
isidor 已提交
35 36 37
import { TPromise } from 'vs/base/common/winjs.base';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
38
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
39 40
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
41
import { OpenEditorsGroupContext, DirtyEditorContext } from 'vs/workbench/parts/files/electron-browser/fileCommands';
B
Benjamin Pasero 已提交
42
import { ResourceContextKey } from 'vs/workbench/common/resources';
43
import { fillResourceDataTransfers, ResourcesDropHandler, LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
I
isidor 已提交
44

J
Joao Moreno 已提交
45
const $ = dom.$;
I
isidor 已提交
46

I
isidor 已提交
47
export class OpenEditorsView extends ViewsViewletPanel {
I
isidor 已提交
48

49
	private static readonly DEFAULT_VISIBLE_OPEN_EDITORS = 9;
50
	static readonly ID = 'workbench.explorer.openEditorsView';
51
	static NAME = nls.localize({ key: 'openEditors', comment: ['Open is an adjective'] }, "Open Editors");
I
isidor 已提交
52

I
isidor 已提交
53
	private model: IEditorStacksModel;
I
isidor 已提交
54
	private dirtyCountElement: HTMLElement;
I
isidor 已提交
55
	private listRefreshScheduler: RunOnceScheduler;
56
	private structuralRefreshDelay: number;
I
isidor 已提交
57
	private list: WorkbenchList<OpenEditor | IEditorGroup>;
58
	private contributedContextMenu: IMenu;
I
isidor 已提交
59
	private needsRefresh: boolean;
B
Benjamin Pasero 已提交
60
	private resourceContext: ResourceContextKey;
61
	private groupFocusedContext: IContextKey<boolean>;
62
	private dirtyEditorFocusedContext: IContextKey<boolean>;
I
isidor 已提交
63

J
Joao 已提交
64 65
	constructor(
		options: IViewletViewOptions,
I
isidor 已提交
66
		@IInstantiationService private instantiationService: IInstantiationService,
I
isidor 已提交
67 68
		@IContextMenuService contextMenuService: IContextMenuService,
		@ITextFileService private textFileService: ITextFileService,
I
isidor 已提交
69 70
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IEditorGroupService private editorGroupService: IEditorGroupService,
71
		@IConfigurationService configurationService: IConfigurationService,
B
Benjamin Pasero 已提交
72
		@IKeybindingService keybindingService: IKeybindingService,
73
		@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
J
Joao Moreno 已提交
74
		@IContextKeyService private contextKeyService: IContextKeyService,
I
isidor 已提交
75
		@IThemeService private themeService: IThemeService,
76
		@ITelemetryService private telemetryService: ITelemetryService,
77
		@IMenuService private menuService: IMenuService
I
isidor 已提交
78
	) {
79
		super({
80
			...(options as IViewOptions),
S
#27823  
Sandeep Somavarapu 已提交
81
			ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"),
82
		}, keybindingService, contextMenuService, configurationService);
I
isidor 已提交
83

84
		this.model = editorGroupService.getStacksModel();
85

86
		this.structuralRefreshDelay = 0;
I
isidor 已提交
87
		this.listRefreshScheduler = new RunOnceScheduler(() => {
I
isidor 已提交
88
			const previousLength = this.list.length;
I
isidor 已提交
89 90
			this.list.splice(0, this.list.length, this.elements);
			this.focusActiveEditor();
I
isidor 已提交
91 92 93
			if (previousLength !== this.list.length) {
				this.updateSize();
			}
I
isidor 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
			this.needsRefresh = false;
		}, this.structuralRefreshDelay);

		// update on model changes
		this.disposables.push(this.model.onModelChanged(e => this.onEditorStacksModelChanged(e)));

		// Also handle configuration updates
		this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(e)));

		// Handle dirty counter
		this.disposables.push(this.untitledEditorService.onDidChangeDirty(e => this.updateDirtyIndicator()));
		this.disposables.push(this.textFileService.models.onModelsDirty(e => this.updateDirtyIndicator()));
		this.disposables.push(this.textFileService.models.onModelsSaved(e => this.updateDirtyIndicator()));
		this.disposables.push(this.textFileService.models.onModelsSaveError(e => this.updateDirtyIndicator()));
		this.disposables.push(this.textFileService.models.onModelsReverted(e => this.updateDirtyIndicator()));
I
isidor 已提交
109 110
	}

111 112
	protected renderHeaderTitle(container: HTMLElement): void {
		const title = dom.append(container, $('.title'));
J
Joao Moreno 已提交
113
		dom.append(title, $('span', null, this.name));
I
isidor 已提交
114

115 116
		const count = dom.append(container, $('.count'));
		this.dirtyCountElement = dom.append(count, $('.monaco-count-badge'));
117

J
Joao Moreno 已提交
118
		this.disposables.push((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => {
119 120
			const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
			const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null;
121
			const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
122 123 124 125 126 127 128 129 130

			this.dirtyCountElement.style.backgroundColor = background;
			this.dirtyCountElement.style.color = foreground;

			this.dirtyCountElement.style.borderWidth = border ? '1px' : null;
			this.dirtyCountElement.style.borderStyle = border ? 'solid' : null;
			this.dirtyCountElement.style.borderColor = border;
		})));

I
isidor 已提交
131 132 133
		this.updateDirtyIndicator();
	}

I
isidor 已提交
134 135 136 137 138
	public renderBody(container: HTMLElement): void {
		dom.addClass(container, 'explorer-open-editors');
		dom.addClass(container, 'show-file-icons');

		const delegate = new OpenEditorsDelegate();
I
isidor 已提交
139 140 141 142 143 144 145 146 147
		const getSelectedElements = () => {
			const selected = this.list.getSelectedElements();
			const focused = this.list.getFocusedElements();
			if (focused.length && selected.indexOf(focused[0]) >= 0) {
				return selected;
			}

			return focused;
		};
148
		this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [
149
			new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService),
I
isidor 已提交
150
			new OpenEditorRenderer(getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService)
I
isidor 已提交
151
		], {
152
				identityProvider: (element: OpenEditor | EditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString(),
153 154
				selectOnMouseDown: false /* disabled to better support DND */
			}) as WorkbenchList<OpenEditor | IEditorGroup>;
I
isidor 已提交
155

156 157 158
		this.contributedContextMenu = this.menuService.createMenu(MenuId.OpenEditorsContext, this.list.contextKeyService);
		this.disposables.push(this.contributedContextMenu);

I
isidor 已提交
159
		this.updateSize();
B
Benjamin Pasero 已提交
160

I
isidor 已提交
161 162 163
		// Bind context keys
		OpenEditorsFocusedContext.bindTo(this.list.contextKeyService);
		ExplorerFocusedContext.bindTo(this.list.contextKeyService);
B
Benjamin Pasero 已提交
164 165 166

		this.resourceContext = this.instantiationService.createInstance(ResourceContextKey);
		this.groupFocusedContext = OpenEditorsGroupContext.bindTo(this.contextKeyService);
167
		this.dirtyEditorFocusedContext = DirtyEditorContext.bindTo(this.contextKeyService);
I
isidor 已提交
168 169

		this.disposables.push(this.list.onContextMenu(e => this.onListContextMenu(e)));
170
		this.list.onFocusChange(e => {
B
Benjamin Pasero 已提交
171
			this.resourceContext.reset();
172
			this.groupFocusedContext.reset();
173
			this.dirtyEditorFocusedContext.reset();
174 175
			const element = e.elements.length ? e.elements[0] : undefined;
			if (element instanceof OpenEditor) {
176
				this.dirtyEditorFocusedContext.set(this.textFileService.isDirty(element.getResource()));
B
Benjamin Pasero 已提交
177
				this.resourceContext.set(element.getResource());
178
			} else if (!!element) {
179 180 181
				this.groupFocusedContext.set(true);
			}
		});
I
isidor 已提交
182 183

		// Open when selecting via keyboard
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
		this.disposables.push(this.list.onOpen(e => {
			const browserEvent = e.browserEvent;

			let openToSide = false;
			let isSingleClick = false;
			let isDoubleClick = false;
			let isMiddleClick = false;
			if (browserEvent instanceof MouseEvent) {
				isSingleClick = browserEvent.detail === 1;
				isDoubleClick = browserEvent.detail === 2;
				isMiddleClick = browserEvent.button === 1 /* middle button */;
				openToSide = this.list.useAltAsMultipleSelectionModifier ? (browserEvent.ctrlKey || browserEvent.metaKey) : browserEvent.altKey;
			}

			const focused = this.list.getFocusedElements();
			const element = focused.length ? focused[0] : undefined;
			if (element instanceof OpenEditor) {
				if (isMiddleClick) {
202
					const position = this.model.positionOfGroup(element.legacyGroup);
203 204 205
					this.editorService.closeEditor(position, element.editor).done(null, errors.onUnexpectedError);
				} else {
					this.openEditor(element, { preserveFocus: isSingleClick, pinned: isDoubleClick, sideBySide: openToSide });
I
isidor 已提交
206 207 208 209 210 211 212
				}
			}
		}));

		this.listRefreshScheduler.schedule(0);
	}

I
isidor 已提交
213 214
	public getActions(): IAction[] {
		return [
215
			this.instantiationService.createInstance(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL),
I
isidor 已提交
216
			this.instantiationService.createInstance(SaveAllAction, SaveAllAction.ID, SaveAllAction.LABEL),
I
isidor 已提交
217
			this.instantiationService.createInstance(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL)
I
isidor 已提交
218 219 220
		];
	}

I
isidor 已提交
221 222 223 224 225 226
	public setExpanded(expanded: boolean): void {
		super.setExpanded(expanded);
		if (expanded && this.needsRefresh) {
			this.listRefreshScheduler.schedule(0);
		}
	}
227

I
isidor 已提交
228 229 230 231
	public setVisible(visible: boolean): TPromise<void> {
		return super.setVisible(visible).then(() => {
			if (visible && this.needsRefresh) {
				this.listRefreshScheduler.schedule(0);
232
			}
I
isidor 已提交
233 234 235
		});
	}

S
Sandeep Somavarapu 已提交
236 237 238 239 240
	public focus(): void {
		this.list.domFocus();
		super.focus();
	}

I
isidor 已提交
241 242 243
	public getList(): WorkbenchList<OpenEditor | IEditorGroup> {
		return this.list;
	}
244

I
isidor 已提交
245 246 247 248 249 250
	protected layoutBody(size: number): void {
		if (this.list) {
			this.list.layout(size);
		}
	}

251 252 253 254
	private get showGroups(): boolean {
		return this.model.groups.length > 1;
	}

I
isidor 已提交
255 256 257
	private get elements(): (IEditorGroup | OpenEditor)[] {
		const result: (IEditorGroup | OpenEditor)[] = [];
		this.model.groups.forEach(g => {
258
			if (this.showGroups) {
I
isidor 已提交
259
				result.push(g);
B
Benjamin Pasero 已提交
260
			}
I
isidor 已提交
261 262
			result.push(...g.getEditors().map(ei => new OpenEditor(ei, g)));
		});
B
Benjamin Pasero 已提交
263

I
isidor 已提交
264
		return result;
I
isidor 已提交
265 266
	}

I
isidor 已提交
267
	private getIndex(group: IEditorGroup, editor: IEditorInput): number {
268
		let index = editor ? group.indexOf(editor) : 0;
269
		if (!this.showGroups) {
270 271 272
			return index;
		}

I
isidor 已提交
273
		for (let g of this.model.groups) {
274
			if (g.id === group.id) {
275
				return index + (!!editor ? 1 : 0);
I
isidor 已提交
276
			} else {
277
				index += g.count + 1;
I
isidor 已提交
278 279
			}
		}
I
isidor 已提交
280

I
isidor 已提交
281 282
		return -1;
	}
I
isidor 已提交
283

I
isidor 已提交
284 285 286 287 288 289 290 291 292
	private openEditor(element: OpenEditor, options: { preserveFocus: boolean; pinned: boolean; sideBySide: boolean; }): void {
		if (element) {
			/* __GDPR__
				"workbenchActionExecuted" : {
					"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
					"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}
			*/
			this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'openEditors' });
293

294
			let position = this.model.positionOfGroup(element.legacyGroup);
I
isidor 已提交
295 296 297
			if (options.sideBySide && position !== Position.THREE) {
				position++;
			}
298 299 300 301 302 303 304 305 306 307

			const preserveActivateGroup = options.sideBySide && options.preserveFocus; // needed for https://github.com/Microsoft/vscode/issues/42399
			if (!preserveActivateGroup) {
				this.editorGroupService.activateGroup(this.model.groupAt(position)); // needed for https://github.com/Microsoft/vscode/issues/6672
			}
			this.editorService.openEditor(element.editor, options, position).done(() => {
				if (!preserveActivateGroup) {
					this.editorGroupService.activateGroup(this.model.groupAt(position));
				}
			}, errors.onUnexpectedError);
I
isidor 已提交
308 309
		}
	}
I
isidor 已提交
310

I
isidor 已提交
311
	private onListContextMenu(e: IListContextMenuEvent<OpenEditor | IEditorGroup>): void {
I
isidor 已提交
312
		const element = e.element;
I
isidor 已提交
313 314
		this.contextMenuService.showContextMenu({
			getAnchor: () => e.anchor,
I
isidor 已提交
315
			getActions: () => {
316
				const actions: IAction[] = [];
317
				fillInActions(this.contributedContextMenu, { shouldForwardArgs: true, arg: element instanceof OpenEditor ? element.editor.getResource() : {} }, actions, this.contextMenuService);
I
isidor 已提交
318 319
				return TPromise.as(actions);
			},
320
			getActionsContext: () => element instanceof OpenEditor ? { groupId: element.group, editorIndex: element.editorIndex } : { groupId: element.id }
I
isidor 已提交
321
		});
I
isidor 已提交
322 323
	}

324
	private onEditorStacksModelChanged(e: IStacksModelChangeEvent): void {
I
isidor 已提交
325 326
		if (!this.isVisible() || !this.list || !this.isExpanded()) {
			this.needsRefresh = true;
327 328 329
			return;
		}

330
		// Do a minimal list update based on if the change is structural or not #6670
331
		if (e.structural) {
I
isidor 已提交
332
			this.listRefreshScheduler.schedule(this.structuralRefreshDelay);
333
		} else if (!this.listRefreshScheduler.isScheduled()) {
334

335 336 337 338 339
			const newElement = e.editor ? new OpenEditor(e.editor, e.group) : this.showGroups ? e.group : undefined;
			if (newElement) {
				const index = this.getIndex(e.group, e.editor);
				const previousLength = this.list.length;
				this.list.splice(index, 1, [newElement]);
I
isidor 已提交
340

341 342 343 344
				if (previousLength !== this.list.length) {
					this.updateSize();
				}
				this.focusActiveEditor();
I
isidor 已提交
345
			}
I
isidor 已提交
346
		}
347
	}
348

I
isidor 已提交
349
	private focusActiveEditor(): void {
350
		if (this.model.activeGroup && this.model.activeGroup.activeEditor /* could be empty */) {
I
isidor 已提交
351 352
			const index = this.getIndex(this.model.activeGroup, this.model.activeGroup.activeEditor);
			this.list.setFocus([index]);
I
isidor 已提交
353
			this.list.setSelection([index]);
I
isidor 已提交
354
			this.list.reveal(index);
I
isidor 已提交
355 356
		}
	}
357

358 359 360
	private onConfigurationChange(event: IConfigurationChangeEvent): void {
		if (event.affectsConfiguration('explorer.openEditors')) {
			this.updateSize();
I
isidor 已提交
361 362
		}

363 364
		// Trigger a 'repaint' when decoration settings change
		if (event.affectsConfiguration('explorer.decorations')) {
I
isidor 已提交
365
			this.listRefreshScheduler.schedule();
I
isidor 已提交
366
		}
367
	}
368

369
	private updateSize(): void {
370
		// Adjust expanded body size
C
Changyu 已提交
371 372
		this.minimumBodySize = this.getMinExpandedBodySize();
		this.maximumBodySize = this.getMaxExpandedBodySize();
I
isidor 已提交
373 374
	}

I
isidor 已提交
375
	private updateDirtyIndicator(): void {
376 377
		let dirty = this.textFileService.getAutoSaveMode() !== AutoSaveMode.AFTER_SHORT_DELAY ? this.textFileService.getDirty().length
			: this.untitledEditorService.getDirty().length;
I
isidor 已提交
378
		if (dirty === 0) {
I
isidor 已提交
379
			dom.addClass(this.dirtyCountElement, 'hidden');
I
isidor 已提交
380 381
		} else {
			this.dirtyCountElement.textContent = nls.localize('dirtyCounter', "{0} unsaved", dirty);
I
isidor 已提交
382
			dom.removeClass(this.dirtyCountElement, 'hidden');
I
isidor 已提交
383 384 385
		}
	}

I
isidor 已提交
386 387
	private get elementCount(): number {
		return this.model.groups.map(g => g.count)
388
			.reduce((first, second) => first + second, this.showGroups ? this.model.groups.length : 0);
I
isidor 已提交
389 390
	}

C
Changyu 已提交
391
	private getMaxExpandedBodySize(): number {
I
isidor 已提交
392
		return this.elementCount * OpenEditorsDelegate.ITEM_HEIGHT;
C
Changyu 已提交
393 394 395
	}

	private getMinExpandedBodySize(): number {
396 397 398 399 400
		let visibleOpenEditors = this.configurationService.getValue<number>('explorer.openEditors.visible');
		if (typeof visibleOpenEditors !== 'number') {
			visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS;
		}

401
		return this.computeMinExpandedBodySize(visibleOpenEditors);
I
isidor 已提交
402
	}
403

404
	private computeMinExpandedBodySize(visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS): number {
I
isidor 已提交
405
		const itemsToShow = Math.min(Math.max(visibleOpenEditors, 1), this.elementCount);
I
isidor 已提交
406
		return itemsToShow * OpenEditorsDelegate.ITEM_HEIGHT;
407 408
	}

409 410 411 412
	public setStructuralRefreshDelay(delay: number): void {
		this.structuralRefreshDelay = delay;
	}

J
Johannes Rieken 已提交
413
	public getOptimalWidth(): number {
I
isidor 已提交
414
		let parentNode = this.list.getHTMLElement();
415 416
		let childNodes = [].slice.call(parentNode.querySelectorAll('.open-editor > a'));

I
isidor 已提交
417 418
		return dom.getLargestChildWidth(parentNode, childNodes);
	}
I
isidor 已提交
419
}
I
isidor 已提交
420 421 422 423 424

interface IOpenEditorTemplateData {
	container: HTMLElement;
	root: EditorLabel;
	actionBar: ActionBar;
I
isidor 已提交
425
	openEditor: OpenEditor;
426
	toDispose: IDisposable[];
I
isidor 已提交
427 428 429 430 431 432
}

interface IEditorGroupTemplateData {
	root: HTMLElement;
	name: HTMLSpanElement;
	actionBar: ActionBar;
433 434
	editorGroup: IEditorGroup;
	toDispose: IDisposable[];
I
isidor 已提交
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
}

class OpenEditorsDelegate implements IDelegate<OpenEditor | IEditorGroup> {

	public static readonly ITEM_HEIGHT = 22;

	getHeight(element: OpenEditor | IEditorGroup): number {
		return OpenEditorsDelegate.ITEM_HEIGHT;
	}

	getTemplateId(element: OpenEditor | IEditorGroup): string {
		if (element instanceof EditorGroup) {
			return EditorGroupRenderer.ID;
		}

		return OpenEditorRenderer.ID;
	}
}

class EditorGroupRenderer implements IRenderer<IEditorGroup, IEditorGroupTemplateData> {
455
	static readonly ID = 'editorgroup';
I
isidor 已提交
456

457 458
	private transfer = LocalSelectionTransfer.getInstance<OpenEditor>();

I
isidor 已提交
459 460
	constructor(
		private keybindingService: IKeybindingService,
461 462
		private instantiationService: IInstantiationService,
		private editorGroupService: IEditorGroupService
I
isidor 已提交
463 464 465 466 467 468 469 470 471 472 473 474 475 476
	) {
		// noop
	}

	get templateId() {
		return EditorGroupRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IEditorGroupTemplateData {
		const editorGroupTemplate: IEditorGroupTemplateData = Object.create(null);
		editorGroupTemplate.root = dom.append(container, $('.editor-group'));
		editorGroupTemplate.name = dom.append(editorGroupTemplate.root, $('span.name'));
		editorGroupTemplate.actionBar = new ActionBar(container);

477 478 479
		const saveAllInGroupAction = this.instantiationService.createInstance(SaveAllInGroupAction, SaveAllInGroupAction.ID, SaveAllInGroupAction.LABEL);
		const key = this.keybindingService.lookupKeybinding(saveAllInGroupAction.id);
		editorGroupTemplate.actionBar.push(saveAllInGroupAction, { icon: true, label: false, keybinding: key ? key.getLabel() : void 0 });
I
isidor 已提交
480

481 482
		editorGroupTemplate.toDispose = [];
		editorGroupTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_OVER, (e: DragEvent) => {
483
			dom.addClass(container, 'focused');
484 485 486 487
		}));
		editorGroupTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_LEAVE, (e: DragEvent) => {
			dom.removeClass(container, 'focused');
		}));
488
		editorGroupTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DROP, e => {
489
			dom.removeClass(container, 'focused');
490 491 492 493 494 495

			const model = this.editorGroupService.getStacksModel();
			const positionOfTargetGroup = model.positionOfGroup(editorGroupTemplate.editorGroup);

			if (this.transfer.hasData(OpenEditor.prototype)) {
				this.transfer.getData(OpenEditor.prototype).forEach(oe =>
496
					this.editorGroupService.moveEditor(oe.editor, model.positionOfGroup(oe.legacyGroup), positionOfTargetGroup, { preserveFocus: true }));
497
				this.editorGroupService.activateGroup(positionOfTargetGroup);
498 499
			} else {
				const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false });
500
				dropHandler.handleDrop(e, () => this.editorGroupService.activateGroup(positionOfTargetGroup), () => positionOfTargetGroup);
501 502 503
			}
		}));

I
isidor 已提交
504 505 506 507
		return editorGroupTemplate;
	}

	renderElement(editorGroup: IEditorGroup, index: number, templateData: IEditorGroupTemplateData): void {
508
		templateData.editorGroup = editorGroup;
B
Benjamin Pasero 已提交
509
		templateData.name.textContent = `Group <${editorGroup.id}>`;
510
		templateData.actionBar.context = { groupId: editorGroup.id };
I
isidor 已提交
511 512 513 514
	}

	disposeTemplate(templateData: IEditorGroupTemplateData): void {
		templateData.actionBar.dispose();
515
		dispose(templateData.toDispose);
I
isidor 已提交
516 517 518 519
	}
}

class OpenEditorRenderer implements IRenderer<OpenEditor, IOpenEditorTemplateData> {
520
	static readonly ID = 'openeditor';
521 522

	private transfer = LocalSelectionTransfer.getInstance<OpenEditor>();
I
isidor 已提交
523 524

	constructor(
I
isidor 已提交
525
		private getSelectedElements: () => (OpenEditor | IEditorGroup)[],
I
isidor 已提交
526 527
		private instantiationService: IInstantiationService,
		private keybindingService: IKeybindingService,
I
isidor 已提交
528 529
		private configurationService: IConfigurationService,
		private editorGroupService: IEditorGroupService
I
isidor 已提交
530 531 532 533 534 535 536 537 538 539 540 541
	) {
		// noop
	}

	get templateId() {
		return OpenEditorRenderer.ID;
	}

	renderTemplate(container: HTMLElement): IOpenEditorTemplateData {
		const editorTemplate: IOpenEditorTemplateData = Object.create(null);
		editorTemplate.container = container;
		editorTemplate.actionBar = new ActionBar(container);
I
isidor 已提交
542
		container.draggable = true;
I
isidor 已提交
543 544 545 546 547 548 549

		const closeEditorAction = this.instantiationService.createInstance(CloseEditorAction, CloseEditorAction.ID, CloseEditorAction.LABEL);
		const key = this.keybindingService.lookupKeybinding(closeEditorAction.id);
		editorTemplate.actionBar.push(closeEditorAction, { icon: true, label: false, keybinding: key ? key.getLabel() : void 0 });

		editorTemplate.root = this.instantiationService.createInstance(EditorLabel, container, void 0);

550 551
		editorTemplate.toDispose = [];

552
		editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_START, (e: DragEvent) => {
553
			const dragged = <OpenEditor[]>this.getSelectedElements().filter(e => e instanceof OpenEditor && !!e.getResource());
554 555 556 557

			const dragImage = document.createElement('div');
			e.dataTransfer.effectAllowed = 'copyMove';
			dragImage.className = 'monaco-tree-drag-image';
558
			dragImage.textContent = dragged.length === 1 ? editorTemplate.openEditor.editor.getName() : String(dragged.length);
559 560 561 562
			document.body.appendChild(dragImage);
			e.dataTransfer.setDragImage(dragImage, -10, -10);
			setTimeout(() => document.body.removeChild(dragImage), 0);

563
			this.transfer.setData(dragged, OpenEditor.prototype);
564

565
			if (editorTemplate.openEditor && editorTemplate.openEditor.editor) {
566
				this.instantiationService.invokeFunction(fillResourceDataTransfers, dragged.map(d => d.getResource()), e);
567
			}
I
isidor 已提交
568
		}));
569
		editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_OVER, () => {
570
			dom.addClass(container, 'focused');
I
isidor 已提交
571
		}));
572
		editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_LEAVE, () => {
I
isidor 已提交
573 574
			dom.removeClass(container, 'focused');
		}));
575
		editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DROP, (e: DragEvent) => {
I
isidor 已提交
576
			dom.removeClass(container, 'focused');
577
			const model = this.editorGroupService.getStacksModel();
578 579
			const positionOfTargetGroup = model.positionOfGroup(editorTemplate.openEditor.legacyGroup);
			const index = editorTemplate.openEditor.legacyGroup.indexOf(editorTemplate.openEditor.editor);
I
isidor 已提交
580

581
			if (this.transfer.hasData(OpenEditor.prototype)) {
582
				this.transfer.getData(OpenEditor.prototype).forEach((oe, offset) =>
583
					this.editorGroupService.moveEditor(oe.editor, model.positionOfGroup(oe.legacyGroup), positionOfTargetGroup, { index: index + offset, preserveFocus: true }));
584
				this.editorGroupService.activateGroup(positionOfTargetGroup);
585 586
			} else {
				const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false });
587
				dropHandler.handleDrop(e, () => this.editorGroupService.activateGroup(positionOfTargetGroup), () => positionOfTargetGroup, index);
I
isidor 已提交
588 589 590
			}
		}));
		editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_END, () => {
591
			this.transfer.clearData(OpenEditor.prototype);
I
isidor 已提交
592 593
		}));

I
isidor 已提交
594 595 596 597
		return editorTemplate;
	}

	renderElement(editor: OpenEditor, index: number, templateData: IOpenEditorTemplateData): void {
I
isidor 已提交
598
		templateData.openEditor = editor;
I
isidor 已提交
599
		editor.isDirty() ? dom.addClass(templateData.container, 'dirty') : dom.removeClass(templateData.container, 'dirty');
600
		templateData.root.setEditor(editor.editor, {
I
isidor 已提交
601 602 603 604
			italic: editor.isPreview(),
			extraClasses: ['open-editor'],
			fileDecorations: this.configurationService.getValue<IFilesConfiguration>().explorer.decorations
		});
605
		templateData.actionBar.context = { groupId: editor.group, editorIndex: editor.editorIndex };
I
isidor 已提交
606 607 608 609 610
	}

	disposeTemplate(templateData: IOpenEditorTemplateData): void {
		templateData.actionBar.dispose();
		templateData.root.dispose();
611 612 613
		dispose(templateData.toDispose);
	}
}