Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/tree';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom';
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent, TreeMouseEventTarget } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd';
import { range, equals, distinctES6 } from 'vs/base/common/arrays';
@@ -25,6 +25,7 @@ import { isMacintosh } from 'vs/base/common/platform';
import { values } from 'vs/base/common/map';
import { clamp } from 'vs/base/common/numbers';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { SetMap } from 'vs/base/common/collections';
function asTreeDragAndDropData<T, TFilterData>(data: IDragAndDropData): IDragAndDropData {
if (data instanceof ElementsDragAndDropData) {
@@ -152,7 +153,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
}
},
enableKeyboardNavigation: options.simpleKeyboardNavigation,
ariaSetProvider: {
ariaProvider: {
getSetSize(node) {
return node.parent!.visibleChildrenCount;
},
@@ -188,12 +189,48 @@ export class ComposedTreeDelegate<T, N extends { element: T }> implements IListV
interface ITreeListTemplateData<T> {
readonly container: HTMLElement;
readonly indent: HTMLElement;
readonly twistie: HTMLElement;
indentGuidesDisposable: IDisposable;
readonly templateData: T;
}
export enum RenderIndentGuides {
None = 'none',
OnHover = 'onHover',
Always = 'always'
}
interface ITreeRendererOptions {
readonly indent?: number;
readonly renderIndentGuides?: RenderIndentGuides;
}
interface IRenderData<TTemplateData> {
templateData: ITreeListTemplateData<TTemplateData>;
height: number;
}
interface Collection<T> {
readonly elements: T[];
readonly onDidChange: Event<T[]>;
}
class EventCollection<T> implements Collection<T> {
private disposables = new DisposableStore();
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();
}
}
class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>> {
@@ -202,13 +239,20 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
readonly templateId: string;
private renderedElements = new Map<T, ITreeNode<T, TFilterData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, IRenderData<TTemplateData>>();
private indent: number = TreeRenderer.DefaultIndent;
private _renderIndentGuides: RenderIndentGuides = RenderIndentGuides.None;
private renderedIndentGuides = new SetMap<ITreeNode<T, TFilterData>, HTMLDivElement>();
private activeParentNodes = new Set<ITreeNode<T, TFilterData>>();
private indentGuidesDisposable: IDisposable = Disposable.None;
private disposables: IDisposable[] = [];
constructor(
private renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>>,
private activeNodes: Collection<ITreeNode<T, TFilterData>>,
options: ITreeRendererOptions = {}
) {
this.templateId = renderer.templateId;
@@ -226,34 +270,57 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
this.indent = clamp(options.indent, 0, 40);
}
this.renderedNodes.forEach((templateData, node) => {
templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`;
});
if (typeof options.renderIndentGuides !== 'undefined') {
const renderIndentGuides = options.renderIndentGuides;
if (renderIndentGuides !== this._renderIndentGuides) {
this._renderIndentGuides = renderIndentGuides;
if (renderIndentGuides) {
const disposables = new DisposableStore();
this.activeNodes.onDidChange(this._onDidChangeActiveNodes, this, disposables);
this.indentGuidesDisposable = disposables;
this._onDidChangeActiveNodes(this.activeNodes.elements);
} else {
this.indentGuidesDisposable.dispose();
}
}
}
}
renderTemplate(container: HTMLElement): ITreeListTemplateData<TTemplateData> {
const el = append(container, $('.monaco-tl-row'));
const indent = append(el, $('.monaco-tl-indent'));
const twistie = append(el, $('.monaco-tl-twistie'));
const contents = append(el, $('.monaco-tl-contents'));
const templateData = this.renderer.renderTemplate(contents);
return { container, twistie, templateData };
return { container, indent, twistie, indentGuidesDisposable: Disposable.None, templateData };
}
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
if (typeof height === 'number') {
this.renderedNodes.set(node, templateData);
this.renderedNodes.set(node, { templateData, height });
this.renderedElements.set(node.element, node);
}
const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent;
templateData.twistie.style.marginLeft = `${indent}px`;
this.update(node, templateData);
templateData.indent.style.width = `${indent + this.indent - 16}px`;
this.renderTwistie(node, templateData);
if (typeof height === 'number') {
this.renderIndentGuides(node, templateData);
}
this.renderer.renderElement(node, index, templateData.templateData, height);
}
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
templateData.indentGuidesDisposable.dispose();
if (this.renderer.disposeElement) {
this.renderer.disposeElement(node, index, templateData.templateData, height);
}
@@ -279,16 +346,17 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
}
private onDidChangeNodeTwistieState(node: ITreeNode<T, TFilterData>): void {
const templateData = this.renderedNodes.get(node);
const data = this.renderedNodes.get(node);
if (!templateData) {
if (!data) {
return;
}
this.update(node, templateData);
this.renderTwistie(node, data.templateData);
this.renderIndentGuides(node, data.templateData);
}
private update(node: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>) {
private renderTwistie(node: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>) {
if (this.renderer.renderTwistie) {
this.renderer.renderTwistie(node.element, templateData.twistie);
}
@@ -303,9 +371,72 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
}
}
private renderIndentGuides(target: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>): void {
clearNode(templateData.indent);
templateData.indentGuidesDisposable.dispose();
if (this._renderIndentGuides === RenderIndentGuides.None) {
return;
}
const disposableStore = new DisposableStore();
let node = target;
while (node.parent && node.parent.parent) {
const parent = node.parent;
const guide = $<HTMLDivElement>('.indent-guide', { style: `width: ${this.indent}px` });
if (this.activeParentNodes.has(parent)) {
addClass(guide, 'active');
}
if (templateData.indent.childElementCount === 0) {
templateData.indent.appendChild(guide);
} else {
templateData.indent.insertBefore(guide, templateData.indent.firstElementChild);
}
this.renderedIndentGuides.add(parent, guide);
disposableStore.add(toDisposable(() => this.renderedIndentGuides.delete(parent, guide)));
node = parent;
}
templateData.indentGuidesDisposable = disposableStore;
}
private _onDidChangeActiveNodes(nodes: ITreeNode<T, TFilterData>[]): void {
if (this._renderIndentGuides === RenderIndentGuides.None) {
return;
}
const set = new Set<ITreeNode<T, TFilterData>>();
nodes.forEach(node => {
if (node.parent) {
set.add(node.parent);
}
});
this.activeParentNodes.forEach(node => {
if (!set.has(node)) {
this.renderedIndentGuides.forEach(node, line => removeClass(line, 'active'));
}
});
set.forEach(node => {
if (!this.activeParentNodes.has(node)) {
this.renderedIndentGuides.forEach(node, line => addClass(line, 'active'));
}
});
this.activeParentNodes = set;
}
dispose(): void {
this.renderedNodes.clear();
this.renderedElements.clear();
this.indentGuidesDisposable.dispose();
this.disposables = dispose(this.disposables);
}
}
@@ -722,9 +853,18 @@ function asTreeEvent<T>(event: IListEvent<ITreeNode<T, any>>): ITreeEvent<T> {
}
function asTreeMouseEvent<T>(event: IListMouseEvent<ITreeNode<T, any>>): ITreeMouseEvent<T> {
let target: TreeMouseEventTarget = TreeMouseEventTarget.Unknown;
if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-twistie', 'monaco-tl-row')) {
target = TreeMouseEventTarget.Twistie;
} else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-contents', 'monaco-tl-row')) {
target = TreeMouseEventTarget.Element;
}
return {
browserEvent: event.browserEvent,
element: event.element ? event.element.element : null
element: event.element ? event.element.element : null,
target
};
}
@@ -811,6 +951,10 @@ class Trait<T> {
return [...this.elements];
}
getNodes(): readonly ITreeNode<T, any>[] {
return this.nodes;
}
has(node: ITreeNode<T, any>): boolean {
return this.nodeSet.has(node);
}
@@ -999,6 +1143,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
private eventBufferer = new EventBufferer();
private typeFilterController?: TypeFilterController<T, TFilterData>;
private focusNavigationFilter: ((node: ITreeNode<T, TFilterData>) => boolean) | undefined;
private styleElement: HTMLStyleElement;
protected disposables: IDisposable[] = [];
get onDidScroll(): Event<ScrollEvent> { return this.view.onDidScroll; }
@@ -1027,7 +1172,6 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
get filterOnType(): boolean { return !!this._options.filterOnType; }
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
// Options TODO@joao expose options only, not Optional<>
get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; }
@@ -1045,7 +1189,11 @@ 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>>();
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, _options));
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, any>(r, onDidChangeCollapseStateRelay.event, activeNodes, _options));
this.disposables.push(...this.renderers);
let filter: TypeFilter<T> | undefined;
@@ -1068,6 +1216,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.selection.onDidModelSplice(e);
}, null, this.disposables);
onDidChangeActiveNodes.input = Event.map(Event.any<any>(this.focus.onDidChange, this.selection.onDidChange, this.model.onDidSplice), () => [...this.focus.getNodes(), ...this.selection.getNodes()]);
if (_options.keyboardSupport !== false) {
const onKeyDown = Event.chain(this.view.onKeyDown)
.filter(e => !isInputElement(e.target as HTMLElement))
@@ -1083,6 +1233,9 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node);
this.disposables.push(this.typeFilterController!);
}
this.styleElement = createStyleSheet(this.view.getHTMLElement());
toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always);
}
updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void {
@@ -1102,6 +1255,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
this._onDidUpdateOptions.fire(this._options);
toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always);
}
get options(): IAbstractTreeOptions<T, TFilterData> {
@@ -1183,6 +1338,19 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
style(styles: IListStyles): void {
const suffix = `.${this.view.domId}`;
const content: string[] = [];
if (styles.treeIndentGuidesStroke) {
content.push(`.monaco-list${suffix}:hover .monaco-tl-indent > .indent-guide, .monaco-list${suffix}.always .monaco-tl-indent > .indent-guide { border-color: ${styles.treeIndentGuidesStroke.transparent(0.4)}; }`);
content.push(`.monaco-list${suffix} .monaco-tl-indent > .indent-guide.active { border-color: ${styles.treeIndentGuidesStroke}; }`);
}
const newStyles = content.join('\n');
if (newStyles !== this.styleElement.innerHTML) {
this.styleElement.innerHTML = newStyles;
}
this.view.style(styles);
}

View File

@@ -28,6 +28,7 @@ interface IAsyncDataTreeNode<TInput, T> {
hasChildren: boolean;
stale: boolean;
slow: boolean;
collapsedByDefault: boolean | undefined;
}
interface IAsyncDataTreeNodeRequiredProps<TInput, T> extends Partial<IAsyncDataTreeNode<TInput, T>> {
@@ -42,7 +43,8 @@ function createAsyncDataTreeNode<TInput, T>(props: IAsyncDataTreeNodeRequiredPro
children: [],
loading: false,
stale: true,
slow: false
slow: false,
collapsedByDefault: undefined
};
}
@@ -133,7 +135,8 @@ function asTreeEvent<TInput, T>(e: ITreeEvent<IAsyncDataTreeNode<TInput, T>>): I
function asTreeMouseEvent<TInput, T>(e: ITreeMouseEvent<IAsyncDataTreeNode<TInput, T>>): ITreeMouseEvent<T> {
return {
browserEvent: e.browserEvent,
element: e.element && e.element.element as T
element: e.element && e.element.element as T,
target: e.target
};
}
@@ -224,6 +227,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
}
},
keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && {
...options.keyboardNavigationLabelProvider,
getKeyboardNavigationLabel(e) {
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element as T);
}
@@ -234,17 +238,21 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T)
)
),
ariaSetProvider: undefined
ariaProvider: undefined
};
}
function asTreeElement<TInput, T>(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): ITreeElement<IAsyncDataTreeNode<TInput, T>> {
let collapsed: boolean | undefined;
if (viewStateContext && viewStateContext.viewState.expanded && node.id) {
collapsed = viewStateContext.viewState.expanded.indexOf(node.id) === -1;
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 ? Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)) : [],
@@ -255,10 +263,11 @@ function asTreeElement<TInput, T>(node: IAsyncDataTreeNode<TInput, T>, viewState
export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { }
export interface IAsyncDataTreeOptions<T, TFilterData = void> extends IAsyncDataTreeOptionsUpdate, IAbstractTreeOptions<T, TFilterData> {
identityProvider?: IIdentityProvider<T>;
sorter?: ITreeSorter<T>;
autoExpandSingleChildren?: boolean;
export interface IAsyncDataTreeOptions<T, TFilterData = void> extends IAsyncDataTreeOptionsUpdate, Pick<IAbstractTreeOptions<T, TFilterData>, Exclude<keyof IAbstractTreeOptions<T, TFilterData>, 'collapseByDefault'>> {
readonly collapseByDefault?: { (e: T): boolean; };
readonly identityProvider?: IIdentityProvider<T>;
readonly sorter?: ITreeSorter<T>;
readonly autoExpandSingleChildren?: boolean;
}
export interface IAsyncDataTreeViewState {
@@ -285,6 +294,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
private 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; };
private readonly subTreeRefreshPromises = new Map<IAsyncDataTreeNode<TInput, T>, Promise<void>>();
private readonly refreshPromises = new Map<IAsyncDataTreeNode<TInput, T>, CancelablePromise<T[]>>();
@@ -310,10 +320,20 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
get onDidFocus(): Event<void> { return this.tree.onDidFocus; }
get onDidBlur(): Event<void> { return this.tree.onDidBlur; }
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T>, TFilterData>> { return this.tree.onDidChangeCollapseState; }
get onDidUpdateOptions(): Event<IAsyncDataTreeOptionsUpdate> { return this.tree.onDidUpdateOptions; }
get filterOnType(): boolean { return this.tree.filterOnType; }
get openOnSingleClick(): boolean { return this.tree.openOnSingleClick; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) {
if (typeof this.tree.expandOnlyOnTwistieClick === 'boolean') {
return this.tree.expandOnlyOnTwistieClick;
}
const fn = this.tree.expandOnlyOnTwistieClick;
return element => fn(this.nodes.get((element === this.root.element ? null : element) as T) || null);
}
get onDidDispose(): Event<void> { return this.tree.onDidDispose; }
@@ -327,6 +347,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
this.identityProvider = options.identityProvider;
this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren;
this.sorter = options.sorter;
this.collapseByDefault = options.collapseByDefault;
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event));
@@ -762,12 +783,17 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
const childrenToRefresh: IAsyncDataTreeNode<TInput, T>[] = [];
const children = childrenElements.map<IAsyncDataTreeNode<TInput, T>>(element => {
const hasChildren = !!this.dataSource.hasChildren(element);
if (!this.identityProvider) {
return createAsyncDataTreeNode({
element,
parent: node,
hasChildren: !!this.dataSource.hasChildren(element)
});
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();
@@ -781,7 +807,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
this.nodes.set(element, asyncDataTreeNode);
asyncDataTreeNode.element = element;
asyncDataTreeNode.hasChildren = !!this.dataSource.hasChildren(element);
asyncDataTreeNode.hasChildren = hasChildren;
if (recursive) {
if (childNode.collapsed) {
@@ -789,17 +815,15 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
} 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: !!this.dataSource.hasChildren(element)
});
const childAsyncDataTreeNode = createAsyncDataTreeNode({ element, parent: node, id, hasChildren });
if (viewStateContext && viewStateContext.viewState.focus && viewStateContext.viewState.focus.indexOf(id) > -1) {
viewStateContext.focus.push(childAsyncDataTreeNode);
@@ -811,6 +835,9 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
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;

View File

@@ -18,6 +18,7 @@ export interface IDataTreeViewState {
readonly focus: string[];
readonly selection: string[];
readonly expanded: string[];
readonly scrollTop: number;
}
export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
@@ -80,6 +81,10 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
this._refresh(input, isCollapsed, onDidCreateNode);
this.setFocus(focus);
this.setSelection(selection);
if (viewState && typeof viewState.scrollTop === 'number') {
this.scrollTop = viewState.scrollTop;
}
}
updateChildren(element: TInput | T = this.input!): void {
@@ -193,6 +198,6 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
queue.push(...node.children);
}
return { focus, selection, expanded };
return { focus, selection, expanded, scrollTop: this.scrollTop };
}
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

Before

Width:  |  Height:  |  Size: 139 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fff" d="M6 4v8l4-4-4-4zm1 2.414l1.586 1.586-1.586 1.586v-3.172z"/></svg>

Before

Width:  |  Height:  |  Size: 148 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

Before

Width:  |  Height:  |  Size: 139 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M11 10H5.344L11 4.414V10z"/></svg>

Before

Width:  |  Height:  |  Size: 118 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fff" d="M11 10.07h-5.656l5.656-5.656v5.656z"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M11 10H5.344L11 4.414V10z"/></svg>

Before

Width:  |  Height:  |  Size: 118 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.71461 12.3573L6.33333 12.976L11 8.30935V7.69064L6.33333 3.02397L5.71461 3.64269L10.0719 7.99999Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 286 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.71461 12.3573L6.33333 12.976L11 8.30935V7.69063L6.33333 3.02396L5.71461 3.64268L10.0719 7.99999Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 284 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.71461 12.3573L6.33333 12.976L11 8.30935V7.69063L6.33333 3.02396L5.71461 3.64268L10.0719 7.99999Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 286 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97603 10.0719L12.3333 5.71461L12.9521 6.33333L8.28539 11L7.66667 11L3 6.33333L3.61872 5.71461L7.97603 10.0719Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 284 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97603 10.0719L12.3333 5.71461L12.9521 6.33333L8.28539 11L7.66667 11L3 6.33333L3.61872 5.71461L7.97603 10.0719Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 282 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97603 10.0719L12.3333 5.71461L12.9521 6.33333L8.28539 11L7.66667 11L3 6.33333L3.61872 5.71461L7.97603 10.0719Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 284 B

View File

@@ -7,6 +7,30 @@
display: flex;
height: 100%;
align-items: center;
position: relative;
}
.monaco-tl-indent {
height: 100%;
position: absolute;
top: 0;
left: 18px;
pointer-events: none;
}
.hide-arrows .monaco-tl-indent {
left: 12px;
}
.monaco-tl-indent > .indent-guide {
display: inline-block;
box-sizing: border-box;
height: 100%;
border-left: 1px solid transparent;
}
.monaco-tl-indent > .indent-guide {
transition: border-color 0.1s linear;
}
.monaco-tl-twistie,
@@ -31,28 +55,28 @@
background-size: 16px;
background-position: 3px 50%;
background-repeat: no-repeat;
background-image: url("expanded.svg");
background-image: url("tree-expanded-light.svg");
}
.monaco-tl-twistie.collapsible.collapsed:not(.loading) {
display: inline-block;
background-image: url("collapsed.svg");
background-image: url("tree-collapsed-light.svg");
}
.vs-dark .monaco-tl-twistie.collapsible:not(.loading) {
background-image: url("expanded-dark.svg");
background-image: url("tree-expanded-dark.svg");
}
.vs-dark .monaco-tl-twistie.collapsible.collapsed:not(.loading) {
background-image: url("collapsed-dark.svg");
background-image: url("tree-collapsed-dark.svg");
}
.hc-black .monaco-tl-twistie.collapsible:not(.loading) {
background-image: url("expanded-hc.svg");
background-image: url("tree-expanded-hc.svg");
}
.hc-black .monaco-tl-twistie.collapsible.collapsed:not(.loading) {
background-image: url("collapsed-hc.svg");
background-image: url("tree-collapsed-hc.svg");
}
.monaco-tl-twistie.loading {
@@ -66,4 +90,4 @@
.hc-black .monaco-tl-twistie.loading {
background-image: url("loading-hc.svg");
}
}

View File

@@ -6,9 +6,10 @@
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { Event } from 'vs/base/common/event';
export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
sorter?: ITreeSorter<T>;
@@ -18,6 +19,8 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
protected model: ObjectTreeModel<T, TFilterData>;
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T, TFilterData>> { return this.model.onDidChangeCollapseState; }
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,

View File

@@ -8,9 +8,11 @@ import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterato
import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel';
import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
readonly sorter?: ITreeSorter<T>;
readonly identityProvider?: IIdentityProvider<T>;
}
export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> implements ITreeModel<T | null, TFilterData, T | null> {
@@ -19,6 +21,8 @@ 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 readonly nodesByIdentity = new Map<string, ITreeNode<T, TFilterData>>();
private readonly identityProvider?: IIdentityProvider<T>;
private sorter?: ITreeSorter<{ element: T; }>;
readonly onDidSplice: Event<ITreeModelSpliceEvent<T | null, TFilterData>>;
@@ -40,6 +44,8 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
}
};
}
this.identityProvider = options.identityProvider;
}
setChildren(
@@ -59,11 +65,18 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
onDidDeleteNode?: (node: ITreeNode<T, TFilterData>) => void
): Iterator<ITreeElement<T | null>> {
const insertedElements = new Set<T | null>();
const insertedElementIds = new Set<string>();
const _onDidCreateNode = (node: ITreeNode<T, TFilterData>) => {
insertedElements.add(node.element);
this.nodes.set(node.element, node);
if (this.identityProvider) {
const id = this.identityProvider.getId(node.element).toString();
insertedElementIds.add(id);
this.nodesByIdentity.set(id, node);
}
if (onDidCreateNode) {
onDidCreateNode(node);
}
@@ -74,18 +87,27 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
this.nodes.delete(node.element);
}
if (this.identityProvider) {
const id = this.identityProvider.getId(node.element).toString();
if (!insertedElementIds.has(id)) {
this.nodesByIdentity.delete(id);
}
}
if (onDidDeleteNode) {
onDidDeleteNode(node);
}
};
return this.model.splice(
const result = this.model.splice(
[...location, 0],
Number.MAX_VALUE,
children,
_onDidCreateNode,
_onDidDeleteNode
);
return result;
}
private preserveCollapseState(elements: ISequence<ITreeElement<T>> | undefined): ISequence<ITreeElement<T>> {
@@ -96,7 +118,12 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
}
return Iterator.map(iterator, treeElement => {
const node = this.nodes.get(treeElement.element);
let node = this.nodes.get(treeElement.element);
if (!node && this.identityProvider) {
const id = this.identityProvider.getId(treeElement.element).toString();
node = this.nodesByIdentity.get(id);
}
if (!node) {
return {

View File

@@ -137,9 +137,16 @@ export interface ITreeEvent<T> {
browserEvent?: UIEvent;
}
export enum TreeMouseEventTarget {
Unknown,
Twistie,
Element
}
export interface ITreeMouseEvent<T> {
browserEvent: MouseEvent;
element: T | null;
target: TreeMouseEventTarget;
}
export interface ITreeContextMenuEvent<T> {

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
export class CollapseAllAction<TInput, T, TFilterData = void> extends Action {
constructor(private viewer: AsyncDataTree<TInput, T, TFilterData>, enabled: boolean) {
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled);
}
public run(context?: any): Promise<any> {
this.viewer.collapseAll();
this.viewer.setSelection([]);
this.viewer.setFocus([]);
this.viewer.domFocus();
this.viewer.focusFirst();
return Promise.resolve();
}
}