From 6278e4ff51b82b96e769e7552faf23c8d856918c Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 15 Jan 2020 11:25:32 +0100 Subject: [PATCH 1/3] use WorkbenchCompressibleAsyncDataTree --- .../debug/browser/loadedScriptsView.ts | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 0866f659ed8..99785153668 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -26,17 +26,19 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel } from 'vs/workbench/browser/labels'; import { FileKind } from 'vs/platform/files/common/files'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; +import { TreeResourceNavigator2, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; import { dispose } from 'vs/base/common/lifecycle'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { ILabelService } from 'vs/platform/label/common/label'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import type { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import type { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -const SMART = true; +const NEW_STYLE_COMPRESS = true; // RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt const URI_SCHEMA_PATTERN = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/; @@ -49,7 +51,7 @@ class BaseTreeItem { private _children = new Map(); private _source: Source | undefined; - constructor(private _parent: BaseTreeItem | undefined, private _label: string) { + constructor(private _parent: BaseTreeItem | undefined, private _label: string, public readonly isIncompressible = false) { this._showedMoreThanOne = false; } @@ -205,7 +207,7 @@ class BaseTreeItem { } private oneChild(): BaseTreeItem | undefined { - if (SMART && !this._source && !this._showedMoreThanOne && !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem)) { + if (!this._source && !this._showedMoreThanOne && this.skipOneChild()) { if (this._children.size === 1) { return this._children.values().next().value; } @@ -216,19 +218,28 @@ class BaseTreeItem { } return undefined; } + + private skipOneChild(): boolean { + if (NEW_STYLE_COMPRESS) { + // if the root node has only one Session, don't show the session + return this instanceof RootTreeItem; + } else { + return !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem); + } + } } class RootFolderTreeItem extends BaseTreeItem { constructor(parent: BaseTreeItem, public folder: IWorkspaceFolder) { - super(parent, folder.name); + super(parent, folder.name, true); } } class RootTreeItem extends BaseTreeItem { constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) { - super(undefined, 'Root'); + super(undefined, 'Root', true); this._debugModel.getSessions().forEach(session => { this.add(session); }); @@ -253,7 +264,7 @@ class SessionTreeItem extends BaseTreeItem { private _labelService: ILabelService; constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) { - super(parent, session.getLabel()); + super(parent, session.getLabel(), true); this._labelService = labelService; this._initialized = false; this._session = session; @@ -392,7 +403,7 @@ export class LoadedScriptsView extends ViewPane { private treeContainer!: HTMLElement; private loadedScriptsItemType: IContextKey; - private tree!: WorkbenchAsyncDataTree; + private tree!: WorkbenchCompressibleAsyncDataTree; private treeLabels!: ResourceLabels; private changeScheduler!: RunOnceScheduler; private treeNeedsRefreshOnVisible = false; @@ -428,15 +439,25 @@ export class LoadedScriptsView extends ViewPane { this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); this._register(this.treeLabels); - this.tree = this.instantiationService.createInstance>(WorkbenchAsyncDataTree, 'LoadedScriptsView', this.treeContainer, new LoadedScriptsDelegate(), + this.tree = this.instantiationService.createInstance>( + WorkbenchCompressibleAsyncDataTree, + 'LoadedScriptsView', + this.treeContainer, + new LoadedScriptsDelegate(), + { + isIncompressible: (element: BaseTreeItem): boolean => element.isIncompressible + }, [new LoadedScriptsRenderer(this.treeLabels)], new LoadedScriptsDataSource(), { + compressionEnabled: NEW_STYLE_COMPRESS, + autoExpandSingleChildren: NEW_STYLE_COMPRESS, identityProvider: { getId: (element: LoadedScriptsItem) => element.getId() }, keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: (element: LoadedScriptsItem) => element.getLabel() + getKeyboardNavigationLabel: (element: LoadedScriptsItem) => element.getLabel(), + getCompressedNodeKeyboardNavigationLabel: () => 'foo' // TODO }, filter: this.filter, accessibilityProvider: new LoadedSciptsAccessibilityProvider(), @@ -573,7 +594,7 @@ interface ILoadedScriptsItemTemplateData { label: IResourceLabel; } -class LoadedScriptsRenderer implements ITreeRenderer { +class LoadedScriptsRenderer implements ICompressibleTreeRenderer { static readonly ID = 'lsrenderer'; @@ -594,9 +615,23 @@ class LoadedScriptsRenderer implements ITreeRenderer, index: number, data: ILoadedScriptsItemTemplateData): void { const element = node.element; + const label = element.getLabel(); + + this.render(element, label, data, node.filterData); + } + + renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, data: ILoadedScriptsItemTemplateData, height: number | undefined): void { + + const element = node.element.elements[node.element.elements.length - 1]; + const labels = node.element.elements.map(e => e.getLabel()); + + this.render(element, labels, data, node.filterData); + } + + private render(element: BaseTreeItem, labels: string | string[], data: ILoadedScriptsItemTemplateData, filterData: FuzzyScore | undefined) { const label: IResourceLabelProps = { - name: element.getLabel() + name: labels }; const options: IResourceLabelOptions = { title: element.getHoverLabel() @@ -621,7 +656,7 @@ class LoadedScriptsRenderer implements ITreeRenderer Date: Thu, 16 Jan 2020 14:15:59 +0100 Subject: [PATCH 2/3] fix missing type --- src/vs/platform/list/browser/listService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 77dbbdd54cf..845eb28845e 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -687,7 +687,7 @@ export class TreeResourceNavigator2 extends Disposable { readonly onDidOpenResource: Event> = this._onDidOpenResource.event; constructor( - private tree: WorkbenchObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, + private tree: WorkbenchObjectTree | WorkbenchCompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, options?: IResourceResultsNavigationOptions2 ) { super(); From 9a2874e954857f1a15b5bffd64075f1f16b5c13b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 16 Jan 2020 15:07:08 +0100 Subject: [PATCH 3/3] loaded scripts: support filtering across all expanded items --- .../debug/browser/loadedScriptsView.ts | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index f7dc0e745f0..f11d256047e 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -381,17 +381,22 @@ class SessionTreeItem extends BaseTreeItem { } } +interface IViewState { + readonly expanded: Set; +} + /** * This maps a model item into a view model item. */ -function asTreeElement(item: BaseTreeItem): ITreeElement { +function asTreeElement(item: BaseTreeItem, viewState?: IViewState): ITreeElement { const children = item.getChildren(); + const collapsed = viewState ? !viewState.expanded.has(item.getId()) : !(item instanceof SessionTreeItem); return { element: item, - collapsed: !(item instanceof SessionTreeItem), + collapsed, collapsible: item.hasChildren(), - children: children.map(asTreeElement) + children: children.map(i => asTreeElement(i, viewState)) }; } @@ -443,6 +448,7 @@ export class LoadedScriptsView extends ViewPane { { compressionEnabled: NEW_STYLE_COMPRESS, collapseByDefault: true, + hideTwistiesOfChildlessElements: true, identityProvider: { getId: (element: LoadedScriptsItem) => element.getId() }, @@ -459,7 +465,7 @@ export class LoadedScriptsView extends ViewPane { } ); - const updateView = () => this.tree.setChildren(null, asTreeElement(root).children); + const updateView = (viewState?: IViewState) => this.tree.setChildren(null, asTreeElement(root, viewState).children); updateView(); @@ -556,6 +562,35 @@ export class LoadedScriptsView extends ViewPane { } })); + // feature: expand all nodes when filtering (not when finding) + let viewState: IViewState | undefined; + this._register(this.tree.onDidChangeTypeFilterPattern(pattern => { + if (!this.tree.options.filterOnType) { + return; + } + + if (!viewState && pattern) { + const expanded = new Set(); + const visit = (node: ITreeNode) => { + if (node.element && !node.collapsed) { + expanded.add(node.element.getId()); + } + + for (const child of node.children) { + visit(child); + } + }; + + visit(this.tree.getNode()); + viewState = { expanded }; + this.tree.expandAll(); + } else if (!pattern && viewState) { + this.tree.setFocus([]); + updateView(viewState); + viewState = undefined; + } + })); + // populate tree model with source paths from all debug sessions this.debugService.getModel().getSessions().forEach(session => addSourcePathsToSession(session)); }