mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 01:25:37 -05:00
Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)
* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c * remove files we don't want * fix hygiene * update distro * update distro * fix hygiene * fix strict nulls * distro * distro * fix tests * fix tests * add another edit * fix viewlet icon * fix azure dialog * fix some padding * fix more padding issues
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list';
|
||||
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
@@ -19,6 +19,8 @@ import { toggleClass } from 'vs/base/browser/dom';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { ScrollEvent } from 'vs/base/common/scrollable';
|
||||
import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
import { IThemable } from 'vs/base/common/styler';
|
||||
import { isFilterResult, getVisibleState } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
|
||||
interface IAsyncDataTreeNode<TInput, T> {
|
||||
element: TInput | T;
|
||||
@@ -149,20 +151,24 @@ function asTreeContextMenuEvent<TInput, T>(e: ITreeContextMenuEvent<IAsyncDataTr
|
||||
};
|
||||
}
|
||||
|
||||
export enum ChildrenResolutionReason {
|
||||
Refresh,
|
||||
Expand
|
||||
}
|
||||
class AsyncDataTreeElementsDragAndDropData<TInput, T, TContext> extends ElementsDragAndDropData<T, TContext> {
|
||||
|
||||
export interface IChildrenResolutionEvent<T> {
|
||||
readonly element: T | null;
|
||||
readonly reason: ChildrenResolutionReason;
|
||||
set context(context: TContext | undefined) {
|
||||
this.data.context = context;
|
||||
}
|
||||
|
||||
get context(): TContext | undefined {
|
||||
return this.data.context;
|
||||
}
|
||||
|
||||
constructor(private data: ElementsDragAndDropData<IAsyncDataTreeNode<TInput, T>, TContext>) {
|
||||
super(data.elements.map(node => node.element as T));
|
||||
}
|
||||
}
|
||||
|
||||
function asAsyncDataTreeDragAndDropData<TInput, T>(data: IDragAndDropData): IDragAndDropData {
|
||||
if (data instanceof ElementsDragAndDropData) {
|
||||
const nodes = (data as ElementsDragAndDropData<IAsyncDataTreeNode<TInput, T>>).elements;
|
||||
return new ElementsDragAndDropData(nodes.map(node => node.element));
|
||||
return new AsyncDataTreeElementsDragAndDropData(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -176,9 +182,9 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
|
||||
return this.dnd.getDragURI(node.element as T);
|
||||
}
|
||||
|
||||
getDragLabel(nodes: IAsyncDataTreeNode<TInput, T>[]): string | undefined {
|
||||
getDragLabel(nodes: IAsyncDataTreeNode<TInput, T>[], originalEvent: DragEvent): string | undefined {
|
||||
if (this.dnd.getDragLabel) {
|
||||
return this.dnd.getDragLabel(nodes.map(node => node.element as T));
|
||||
return this.dnd.getDragLabel(nodes.map(node => node.element as T), originalEvent);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -197,6 +203,12 @@ class AsyncDataTreeNodeListDragAndDrop<TInput, T> implements IListDragAndDrop<IA
|
||||
drop(data: IDragAndDropData, targetNode: IAsyncDataTreeNode<TInput, T> | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void {
|
||||
this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent);
|
||||
}
|
||||
|
||||
onDragEnd(originalEvent: DragEvent): void {
|
||||
if (this.dnd.onDragEnd) {
|
||||
this.dnd.onDragEnd(originalEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOptions<T, TFilterData>): IObjectTreeOptions<IAsyncDataTreeNode<TInput, T>, TFilterData> | undefined {
|
||||
@@ -218,9 +230,16 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
|
||||
}
|
||||
},
|
||||
accessibilityProvider: options.accessibilityProvider && {
|
||||
...options.accessibilityProvider,
|
||||
getAriaLabel(e) {
|
||||
return options.accessibilityProvider!.getAriaLabel(e.element as T);
|
||||
}
|
||||
},
|
||||
getAriaLevel: options.accessibilityProvider!.getAriaLevel && (node => {
|
||||
return options.accessibilityProvider!.getAriaLevel!(node.element as T);
|
||||
}),
|
||||
getActiveDescendantId: options.accessibilityProvider.getActiveDescendantId && (node => {
|
||||
return options.accessibilityProvider!.getActiveDescendantId!(node.element as T);
|
||||
})
|
||||
},
|
||||
filter: options.filter && {
|
||||
filter(e, parentVisibility) {
|
||||
@@ -271,10 +290,10 @@ function dfs<TInput, T>(node: IAsyncDataTreeNode<TInput, T>, fn: (node: IAsyncDa
|
||||
node.children.forEach(child => dfs(child, fn));
|
||||
}
|
||||
|
||||
export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable {
|
||||
export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable, IThemable {
|
||||
|
||||
private readonly tree: ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
|
||||
private readonly root: IAsyncDataTreeNode<TInput, T>;
|
||||
protected readonly tree: ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
|
||||
protected readonly root: IAsyncDataTreeNode<TInput, T>;
|
||||
private readonly nodes = new Map<null | T, IAsyncDataTreeNode<TInput, T>>();
|
||||
private readonly sorter?: ITreeSorter<T>;
|
||||
private readonly collapseByDefault?: { (e: T): boolean; };
|
||||
@@ -282,7 +301,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
private readonly subTreeRefreshPromises = new Map<IAsyncDataTreeNode<TInput, T>, Promise<void>>();
|
||||
private readonly refreshPromises = new Map<IAsyncDataTreeNode<TInput, T>, CancelablePromise<T[]>>();
|
||||
|
||||
private readonly identityProvider?: IIdentityProvider<T>;
|
||||
protected readonly identityProvider?: IIdentityProvider<T>;
|
||||
private readonly autoExpandSingleChildren: boolean;
|
||||
|
||||
private readonly _onDidRender = new Emitter<void>();
|
||||
@@ -323,7 +342,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
get onDidDispose(): Event<void> { return this.tree.onDidDispose; }
|
||||
|
||||
constructor(
|
||||
private user: string,
|
||||
protected user: string,
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
@@ -411,10 +430,6 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
return this.tree.renderHeight;
|
||||
}
|
||||
|
||||
get firstVisibleElement(): T {
|
||||
return this.tree.firstVisibleElement!.element as T;
|
||||
}
|
||||
|
||||
get lastVisibleElement(): T {
|
||||
return this.tree.lastVisibleElement!.element as T;
|
||||
}
|
||||
@@ -445,7 +460,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
|
||||
const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext<TInput, T>;
|
||||
|
||||
await this._updateChildren(input, true, viewStateContext);
|
||||
await this._updateChildren(input, true, false, viewStateContext);
|
||||
|
||||
if (viewStateContext) {
|
||||
this.tree.setFocus(viewStateContext.focus);
|
||||
@@ -457,11 +472,11 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
async updateChildren(element: TInput | T = this.root.element, recursive = true): Promise<void> {
|
||||
await this._updateChildren(element, recursive);
|
||||
async updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false): Promise<void> {
|
||||
await this._updateChildren(element, recursive, rerender);
|
||||
}
|
||||
|
||||
private async _updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
|
||||
private async _updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
|
||||
if (typeof this.root.element === 'undefined') {
|
||||
throw new TreeError(this.user, 'Tree input not set');
|
||||
}
|
||||
@@ -471,7 +486,17 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
await Event.toPromise(this._onDidRender.event);
|
||||
}
|
||||
|
||||
await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext);
|
||||
const node = this.getDataNode(element);
|
||||
await this.refreshAndRenderNode(node, recursive, viewStateContext);
|
||||
|
||||
if (rerender) {
|
||||
try {
|
||||
this.tree.rerender(node);
|
||||
} catch {
|
||||
// missing nodes are fine, this could've resulted from
|
||||
// parallel refresh calls, removing `node` altogether
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resort(element: TInput | T = this.root.element, recursive = true): void {
|
||||
@@ -653,18 +678,9 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
return node;
|
||||
}
|
||||
|
||||
private async refreshAndRenderNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
|
||||
private async refreshAndRenderNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
|
||||
await this.refreshNode(node, recursive, viewStateContext);
|
||||
this.render(node, viewStateContext);
|
||||
|
||||
if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) {
|
||||
const treeNode = this.tree.getNode(node);
|
||||
const visibleChildren = treeNode.children.filter(node => node.visible);
|
||||
|
||||
if (visibleChildren.length === 1) {
|
||||
await this.tree.expand(visibleChildren[0].element, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
|
||||
@@ -752,12 +768,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
|
||||
result = createCancelablePromise(async () => {
|
||||
const children = await this.dataSource.getChildren(node.element!);
|
||||
|
||||
if (this.sorter) {
|
||||
children.sort(this.sorter.compare.bind(this.sorter));
|
||||
}
|
||||
|
||||
return children;
|
||||
return this.processChildren(children);
|
||||
});
|
||||
|
||||
this.refreshPromises.set(node, result);
|
||||
@@ -770,7 +781,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
if (deep) {
|
||||
this.collapse(node.element.element as T);
|
||||
} else {
|
||||
this.refreshAndRenderNode(node.element, false, ChildrenResolutionReason.Expand)
|
||||
this.refreshAndRenderNode(node.element, false)
|
||||
.catch(onUnexpectedError);
|
||||
}
|
||||
}
|
||||
@@ -783,13 +794,14 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
}
|
||||
|
||||
const nodesToForget = new Map<T, IAsyncDataTreeNode<TInput, T>>();
|
||||
const childrenTreeNodesById = new Map<string, ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>>();
|
||||
const childrenTreeNodesById = new Map<string, { node: IAsyncDataTreeNode<TInput, T>, collapsed: boolean }>();
|
||||
|
||||
for (const child of node.children) {
|
||||
nodesToForget.set(child.element as T, child);
|
||||
|
||||
if (this.identityProvider) {
|
||||
childrenTreeNodesById.set(child.id!, this.tree.getNode(child));
|
||||
const collapsed = this.tree.isCollapsed(child);
|
||||
childrenTreeNodesById.set(child.id!, { node: child, collapsed });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,10 +822,10 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
}
|
||||
|
||||
const id = this.identityProvider.getId(element).toString();
|
||||
const childNode = childrenTreeNodesById.get(id);
|
||||
const result = childrenTreeNodesById.get(id);
|
||||
|
||||
if (childNode) {
|
||||
const asyncDataTreeNode = childNode.element!;
|
||||
if (result) {
|
||||
const asyncDataTreeNode = result.node;
|
||||
|
||||
nodesToForget.delete(asyncDataTreeNode.element as T);
|
||||
this.nodes.delete(asyncDataTreeNode.element as T);
|
||||
@@ -823,8 +835,10 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
asyncDataTreeNode.hasChildren = hasChildren;
|
||||
|
||||
if (recursive) {
|
||||
if (childNode.collapsed) {
|
||||
dfs(asyncDataTreeNode, node => node.stale = true);
|
||||
if (result.collapsed) {
|
||||
asyncDataTreeNode.children.forEach(node => dfs(node, node => this.nodes.delete(node.element as T)));
|
||||
asyncDataTreeNode.children.splice(0, asyncDataTreeNode.children.length);
|
||||
asyncDataTreeNode.stale = true;
|
||||
} else {
|
||||
childrenToRefresh.push(asyncDataTreeNode);
|
||||
}
|
||||
@@ -866,10 +880,16 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
|
||||
node.children.splice(0, node.children.length, ...children);
|
||||
|
||||
// TODO@joao this doesn't take filter into account
|
||||
if (node !== this.root && this.autoExpandSingleChildren && children.length === 1 && childrenToRefresh.length === 0) {
|
||||
children[0].collapsedByDefault = false;
|
||||
childrenToRefresh.push(children[0]);
|
||||
}
|
||||
|
||||
return childrenToRefresh;
|
||||
}
|
||||
|
||||
private render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
|
||||
protected render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
|
||||
const children = node.children.map(node => this.asTreeElement(node, viewStateContext));
|
||||
this.tree.setChildren(node === this.root ? null : node, children);
|
||||
|
||||
@@ -881,6 +901,14 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
}
|
||||
|
||||
protected asTreeElement(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): ITreeElement<IAsyncDataTreeNode<TInput, T>> {
|
||||
if (node.stale) {
|
||||
return {
|
||||
element: node,
|
||||
collapsible: node.hasChildren,
|
||||
collapsed: true
|
||||
};
|
||||
}
|
||||
|
||||
let collapsed: boolean | undefined;
|
||||
|
||||
if (viewStateContext && viewStateContext.viewState.expanded && node.id && viewStateContext.viewState.expanded.indexOf(node.id) > -1) {
|
||||
@@ -899,6 +927,14 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
};
|
||||
}
|
||||
|
||||
protected processChildren(children: T[]): T[] {
|
||||
if (this.sorter) {
|
||||
children.sort(this.sorter.compare.bind(this.sorter));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
// view state
|
||||
|
||||
getViewState(): IAsyncDataTreeViewState {
|
||||
@@ -994,6 +1030,12 @@ class CompressibleAsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> i
|
||||
}
|
||||
}
|
||||
|
||||
disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeCompressedElements) {
|
||||
this.renderer.disposeCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode<ICompressedTreeNode<T>, TFilterData>, index, templateData.templateData, height);
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
this.renderer.disposeTemplate(templateData.templateData);
|
||||
}
|
||||
@@ -1023,12 +1065,19 @@ function asCompressibleObjectTreeOptions<TInput, T, TFilterData>(options?: IComp
|
||||
}
|
||||
|
||||
export interface ICompressibleAsyncDataTreeOptions<T, TFilterData = void> extends IAsyncDataTreeOptions<T, TFilterData> {
|
||||
readonly compressionEnabled?: boolean;
|
||||
readonly keyboardNavigationLabelProvider?: ICompressibleKeyboardNavigationLabelProvider<T>;
|
||||
}
|
||||
|
||||
export interface ICompressibleAsyncDataTreeOptionsUpdate extends IAsyncDataTreeOptionsUpdate {
|
||||
readonly compressionEnabled?: boolean;
|
||||
}
|
||||
|
||||
export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
|
||||
|
||||
protected readonly tree!: CompressibleObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
|
||||
protected readonly compressibleNodeMapper: CompressibleAsyncDataTreeNodeMapper<TInput, T, TFilterData> = new WeakMapper(node => new CompressibleAsyncDataTreeNodeWrapper(node));
|
||||
private filter?: ITreeFilter<T, TFilterData>;
|
||||
|
||||
constructor(
|
||||
user: string,
|
||||
@@ -1037,9 +1086,10 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
|
||||
private compressionDelegate: ITreeCompressionDelegate<T>,
|
||||
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IAsyncDataTreeOptions<T, TFilterData> = {}
|
||||
options: ICompressibleAsyncDataTreeOptions<T, TFilterData> = {}
|
||||
) {
|
||||
super(user, container, virtualDelegate, renderers, dataSource, options);
|
||||
this.filter = options.filter;
|
||||
}
|
||||
|
||||
protected createTree(
|
||||
@@ -1062,4 +1112,137 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
|
||||
...super.asTreeElement(node, viewStateContext)
|
||||
};
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
const getId = (element: T) => this.identityProvider!.getId(element).toString();
|
||||
const focus = this.getFocus().map(getId);
|
||||
const selection = this.getSelection().map(getId);
|
||||
|
||||
const expanded: string[] = [];
|
||||
const root = this.tree.getCompressedTreeNode();
|
||||
const queue = [root];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const node = queue.shift()!;
|
||||
|
||||
if (node !== root && node.collapsible && !node.collapsed) {
|
||||
for (const asyncNode of node.element!.elements) {
|
||||
expanded.push(getId(asyncNode.element as T));
|
||||
}
|
||||
}
|
||||
|
||||
queue.push(...node.children);
|
||||
}
|
||||
|
||||
return { focus, selection, expanded, scrollTop: this.scrollTop };
|
||||
}
|
||||
|
||||
protected render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
|
||||
if (!this.identityProvider) {
|
||||
return super.render(node, viewStateContext);
|
||||
}
|
||||
|
||||
// Preserve traits across compressions. Hacky but does the trick.
|
||||
// This is hard to fix properly since it requires rewriting the traits
|
||||
// across trees and lists. Let's just keep it this way for now.
|
||||
const getId = (element: T) => this.identityProvider!.getId(element).toString();
|
||||
const getUncompressedIds = (nodes: IAsyncDataTreeNode<TInput, T>[]): Set<string> => {
|
||||
const result = new Set<string>();
|
||||
|
||||
for (const node of nodes) {
|
||||
const compressedNode = this.tree.getCompressedTreeNode(node === this.root ? null : node);
|
||||
|
||||
if (!compressedNode.element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const node of compressedNode.element.elements) {
|
||||
result.add(getId(node.element as T));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const oldSelection = getUncompressedIds(this.tree.getSelection() as IAsyncDataTreeNode<TInput, T>[]);
|
||||
const oldFocus = getUncompressedIds(this.tree.getFocus() as IAsyncDataTreeNode<TInput, T>[]);
|
||||
|
||||
super.render(node, viewStateContext);
|
||||
|
||||
const selection = this.getSelection();
|
||||
let didChangeSelection = false;
|
||||
|
||||
const focus = this.getFocus();
|
||||
let didChangeFocus = false;
|
||||
|
||||
const visit = (node: ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>> | null, TFilterData>) => {
|
||||
const compressedNode = node.element;
|
||||
|
||||
if (compressedNode) {
|
||||
for (let i = 0; i < compressedNode.elements.length; i++) {
|
||||
const id = getId(compressedNode.elements[i].element as T);
|
||||
|
||||
if (oldSelection.has(id)) {
|
||||
selection.push(compressedNode.elements[compressedNode.elements.length - 1].element as T);
|
||||
didChangeSelection = true;
|
||||
}
|
||||
|
||||
if (oldFocus.has(id)) {
|
||||
focus.push(compressedNode.elements[compressedNode.elements.length - 1].element as T);
|
||||
didChangeFocus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.children.forEach(visit);
|
||||
};
|
||||
|
||||
visit(this.tree.getCompressedTreeNode(node === this.root ? null : node));
|
||||
|
||||
if (didChangeSelection) {
|
||||
this.setSelection(selection);
|
||||
}
|
||||
|
||||
if (didChangeFocus) {
|
||||
this.setFocus(focus);
|
||||
}
|
||||
}
|
||||
|
||||
// For compressed async data trees, `TreeVisibility.Recurse` doesn't currently work
|
||||
// and we have to filter everything beforehand
|
||||
// Related to #85193 and #85835
|
||||
protected processChildren(children: T[]): T[] {
|
||||
if (this.filter) {
|
||||
children = children.filter(e => {
|
||||
const result = this.filter!.filter(e, TreeVisibility.Visible);
|
||||
const visibility = getVisibility(result);
|
||||
|
||||
if (visibility === TreeVisibility.Recurse) {
|
||||
throw new Error('Recursive tree visibility not supported in async data compressed trees');
|
||||
}
|
||||
|
||||
return visibility === TreeVisibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
return super.processChildren(children);
|
||||
}
|
||||
}
|
||||
|
||||
function getVisibility<TFilterData>(filterResult: TreeFilterResult<TFilterData>): TreeVisibility {
|
||||
if (typeof filterResult === 'boolean') {
|
||||
return filterResult ? TreeVisibility.Visible : TreeVisibility.Hidden;
|
||||
} else if (isFilterResult(filterResult)) {
|
||||
return getVisibleState(filterResult.visibility);
|
||||
} else {
|
||||
return getVisibleState(filterResult);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user