Skip to content

Commit

Permalink
fixes #81393
Browse files Browse the repository at this point in the history
  • Loading branch information
joaomoreno committed Sep 25, 2019
1 parent 9bc1083 commit e066781
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 33 deletions.
65 changes: 32 additions & 33 deletions src/vs/base/browser/ui/tree/abstractTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,18 +225,14 @@ interface Collection<T> {

class EventCollection<T> implements Collection<T> {

private disposables = new DisposableStore();
readonly onDidChange: Event<T[]>;

get elements(): T[] {
return this._elements;
}

constructor(readonly onDidChange: Event<T[]>, private _elements: T[] = []) {
onDidChange(e => this._elements = e, null, this.disposables);
}

dispose() {
this.disposables.dispose();
constructor(onDidChange: Event<T[]>, private _elements: T[] = []) {
this.onDidChange = Event.forEach(onDidChange, elements => this._elements = elements);
}
}

Expand All @@ -249,7 +245,7 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
private renderedNodes = new Map<ITreeNode<T, TFilterData>, IRenderData<TTemplateData>>();
private indent: number = TreeRenderer.DefaultIndent;

private _renderIndentGuides: RenderIndentGuides = RenderIndentGuides.None;
private shouldRenderIndentGuides: boolean = false;
private renderedIndentGuides = new SetMap<ITreeNode<T, TFilterData>, HTMLDivElement>();
private activeIndentNodes = new Set<ITreeNode<T, TFilterData>>();
private indentGuidesDisposable: IDisposable = Disposable.None;
Expand Down Expand Up @@ -279,19 +275,18 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
}

if (typeof options.renderIndentGuides !== 'undefined') {
const renderIndentGuides = options.renderIndentGuides;
const shouldRenderIndentGuides = options.renderIndentGuides !== RenderIndentGuides.None;

if (renderIndentGuides !== this._renderIndentGuides) {
this._renderIndentGuides = renderIndentGuides;
if (shouldRenderIndentGuides !== this.shouldRenderIndentGuides) {
this.shouldRenderIndentGuides = shouldRenderIndentGuides;
this.indentGuidesDisposable.dispose();

if (renderIndentGuides) {
if (shouldRenderIndentGuides) {
const disposables = new DisposableStore();
this.activeNodes.onDidChange(this._onDidChangeActiveNodes, this, disposables);
this.indentGuidesDisposable = disposables;

this._onDidChangeActiveNodes(this.activeNodes.elements);
} else {
this.indentGuidesDisposable.dispose();
}
}
}
Expand Down Expand Up @@ -384,7 +379,7 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
clearNode(templateData.indent);
templateData.indentGuidesDisposable.dispose();

if (this._renderIndentGuides === RenderIndentGuides.None) {
if (!this.shouldRenderIndentGuides) {
return;
}

Expand Down Expand Up @@ -424,7 +419,7 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
}

private _onDidChangeActiveNodes(nodes: ITreeNode<T, TFilterData>[]): void {
if (this._renderIndentGuides === RenderIndentGuides.None) {
if (!this.shouldRenderIndentGuides) {
return;
}

Expand Down Expand Up @@ -1001,7 +996,6 @@ class Trait<T> {
insertedNodes.forEach(node => dfs(node, insertedNodesVisitor));

const nodes: ITreeNode<T, any>[] = [];
let silent = true;

for (const node of this.nodes) {
const id = this.identityProvider.getId(node.element).toString();
Expand All @@ -1014,13 +1008,11 @@ class Trait<T> {

if (insertedNode) {
nodes.push(insertedNode);
} else {
silent = false;
}
}
}

this._set(nodes, silent);
this._set(nodes, true);
}

private createNodeSet(): Set<ITreeNode<T, any>> {
Expand Down Expand Up @@ -1228,9 +1220,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);

const onDidChangeCollapseStateRelay = new Relay<ICollapseStateChangeEvent<T, TFilterData>>();
const onDidChangeActiveNodes = new Emitter<ITreeNode<T, TFilterData>[]>();
const onDidChangeActiveNodes = new Relay<ITreeNode<T, TFilterData>[]>();
const activeNodes = new EventCollection(onDidChangeActiveNodes.event);
this.disposables.push(activeNodes);

this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, TRef, any>(r, () => this.model, onDidChangeCollapseStateRelay.event, activeNodes, _options));
this.disposables.push(...this.renderers);
Expand All @@ -1250,24 +1241,32 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.model = this.createModel(user, this.view, _options);
onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState;

this.model.onDidSplice(e => {
const onDidModelSplice = Event.forEach(this.model.onDidSplice, e => {
this.eventBufferer.bufferEvents(() => {
this.focus.onDidModelSplice(e);
this.selection.onDidModelSplice(e);
});
});

const set = new Set<ITreeNode<T, TFilterData>>();

for (const node of this.focus.getNodes()) {
set.add(node);
}
// Active nodes can change when the model changes or when focus or selection change.
// We debouce it with 0 delay since these events may fire in the same stack and we only
// want to run this once. It also doesn't matter if it runs on the next tick since it's only
// a nice to have UI feature.
onDidChangeActiveNodes.input = Event.chain(Event.any<any>(onDidModelSplice, this.focus.onDidChange, this.selection.onDidChange))
.debounce(() => null, 0)
.map(() => {
const set = new Set<ITreeNode<T, TFilterData>>();

for (const node of this.focus.getNodes()) {
set.add(node);
}

for (const node of this.selection.getNodes()) {
set.add(node);
}
for (const node of this.selection.getNodes()) {
set.add(node);
}

onDidChangeActiveNodes.fire(fromSet(set));
}, null, this.disposables);
return fromSet(set);
}).event;

if (_options.keyboardSupport !== false) {
const onKeyDown = Event.chain(this.view.onKeyDown)
Expand Down
8 changes: 8 additions & 0 deletions src/vs/base/common/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ export namespace Event {
filter(fn: (e: T) => boolean): IChainableEvent<T>;
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R>;
latch(): IChainableEvent<T>;
debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent<T>;
debounce<R>(merge: (last: R | undefined, event: T) => R, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent<R>;
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
}
Expand Down Expand Up @@ -299,6 +301,12 @@ export namespace Event {
return new ChainableEvent(latch(this.event));
}

debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent<T>;
debounce<R>(merge: (last: R | undefined, event: T) => R, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent<R>;
debounce<R>(merge: (last: R | undefined, event: T) => R, delay: number = 100, leading = false, leakWarningThreshold?: number): IChainableEvent<R> {
return new ChainableEvent(debounce(this.event, merge, delay, leading, leakWarningThreshold));
}

on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[] | DisposableStore) {
return this.event(listener, thisArgs, disposables);
}
Expand Down

0 comments on commit e066781

Please sign in to comment.