Skip to content

Commit 95d7625

Browse files
committed
Merge branch 'joao/scm-tree'
2 parents b590547 + ef326b9 commit 95d7625

36 files changed

Lines changed: 1861 additions & 1119 deletions

extensions/git/package.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,53 @@
850850
"group": "inline"
851851
}
852852
],
853+
"scm/resourceFolder/context": [
854+
{
855+
"command": "git.stage",
856+
"when": "scmProvider == git && scmResourceGroup == merge",
857+
"group": "1_modification"
858+
},
859+
{
860+
"command": "git.stage",
861+
"when": "scmProvider == git && scmResourceGroup == merge",
862+
"group": "inline"
863+
},
864+
{
865+
"command": "git.unstage",
866+
"when": "scmProvider == git && scmResourceGroup == index",
867+
"group": "1_modification"
868+
},
869+
{
870+
"command": "git.unstage",
871+
"when": "scmProvider == git && scmResourceGroup == index",
872+
"group": "inline"
873+
},
874+
{
875+
"command": "git.stage",
876+
"when": "scmProvider == git && scmResourceGroup == workingTree",
877+
"group": "1_modification"
878+
},
879+
{
880+
"command": "git.clean",
881+
"when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository",
882+
"group": "1_modification"
883+
},
884+
{
885+
"command": "git.clean",
886+
"when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository",
887+
"group": "inline"
888+
},
889+
{
890+
"command": "git.stage",
891+
"when": "scmProvider == git && scmResourceGroup == workingTree",
892+
"group": "inline"
893+
},
894+
{
895+
"command": "git.ignore",
896+
"when": "scmProvider == git && scmResourceGroup == workingTree",
897+
"group": "1_modification@3"
898+
}
899+
],
853900
"scm/resourceState/context": [
854901
{
855902
"command": "git.stage",

extensions/git/package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"command.syncRebase": "Sync (Rebase)",
5656
"command.publish": "Publish Branch",
5757
"command.showOutput": "Show Git Output",
58-
"command.ignore": "Add File to .gitignore",
58+
"command.ignore": "Add to .gitignore",
5959
"command.stashIncludeUntracked": "Stash (Include Untracked)",
6060
"command.stash": "Stash",
6161
"command.stashPop": "Pop Stash...",

src/vs/base/browser/ui/list/list.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ export interface IKeyboardNavigationLabelProvider<T> {
7979
* element always match.
8080
*/
8181
getKeyboardNavigationLabel(element: T): { toString(): string | undefined; } | undefined;
82-
mightProducePrintableCharacter?(event: IKeyboardEvent): boolean;
82+
}
83+
84+
export interface IKeyboardNavigationDelegate {
85+
mightProducePrintableCharacter(event: IKeyboardEvent): boolean;
8386
}
8487

8588
export const enum ListDragOverEffect {

src/vs/base/browser/ui/list/listPaging.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class PagedList<T> implements IDisposable {
127127
}
128128

129129
get onPin(): Event<IListEvent<T>> {
130-
return Event.map(this.list.onPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
130+
return Event.map(this.list.onDidPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
131131
}
132132

133133
get onContextMenu(): Event<IListContextMenuEvent<T>> {

src/vs/base/browser/ui/list/listWidget.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
1616
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
1717
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
1818
import { domEvent } from 'vs/base/browser/event';
19-
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole, ListError } from './list';
19+
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole, ListError, IKeyboardNavigationDelegate } from './list';
2020
import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaProvider } from './listView';
2121
import { Color } from 'vs/base/common/color';
2222
import { mixin } from 'vs/base/common/objects';
@@ -322,16 +322,18 @@ enum TypeLabelControllerState {
322322
Typing
323323
}
324324

325-
export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
326-
if (event.ctrlKey || event.metaKey || event.altKey) {
327-
return false;
328-
}
325+
export const DefaultKeyboardNavigationDelegate = new class implements IKeyboardNavigationDelegate {
326+
mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
327+
if (event.ctrlKey || event.metaKey || event.altKey) {
328+
return false;
329+
}
329330

330-
return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z)
331-
|| (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9)
332-
|| (event.keyCode >= KeyCode.NUMPAD_0 && event.keyCode <= KeyCode.NUMPAD_9)
333-
|| (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE);
334-
}
331+
return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z)
332+
|| (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9)
333+
|| (event.keyCode >= KeyCode.NUMPAD_0 && event.keyCode <= KeyCode.NUMPAD_9)
334+
|| (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE);
335+
}
336+
};
335337

336338
class TypeLabelController<T> implements IDisposable {
337339

@@ -347,7 +349,8 @@ class TypeLabelController<T> implements IDisposable {
347349
constructor(
348350
private list: List<T>,
349351
private view: ListView<T>,
350-
private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>
352+
private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>,
353+
private delegate: IKeyboardNavigationDelegate
351354
) {
352355
this.updateOptions(list.options);
353356
}
@@ -379,7 +382,7 @@ class TypeLabelController<T> implements IDisposable {
379382
.filter(e => !isInputElement(e.target as HTMLElement))
380383
.filter(() => this.automaticKeyboardNavigation || this.triggered)
381384
.map(event => new StandardKeyboardEvent(event))
382-
.filter(this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e))
385+
.filter(e => this.delegate.mightProducePrintableCharacter(e))
383386
.forEach(e => { e.stopPropagation(); e.preventDefault(); })
384387
.map(event => event.browserEvent.key)
385388
.event;
@@ -818,6 +821,7 @@ export interface IListOptions<T> extends IListStyles {
818821
readonly enableKeyboardNavigation?: boolean;
819822
readonly automaticKeyboardNavigation?: boolean;
820823
readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider<T>;
824+
readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate;
821825
readonly ariaRole?: ListAriaRootRole;
822826
readonly ariaLabel?: string;
823827
readonly keyboardSupport?: boolean;
@@ -1110,10 +1114,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
11101114
private _onDidOpen = new Emitter<IListEvent<T>>();
11111115
readonly onDidOpen: Event<IListEvent<T>> = this._onDidOpen.event;
11121116

1113-
private _onPin = new Emitter<number[]>();
1114-
@memoize get onPin(): Event<IListEvent<T>> {
1115-
return Event.map(this._onPin.event, indexes => this.toListEvent({ indexes }));
1116-
}
1117+
private _onDidPin = new Emitter<IListEvent<T>>();
1118+
readonly onDidPin: Event<IListEvent<T>> = this._onDidPin.event;
11171119

11181120
get domId(): string { return this.view.domId; }
11191121
get onDidScroll(): Event<ScrollEvent> { return this.view.onDidScroll; }
@@ -1228,7 +1230,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
12281230
}
12291231

12301232
if (_options.keyboardNavigationLabelProvider) {
1231-
this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider);
1233+
const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate;
1234+
this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider, delegate);
12321235
this.disposables.add(this.typeLabelController);
12331236
}
12341237

@@ -1582,14 +1585,14 @@ export class List<T> implements ISpliceable<T>, IDisposable {
15821585
this._onDidOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent });
15831586
}
15841587

1585-
pin(indexes: number[]): void {
1588+
pin(indexes: number[], browserEvent?: UIEvent): void {
15861589
for (const index of indexes) {
15871590
if (index < 0 || index >= this.length) {
15881591
throw new ListError(this.user, `Invalid index ${index}`);
15891592
}
15901593
}
15911594

1592-
this._onPin.fire(indexes);
1595+
this._onDidPin.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent });
15931596
}
15941597

15951598
style(styles: IListStyles): void {
@@ -1626,7 +1629,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
16261629
this.disposables.dispose();
16271630

16281631
this._onDidOpen.dispose();
1629-
this._onPin.dispose();
1632+
this._onDidPin.dispose();
16301633
this._onDidDispose.dispose();
16311634
}
16321635
}

src/vs/base/browser/ui/tree/abstractTree.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
import 'vs/css!./media/tree';
77
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
8-
import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget';
9-
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
8+
import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/listWidget';
9+
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list';
1010
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom';
1111
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
12-
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
12+
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
1313
import { KeyCode } from 'vs/base/common/keyCodes';
1414
import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent, TreeMouseEventTarget } from 'vs/base/browser/ui/tree/tree';
1515
import { ISpliceable } from 'vs/base/common/sequence';
@@ -592,7 +592,7 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
592592
model: ITreeModel<T, TFilterData, any>,
593593
private view: List<ITreeNode<T, TFilterData>>,
594594
private filter: TypeFilter<T>,
595-
private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>
595+
private keyboardNavigationDelegate: IKeyboardNavigationDelegate
596596
) {
597597
this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`);
598598
this.domNode.draggable = true;
@@ -658,13 +658,12 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
658658
return;
659659
}
660660

661-
const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e);
662661
const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown'))
663662
.filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode)
664663
.map(e => new StandardKeyboardEvent(e))
665664
.filter(this.keyboardNavigationEventFilter || (() => true))
666665
.filter(() => this.automaticKeyboardNavigation || this.triggered)
667-
.filter(e => isPrintableCharEvent(e) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
666+
.filter(e => this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
668667
.forEach(e => { e.stopPropagation(); e.preventDefault(); })
669668
.event;
670669

@@ -1189,6 +1188,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
11891188
get onDidChangeFocus(): Event<ITreeEvent<T>> { return this.eventBufferer.wrapEvent(this.focus.onDidChange); }
11901189
get onDidChangeSelection(): Event<ITreeEvent<T>> { return this.eventBufferer.wrapEvent(this.selection.onDidChange); }
11911190
get onDidOpen(): Event<ITreeEvent<T>> { return Event.map(this.view.onDidOpen, asTreeEvent); }
1191+
get onDidPin(): Event<ITreeEvent<T>> { return Event.map(this.view.onDidPin, asTreeEvent); }
11921192

11931193
get onMouseClick(): Event<ITreeMouseEvent<T>> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); }
11941194
get onMouseDblClick(): Event<ITreeMouseEvent<T>> { return Event.map(this.view.onMouseDblClick, asTreeMouseEvent); }
@@ -1228,7 +1228,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
12281228
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
12291229

12301230
const onDidChangeCollapseStateRelay = new Relay<ICollapseStateChangeEvent<T, TFilterData>>();
1231-
const onDidChangeActiveNodes = new Relay<ITreeNode<T, TFilterData>[]>();
1231+
const onDidChangeActiveNodes = new Emitter<ITreeNode<T, TFilterData>[]>();
12321232
const activeNodes = new EventCollection(onDidChangeActiveNodes.event);
12331233
this.disposables.push(activeNodes);
12341234

@@ -1251,11 +1251,23 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
12511251
onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState;
12521252

12531253
this.model.onDidSplice(e => {
1254-
this.focus.onDidModelSplice(e);
1255-
this.selection.onDidModelSplice(e);
1256-
}, null, this.disposables);
1254+
this.eventBufferer.bufferEvents(() => {
1255+
this.focus.onDidModelSplice(e);
1256+
this.selection.onDidModelSplice(e);
1257+
});
1258+
1259+
const set = new Set<ITreeNode<T, TFilterData>>();
1260+
1261+
for (const node of this.focus.getNodes()) {
1262+
set.add(node);
1263+
}
12571264

1258-
onDidChangeActiveNodes.input = Event.map(Event.any<any>(this.focus.onDidChange, this.selection.onDidChange, this.model.onDidSplice), () => [...this.focus.getNodes(), ...this.selection.getNodes()]);
1265+
for (const node of this.selection.getNodes()) {
1266+
set.add(node);
1267+
}
1268+
1269+
onDidChangeActiveNodes.fire(Array.from(set.values()));
1270+
}, null, this.disposables);
12591271

12601272
if (_options.keyboardSupport !== false) {
12611273
const onKeyDown = Event.chain(this.view.onKeyDown)
@@ -1268,7 +1280,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
12681280
}
12691281

12701282
if (_options.keyboardNavigationLabelProvider) {
1271-
this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider);
1283+
const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate;
1284+
this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, delegate);
12721285
this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node);
12731286
this.disposables.push(this.typeFilterController!);
12741287
}

src/vs/base/browser/ui/tree/asyncDataTree.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
7-
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
7+
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree';
88
import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list';
99
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper } from 'vs/base/browser/ui/tree/tree';
1010
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -1010,6 +1010,24 @@ export interface ITreeCompressionDelegate<T> {
10101010
isIncompressible(element: T): boolean;
10111011
}
10121012

1013+
function asCompressibleObjectTreeOptions<TInput, T, TFilterData>(options?: ICompressibleAsyncDataTreeOptions<T, TFilterData>): ICompressibleObjectTreeOptions<IAsyncDataTreeNode<TInput, T>, TFilterData> | undefined {
1014+
const objectTreeOptions = options && asObjectTreeOptions(options);
1015+
1016+
return objectTreeOptions && {
1017+
...objectTreeOptions,
1018+
keyboardNavigationLabelProvider: objectTreeOptions.keyboardNavigationLabelProvider && {
1019+
...objectTreeOptions.keyboardNavigationLabelProvider,
1020+
getCompressedNodeKeyboardNavigationLabel(els) {
1021+
return options!.keyboardNavigationLabelProvider!.getCompressedNodeKeyboardNavigationLabel(els.map(e => e.element as T));
1022+
}
1023+
}
1024+
};
1025+
}
1026+
1027+
export interface ICompressibleAsyncDataTreeOptions<T, TFilterData = void> extends IAsyncDataTreeOptions<T, TFilterData> {
1028+
readonly keyboardNavigationLabelProvider?: ICompressibleKeyboardNavigationLabelProvider<T>;
1029+
}
1030+
10131031
export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
10141032

10151033
protected readonly compressibleNodeMapper: CompressibleAsyncDataTreeNodeMapper<TInput, T, TFilterData> = new WeakMapper(node => new CompressibleAsyncDataTreeNodeWrapper(node));
@@ -1031,11 +1049,11 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
10311049
container: HTMLElement,
10321050
delegate: IListVirtualDelegate<T>,
10331051
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
1034-
options: IAsyncDataTreeOptions<T, TFilterData>
1052+
options: ICompressibleAsyncDataTreeOptions<T, TFilterData>
10351053
): ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData> {
10361054
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
10371055
const objectTreeRenderers = renderers.map(r => new CompressibleAsyncDataTreeRenderer(r, this.nodeMapper, () => this.compressibleNodeMapper, this._onDidChangeNodeSlowState.event));
1038-
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
1056+
const objectTreeOptions = asCompressibleObjectTreeOptions<TInput, T, TFilterData>(options) || {};
10391057

10401058
return new CompressibleObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
10411059
}

src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ function mapOptions<T, TFilterData>(compressedNodeUnwrapper: CompressedNodeUnwra
336336
...options,
337337
sorter: options.sorter && {
338338
compare(node: ICompressedTreeNode<T>, otherNode: ICompressedTreeNode<T>): number {
339-
return options.sorter!.compare(compressedNodeUnwrapper(node), compressedNodeUnwrapper(otherNode));
339+
return options.sorter!.compare(node.elements[0], otherNode.elements[0]);
340340
}
341341
},
342342
identityProvider: options.identityProvider && {

src/vs/base/browser/ui/tree/dataTree.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,10 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
162162
const children = this.dataSource.getChildren(element);
163163
const elements = Iterator.map<any, ITreeElement<T>>(Iterator.fromArray(children), element => {
164164
const { elements: children, size } = this.iterate(element, isCollapsed);
165+
const collapsible = this.dataSource.hasChildren ? this.dataSource.hasChildren(element) : undefined;
165166
const collapsed = size === 0 ? undefined : (isCollapsed && isCollapsed(element));
166167

167-
return { element, children, collapsed };
168+
return { element, children, collapsible, collapsed };
168169
});
169170

170171
return { elements, size: children.length };

0 commit comments

Comments
 (0)