Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463 (#7206)

* Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463

* fix config changes

* fix strictnull checks
This commit is contained in:
Anthony Dresser
2019-09-15 22:38:26 -07:00
committed by GitHub
parent fa6c52699e
commit ea0f9e6ce9
1226 changed files with 21541 additions and 17633 deletions

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree';
import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list';
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree';
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper } from 'vs/base/browser/ui/tree/tree';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
@@ -18,13 +18,14 @@ import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors
import { toggleClass } from 'vs/base/browser/dom';
import { values } from 'vs/base/common/map';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
interface IAsyncDataTreeNode<TInput, T> {
element: TInput | T;
readonly parent: IAsyncDataTreeNode<TInput, T> | null;
readonly children: IAsyncDataTreeNode<TInput, T>[];
readonly id?: string | null;
loading: boolean;
refreshPromise: Promise<void> | undefined;
hasChildren: boolean;
stale: boolean;
slow: boolean;
@@ -41,7 +42,7 @@ function createAsyncDataTreeNode<TInput, T>(props: IAsyncDataTreeNodeRequiredPro
return {
...props,
children: [],
loading: false,
refreshPromise: undefined,
stale: true,
slow: false,
collapsedByDefault: undefined
@@ -66,10 +67,11 @@ interface IDataTreeListTemplateData<T> {
templateData: T;
}
type AsyncDataTreeNodeMapper<TInput, T, TFilterData> = WeakMapper<ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>, ITreeNode<TInput | T, TFilterData>>;
class AsyncDataTreeNodeWrapper<TInput, T, TFilterData> implements ITreeNode<TInput | T, TFilterData> {
get element(): T { return this.node.element!.element as T; }
get parent(): ITreeNode<T, TFilterData> | undefined { return this.node.parent && new AsyncDataTreeNodeWrapper(this.node.parent); }
get children(): ITreeNode<T, TFilterData>[] { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); }
get depth(): number { return this.node.depth; }
get visibleChildrenCount(): number { return this.node.visibleChildrenCount; }
@@ -82,14 +84,15 @@ class AsyncDataTreeNodeWrapper<TInput, T, TFilterData> implements ITreeNode<TInp
constructor(private node: ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>) { }
}
class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRenderer<IAsyncDataTreeNode<TInput, T>, TFilterData, IDataTreeListTemplateData<TTemplateData>> {
class AsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRenderer<IAsyncDataTreeNode<TInput, T>, TFilterData, IDataTreeListTemplateData<TTemplateData>> {
readonly templateId: string;
private renderedNodes = new Map<IAsyncDataTreeNode<TInput, T>, IDataTreeListTemplateData<TTemplateData>>();
private disposables: IDisposable[] = [];
constructor(
private renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
protected renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
protected nodeMapper: AsyncDataTreeNodeMapper<TInput, T, TFilterData>,
readonly onDidChangeTwistieState: Event<IAsyncDataTreeNode<TInput, T>>
) {
this.templateId = renderer.templateId;
@@ -101,7 +104,7 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
}
renderElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height);
this.renderer.renderElement(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
}
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
@@ -111,7 +114,7 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
if (this.renderer.disposeElement) {
this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height);
this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
}
}
@@ -243,25 +246,6 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
};
}
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 && 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)) : [],
collapsible: node.hasChildren,
collapsed
};
}
export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { }
export interface IAsyncDataTreeOptions<T, TFilterData = void> extends IAsyncDataTreeOptionsUpdate, Pick<IAbstractTreeOptions<T, TFilterData>, Exclude<keyof IAbstractTreeOptions<T, TFilterData>, 'collapseByDefault'>> {
@@ -304,7 +288,9 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
private readonly autoExpandSingleChildren: boolean;
private readonly _onDidRender = new Emitter<void>();
private readonly _onDidChangeNodeSlowState = new Emitter<IAsyncDataTreeNode<TInput, T>>();
protected readonly _onDidChangeNodeSlowState = new Emitter<IAsyncDataTreeNode<TInput, T>>();
protected readonly nodeMapper: AsyncDataTreeNodeMapper<TInput, T, TFilterData> = new WeakMapper(node => new AsyncDataTreeNodeWrapper(node));
protected readonly disposables: IDisposable[] = [];
@@ -339,6 +325,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
get onDidDispose(): Event<void> { return this.tree.onDidDispose; }
constructor(
private user: string,
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<T, TFilterData, any>[],
@@ -350,11 +337,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
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));
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
this.tree = this.createTree(user, container, delegate, renderers, options);
this.root = createAsyncDataTreeNode({
element: undefined!,
@@ -374,6 +357,20 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables);
}
protected createTree(
user: string,
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<T, TFilterData, any>[],
options: IAsyncDataTreeOptions<T, TFilterData>
): ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData> {
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
const objectTreeRenderers = renderers.map(r => new AsyncDataTreeRenderer(r, this.nodeMapper, this._onDidChangeNodeSlowState.event));
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
return new ObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
}
updateOptions(options: IAsyncDataTreeOptionsUpdate = {}): void {
this.tree.updateOptions(options);
}
@@ -450,7 +447,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext<TInput, T>;
await this.updateChildren(input, true, viewStateContext);
await this._updateChildren(input, true, viewStateContext);
if (viewStateContext) {
this.tree.setFocus(viewStateContext.focus);
@@ -462,13 +459,17 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
}
}
async updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
async updateChildren(element: TInput | T = this.root.element, recursive = true): Promise<void> {
await this._updateChildren(element, recursive);
}
private async _updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
if (typeof this.root.element === 'undefined') {
throw new Error('Tree input not set');
throw new TreeError(this.user, 'Tree input not set');
}
if (this.root.loading) {
await this.subTreeRefreshPromises.get(this.root)!;
if (this.root.refreshPromise) {
await this.root.refreshPromise;
await Event.toPromise(this._onDidRender.event);
}
@@ -505,7 +506,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
getNode(element: TInput | T = this.root.element): ITreeNode<TInput | T, TFilterData> {
const dataNode = this.getDataNode(element);
const node = this.tree.getNode(dataNode === this.root ? null : dataNode);
return new AsyncDataTreeNodeWrapper<TInput, T, TFilterData>(node);
return this.nodeMapper.map(node);
}
collapse(element: T, recursive: boolean = false): boolean {
@@ -515,24 +516,29 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
async expand(element: T, recursive: boolean = false): Promise<boolean> {
if (typeof this.root.element === 'undefined') {
throw new Error('Tree input not set');
throw new TreeError(this.user, 'Tree input not set');
}
if (this.root.loading) {
await this.subTreeRefreshPromises.get(this.root)!;
if (this.root.refreshPromise) {
await this.root.refreshPromise;
await Event.toPromise(this._onDidRender.event);
}
const node = this.getDataNode(element);
if (node !== this.root && !node.loading && !this.tree.isCollapsed(node)) {
if (node.refreshPromise) {
await this.root.refreshPromise;
await 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.loading) {
await this.subTreeRefreshPromises.get(node)!;
if (node.refreshPromise) {
await this.root.refreshPromise;
await Event.toPromise(this._onDidRender.event);
}
@@ -643,7 +649,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
const node: IAsyncDataTreeNode<TInput, T> | undefined = this.nodes.get((element === this.root.element ? null : element) as T);
if (!node) {
throw new Error(`Data tree node not found: ${element}`);
throw new TreeError(this.user, `Data tree node not found: ${element}`);
}
return node;
@@ -676,18 +682,18 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
return result;
}
result = this.doRefreshSubTree(node, recursive, viewStateContext);
this.subTreeRefreshPromises.set(node, result);
try {
await result;
} finally {
this.subTreeRefreshPromises.delete(node);
}
return this.doRefreshSubTree(node, recursive, viewStateContext);
}
private async doRefreshSubTree(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
node.loading = true;
let done: () => void;
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 = await this.doRefreshNode(node, recursive, viewStateContext);
@@ -695,7 +701,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext)));
} finally {
node.loading = false;
done!();
}
}
@@ -866,16 +872,40 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
}
private render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
const children = node.children.map(c => asTreeElement(c, viewStateContext));
const children = node.children.map(node => this.asTreeElement(node, viewStateContext));
this.tree.setChildren(node === this.root ? null : node, children);
if (node !== this.root) {
this.tree.setCollapsible(node, node.hasChildren);
}
this._onDidRender.fire();
}
protected asTreeElement(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): ITreeElement<IAsyncDataTreeNode<TInput, T>> {
let collapsed: boolean | undefined;
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 => this.asTreeElement(child, viewStateContext)) : [],
collapsible: node.hasChildren,
collapsed
};
}
// view state
getViewState(): IAsyncDataTreeViewState {
if (!this.identityProvider) {
throw new Error('Can\'t get tree view state without an identity provider');
throw new TreeError(this.user, 'Can\'t get tree view state without an identity provider');
}
const getId = (element: T) => this.identityProvider!.getId(element).toString();
@@ -903,3 +933,117 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
dispose(this.disposables);
}
}
type CompressibleAsyncDataTreeNodeMapper<TInput, T, TFilterData> = WeakMapper<ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>>, TFilterData>, ITreeNode<ICompressedTreeNode<TInput | T>, TFilterData>>;
class CompressibleAsyncDataTreeNodeWrapper<TInput, T, TFilterData> implements ITreeNode<ICompressedTreeNode<TInput | T>, TFilterData> {
get element(): ICompressedTreeNode<TInput | T> {
return {
elements: this.node.element.elements.map(e => e.element),
incompressible: this.node.element.incompressible
};
}
get children(): ITreeNode<ICompressedTreeNode<TInput | T>, TFilterData>[] { return this.node.children.map(node => new CompressibleAsyncDataTreeNodeWrapper(node)); }
get depth(): number { return this.node.depth; }
get visibleChildrenCount(): number { return this.node.visibleChildrenCount; }
get visibleChildIndex(): number { return this.node.visibleChildIndex; }
get collapsible(): boolean { return this.node.collapsible; }
get collapsed(): boolean { return this.node.collapsed; }
get visible(): boolean { return this.node.visible; }
get filterData(): TFilterData | undefined { return this.node.filterData; }
constructor(private node: ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>>, TFilterData>) { }
}
class CompressibleAsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ICompressibleTreeRenderer<IAsyncDataTreeNode<TInput, T>, TFilterData, IDataTreeListTemplateData<TTemplateData>> {
readonly templateId: string;
private renderedNodes = new Map<IAsyncDataTreeNode<TInput, T>, IDataTreeListTemplateData<TTemplateData>>();
private disposables: IDisposable[] = [];
constructor(
protected renderer: ICompressibleTreeRenderer<T, TFilterData, TTemplateData>,
protected nodeMapper: AsyncDataTreeNodeMapper<TInput, T, TFilterData>,
private compressibleNodeMapperProvider: () => CompressibleAsyncDataTreeNodeMapper<TInput, T, TFilterData>,
readonly onDidChangeTwistieState: Event<IAsyncDataTreeNode<TInput, T>>
) {
this.templateId = renderer.templateId;
}
renderTemplate(container: HTMLElement): IDataTreeListTemplateData<TTemplateData> {
const templateData = this.renderer.renderTemplate(container);
return { templateData };
}
renderElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
this.renderer.renderElement(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
}
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<IAsyncDataTreeNode<TInput, T>>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
this.renderer.renderCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode<ICompressedTreeNode<T>, TFilterData>, index, templateData.templateData, height);
}
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
toggleClass(twistieElement, 'loading', element.slow);
return false;
}
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
if (this.renderer.disposeElement) {
this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode<T, TFilterData>, index, templateData.templateData, height);
}
}
disposeTemplate(templateData: IDataTreeListTemplateData<TTemplateData>): void {
this.renderer.disposeTemplate(templateData.templateData);
}
dispose(): void {
this.renderedNodes.clear();
this.disposables = dispose(this.disposables);
}
}
export interface ITreeCompressionDelegate<T> {
isIncompressible(element: T): boolean;
}
export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
protected readonly compressibleNodeMapper: CompressibleAsyncDataTreeNodeMapper<TInput, T, TFilterData> = new WeakMapper(node => new CompressibleAsyncDataTreeNodeWrapper(node));
constructor(
user: string,
container: HTMLElement,
virtualDelegate: IListVirtualDelegate<T>,
private compressionDelegate: ITreeCompressionDelegate<T>,
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
dataSource: IAsyncDataSource<TInput, T>,
options: IAsyncDataTreeOptions<T, TFilterData> = {}
) {
super(user, container, virtualDelegate, renderers, dataSource, options);
}
protected createTree(
user: string,
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
options: IAsyncDataTreeOptions<T, TFilterData>
): ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData> {
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
const objectTreeRenderers = renderers.map(r => new CompressibleAsyncDataTreeRenderer(r, this.nodeMapper, () => this.compressibleNodeMapper, this._onDidChangeNodeSlowState.event));
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
return new CompressibleObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
}
protected asTreeElement(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): ICompressedTreeElement<IAsyncDataTreeNode<TInput, T>> {
return {
incompressible: this.compressionDelegate.isIncompressible(node.element as T),
...super.asTreeElement(node, viewStateContext)
};
}
}