Skip to content

Commit

Permalink
ObjectTree.resort
Browse files Browse the repository at this point in the history
  • Loading branch information
joaomoreno authored and sandy081 committed Feb 22, 2019
1 parent b410d91 commit 7ae4138
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/vs/base/browser/ui/tree/asyncDataTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,10 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext);
}

resort(element: TInput | T = this.root.element, recursive = true): void {
this.tree.resort(this.getDataNode(element), recursive);
}

hasNode(element: TInput | T): boolean {
return element === this.root.element || this.nodes.has(element as T);
}
Expand Down
4 changes: 4 additions & 0 deletions src/vs/base/browser/ui/tree/dataTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
this._refresh(element);
}

resort(element: T | TInput = this.input!, recursive = true): void {
this.model.resort((element === this.input ? null : element) as T, recursive);
}

// View

refresh(element: T): void {
Expand Down
4 changes: 4 additions & 0 deletions src/vs/base/browser/ui/tree/objectTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
this.model.refresh(element);
}

resort(element: T, recursive = true): void {
this.model.resort(element, recursive);
}

protected createModel(view: ISpliceable<ITreeNode<T, TFilterData>>, options: IObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new ObjectTreeModel(view, options);
}
Expand Down
41 changes: 38 additions & 3 deletions src/vs/base/browser/ui/tree/objectTreeModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non

private model: IndexTreeModel<T | null, TFilterData>;
private nodes = new Map<T | null, ITreeNode<T, TFilterData>>();
private sorter?: ITreeSorter<ITreeElement<T>>;
private sorter?: ITreeSorter<{ element: T; }>;

readonly onDidSplice: Event<ITreeModelSpliceEvent<T | null, TFilterData>>;
readonly onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>>;
Expand Down Expand Up @@ -49,6 +49,15 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
): Iterator<ITreeElement<T | null>> {
const location = this.getElementLocation(element);
return this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode);
}

private _setChildren(
location: number[],
children: ISequence<ITreeElement<T>> | undefined,
onDidCreateNode?: (node: ITreeNode<T, TFilterData>) => void,
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
): Iterator<ITreeElement<T | null>> {
const insertedElements = new Set<T | null>();

const _onDidCreateNode = (node: ITreeNode<T, TFilterData>) => {
Expand All @@ -73,13 +82,13 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
return this.model.splice(
[...location, 0],
Number.MAX_VALUE,
this.preserveCollapseState(children),
children,
_onDidCreateNode,
_onDidDeleteNode
);
}

private preserveCollapseState(elements: ISequence<ITreeElement<T | null>> | undefined): ISequence<ITreeElement<T | null>> {
private preserveCollapseState(elements: ISequence<ITreeElement<T>> | undefined): ISequence<ITreeElement<T>> {
let iterator = elements ? getSequenceIterator(elements) : Iterator.empty<ITreeElement<T>>();

if (this.sorter) {
Expand Down Expand Up @@ -113,6 +122,32 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
this.model.refresh(location);
}

resort(element: T | null = null, recursive = true): void {
if (!this.sorter) {
return;
}

const location = this.getElementLocation(element);
const node = this.model.getNode(location);

this._setChildren(location, this.resortChildren(node, recursive));
}

private resortChildren(node: ITreeNode<T | null, TFilterData>, recursive: boolean, first = true): ISequence<ITreeElement<T>> {
let childrenNodes = Iterator.fromArray(node.children as ITreeNode<T, TFilterData>[]);

if (recursive || first) {
childrenNodes = Iterator.fromArray(Iterator.collect(childrenNodes).sort(this.sorter!.compare.bind(this.sorter)));
}

return Iterator.map<ITreeNode<T | null, TFilterData>, ITreeElement<T>>(childrenNodes, node => ({
element: node.element as T,
collapsible: node.collapsible,
collapsed: node.collapsed,
children: this.resortChildren(node, recursive, false)
}));
}

getParentElement(ref: T | null = null): T | null {
const location = this.getElementLocation(ref);
return this.model.getParentElement(location);
Expand Down
37 changes: 37 additions & 0 deletions src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,41 @@ suite('ObjectTreeModel', function () {
model.setChildren(null, data);
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']);
});

test('resort', () => {
let compare: (a: string, b: string) => number = () => 0;

const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>(toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const data = [
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
{ element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] },
];

model.setChildren(null, data);
assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric']);

// lexicographical
compare = (a, b) => a < b ? -1 : 1;

// non-recursive
model.resort(null, false);
assert.deepEqual(toArray(list), ['airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric', 'cars', 'sedan', 'convertible', 'compact']);

// recursive
model.resort();
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']);

// reverse
compare = (a, b) => a < b ? 1 : -1;

// scoped
model.resort('cars');
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'sedan', 'convertible', 'compact']);

// recursive
model.resort();
assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'bicycles', 'mountain', 'electric', 'dutch', 'airplanes', 'passenger', 'jet']);
});
});

0 comments on commit 7ae4138

Please sign in to comment.