From 619c082c72ea947aaec1f0c8f8546cb359fcb4a4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Nov 2019 15:50:08 +0100 Subject: [PATCH] explorer.compressSingleChildFolders --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 8 ++++++++ src/vs/base/browser/ui/tree/objectTree.ts | 16 ++++++++++------ .../base/test/browser/ui/tree/objectTree.test.ts | 6 ++---- .../contrib/files/browser/files.contribution.ts | 7 ++++++- .../contrib/files/browser/views/explorerView.ts | 9 ++++++++- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index f6586f94391ab..50ab5ce9be3c3 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -1025,6 +1025,10 @@ export interface ICompressibleAsyncDataTreeOptions extend readonly keyboardNavigationLabelProvider?: ICompressibleKeyboardNavigationLabelProvider; } +export interface ICompressibleAsyncDataTreeOptionsUpdate extends IAsyncDataTreeOptionsUpdate { + readonly compressionEnabled?: boolean; +} + export class CompressibleAsyncDataTree extends AsyncDataTree { protected readonly tree: CompressibleObjectTree, TFilterData>; @@ -1063,6 +1067,10 @@ export class CompressibleAsyncDataTree extends As }; } + updateOptions(options: ICompressibleAsyncDataTreeOptionsUpdate = {}): void { + this.tree.updateOptions(options); + } + getViewState(): IAsyncDataTreeViewState { if (!this.identityProvider) { throw new TreeError(this.user, 'Can\'t get tree view state without an identity provider'); diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index e25d4dd0f1325..757683c18aff3 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ISequence } from 'vs/base/common/iterator'; -import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { AbstractTree, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; @@ -160,6 +160,10 @@ function asObjectTreeOptions(compressedTreeNodeProvider: () => I }; } +export interface ICompressibleObjectTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { + readonly compressionEnabled?: boolean; +} + export class CompressibleObjectTree, TFilterData = void> extends ObjectTree implements ICompressedTreeNodeProvider { protected model!: CompressibleObjectTreeModel; @@ -184,12 +188,12 @@ export class CompressibleObjectTree, TFilterData = vo return new CompressibleObjectTreeModel(user, view, options); } - isCompressionEnabled(): boolean { - return this.model.isCompressionEnabled(); - } + updateOptions(optionsUpdate: ICompressibleObjectTreeOptionsUpdate = {}): void { + super.updateOptions(optionsUpdate); - setCompressionEnabled(enabled: boolean): void { - this.model.setCompressionEnabled(enabled); + if (typeof optionsUpdate.compressionEnabled !== 'undefined') { + this.model.setCompressionEnabled(optionsUpdate.compressionEnabled); + } } getCompressedTreeNode(element: T | null = null): ITreeNode | null, TFilterData> { diff --git a/src/vs/base/test/browser/ui/tree/objectTree.test.ts b/src/vs/base/test/browser/ui/tree/objectTree.test.ts index 6a69ea4bf120c..d384c368d6ee3 100644 --- a/src/vs/base/test/browser/ui/tree/objectTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTree.test.ts @@ -348,8 +348,6 @@ suite('CompressibleObjectTree', function () { const tree = new CompressibleObjectTree('test', container, new Delegate(), [new Renderer()]); tree.layout(200); - assert.equal(tree.isCompressionEnabled(), true); - tree.setChildren(null, Iterator.fromArray([ { element: 1, children: Iterator.fromArray([{ @@ -367,11 +365,11 @@ suite('CompressibleObjectTree', function () { let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']); - tree.setCompressionEnabled(false); + tree.updateOptions({ compressionEnabled: false }); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1', '11', '111', '1111', '1112', '1113']); - tree.setCompressionEnabled(true); + tree.updateOptions({ compressionEnabled: true }); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']); }); diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 350dd14c5f4e1..732e55455e722 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -423,7 +423,12 @@ configurationRegistry.registerConfiguration({ ], description: nls.localize('explorer.incrementalNaming', "Controls what naming strategy to use when a giving a new name to a duplicated explorer item on paste."), default: 'simple' - } + }, + 'explorer.compressSingleChildFolders': { + 'type': 'boolean', + 'description': nls.localize('compressSingleChildFolders', "Controls whether the explorer should compress single child folders in a combined tree element. Useful for Java project folder structures, for example."), + 'default': false + }, } }); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index c14b33de0989a..2b4ff7f25781a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -50,6 +50,7 @@ import { first } from 'vs/base/common/arrays'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { dispose } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; @@ -282,9 +283,11 @@ export class ExplorerView extends ViewletPanel { this._register(createFileIconThemableTreeContainerScope(container, this.themeService)); + const isCompressionEnabled = () => this.configurationService.getValue('explorer.compressSingleChildFolders'); + this.tree = this.instantiationService.createInstance>(WorkbenchCompressibleAsyncDataTree, 'FileExplorer', container, new ExplorerDelegate(), new ExplorerCompressionDelegate(), [filesRenderer], this.instantiationService.createInstance(ExplorerDataSource), { - // compressionEnabled: false, + compressionEnabled: isCompressionEnabled(), accessibilityProvider: new ExplorerAccessibilityProvider(), ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), identityProvider: { @@ -321,6 +324,10 @@ export class ExplorerView extends ViewletPanel { }); this._register(this.tree); + // Bind configuration + const onDidChangeCompressionConfiguration = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('explorer.compressSingleChildFolders')); + this._register(onDidChangeCompressionConfiguration(_ => this.tree.updateOptions({ compressionEnabled: isCompressionEnabled() }))); + // Bind context keys FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); ExplorerFocusedContext.bindTo(this.tree.contextKeyService);