| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
- };
- import { ElementsDragAndDropData } from '../list/listView.js';
- import { ComposedTreeDelegate } from './abstractTree.js';
- import { getVisibleState, isFilterResult } from './indexTreeModel.js';
- import { CompressibleObjectTree, ObjectTree } from './objectTree.js';
- import { TreeError, WeakMapper } from './tree.js';
- import { createCancelablePromise, Promises, timeout } from '../../../common/async.js';
- import { Codicon } from '../../../common/codicons.js';
- import { isCancellationError, onUnexpectedError } from '../../../common/errors.js';
- import { Emitter, Event } from '../../../common/event.js';
- import { Iterable } from '../../../common/iterator.js';
- import { DisposableStore, dispose } from '../../../common/lifecycle.js';
- import { isIterable } from '../../../common/types.js';
- function createAsyncDataTreeNode(props) {
- return Object.assign(Object.assign({}, props), { children: [], refreshPromise: undefined, stale: true, slow: false, collapsedByDefault: undefined });
- }
- function isAncestor(ancestor, descendant) {
- if (!descendant.parent) {
- return false;
- }
- else if (descendant.parent === ancestor) {
- return true;
- }
- else {
- return isAncestor(ancestor, descendant.parent);
- }
- }
- function intersects(node, other) {
- return node === other || isAncestor(node, other) || isAncestor(other, node);
- }
- class AsyncDataTreeNodeWrapper {
- constructor(node) {
- this.node = node;
- }
- get element() { return this.node.element.element; }
- get children() { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); }
- get depth() { return this.node.depth; }
- get visibleChildrenCount() { return this.node.visibleChildrenCount; }
- get visibleChildIndex() { return this.node.visibleChildIndex; }
- get collapsible() { return this.node.collapsible; }
- get collapsed() { return this.node.collapsed; }
- get visible() { return this.node.visible; }
- get filterData() { return this.node.filterData; }
- }
- class AsyncDataTreeRenderer {
- constructor(renderer, nodeMapper, onDidChangeTwistieState) {
- this.renderer = renderer;
- this.nodeMapper = nodeMapper;
- this.onDidChangeTwistieState = onDidChangeTwistieState;
- this.renderedNodes = new Map();
- this.templateId = renderer.templateId;
- }
- renderTemplate(container) {
- const templateData = this.renderer.renderTemplate(container);
- return { templateData };
- }
- renderElement(node, index, templateData, height) {
- this.renderer.renderElement(this.nodeMapper.map(node), index, templateData.templateData, height);
- }
- renderTwistie(element, twistieElement) {
- if (element.slow) {
- twistieElement.classList.add(...Codicon.treeItemLoading.classNamesArray);
- return true;
- }
- else {
- twistieElement.classList.remove(...Codicon.treeItemLoading.classNamesArray);
- return false;
- }
- }
- disposeElement(node, index, templateData, height) {
- var _a, _b;
- (_b = (_a = this.renderer).disposeElement) === null || _b === void 0 ? void 0 : _b.call(_a, this.nodeMapper.map(node), index, templateData.templateData, height);
- }
- disposeTemplate(templateData) {
- this.renderer.disposeTemplate(templateData.templateData);
- }
- dispose() {
- this.renderedNodes.clear();
- }
- }
- function asTreeEvent(e) {
- return {
- browserEvent: e.browserEvent,
- elements: e.elements.map(e => e.element)
- };
- }
- function asTreeMouseEvent(e) {
- return {
- browserEvent: e.browserEvent,
- element: e.element && e.element.element,
- target: e.target
- };
- }
- class AsyncDataTreeElementsDragAndDropData extends ElementsDragAndDropData {
- constructor(data) {
- super(data.elements.map(node => node.element));
- this.data = data;
- }
- }
- function asAsyncDataTreeDragAndDropData(data) {
- if (data instanceof ElementsDragAndDropData) {
- return new AsyncDataTreeElementsDragAndDropData(data);
- }
- return data;
- }
- class AsyncDataTreeNodeListDragAndDrop {
- constructor(dnd) {
- this.dnd = dnd;
- }
- getDragURI(node) {
- return this.dnd.getDragURI(node.element);
- }
- getDragLabel(nodes, originalEvent) {
- if (this.dnd.getDragLabel) {
- return this.dnd.getDragLabel(nodes.map(node => node.element), originalEvent);
- }
- return undefined;
- }
- onDragStart(data, originalEvent) {
- var _a, _b;
- (_b = (_a = this.dnd).onDragStart) === null || _b === void 0 ? void 0 : _b.call(_a, asAsyncDataTreeDragAndDropData(data), originalEvent);
- }
- onDragOver(data, targetNode, targetIndex, originalEvent, raw = true) {
- return this.dnd.onDragOver(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent);
- }
- drop(data, targetNode, targetIndex, originalEvent) {
- this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent);
- }
- onDragEnd(originalEvent) {
- var _a, _b;
- (_b = (_a = this.dnd).onDragEnd) === null || _b === void 0 ? void 0 : _b.call(_a, originalEvent);
- }
- }
- function asObjectTreeOptions(options) {
- return options && Object.assign(Object.assign({}, options), { collapseByDefault: true, identityProvider: options.identityProvider && {
- getId(el) {
- return options.identityProvider.getId(el.element);
- }
- }, dnd: options.dnd && new AsyncDataTreeNodeListDragAndDrop(options.dnd), multipleSelectionController: options.multipleSelectionController && {
- isSelectionSingleChangeEvent(e) {
- return options.multipleSelectionController.isSelectionSingleChangeEvent(Object.assign(Object.assign({}, e), { element: e.element }));
- },
- isSelectionRangeChangeEvent(e) {
- return options.multipleSelectionController.isSelectionRangeChangeEvent(Object.assign(Object.assign({}, e), { element: e.element }));
- }
- }, accessibilityProvider: options.accessibilityProvider && Object.assign(Object.assign({}, options.accessibilityProvider), { getPosInSet: undefined, getSetSize: undefined, getRole: options.accessibilityProvider.getRole ? (el) => {
- return options.accessibilityProvider.getRole(el.element);
- } : () => 'treeitem', isChecked: options.accessibilityProvider.isChecked ? (e) => {
- var _a;
- return !!((_a = options.accessibilityProvider) === null || _a === void 0 ? void 0 : _a.isChecked(e.element));
- } : undefined, getAriaLabel(e) {
- return options.accessibilityProvider.getAriaLabel(e.element);
- },
- getWidgetAriaLabel() {
- return options.accessibilityProvider.getWidgetAriaLabel();
- }, getWidgetRole: options.accessibilityProvider.getWidgetRole ? () => options.accessibilityProvider.getWidgetRole() : () => 'tree', getAriaLevel: options.accessibilityProvider.getAriaLevel && (node => {
- return options.accessibilityProvider.getAriaLevel(node.element);
- }), getActiveDescendantId: options.accessibilityProvider.getActiveDescendantId && (node => {
- return options.accessibilityProvider.getActiveDescendantId(node.element);
- }) }), filter: options.filter && {
- filter(e, parentVisibility) {
- return options.filter.filter(e.element, parentVisibility);
- }
- }, keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && Object.assign(Object.assign({}, options.keyboardNavigationLabelProvider), { getKeyboardNavigationLabel(e) {
- return options.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(e.element);
- } }), sorter: undefined, expandOnlyOnTwistieClick: typeof options.expandOnlyOnTwistieClick === 'undefined' ? undefined : (typeof options.expandOnlyOnTwistieClick !== 'function' ? options.expandOnlyOnTwistieClick : (e => options.expandOnlyOnTwistieClick(e.element))), additionalScrollHeight: options.additionalScrollHeight });
- }
- function dfs(node, fn) {
- fn(node);
- node.children.forEach(child => dfs(child, fn));
- }
- export class AsyncDataTree {
- constructor(user, container, delegate, renderers, dataSource, options = {}) {
- this.user = user;
- this.dataSource = dataSource;
- this.nodes = new Map();
- this.subTreeRefreshPromises = new Map();
- this.refreshPromises = new Map();
- this._onDidRender = new Emitter();
- this._onDidChangeNodeSlowState = new Emitter();
- this.nodeMapper = new WeakMapper(node => new AsyncDataTreeNodeWrapper(node));
- this.disposables = new DisposableStore();
- this.identityProvider = options.identityProvider;
- this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren;
- this.sorter = options.sorter;
- this.collapseByDefault = options.collapseByDefault;
- this.tree = this.createTree(user, container, delegate, renderers, options);
- this.onDidChangeFindMode = this.tree.onDidChangeFindMode;
- this.root = createAsyncDataTreeNode({
- element: undefined,
- parent: null,
- hasChildren: true
- });
- if (this.identityProvider) {
- this.root = Object.assign(Object.assign({}, this.root), { id: null });
- }
- this.nodes.set(null, this.root);
- this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables);
- }
- get onDidChangeFocus() { return Event.map(this.tree.onDidChangeFocus, asTreeEvent); }
- get onDidChangeSelection() { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); }
- get onMouseDblClick() { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); }
- get onPointer() { return Event.map(this.tree.onPointer, asTreeMouseEvent); }
- get onDidFocus() { return this.tree.onDidFocus; }
- get onDidChangeModel() { return this.tree.onDidChangeModel; }
- get onDidChangeCollapseState() { return this.tree.onDidChangeCollapseState; }
- get onDidChangeFindOpenState() { return this.tree.onDidChangeFindOpenState; }
- get onDidDispose() { return this.tree.onDidDispose; }
- createTree(user, container, delegate, renderers, options) {
- const objectTreeDelegate = new ComposedTreeDelegate(delegate);
- const objectTreeRenderers = renderers.map(r => new AsyncDataTreeRenderer(r, this.nodeMapper, this._onDidChangeNodeSlowState.event));
- const objectTreeOptions = asObjectTreeOptions(options) || {};
- return new ObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
- }
- updateOptions(options = {}) {
- this.tree.updateOptions(options);
- }
- // Widget
- getHTMLElement() {
- return this.tree.getHTMLElement();
- }
- get scrollTop() {
- return this.tree.scrollTop;
- }
- set scrollTop(scrollTop) {
- this.tree.scrollTop = scrollTop;
- }
- domFocus() {
- this.tree.domFocus();
- }
- layout(height, width) {
- this.tree.layout(height, width);
- }
- style(styles) {
- this.tree.style(styles);
- }
- // Model
- getInput() {
- return this.root.element;
- }
- setInput(input, viewState) {
- return __awaiter(this, void 0, void 0, function* () {
- this.refreshPromises.forEach(promise => promise.cancel());
- this.refreshPromises.clear();
- this.root.element = input;
- const viewStateContext = viewState && { viewState, focus: [], selection: [] };
- yield this._updateChildren(input, true, false, viewStateContext);
- if (viewStateContext) {
- this.tree.setFocus(viewStateContext.focus);
- this.tree.setSelection(viewStateContext.selection);
- }
- if (viewState && typeof viewState.scrollTop === 'number') {
- this.scrollTop = viewState.scrollTop;
- }
- });
- }
- _updateChildren(element = this.root.element, recursive = true, rerender = false, viewStateContext, options) {
- return __awaiter(this, void 0, void 0, function* () {
- if (typeof this.root.element === 'undefined') {
- throw new TreeError(this.user, 'Tree input not set');
- }
- if (this.root.refreshPromise) {
- yield this.root.refreshPromise;
- yield Event.toPromise(this._onDidRender.event);
- }
- const node = this.getDataNode(element);
- yield this.refreshAndRenderNode(node, recursive, viewStateContext, options);
- if (rerender) {
- try {
- this.tree.rerender(node);
- }
- catch (_a) {
- // missing nodes are fine, this could've resulted from
- // parallel refresh calls, removing `node` altogether
- }
- }
- });
- }
- // View
- rerender(element) {
- if (element === undefined || element === this.root.element) {
- this.tree.rerender();
- return;
- }
- const node = this.getDataNode(element);
- this.tree.rerender(node);
- }
- // Tree
- getNode(element = this.root.element) {
- const dataNode = this.getDataNode(element);
- const node = this.tree.getNode(dataNode === this.root ? null : dataNode);
- return this.nodeMapper.map(node);
- }
- collapse(element, recursive = false) {
- const node = this.getDataNode(element);
- return this.tree.collapse(node === this.root ? null : node, recursive);
- }
- expand(element, recursive = false) {
- return __awaiter(this, void 0, void 0, function* () {
- if (typeof this.root.element === 'undefined') {
- throw new TreeError(this.user, 'Tree input not set');
- }
- if (this.root.refreshPromise) {
- yield this.root.refreshPromise;
- yield Event.toPromise(this._onDidRender.event);
- }
- const node = this.getDataNode(element);
- if (this.tree.hasElement(node) && !this.tree.isCollapsible(node)) {
- return false;
- }
- if (node.refreshPromise) {
- yield this.root.refreshPromise;
- yield Event.toPromise(this._onDidRender.event);
- }
- if (node !== this.root && !node.refreshPromise && !this.tree.isCollapsed(node)) {
- return false;
- }
- const result = this.tree.expand(node === this.root ? null : node, recursive);
- if (node.refreshPromise) {
- yield this.root.refreshPromise;
- yield Event.toPromise(this._onDidRender.event);
- }
- return result;
- });
- }
- setSelection(elements, browserEvent) {
- const nodes = elements.map(e => this.getDataNode(e));
- this.tree.setSelection(nodes, browserEvent);
- }
- getSelection() {
- const nodes = this.tree.getSelection();
- return nodes.map(n => n.element);
- }
- setFocus(elements, browserEvent) {
- const nodes = elements.map(e => this.getDataNode(e));
- this.tree.setFocus(nodes, browserEvent);
- }
- getFocus() {
- const nodes = this.tree.getFocus();
- return nodes.map(n => n.element);
- }
- reveal(element, relativeTop) {
- this.tree.reveal(this.getDataNode(element), relativeTop);
- }
- // Tree navigation
- getParentElement(element) {
- const node = this.tree.getParentElement(this.getDataNode(element));
- return (node && node.element);
- }
- getFirstElementChild(element = this.root.element) {
- const dataNode = this.getDataNode(element);
- const node = this.tree.getFirstElementChild(dataNode === this.root ? null : dataNode);
- return (node && node.element);
- }
- // Implementation
- getDataNode(element) {
- const node = this.nodes.get((element === this.root.element ? null : element));
- if (!node) {
- throw new TreeError(this.user, `Data tree node not found: ${element}`);
- }
- return node;
- }
- refreshAndRenderNode(node, recursive, viewStateContext, options) {
- return __awaiter(this, void 0, void 0, function* () {
- yield this.refreshNode(node, recursive, viewStateContext);
- this.render(node, viewStateContext, options);
- });
- }
- refreshNode(node, recursive, viewStateContext) {
- return __awaiter(this, void 0, void 0, function* () {
- let result;
- this.subTreeRefreshPromises.forEach((refreshPromise, refreshNode) => {
- if (!result && intersects(refreshNode, node)) {
- result = refreshPromise.then(() => this.refreshNode(node, recursive, viewStateContext));
- }
- });
- if (result) {
- return result;
- }
- if (node !== this.root) {
- const treeNode = this.tree.getNode(node);
- if (treeNode.collapsed) {
- node.hasChildren = !!this.dataSource.hasChildren(node.element);
- node.stale = true;
- return;
- }
- }
- return this.doRefreshSubTree(node, recursive, viewStateContext);
- });
- }
- doRefreshSubTree(node, recursive, viewStateContext) {
- return __awaiter(this, void 0, void 0, function* () {
- let done;
- node.refreshPromise = new Promise(c => done = c);
- this.subTreeRefreshPromises.set(node, node.refreshPromise);
- node.refreshPromise.finally(() => {
- node.refreshPromise = undefined;
- this.subTreeRefreshPromises.delete(node);
- });
- try {
- const childrenToRefresh = yield this.doRefreshNode(node, recursive, viewStateContext);
- node.stale = false;
- yield Promises.settled(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext)));
- }
- finally {
- done();
- }
- });
- }
- doRefreshNode(node, recursive, viewStateContext) {
- return __awaiter(this, void 0, void 0, function* () {
- node.hasChildren = !!this.dataSource.hasChildren(node.element);
- let childrenPromise;
- if (!node.hasChildren) {
- childrenPromise = Promise.resolve(Iterable.empty());
- }
- else {
- const children = this.doGetChildren(node);
- if (isIterable(children)) {
- childrenPromise = Promise.resolve(children);
- }
- else {
- const slowTimeout = timeout(800);
- slowTimeout.then(() => {
- node.slow = true;
- this._onDidChangeNodeSlowState.fire(node);
- }, _ => null);
- childrenPromise = children.finally(() => slowTimeout.cancel());
- }
- }
- try {
- const children = yield childrenPromise;
- return this.setChildren(node, children, recursive, viewStateContext);
- }
- catch (err) {
- if (node !== this.root && this.tree.hasElement(node)) {
- this.tree.collapse(node);
- }
- if (isCancellationError(err)) {
- return [];
- }
- throw err;
- }
- finally {
- if (node.slow) {
- node.slow = false;
- this._onDidChangeNodeSlowState.fire(node);
- }
- }
- });
- }
- doGetChildren(node) {
- let result = this.refreshPromises.get(node);
- if (result) {
- return result;
- }
- const children = this.dataSource.getChildren(node.element);
- if (isIterable(children)) {
- return this.processChildren(children);
- }
- else {
- result = createCancelablePromise(() => __awaiter(this, void 0, void 0, function* () { return this.processChildren(yield children); }));
- this.refreshPromises.set(node, result);
- return result.finally(() => { this.refreshPromises.delete(node); });
- }
- }
- _onDidChangeCollapseState({ node, deep }) {
- if (node.element === null) {
- return;
- }
- if (!node.collapsed && node.element.stale) {
- if (deep) {
- this.collapse(node.element.element);
- }
- else {
- this.refreshAndRenderNode(node.element, false)
- .catch(onUnexpectedError);
- }
- }
- }
- setChildren(node, childrenElementsIterable, recursive, viewStateContext) {
- const childrenElements = [...childrenElementsIterable];
- // perf: if the node was and still is a leaf, avoid all this hassle
- if (node.children.length === 0 && childrenElements.length === 0) {
- return [];
- }
- const nodesToForget = new Map();
- const childrenTreeNodesById = new Map();
- for (const child of node.children) {
- nodesToForget.set(child.element, child);
- if (this.identityProvider) {
- const collapsed = this.tree.isCollapsed(child);
- childrenTreeNodesById.set(child.id, { node: child, collapsed });
- }
- }
- const childrenToRefresh = [];
- const children = childrenElements.map(element => {
- const hasChildren = !!this.dataSource.hasChildren(element);
- if (!this.identityProvider) {
- const asyncDataTreeNode = createAsyncDataTreeNode({ element, parent: node, hasChildren });
- if (hasChildren && this.collapseByDefault && !this.collapseByDefault(element)) {
- asyncDataTreeNode.collapsedByDefault = false;
- childrenToRefresh.push(asyncDataTreeNode);
- }
- return asyncDataTreeNode;
- }
- const id = this.identityProvider.getId(element).toString();
- const result = childrenTreeNodesById.get(id);
- if (result) {
- const asyncDataTreeNode = result.node;
- nodesToForget.delete(asyncDataTreeNode.element);
- this.nodes.delete(asyncDataTreeNode.element);
- this.nodes.set(element, asyncDataTreeNode);
- asyncDataTreeNode.element = element;
- asyncDataTreeNode.hasChildren = hasChildren;
- if (recursive) {
- if (result.collapsed) {
- asyncDataTreeNode.children.forEach(node => dfs(node, node => this.nodes.delete(node.element)));
- asyncDataTreeNode.children.splice(0, asyncDataTreeNode.children.length);
- asyncDataTreeNode.stale = true;
- }
- else {
- childrenToRefresh.push(asyncDataTreeNode);
- }
- }
- else if (hasChildren && this.collapseByDefault && !this.collapseByDefault(element)) {
- asyncDataTreeNode.collapsedByDefault = false;
- childrenToRefresh.push(asyncDataTreeNode);
- }
- return asyncDataTreeNode;
- }
- const childAsyncDataTreeNode = createAsyncDataTreeNode({ element, parent: node, id, hasChildren });
- if (viewStateContext && viewStateContext.viewState.focus && viewStateContext.viewState.focus.indexOf(id) > -1) {
- viewStateContext.focus.push(childAsyncDataTreeNode);
- }
- if (viewStateContext && viewStateContext.viewState.selection && viewStateContext.viewState.selection.indexOf(id) > -1) {
- viewStateContext.selection.push(childAsyncDataTreeNode);
- }
- if (viewStateContext && viewStateContext.viewState.expanded && viewStateContext.viewState.expanded.indexOf(id) > -1) {
- childrenToRefresh.push(childAsyncDataTreeNode);
- }
- else if (hasChildren && this.collapseByDefault && !this.collapseByDefault(element)) {
- childAsyncDataTreeNode.collapsedByDefault = false;
- childrenToRefresh.push(childAsyncDataTreeNode);
- }
- return childAsyncDataTreeNode;
- });
- for (const node of nodesToForget.values()) {
- dfs(node, node => this.nodes.delete(node.element));
- }
- for (const child of children) {
- this.nodes.set(child.element, child);
- }
- 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;
- }
- render(node, viewStateContext, options) {
- const children = node.children.map(node => this.asTreeElement(node, viewStateContext));
- const objectTreeOptions = options && Object.assign(Object.assign({}, options), { diffIdentityProvider: options.diffIdentityProvider && {
- getId(node) {
- return options.diffIdentityProvider.getId(node.element);
- }
- } });
- this.tree.setChildren(node === this.root ? null : node, children, objectTreeOptions);
- if (node !== this.root) {
- this.tree.setCollapsible(node, node.hasChildren);
- }
- this._onDidRender.fire();
- }
- asTreeElement(node, viewStateContext) {
- if (node.stale) {
- return {
- element: node,
- collapsible: node.hasChildren,
- collapsed: true
- };
- }
- let collapsed;
- if (viewStateContext && viewStateContext.viewState.expanded && node.id && viewStateContext.viewState.expanded.indexOf(node.id) > -1) {
- collapsed = false;
- }
- else {
- collapsed = node.collapsedByDefault;
- }
- node.collapsedByDefault = undefined;
- return {
- element: node,
- children: node.hasChildren ? Iterable.map(node.children, child => this.asTreeElement(child, viewStateContext)) : [],
- collapsible: node.hasChildren,
- collapsed
- };
- }
- processChildren(children) {
- if (this.sorter) {
- children = [...children].sort(this.sorter.compare.bind(this.sorter));
- }
- return children;
- }
- dispose() {
- this.disposables.dispose();
- }
- }
- class CompressibleAsyncDataTreeNodeWrapper {
- constructor(node) {
- this.node = node;
- }
- get element() {
- return {
- elements: this.node.element.elements.map(e => e.element),
- incompressible: this.node.element.incompressible
- };
- }
- get children() { return this.node.children.map(node => new CompressibleAsyncDataTreeNodeWrapper(node)); }
- get depth() { return this.node.depth; }
- get visibleChildrenCount() { return this.node.visibleChildrenCount; }
- get visibleChildIndex() { return this.node.visibleChildIndex; }
- get collapsible() { return this.node.collapsible; }
- get collapsed() { return this.node.collapsed; }
- get visible() { return this.node.visible; }
- get filterData() { return this.node.filterData; }
- }
- class CompressibleAsyncDataTreeRenderer {
- constructor(renderer, nodeMapper, compressibleNodeMapperProvider, onDidChangeTwistieState) {
- this.renderer = renderer;
- this.nodeMapper = nodeMapper;
- this.compressibleNodeMapperProvider = compressibleNodeMapperProvider;
- this.onDidChangeTwistieState = onDidChangeTwistieState;
- this.renderedNodes = new Map();
- this.disposables = [];
- this.templateId = renderer.templateId;
- }
- renderTemplate(container) {
- const templateData = this.renderer.renderTemplate(container);
- return { templateData };
- }
- renderElement(node, index, templateData, height) {
- this.renderer.renderElement(this.nodeMapper.map(node), index, templateData.templateData, height);
- }
- renderCompressedElements(node, index, templateData, height) {
- this.renderer.renderCompressedElements(this.compressibleNodeMapperProvider().map(node), index, templateData.templateData, height);
- }
- renderTwistie(element, twistieElement) {
- if (element.slow) {
- twistieElement.classList.add(...Codicon.treeItemLoading.classNamesArray);
- return true;
- }
- else {
- twistieElement.classList.remove(...Codicon.treeItemLoading.classNamesArray);
- return false;
- }
- }
- disposeElement(node, index, templateData, height) {
- var _a, _b;
- (_b = (_a = this.renderer).disposeElement) === null || _b === void 0 ? void 0 : _b.call(_a, this.nodeMapper.map(node), index, templateData.templateData, height);
- }
- disposeCompressedElements(node, index, templateData, height) {
- var _a, _b;
- (_b = (_a = this.renderer).disposeCompressedElements) === null || _b === void 0 ? void 0 : _b.call(_a, this.compressibleNodeMapperProvider().map(node), index, templateData.templateData, height);
- }
- disposeTemplate(templateData) {
- this.renderer.disposeTemplate(templateData.templateData);
- }
- dispose() {
- this.renderedNodes.clear();
- this.disposables = dispose(this.disposables);
- }
- }
- function asCompressibleObjectTreeOptions(options) {
- const objectTreeOptions = options && asObjectTreeOptions(options);
- return objectTreeOptions && Object.assign(Object.assign({}, objectTreeOptions), { keyboardNavigationLabelProvider: objectTreeOptions.keyboardNavigationLabelProvider && Object.assign(Object.assign({}, objectTreeOptions.keyboardNavigationLabelProvider), { getCompressedNodeKeyboardNavigationLabel(els) {
- return options.keyboardNavigationLabelProvider.getCompressedNodeKeyboardNavigationLabel(els.map(e => e.element));
- } }) });
- }
- export class CompressibleAsyncDataTree extends AsyncDataTree {
- constructor(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, options = {}) {
- super(user, container, virtualDelegate, renderers, dataSource, options);
- this.compressionDelegate = compressionDelegate;
- this.compressibleNodeMapper = new WeakMapper(node => new CompressibleAsyncDataTreeNodeWrapper(node));
- this.filter = options.filter;
- }
- createTree(user, container, delegate, renderers, options) {
- const objectTreeDelegate = new ComposedTreeDelegate(delegate);
- const objectTreeRenderers = renderers.map(r => new CompressibleAsyncDataTreeRenderer(r, this.nodeMapper, () => this.compressibleNodeMapper, this._onDidChangeNodeSlowState.event));
- const objectTreeOptions = asCompressibleObjectTreeOptions(options) || {};
- return new CompressibleObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
- }
- asTreeElement(node, viewStateContext) {
- return Object.assign({ incompressible: this.compressionDelegate.isIncompressible(node.element) }, super.asTreeElement(node, viewStateContext));
- }
- updateOptions(options = {}) {
- this.tree.updateOptions(options);
- }
- render(node, viewStateContext) {
- 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) => this.identityProvider.getId(element).toString();
- const getUncompressedIds = (nodes) => {
- const result = new Set();
- 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));
- }
- }
- return result;
- };
- const oldSelection = getUncompressedIds(this.tree.getSelection());
- const oldFocus = getUncompressedIds(this.tree.getFocus());
- super.render(node, viewStateContext);
- const selection = this.getSelection();
- let didChangeSelection = false;
- const focus = this.getFocus();
- let didChangeFocus = false;
- const visit = (node) => {
- const compressedNode = node.element;
- if (compressedNode) {
- for (let i = 0; i < compressedNode.elements.length; i++) {
- const id = getId(compressedNode.elements[i].element);
- const element = compressedNode.elements[compressedNode.elements.length - 1].element;
- // github.com/microsoft/vscode/issues/85938
- if (oldSelection.has(id) && selection.indexOf(element) === -1) {
- selection.push(element);
- didChangeSelection = true;
- }
- if (oldFocus.has(id) && focus.indexOf(element) === -1) {
- focus.push(element);
- 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
- processChildren(children) {
- if (this.filter) {
- children = Iterable.filter(children, e => {
- const result = this.filter.filter(e, 1 /* TreeVisibility.Visible */);
- const visibility = getVisibility(result);
- if (visibility === 2 /* TreeVisibility.Recurse */) {
- throw new Error('Recursive tree visibility not supported in async data compressed trees');
- }
- return visibility === 1 /* TreeVisibility.Visible */;
- });
- }
- return super.processChildren(children);
- }
- }
- function getVisibility(filterResult) {
- if (typeof filterResult === 'boolean') {
- return filterResult ? 1 /* TreeVisibility.Visible */ : 0 /* TreeVisibility.Hidden */;
- }
- else if (isFilterResult(filterResult)) {
- return getVisibleState(filterResult.visibility);
- }
- else {
- return getVisibleState(filterResult);
- }
- }
|