explorerView.ts 23.0 KB
Newer Older
E
Erich Gamma 已提交
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 * as nls from 'vs/nls';
7
import { URI } from 'vs/base/common/uri';
8
import * as perf from 'vs/base/common/performance';
9
import { Action, IAction } from 'vs/base/common/actions';
10
import { memoize } from 'vs/base/common/decorators';
11
import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut } from 'vs/workbench/contrib/files/common/files';
12
import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView } from 'vs/workbench/contrib/files/browser/fileActions';
13
import { toResource } from 'vs/workbench/common/editor';
14
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
M
Maxime Quandalle 已提交
15
import * as DOM from 'vs/base/browser/dom';
B
Benjamin Pasero 已提交
16
import { CollapseAction } from 'vs/workbench/browser/viewlet';
17
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
18
import { ExplorerDecorationsProvider } from 'vs/workbench/contrib/files/browser/views/explorerDecorationsProvider';
I
isidor 已提交
19
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
20
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
J
Johannes Rieken 已提交
21 22 23 24
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
25
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
26
import { ResourceContextKey } from 'vs/workbench/common/resources';
I
isidor 已提交
27
import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations';
I
isidor 已提交
28
import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
S
Sandeep Somavarapu 已提交
29
import { DelayedDragHandler } from 'vs/base/browser/dnd';
30
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
I
isidor 已提交
31
import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
I
isidor 已提交
32
import { ILabelService } from 'vs/platform/label/common/label';
33
import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, FilesFilter, FileSorter, FileDragAndDrop } from 'vs/workbench/contrib/files/browser/views/explorerViewer';
I
isidor 已提交
34 35 36 37 38
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
39
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
40
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
41
import { onUnexpectedError } from 'vs/base/common/errors';
42
import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels';
J
Joao Moreno 已提交
43
import { createFileIconThemableTreeContainerScope } from 'vs/workbench/browser/parts/views/views';
44 45
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree';
46
import { FuzzyScore } from 'vs/base/common/filters';
47
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
48 49 50
import { isMacintosh } from 'vs/base/common/platform';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
I
isidor 已提交
51
import { isEqualOrParent } from 'vs/base/common/resources';
I
isidor 已提交
52

I
isidor 已提交
53
export class ExplorerView extends ViewletPanel {
I
isidor 已提交
54
	static readonly ID: string = 'workbench.explorer.fileView';
55
	static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState';
E
Erich Gamma 已提交
56

57
	private tree: WorkbenchAsyncDataTree<ExplorerItem | ExplorerItem[], ExplorerItem, FuzzyScore>;
I
isidor 已提交
58
	private filter: FilesFilter;
E
Erich Gamma 已提交
59

60
	private resourceContext: ResourceContextKey;
61
	private folderContext: IContextKey<boolean>;
I
isidor 已提交
62
	private readonlyContext: IContextKey<boolean>;
63
	private rootContext: IContextKey<boolean>;
64

65
	// Refresh is needed on the initial explorer open
I
isidor 已提交
66
	private shouldRefresh = true;
S
Sandeep Somavarapu 已提交
67
	private dragHandler: DelayedDragHandler;
I
isidor 已提交
68
	private decorationProvider: ExplorerDecorationsProvider;
I
isidor 已提交
69
	private autoReveal = false;
E
Erich Gamma 已提交
70 71

	constructor(
72
		options: IViewletPanelOptions,
E
Erich Gamma 已提交
73
		@IContextMenuService contextMenuService: IContextMenuService,
74 75 76 77
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
		@IProgressService private readonly progressService: IProgressService,
		@IEditorService private readonly editorService: IEditorService,
78
		@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
B
Benjamin Pasero 已提交
79
		@IKeybindingService keybindingService: IKeybindingService,
I
isidor 已提交
80
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
81
		@IConfigurationService configurationService: IConfigurationService,
I
isidor 已提交
82
		@IDecorationsService decorationService: IDecorationsService,
I
isidor 已提交
83 84 85 86 87
		@ILabelService private readonly labelService: ILabelService,
		@IThemeService private readonly themeService: IWorkbenchThemeService,
		@IListService private readonly listService: IListService,
		@IMenuService private readonly menuService: IMenuService,
		@ITelemetryService private readonly telemetryService: ITelemetryService,
88
		@IExplorerService private readonly explorerService: IExplorerService,
89 90
		@IStorageService private readonly storageService: IStorageService,
		@IClipboardService private clipboardService: IClipboardService
E
Erich Gamma 已提交
91
	) {
I
isidor 已提交
92
		super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService);
E
Erich Gamma 已提交
93

94
		this.resourceContext = instantiationService.createInstance(ResourceContextKey);
B
Benjamin Pasero 已提交
95
		this.disposables.push(this.resourceContext);
96
		this.folderContext = ExplorerFolderContext.bindTo(contextKeyService);
97
		this.readonlyContext = ExplorerResourceReadonlyContext.bindTo(contextKeyService);
98
		this.rootContext = ExplorerRootContext.bindTo(contextKeyService);
99

I
isidor 已提交
100
		this.decorationProvider = new ExplorerDecorationsProvider(this.explorerService, contextService);
I
isidor 已提交
101 102
		decorationService.registerDecorationsProvider(this.decorationProvider);
		this.disposables.push(this.decorationProvider);
103
		this.disposables.push(this.resourceContext);
104 105
	}

I
isidor 已提交
106
	get name(): string {
I
isidor 已提交
107
		return this.labelService.getWorkspaceLabel(this.contextService.getWorkspace());
I
isidor 已提交
108 109
	}

I
isidor 已提交
110
	get title(): string {
I
isidor 已提交
111 112 113
		return this.name;
	}

I
isidor 已提交
114
	set title(value: string) {
I
isidor 已提交
115 116 117
		// noop
	}

I
isidor 已提交
118 119 120 121 122 123 124 125 126 127 128
	// Memoized locals
	@memoize private get contributedContextMenu(): IMenu {
		const contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, this.tree.contextKeyService);
		this.disposables.push(contributedContextMenu);
		return contributedContextMenu;
	}

	@memoize private get fileCopiedContextKey(): IContextKey<boolean> {
		return FileCopiedContext.bindTo(this.contextKeyService);
	}

129 130 131 132
	@memoize private get resourceCutContextKey(): IContextKey<boolean> {
		return ExplorerResourceCut.bindTo(this.contextKeyService);
	}

I
isidor 已提交
133 134
	// Split view methods

I
isidor 已提交
135 136 137 138 139
	protected renderHeader(container: HTMLElement): void {
		super.renderHeader(container);

		// Expand on drag over
		this.dragHandler = new DelayedDragHandler(container, () => this.setExpanded(true));
140

I
isidor 已提交
141 142 143 144 145 146 147
		const titleElement = container.querySelector('.title') as HTMLElement;
		const setHeader = () => {
			const workspace = this.contextService.getWorkspace();
			const title = workspace.folders.map(folder => folder.name).join();
			titleElement.textContent = this.name;
			titleElement.title = title;
		};
148

I
isidor 已提交
149
		this.disposables.push(this.contextService.onDidChangeWorkspaceName(setHeader));
150
		this.disposables.push(this.labelService.onDidChangeFormatters(setHeader));
I
isidor 已提交
151 152
		setHeader();
	}
153

154 155
	protected layoutBody(height: number, width: number): void {
		this.tree.layout(height, width);
156 157
	}

I
isidor 已提交
158 159 160
	renderBody(container: HTMLElement): void {
		const treeContainer = DOM.append(container, DOM.$('.explorer-folders-view'));
		this.createTree(treeContainer);
E
Erich Gamma 已提交
161

162
		if (this.toolbar) {
163
			this.toolbar.setActions(this.getActions(), this.getSecondaryActions())();
I
isidor 已提交
164
		}
165

166
		this.disposables.push(this.labelService.onDidChangeFormatters(() => {
167
			this._onDidChangeTitleArea.fire();
I
isidor 已提交
168
			this.refresh();
169
		}));
I
isidor 已提交
170

I
isidor 已提交
171
		this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInput()));
I
isidor 已提交
172
		this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e)));
173
		this.disposables.push(this.explorerService.onDidChangeEditable(async e => {
174
			const isEditing = !!this.explorerService.getEditableData(e);
175 176 177 178

			if (isEditing) {
				await this.tree.expand(e.parent);
			} else {
179
				DOM.removeClass(treeContainer, 'highlight');
180 181 182 183
			}

			await this.refresh(e.parent);

184
			if (isEditing) {
185
				DOM.addClass(treeContainer, 'highlight');
186
				this.tree.reveal(e);
I
isidor 已提交
187 188
			} else {
				this.tree.domFocus();
189 190
			}
		}));
I
isidor 已提交
191
		this.disposables.push(this.explorerService.onDidSelectResource(e => this.onSelectResource(e.resource, e.reveal)));
I
isidor 已提交
192
		this.disposables.push(this.explorerService.onDidCopyItems(e => this.onCopyItems(e.items, e.cut, e.previouslyCutItems)));
I
isidor 已提交
193 194 195 196 197 198

		// Update configuration
		const configuration = this.configurationService.getValue<IFilesConfiguration>();
		this.onConfigurationUpdated(configuration);

		// When the explorer viewer is loaded, listen to changes to the editor input
I
isidor 已提交
199
		this.disposables.push(this.editorService.onDidActiveEditorChange(() => {
I
isidor 已提交
200
			this.selectActiveFile(true);
I
isidor 已提交
201
		}));
I
isidor 已提交
202 203 204

		// Also handle configuration updates
		this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>(), e)));
205

206
		this.disposables.push(this.onDidChangeBodyVisibility(async visible => {
207 208 209 210
			if (visible) {
				// If a refresh was requested and we are now visible, run it
				if (this.shouldRefresh) {
					this.shouldRefresh = false;
I
isidor 已提交
211
					await this.setTreeInput();
212 213
				}
				// Find resource to focus from active editor input if set
I
isidor 已提交
214
				this.selectActiveFile(false, true);
215 216
			}
		}));
E
Erich Gamma 已提交
217 218
	}

219
	getActions(): IAction[] {
I
isidor 已提交
220
		const actions: Action[] = [];
E
Erich Gamma 已提交
221

222 223 224 225 226 227
		const getFocus = () => {
			const focus = this.tree.getFocus();
			return focus.length > 0 ? focus[0] : undefined;
		};
		actions.push(this.instantiationService.createInstance(NewFileAction, getFocus));
		actions.push(this.instantiationService.createInstance(NewFolderAction, getFocus));
228
		actions.push(this.instantiationService.createInstance(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL));
B
Benjamin Pasero 已提交
229
		actions.push(this.instantiationService.createInstance(CollapseAction, this.tree, true, 'explorer-action collapse-explorer'));
E
Erich Gamma 已提交
230

I
isidor 已提交
231
		return actions;
E
Erich Gamma 已提交
232 233
	}

I
isidor 已提交
234
	focus(): void {
I
isidor 已提交
235
		this.tree.domFocus();
I
isidor 已提交
236

I
isidor 已提交
237 238 239 240 241 242 243 244 245 246 247
		const focused = this.tree.getFocus();
		if (focused.length === 1) {
			if (this.autoReveal) {
				this.tree.reveal(focused[0], 0.5);
			}

			const activeFile = this.getActiveFile();
			if (!activeFile && !focused[0].isDirectory) {
				// Open the focused element in the editor if there is currently no file opened #67708
				this.editorService.openEditor({ resource: focused[0].resource, options: { preserveFocus: true, revealIfVisible: true } })
					.then(undefined, onUnexpectedError);
I
isidor 已提交
248
			}
I
isidor 已提交
249
		}
I
isidor 已提交
250 251
	}

I
isidor 已提交
252
	private selectActiveFile(deselect?: boolean, reveal = this.autoReveal): void {
253 254 255
		if (this.autoReveal) {
			const activeFile = this.getActiveFile();
			if (activeFile) {
I
isidor 已提交
256 257 258 259 260
				const focus = this.tree.getFocus();
				if (focus.length === 1 && focus[0].resource.toString() === activeFile.toString()) {
					// No action needed, active file is already focused
					return;
				}
261
				this.explorerService.select(this.getActiveFile(), reveal);
I
isidor 已提交
262
			} else if (deselect) {
263
				this.tree.setSelection([]);
264
				this.tree.setFocus([]);
265 266 267 268
			}
		}
	}

I
isidor 已提交
269 270
	private createTree(container: HTMLElement): void {
		this.filter = this.instantiationService.createInstance(FilesFilter);
J
Joao Moreno 已提交
271
		this.disposables.push(this.filter);
I
isidor 已提交
272 273 274
		const explorerLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer);
		this.disposables.push(explorerLabels);

J
Joao Moreno 已提交
275 276
		const updateWidth = (stat: ExplorerItem) => this.tree.updateWidth(stat);
		const filesRenderer = this.instantiationService.createInstance(FilesRenderer, explorerLabels, updateWidth);
I
isidor 已提交
277 278
		this.disposables.push(filesRenderer);

J
Joao Moreno 已提交
279 280
		this.disposables.push(createFileIconThemableTreeContainerScope(container, this.themeService));

I
isidor 已提交
281
		this.tree = new WorkbenchAsyncDataTree(container, new ExplorerDelegate(), [filesRenderer],
I
isidor 已提交
282
			this.instantiationService.createInstance(ExplorerDataSource), {
I
isidor 已提交
283 284 285 286 287
				accessibilityProvider: new ExplorerAccessibilityProvider(),
				ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"),
				identityProvider: {
					getId: stat => stat.resource
				},
I
isidor 已提交
288
				keyboardNavigationLabelProvider: {
289 290 291 292 293 294 295
					getKeyboardNavigationLabel: stat => {
						if (this.explorerService.isEditable(stat)) {
							return undefined;
						}

						return stat.name;
					}
I
isidor 已提交
296
				},
I
isidor 已提交
297
				multipleSelectionSupport: true,
I
isidor 已提交
298
				filter: this.filter,
I
isidor 已提交
299
				sorter: this.instantiationService.createInstance(FileSorter),
J
Joao Moreno 已提交
300 301
				dnd: this.instantiationService.createInstance(FileDragAndDrop),
				autoExpandSingleChildren: true
I
isidor 已提交
302
			}, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService);
I
isidor 已提交
303
		this.disposables.push(this.tree);
304

J
Joao Moreno 已提交
305
		// Bind context keys
I
isidor 已提交
306 307
		FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService);
		ExplorerFocusedContext.bindTo(this.tree.contextKeyService);
E
Erich Gamma 已提交
308

309
		// Update resource context based on focused element
I
isidor 已提交
310 311
		this.disposables.push(this.tree.onDidChangeFocus(e => this.onFocusChanged(e.elements)));
		this.onFocusChanged([]);
I
isidor 已提交
312 313
		const explorerNavigator = new TreeResourceNavigator2(this.tree);
		this.disposables.push(explorerNavigator);
314
		// Open when selecting via keyboard
I
isidor 已提交
315 316
		this.disposables.push(explorerNavigator.onDidOpenResource(e => {
			const selection = this.tree.getSelection();
I
isidor 已提交
317
			// Do not react if the user is expanding selection via keyboard.
318
			// Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection #66589.
319 320
			const shiftDown = e.browserEvent instanceof KeyboardEvent && e.browserEvent.shiftKey;
			if (selection.length === 1 && !shiftDown) {
321
				// Do not react if user is clicking on explorer items which are input placeholders
322
				if (!selection[0].name || selection[0].isDirectory) {
I
isidor 已提交
323
					// Do not react if user is clicking on explorer items which are input placeholders
324
					// Do not react if clicking on directories
325 326
					return;
				}
I
isidor 已提交
327

328 329 330 331 332 333
				/* __GDPR__
				"workbenchActionExecuted" : {
					"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
					"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}*/
				this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' });
I
isidor 已提交
334
				this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned } }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP)
335
					.then(undefined, onUnexpectedError);
336 337 338
			}
		}));

I
isidor 已提交
339
		this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e)));
340 341 342 343 344 345 346 347 348 349 350
		this.disposables.push(this.tree.onKeyDown(e => {
			const event = new StandardKeyboardEvent(e);
			const toggleCollapsed = isMacintosh ? (event.keyCode === KeyCode.DownArrow && event.metaKey) : event.keyCode === KeyCode.Enter;
			if (toggleCollapsed) {
				const focus = this.tree.getFocus();
				if (focus.length === 1 && focus[0].isDirectory) {
					this.tree.toggleCollapsed(focus[0]);
				}
			}
		}));

351 352 353 354 355

		// save view state on shutdown
		this.storageService.onWillSaveState(() => {
			this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE);
		}, null, this.disposables);
S
Sandeep Somavarapu 已提交
356 357
	}

I
isidor 已提交
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
	// React on events

	private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void {
		this.autoReveal = configuration && configuration.explorer && configuration.explorer.autoReveal;

		// Push down config updates to components of viewer
		let needsRefresh = false;
		if (this.filter) {
			needsRefresh = this.filter.updateConfiguration();
		}

		if (event && !needsRefresh) {
			needsRefresh = event.affectsConfiguration('explorer.decorations.colors')
				|| event.affectsConfiguration('explorer.decorations.badges');
		}

		// Refresh viewer as needed if this originates from a config event
		if (event && needsRefresh) {
376
			this.refresh();
I
isidor 已提交
377 378 379 380 381 382
		}
	}

	private onContextMenu(e: ITreeContextMenuEvent<ExplorerItem>): void {
		const stat = e.element;

383 384 385
		// update dynamic contexts
		this.fileCopiedContextKey.set(this.clipboardService.hasResources());

I
isidor 已提交
386 387 388 389 390
		const selection = this.tree.getSelection();
		this.contextMenuService.showContextMenu({
			getAnchor: () => e.anchor,
			getActions: () => {
				const actions: IAction[] = [];
391 392 393 394
				// If the click is outside of the elements pass the root resource if there is only one root. If there are multiple roots pass empty object.
				const roots = this.explorerService.roots;
				const arg = stat instanceof ExplorerItem ? stat.resource : roots.length === 1 ? roots[0].resource : {};
				fillInContextMenuActions(this.contributedContextMenu, { arg, shouldForwardArgs: true }, actions, this.contextMenuService);
I
isidor 已提交
395 396 397 398 399 400 401 402 403 404 405 406 407
				return actions;
			},
			onHide: (wasCancelled?: boolean) => {
				if (wasCancelled) {
					this.tree.domFocus();
				}
			},
			getActionsContext: () => selection && selection.indexOf(stat) >= 0
				? selection.map((fs: ExplorerItem) => fs.resource)
				: stat instanceof ExplorerItem ? [stat.resource] : []
		});
	}

I
isidor 已提交
408 409 410 411 412 413 414 415 416 417
	private onFocusChanged(elements: ExplorerItem[]): void {
		const stat = elements && elements.length ? elements[0] : undefined;
		const isSingleFolder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER;
		const resource = stat ? stat.resource : isSingleFolder ? this.contextService.getWorkspace().folders[0].uri : undefined;
		this.resourceContext.set(resource);
		this.folderContext.set((isSingleFolder && !stat) || !!stat && stat.isDirectory);
		this.readonlyContext.set(!!stat && stat.isReadonly);
		this.rootContext.set(!stat || (stat && stat.isRoot));
	}

I
isidor 已提交
418 419
	// General methods

E
Erich Gamma 已提交
420 421
	/**
	 * Refresh the contents of the explorer to get up to date data from the disk about the file structure.
422
	 * If the item is passed we refresh only that level of the tree, otherwise we do a full refresh.
E
Erich Gamma 已提交
423
	 */
I
isidor 已提交
424
	private refresh(item?: ExplorerItem): Promise<void> {
I
isidor 已提交
425
		if (!this.tree || !this.isBodyVisible()) {
I
isidor 已提交
426
			this.shouldRefresh = true;
R
Rob Lourens 已提交
427
			return Promise.resolve(undefined);
428
		}
J
Joao Moreno 已提交
429 430 431 432 433 434

		// Tree node doesn't exist yet
		if (item && !this.tree.hasNode(item)) {
			return Promise.resolve(undefined);
		}

435
		const recursive = !item;
436
		const toRefresh = item || this.tree.getInput();
437

438
		return this.tree.updateChildren(toRefresh, recursive);
439
	}
440

I
isidor 已提交
441 442 443 444 445 446 447
	getOptimalWidth(): number {
		const parentNode = this.tree.getHTMLElement();
		const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels

		return DOM.getLargestChildWidth(parentNode, childNodes);
	}

448 449
	// private didLoad = false;

I
isidor 已提交
450
	private setTreeInput(): Promise<void> {
I
isidor 已提交
451
		if (!this.isBodyVisible()) {
452
			this.shouldRefresh = true;
I
isidor 已提交
453
			return Promise.resolve(undefined);
454 455
		}

I
isidor 已提交
456 457 458 459
		const initialInputSetup = !this.tree.getInput();
		if (initialInputSetup) {
			perf.mark('willResolveExplorer');
		}
460 461 462 463 464 465
		const roots = this.explorerService.roots;
		let input: ExplorerItem | ExplorerItem[] = roots[0];
		if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || roots[0].isError) {
			// Display roots only when multi folder workspace
			input = roots;
		}
466

467
		let viewState: IAsyncDataTreeViewState | undefined;
468 469 470 471 472 473 474
		if (this.tree && this.tree.getInput()) {
			viewState = this.tree.getViewState();
		} else {
			const rawViewState = this.storageService.get(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, StorageScope.WORKSPACE);
			if (rawViewState) {
				viewState = JSON.parse(rawViewState) as IAsyncDataTreeViewState;
			}
475 476
		}

477
		const previousInput = this.tree.getInput();
478
		const promise = this.tree.setInput(input, viewState).then(() => {
I
isidor 已提交
479 480 481 482 483 484 485 486 487
			if (Array.isArray(input)) {
				if (!viewState || previousInput instanceof ExplorerItem) {
					// There is no view state for this workspace, expand all roots. Or we transitioned from a folder workspace.
					input.forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError));
				}
				if (Array.isArray(previousInput) && previousInput.length < input.length) {
					// Roots added to the explorer -> expand them.
					input.slice(previousInput.length).forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError));
				}
488
			}
I
isidor 已提交
489 490 491 492
			if (initialInputSetup) {
				perf.mark('didResolveExplorer');
			}
		});
E
Erich Gamma 已提交
493

494
		this.progressService.showWhile(promise, this.layoutService.isRestored() ? 800 : 1200 /* less ugly initial startup */);
495 496 497
		return promise;
	}

M
Matt Bierner 已提交
498
	private getActiveFile(): URI | undefined {
I
isidor 已提交
499 500 501 502 503 504 505 506 507 508 509
		const input = this.editorService.activeEditor;

		// ignore diff editor inputs (helps to get out of diffing when returning to explorer)
		if (input instanceof DiffEditorInput) {
			return undefined;
		}

		// check for files
		return toResource(input, { supportSideBySide: true });
	}

I
isidor 已提交
510 511 512
	private onSelectResource(resource: URI, reveal = this.autoReveal): void {
		if (!resource || !this.isBodyVisible()) {
			return;
E
Erich Gamma 已提交
513 514
		}

515
		// Expand all stats in the parent chain
I
isidor 已提交
516 517 518 519 520 521 522
		const root = this.explorerService.roots.filter(r => isEqualOrParent(resource, r.resource)).pop();
		if (root) {
			const traverse = async (item: ExplorerItem) => {
				if (item.resource.toString() === resource.toString()) {
					if (reveal) {
						this.tree.reveal(item, 0.5);
					}
523

I
isidor 已提交
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
					this.tree.setFocus([item]);
					this.tree.setSelection([item]);
				} else {
					await this.tree.expand(item);
					let found = false;
					item.children.forEach(async child => {
						if (!found && isEqualOrParent(resource, child.resource)) {
							found = true;
							await traverse(child);
						}
					});
				}
			};
			traverse(root);
		}
E
Erich Gamma 已提交
539 540
	}

I
isidor 已提交
541 542
	private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[]): void {
		this.fileCopiedContextKey.set(stats.length > 0);
543
		this.resourceCutContextKey.set(cut && stats.length > 0);
I
isidor 已提交
544
		if (previousCut) {
I
isidor 已提交
545
			previousCut.forEach(item => this.tree.rerender(item));
I
isidor 已提交
546 547
		}
		if (cut) {
I
isidor 已提交
548
			stats.forEach(s => this.tree.rerender(s));
I
isidor 已提交
549 550 551
		}
	}

I
isidor 已提交
552 553
	collapseAll(): void {
		this.tree.collapseAll();
E
Erich Gamma 已提交
554
	}
S
Sandeep Somavarapu 已提交
555 556 557 558 559 560 561

	dispose(): void {
		if (this.dragHandler) {
			this.dragHandler.dispose();
		}
		super.dispose();
	}
J
Johannes Rieken 已提交
562
}