mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-12 11:39:57 +01:00
@@ -21,11 +21,11 @@ enum AsyncDataTreeNodeState {
|
||||
Slow
|
||||
}
|
||||
|
||||
interface IAsyncDataTreeNode<T extends NonNullable<any>> {
|
||||
element: T | null;
|
||||
readonly parent: IAsyncDataTreeNode<T> | null;
|
||||
interface IAsyncDataTreeNode<TInput, T> {
|
||||
element: TInput | T;
|
||||
readonly parent: IAsyncDataTreeNode<TInput, T> | null;
|
||||
readonly id?: string | null;
|
||||
readonly children?: IAsyncDataTreeNode<T>[];
|
||||
readonly children?: IAsyncDataTreeNode<TInput, T>[];
|
||||
state: AsyncDataTreeNodeState;
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ interface IDataTreeListTemplateData<T> {
|
||||
templateData: T;
|
||||
}
|
||||
|
||||
class AsyncDataTreeNodeWrapper<T, TFilterData> implements ITreeNode<T, TFilterData> {
|
||||
class AsyncDataTreeNodeWrapper<TInput, T, TFilterData> implements ITreeNode<TInput | T, TFilterData> {
|
||||
|
||||
get element(): T { return this.node.element!.element!; }
|
||||
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; }
|
||||
@@ -44,18 +44,18 @@ class AsyncDataTreeNodeWrapper<T, TFilterData> implements ITreeNode<T, TFilterDa
|
||||
get visible(): boolean { return this.node.visible; }
|
||||
get filterData(): TFilterData | undefined { return this.node.filterData; }
|
||||
|
||||
constructor(private node: ITreeNode<IAsyncDataTreeNode<T> | null, TFilterData>) { }
|
||||
constructor(private node: ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>) { }
|
||||
}
|
||||
|
||||
class DataTreeRenderer<T, TFilterData, TTemplateData> implements ITreeRenderer<IAsyncDataTreeNode<T>, TFilterData, IDataTreeListTemplateData<TTemplateData>> {
|
||||
class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRenderer<IAsyncDataTreeNode<TInput, T>, TFilterData, IDataTreeListTemplateData<TTemplateData>> {
|
||||
|
||||
readonly templateId: string;
|
||||
private renderedNodes = new Map<IAsyncDataTreeNode<T>, IDataTreeListTemplateData<TTemplateData>>();
|
||||
private renderedNodes = new Map<IAsyncDataTreeNode<TInput, T>, IDataTreeListTemplateData<TTemplateData>>();
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
|
||||
readonly onDidChangeTwistieState: Event<IAsyncDataTreeNode<T>>
|
||||
readonly onDidChangeTwistieState: Event<IAsyncDataTreeNode<TInput, T>>
|
||||
) {
|
||||
this.templateId = renderer.templateId;
|
||||
}
|
||||
@@ -65,16 +65,16 @@ class DataTreeRenderer<T, TFilterData, TTemplateData> implements ITreeRenderer<I
|
||||
return { templateData };
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<IAsyncDataTreeNode<T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
renderElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData);
|
||||
}
|
||||
|
||||
renderTwistie(element: IAsyncDataTreeNode<T>, twistieElement: HTMLElement): boolean {
|
||||
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
|
||||
toggleClass(twistieElement, 'loading', element.state === AsyncDataTreeNodeState.Slow);
|
||||
return false;
|
||||
}
|
||||
|
||||
disposeElement(node: ITreeNode<IAsyncDataTreeNode<T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>): void {
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData);
|
||||
}
|
||||
@@ -90,24 +90,24 @@ class DataTreeRenderer<T, TFilterData, TTemplateData> implements ITreeRenderer<I
|
||||
}
|
||||
}
|
||||
|
||||
function asTreeEvent<T>(e: ITreeEvent<IAsyncDataTreeNode<T>>): ITreeEvent<T> {
|
||||
function asTreeEvent<TInput, T>(e: ITreeEvent<IAsyncDataTreeNode<TInput, T>>): ITreeEvent<T> {
|
||||
return {
|
||||
browserEvent: e.browserEvent,
|
||||
elements: e.elements.map(e => e.element!)
|
||||
elements: e.elements.map(e => e.element as T)
|
||||
};
|
||||
}
|
||||
|
||||
function asTreeMouseEvent<T>(e: ITreeMouseEvent<IAsyncDataTreeNode<T>>): ITreeMouseEvent<T> {
|
||||
function asTreeMouseEvent<TInput, T>(e: ITreeMouseEvent<IAsyncDataTreeNode<TInput, T>>): ITreeMouseEvent<T> {
|
||||
return {
|
||||
browserEvent: e.browserEvent,
|
||||
element: e.element && e.element.element!
|
||||
element: e.element && e.element.element as T
|
||||
};
|
||||
}
|
||||
|
||||
function asTreeContextMenuEvent<T>(e: ITreeContextMenuEvent<IAsyncDataTreeNode<T>>): ITreeContextMenuEvent<T> {
|
||||
function asTreeContextMenuEvent<TInput, T>(e: ITreeContextMenuEvent<IAsyncDataTreeNode<TInput, T>>): ITreeContextMenuEvent<T> {
|
||||
return {
|
||||
browserEvent: e.browserEvent,
|
||||
element: e.element && e.element.element!,
|
||||
element: e.element && e.element.element as T,
|
||||
anchor: e.anchor
|
||||
};
|
||||
}
|
||||
@@ -122,12 +122,12 @@ export interface IChildrenResolutionEvent<T> {
|
||||
readonly reason: ChildrenResolutionReason;
|
||||
}
|
||||
|
||||
function asObjectTreeOptions<T, TFilterData>(options?: IAsyncDataTreeOptions<T, TFilterData>): IObjectTreeOptions<IAsyncDataTreeNode<T>, TFilterData> | undefined {
|
||||
function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOptions<T, TFilterData>): IObjectTreeOptions<IAsyncDataTreeNode<TInput, T>, TFilterData> | undefined {
|
||||
return options && {
|
||||
...options,
|
||||
identityProvider: options.identityProvider && {
|
||||
getId(el) {
|
||||
return options.identityProvider!.getId(el.element!);
|
||||
return options.identityProvider!.getId(el.element as T);
|
||||
}
|
||||
},
|
||||
multipleSelectionController: options.multipleSelectionController && {
|
||||
@@ -140,28 +140,28 @@ function asObjectTreeOptions<T, TFilterData>(options?: IAsyncDataTreeOptions<T,
|
||||
},
|
||||
accessibilityProvider: options.accessibilityProvider && {
|
||||
getAriaLabel(e) {
|
||||
return options.accessibilityProvider!.getAriaLabel(e.element!);
|
||||
return options.accessibilityProvider!.getAriaLabel(e.element as T);
|
||||
}
|
||||
},
|
||||
filter: options.filter && {
|
||||
filter(e, parentVisibility) {
|
||||
return options.filter!.filter(e.element!, parentVisibility);
|
||||
return options.filter!.filter(e.element as T, parentVisibility);
|
||||
}
|
||||
},
|
||||
keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && {
|
||||
getKeyboardNavigationLabel(e) {
|
||||
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element!);
|
||||
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element as T);
|
||||
}
|
||||
},
|
||||
sorter: options.sorter && {
|
||||
compare(a, b) {
|
||||
return options.sorter!.compare(a.element!, b.element!);
|
||||
return options.sorter!.compare(a.element as T, b.element as T);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function asTreeElement<T>(node: IAsyncDataTreeNode<T>): ITreeElement<IAsyncDataTreeNode<T>> {
|
||||
function asTreeElement<TInput, T>(node: IAsyncDataTreeNode<TInput, T>): ITreeElement<IAsyncDataTreeNode<TInput, T>> {
|
||||
return {
|
||||
element: node,
|
||||
children: Iterator.map(Iterator.fromArray(node.children!), asTreeElement)
|
||||
@@ -173,15 +173,15 @@ export interface IAsyncDataTreeOptions<T, TFilterData = void> extends IAbstractT
|
||||
sorter?: ITreeSorter<T>;
|
||||
}
|
||||
|
||||
export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> implements IDisposable {
|
||||
export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable {
|
||||
|
||||
private readonly tree: ObjectTree<IAsyncDataTreeNode<T>, TFilterData>;
|
||||
private readonly root: IAsyncDataTreeNode<T>;
|
||||
private readonly nodes = new Map<T | null, IAsyncDataTreeNode<T>>();
|
||||
private readonly refreshPromises = new Map<IAsyncDataTreeNode<T>, Promise<void>>();
|
||||
private readonly tree: ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
|
||||
private readonly root: IAsyncDataTreeNode<TInput, T>;
|
||||
private readonly nodes = new Map<null | T, IAsyncDataTreeNode<TInput, T>>();
|
||||
private readonly refreshPromises = new Map<IAsyncDataTreeNode<TInput, T>, Promise<void>>();
|
||||
private readonly identityProvider?: IIdentityProvider<T>;
|
||||
|
||||
private readonly _onDidChangeNodeState = new Emitter<IAsyncDataTreeNode<T>>();
|
||||
private readonly _onDidChangeNodeState = new Emitter<IAsyncDataTreeNode<TInput, T>>();
|
||||
|
||||
protected readonly disposables: IDisposable[] = [];
|
||||
|
||||
@@ -204,20 +204,20 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
private dataSource: IAsyncDataSource<T>,
|
||||
private dataSource: IAsyncDataSource<TInput, T>,
|
||||
options?: IAsyncDataTreeOptions<T, TFilterData>
|
||||
) {
|
||||
this.identityProvider = options && options.identityProvider;
|
||||
|
||||
const objectTreeDelegate = new ComposedTreeDelegate<T | null, IAsyncDataTreeNode<T>>(delegate);
|
||||
const objectTreeDelegate = new ComposedTreeDelegate<TInput | T, IAsyncDataTreeNode<TInput, T>>(delegate);
|
||||
const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event));
|
||||
const objectTreeOptions = asObjectTreeOptions(options) || {};
|
||||
const objectTreeOptions = asObjectTreeOptions<TInput, T, TFilterData>(options) || {};
|
||||
objectTreeOptions.collapseByDefault = true;
|
||||
|
||||
this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
|
||||
|
||||
this.root = {
|
||||
element: null,
|
||||
element: undefined!,
|
||||
parent: null,
|
||||
state: AsyncDataTreeNodeState.Uninitialized,
|
||||
};
|
||||
@@ -279,16 +279,34 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
|
||||
// Data Tree
|
||||
|
||||
refresh(element: T | null, recursive = true): Promise<void> {
|
||||
getInput(): TInput | undefined {
|
||||
return this.root.element as TInput;
|
||||
}
|
||||
|
||||
setInput(input: TInput | undefined): Promise<void> {
|
||||
this.root.element = input!;
|
||||
|
||||
if (typeof input === 'undefined') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.refresh(input);
|
||||
}
|
||||
|
||||
refresh(element: TInput | T = this.root.element, recursive = true): Promise<void> {
|
||||
if (typeof this.root.element === 'undefined') {
|
||||
throw new Error('Tree input not set');
|
||||
}
|
||||
|
||||
return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh);
|
||||
}
|
||||
|
||||
// Tree
|
||||
|
||||
getNode(element: T | null): ITreeNode<T | null, TFilterData> {
|
||||
getNode(element: TInput | T): ITreeNode<TInput | T, TFilterData> {
|
||||
const dataNode = this.getDataNode(element);
|
||||
const node = this.tree.getNode(dataNode === this.root ? null : dataNode);
|
||||
return new AsyncDataTreeNodeWrapper<T | null, TFilterData>(node);
|
||||
return new AsyncDataTreeNodeWrapper<TInput, T, TFilterData>(node);
|
||||
}
|
||||
|
||||
collapse(element: T, recursive: boolean = false): boolean {
|
||||
@@ -342,7 +360,7 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
|
||||
getSelection(): T[] {
|
||||
const nodes = this.tree.getSelection();
|
||||
return nodes.map(n => n!.element!);
|
||||
return nodes.map(n => n!.element as T);
|
||||
}
|
||||
|
||||
setFocus(elements: T[], browserEvent?: UIEvent): void {
|
||||
@@ -376,7 +394,7 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
|
||||
getFocus(): T[] {
|
||||
const nodes = this.tree.getFocus();
|
||||
return nodes.map(n => n!.element!);
|
||||
return nodes.map(n => n!.element as T);
|
||||
}
|
||||
|
||||
open(elements: T[]): void {
|
||||
@@ -394,21 +412,21 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
|
||||
// Tree navigation
|
||||
|
||||
getParentElement(element: T): T | null {
|
||||
getParentElement(element: T): TInput | T {
|
||||
const node = this.tree.getParentElement(this.getDataNode(element));
|
||||
return node && node.element;
|
||||
return (node && node.element)!;
|
||||
}
|
||||
|
||||
getFirstElementChild(element: T | null = null): T | null | undefined {
|
||||
getFirstElementChild(element: TInput | T = this.root.element): TInput | T | undefined {
|
||||
const dataNode = this.getDataNode(element);
|
||||
const node = this.tree.getFirstElementChild(dataNode === this.root ? null : dataNode);
|
||||
return node && node.element;
|
||||
return (node && node.element)!;
|
||||
}
|
||||
|
||||
getLastElementAncestor(element: T | null = null): T | null | undefined {
|
||||
getLastElementAncestor(element: TInput | T = this.root.element): TInput | T | undefined {
|
||||
const dataNode = this.getDataNode(element);
|
||||
const node = this.tree.getLastElementAncestor(dataNode === this.root ? null : dataNode);
|
||||
return node && node.element;
|
||||
return (node && node.element)!;
|
||||
}
|
||||
|
||||
// List
|
||||
@@ -419,8 +437,8 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
|
||||
// Implementation
|
||||
|
||||
private getDataNode(element: T | null): IAsyncDataTreeNode<T> {
|
||||
const node: IAsyncDataTreeNode<T> = this.nodes.get(element);
|
||||
private getDataNode(element: TInput | T): IAsyncDataTreeNode<TInput, T> {
|
||||
const node: IAsyncDataTreeNode<TInput, T> = this.nodes.get((element === this.root.element ? null : element) as T);
|
||||
|
||||
if (typeof node === 'undefined') {
|
||||
throw new Error(`Data tree node not found: ${element}`);
|
||||
@@ -429,7 +447,7 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
return node;
|
||||
}
|
||||
|
||||
private async refreshNode(node: IAsyncDataTreeNode<T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
|
||||
private async refreshNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
|
||||
await this._refreshNode(node, recursive, reason);
|
||||
|
||||
if (recursive && node.children) {
|
||||
@@ -437,7 +455,7 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
}
|
||||
}
|
||||
|
||||
private _refreshNode(node: IAsyncDataTreeNode<T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
|
||||
private _refreshNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
|
||||
let result = this.refreshPromises.get(node);
|
||||
|
||||
if (result) {
|
||||
@@ -449,8 +467,8 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
return always(result, () => this.refreshPromises.delete(node));
|
||||
}
|
||||
|
||||
private doRefresh(node: IAsyncDataTreeNode<T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
|
||||
const hasChildren = !!this.dataSource.hasChildren(node.element);
|
||||
private doRefresh(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, reason: ChildrenResolutionReason): Promise<void> {
|
||||
const hasChildren = !!this.dataSource.hasChildren(node.element!);
|
||||
|
||||
if (!hasChildren) {
|
||||
this.setChildren(node, [], recursive);
|
||||
@@ -468,14 +486,14 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
this._onDidChangeNodeState.fire(node);
|
||||
}, _ => null);
|
||||
|
||||
return Promise.resolve(this.dataSource.getChildren(node.element))
|
||||
return Promise.resolve(this.dataSource.getChildren(node.element!))
|
||||
.then(children => {
|
||||
slowTimeout.cancel();
|
||||
node.state = AsyncDataTreeNodeState.Loaded;
|
||||
this._onDidChangeNodeState.fire(node);
|
||||
|
||||
this.setChildren(node, children, recursive);
|
||||
this._onDidResolveChildren.fire({ element: node.element, reason });
|
||||
this._onDidResolveChildren.fire({ element: node.element as T, reason });
|
||||
}, err => {
|
||||
slowTimeout.cancel();
|
||||
node.state = AsyncDataTreeNodeState.Uninitialized;
|
||||
@@ -490,18 +508,18 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent<IAsyncDataTreeNode<T>, any>): void {
|
||||
private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T>, any>): void {
|
||||
if (!node.collapsed && node.element.state === AsyncDataTreeNodeState.Uninitialized) {
|
||||
if (deep) {
|
||||
this.collapse(node.element.element!);
|
||||
this.collapse(node.element.element as T);
|
||||
} else {
|
||||
this.refreshNode(node.element, false, ChildrenResolutionReason.Expand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setChildren(node: IAsyncDataTreeNode<T>, childrenElements: T[], recursive: boolean): void {
|
||||
const children = childrenElements.map<ITreeElement<IAsyncDataTreeNode<T>>>(element => {
|
||||
private setChildren(node: IAsyncDataTreeNode<TInput, T>, childrenElements: T[], recursive: boolean): void {
|
||||
const children = childrenElements.map<ITreeElement<IAsyncDataTreeNode<TInput, T>>>(element => {
|
||||
if (!this.identityProvider) {
|
||||
return {
|
||||
element: {
|
||||
@@ -514,7 +532,7 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
};
|
||||
}
|
||||
|
||||
const nodeChildren = new Map<string, IAsyncDataTreeNode<T>>();
|
||||
const nodeChildren = new Map<string, IAsyncDataTreeNode<TInput, T>>();
|
||||
|
||||
for (const child of node.children!) {
|
||||
nodeChildren.set(child.id!, child);
|
||||
@@ -556,7 +574,7 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
}
|
||||
}
|
||||
|
||||
let children: Iterator<ITreeElement<IAsyncDataTreeNode<T>>> | undefined = undefined;
|
||||
let children: Iterator<ITreeElement<IAsyncDataTreeNode<TInput, T>>> | undefined = undefined;
|
||||
|
||||
if (collapsible) {
|
||||
children = Iterator.map(Iterator.fromArray(asyncDataTreeNode.children!), asTreeElement);
|
||||
@@ -572,17 +590,17 @@ export class AsyncDataTree<T extends NonNullable<any>, TFilterData = void> imple
|
||||
|
||||
const insertedElements = new Set<T>();
|
||||
|
||||
const onDidCreateNode = (treeNode: ITreeNode<IAsyncDataTreeNode<T>, TFilterData>) => {
|
||||
const onDidCreateNode = (treeNode: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>) => {
|
||||
if (treeNode.element.element) {
|
||||
insertedElements.add(treeNode.element.element);
|
||||
this.nodes.set(treeNode.element.element, treeNode.element);
|
||||
insertedElements.add(treeNode.element.element as T);
|
||||
this.nodes.set(treeNode.element.element as T, treeNode.element);
|
||||
}
|
||||
};
|
||||
|
||||
const onDidDeleteNode = (treeNode: ITreeNode<IAsyncDataTreeNode<T>, TFilterData>) => {
|
||||
const onDidDeleteNode = (treeNode: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>) => {
|
||||
if (treeNode.element.element) {
|
||||
if (!insertedElements.has(treeNode.element.element)) {
|
||||
this.nodes.delete(treeNode.element.element);
|
||||
if (!insertedElements.has(treeNode.element.element as T)) {
|
||||
this.nodes.delete(treeNode.element.element as T);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,17 +17,7 @@ export interface IDataTreeOptions<T, TFilterData = void> extends IAbstractTreeOp
|
||||
export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | null, TFilterData, TInput | T> {
|
||||
|
||||
protected model: ObjectTreeModel<T | null, TFilterData>;
|
||||
|
||||
private _input: TInput | undefined;
|
||||
|
||||
get input(): TInput | undefined {
|
||||
return this._input;
|
||||
}
|
||||
|
||||
set input(input: TInput | undefined) {
|
||||
this._input = input;
|
||||
this.refresh(input);
|
||||
}
|
||||
private input: TInput | undefined;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@@ -39,8 +29,22 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
|
||||
super(container, delegate, renderers, options);
|
||||
}
|
||||
|
||||
refresh(element: TInput | T): void {
|
||||
if (!this._input) {
|
||||
getInput(): TInput | undefined {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
setInput(input: TInput | undefined): void {
|
||||
this.input = input;
|
||||
|
||||
if (typeof input === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.refresh(input);
|
||||
}
|
||||
|
||||
refresh(element: TInput | T = this.input): void {
|
||||
if (typeof this.input === 'undefined') {
|
||||
throw new Error('Tree input not set');
|
||||
}
|
||||
|
||||
|
||||
@@ -149,9 +149,9 @@ export interface IDataSource<TInput, T> {
|
||||
getChildren(element: TInput | T): T[];
|
||||
}
|
||||
|
||||
export interface IAsyncDataSource<T extends NonNullable<any>> {
|
||||
hasChildren(element: T | null): boolean;
|
||||
getChildren(element: T | null): T[] | Promise<T[]>;
|
||||
export interface IAsyncDataSource<TInput, T> {
|
||||
hasChildren(element: TInput | T): boolean;
|
||||
getChildren(element: TInput | T): T[] | Promise<T[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,15 +51,11 @@ suite('AsyncDataTree', function () {
|
||||
}
|
||||
};
|
||||
|
||||
const dataSource = new class implements IAsyncDataSource<Element> {
|
||||
hasChildren(element: Element | null): boolean {
|
||||
return !element || (element.children && element.children.length > 0);
|
||||
const dataSource = new class implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element | null): Promise<Element[]> {
|
||||
if (!element) {
|
||||
return Promise.resolve(root.children);
|
||||
}
|
||||
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
return Promise.resolve(element.children || []);
|
||||
}
|
||||
};
|
||||
@@ -79,11 +75,11 @@ suite('AsyncDataTree', function () {
|
||||
|
||||
const _: (id: string) => Element = find.bind(null, root.children);
|
||||
|
||||
const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider });
|
||||
const tree = new AsyncDataTree<Element, Element>(container, delegate, [renderer], dataSource, { identityProvider });
|
||||
tree.layout(200);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 0);
|
||||
|
||||
await tree.refresh(null);
|
||||
await tree.setInput(root);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!hasClass(twistie, 'collapsible'));
|
||||
|
||||
@@ -26,18 +26,12 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
|
||||
export type TreeElement = FileReferences | OneReference;
|
||||
|
||||
export class DataSource implements IAsyncDataSource<TreeElement> {
|
||||
export class DataSource implements IAsyncDataSource<ReferencesModel | FileReferences, TreeElement> {
|
||||
|
||||
root: ReferencesModel | FileReferences;
|
||||
constructor(@ITextModelService private readonly _resolverService: ITextModelService) { }
|
||||
|
||||
constructor(
|
||||
@ITextModelService private readonly _resolverService: ITextModelService,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
hasChildren(element: TreeElement): boolean {
|
||||
if (!element) {
|
||||
hasChildren(element: ReferencesModel | FileReferences | TreeElement): boolean {
|
||||
if (element instanceof ReferencesModel) {
|
||||
return true;
|
||||
}
|
||||
if (element instanceof FileReferences && !element.failure) {
|
||||
@@ -46,10 +40,11 @@ export class DataSource implements IAsyncDataSource<TreeElement> {
|
||||
return false;
|
||||
}
|
||||
|
||||
getChildren(element: TreeElement): Promise<TreeElement[]> {
|
||||
if (!element && this.root instanceof FileReferences) {
|
||||
element = this.root;
|
||||
getChildren(element: ReferencesModel | FileReferences | TreeElement): TreeElement[] | Promise<TreeElement[]> {
|
||||
if (element instanceof ReferencesModel) {
|
||||
return element.groups;
|
||||
}
|
||||
|
||||
if (element instanceof FileReferences) {
|
||||
return element.resolve(this._resolverService).then(val => {
|
||||
// if (element.failure) {
|
||||
@@ -60,9 +55,7 @@ export class DataSource implements IAsyncDataSource<TreeElement> {
|
||||
return val.children;
|
||||
});
|
||||
}
|
||||
if (this.root instanceof ReferencesModel) {
|
||||
return Promise.resolve(this.root.groups);
|
||||
}
|
||||
|
||||
throw new Error('bad tree');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,8 +237,7 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
private _callOnDispose: IDisposable[] = [];
|
||||
private _onDidSelectReference = new Emitter<SelectionEvent>();
|
||||
|
||||
private _treeDataSource: DataSource;
|
||||
private _tree: WorkbenchAsyncDataTree<TreeElement>;
|
||||
private _tree: WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement>;
|
||||
private _treeContainer: HTMLElement;
|
||||
private _sash: VSash;
|
||||
private _preview: ICodeEditor;
|
||||
@@ -357,14 +356,14 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
accessibilityProvider: new AriaProvider()
|
||||
};
|
||||
|
||||
this._treeDataSource = this._instantiationService.createInstance(DataSource);
|
||||
const treeDataSource = this._instantiationService.createInstance(DataSource);
|
||||
|
||||
this._tree = this._instantiationService.createInstance<HTMLElement, IListVirtualDelegate<TreeElement>, ITreeRenderer<any, void, any>[], IAsyncDataSource<TreeElement>, IAsyncDataTreeOptions<TreeElement, void>, WorkbenchAsyncDataTree<TreeElement, void>>(
|
||||
this._tree = this._instantiationService.createInstance<HTMLElement, IListVirtualDelegate<TreeElement>, ITreeRenderer<any, void, any>[], IAsyncDataSource<ReferencesModel | FileReferences, TreeElement>, IAsyncDataTreeOptions<TreeElement, void>, WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, void>>(
|
||||
WorkbenchAsyncDataTree,
|
||||
this._treeContainer,
|
||||
new Delegate(),
|
||||
renderers,
|
||||
this._treeDataSource,
|
||||
treeDataSource,
|
||||
treeOptions
|
||||
);
|
||||
|
||||
@@ -498,8 +497,7 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
this.focus();
|
||||
|
||||
// pick input and a reference to begin with
|
||||
this._treeDataSource.root = this._model.groups.length === 1 ? this._model.groups[0] : this._model;
|
||||
return this._tree.refresh(null);
|
||||
return this._tree.setInput(this._model.groups.length === 1 ? this._model.groups[0] : this._model);
|
||||
}
|
||||
|
||||
private _getFocusedReference(): OneReference {
|
||||
@@ -533,7 +531,7 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
|
||||
const promise = this._textModelResolverService.createModelReference(reference.uri);
|
||||
|
||||
if (this._treeDataSource.root === reference.parent) {
|
||||
if (this._tree.getInput() === reference.parent) {
|
||||
this._tree.reveal(reference);
|
||||
} else {
|
||||
if (revealParent) {
|
||||
|
||||
@@ -35,7 +35,7 @@ import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTr
|
||||
import { ITreeEvent, ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { AsyncDataTree, IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
|
||||
export type ListWidget = List<any> | PagedList<any> | ITree | ObjectTree<any, any> | AsyncDataTree<any, any>;
|
||||
export type ListWidget = List<any> | PagedList<any> | ITree | ObjectTree<any, any> | AsyncDataTree<any, any, any>;
|
||||
|
||||
export const IListService = createDecorator<IListService>('listService');
|
||||
|
||||
@@ -590,7 +590,7 @@ export class TreeResourceNavigator2<T, TFilterData> extends Disposable {
|
||||
private readonly _openResource: Emitter<IOpenEvent<T>> = new Emitter<IOpenEvent<T>>();
|
||||
readonly openResource: Event<IOpenEvent<T>> = this._openResource.event;
|
||||
|
||||
constructor(private tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchAsyncDataTree<T, TFilterData>, private options?: IResourceResultsNavigationOptions) {
|
||||
constructor(private tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchAsyncDataTree<any, T, TFilterData>, private options?: IResourceResultsNavigationOptions) {
|
||||
super();
|
||||
|
||||
this.registerListeners();
|
||||
@@ -955,7 +955,7 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkbenchAsyncDataTree<T extends NonNullable<any>, TFilterData = void> extends AsyncDataTree<T, TFilterData> {
|
||||
export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
|
||||
|
||||
readonly contextKeyService: IContextKeyService;
|
||||
|
||||
@@ -969,7 +969,7 @@ export class WorkbenchAsyncDataTree<T extends NonNullable<any>, TFilterData = vo
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
|
||||
dataSource: IAsyncDataSource<T>,
|
||||
dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IAsyncDataTreeOptions<T, TFilterData>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
|
||||
@@ -186,7 +186,7 @@ export class CollapseAction extends Action {
|
||||
|
||||
// Collapse All action for the new tree
|
||||
export class CollapseAction2 extends Action {
|
||||
constructor(tree: AsyncDataTree<any>, enabled: boolean, clazz: string) {
|
||||
constructor(tree: AsyncDataTree<any, any>, enabled: boolean, clazz: string) {
|
||||
super('workbench.action.collapse', nls.localize('collapse', "Collapse All"), clazz, enabled, () => {
|
||||
tree.collapseAll();
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -804,7 +804,7 @@ export class ReverseContinueAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ReplCollapseAllAction extends CollapseAction2 {
|
||||
constructor(tree: AsyncDataTree<any>, private toFocus: { focus(): void; }) {
|
||||
constructor(tree: AsyncDataTree<any, any>, private toFocus: { focus(): void; }) {
|
||||
super(tree, true, undefined);
|
||||
}
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ export class LoadedScriptsView extends ViewletPanel {
|
||||
|
||||
private treeContainer: HTMLElement;
|
||||
private loadedScriptsItemType: IContextKey<string>;
|
||||
private tree: WorkbenchAsyncDataTree<any>;
|
||||
private tree: WorkbenchAsyncDataTree<LoadedScriptsItem, LoadedScriptsItem>;
|
||||
private changeScheduler: RunOnceScheduler;
|
||||
private treeNeedsRefreshOnVisible: boolean;
|
||||
private filter: LoadedScriptsFilter;
|
||||
@@ -399,7 +399,7 @@ export class LoadedScriptsView extends ViewletPanel {
|
||||
[
|
||||
this.instantiationService.createInstance(LoadedScriptsRenderer)
|
||||
],
|
||||
new LoadedScriptsDataSource(root),
|
||||
new LoadedScriptsDataSource(),
|
||||
{
|
||||
identityProvider: {
|
||||
getId: element => element.getId()
|
||||
@@ -414,10 +414,12 @@ export class LoadedScriptsView extends ViewletPanel {
|
||||
this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService
|
||||
);
|
||||
|
||||
this.tree.setInput(root);
|
||||
|
||||
this.changeScheduler = new RunOnceScheduler(() => {
|
||||
this.treeNeedsRefreshOnVisible = false;
|
||||
if (this.tree) {
|
||||
this.tree.refresh(null);
|
||||
this.tree.refresh();
|
||||
}
|
||||
}, 300);
|
||||
this.disposables.push(this.changeScheduler);
|
||||
@@ -535,19 +537,13 @@ class LoadedScriptsDelegate implements IListVirtualDelegate<LoadedScriptsItem> {
|
||||
}
|
||||
}
|
||||
|
||||
class LoadedScriptsDataSource implements IAsyncDataSource<LoadedScriptsItem> {
|
||||
class LoadedScriptsDataSource implements IAsyncDataSource<LoadedScriptsItem, LoadedScriptsItem> {
|
||||
|
||||
constructor(private root: LoadedScriptsItem) {
|
||||
hasChildren(element: LoadedScriptsItem): boolean {
|
||||
return element.hasChildren();
|
||||
}
|
||||
|
||||
hasChildren(element: LoadedScriptsItem | null): boolean {
|
||||
return element === null || element.hasChildren();
|
||||
}
|
||||
|
||||
getChildren(element: LoadedScriptsItem | null): Promise<LoadedScriptsItem[]> {
|
||||
if (element === null) {
|
||||
element = this.root;
|
||||
}
|
||||
getChildren(element: LoadedScriptsItem): Promise<LoadedScriptsItem[]> {
|
||||
return element.getChildren();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler, ignoreErrors } from 'vs/base/common/async';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, State, IStackFrame, IDebugSession, IThread, CONTEXT_CALLSTACK_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugService, State, IStackFrame, IDebugSession, IThread, CONTEXT_CALLSTACK_ITEM_TYPE, IDebugModel } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Thread, StackFrame, ThreadAndSessionIds } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -45,7 +45,7 @@ export class CallStackView extends ViewletPanel {
|
||||
private ignoreFocusStackFrameEvent: boolean;
|
||||
private callStackItemType: IContextKey<string>;
|
||||
private dataSource: CallStackDataSource;
|
||||
private tree: WorkbenchAsyncDataTree<CallStackItem>;
|
||||
private tree: WorkbenchAsyncDataTree<IDebugModel, CallStackItem>;
|
||||
private contributedContextMenu: IMenu;
|
||||
|
||||
constructor(
|
||||
@@ -84,7 +84,7 @@ export class CallStackView extends ViewletPanel {
|
||||
|
||||
this.needsRefresh = false;
|
||||
this.dataSource.deemphasizedStackFramesToShow = [];
|
||||
this.tree.refresh(null).then(() => this.updateTreeSelection());
|
||||
this.tree.refresh().then(() => this.updateTreeSelection());
|
||||
}, 50);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ export class CallStackView extends ViewletPanel {
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
const treeContainer = renderViewTree(container);
|
||||
|
||||
this.dataSource = new CallStackDataSource(this.debugService);
|
||||
this.dataSource = new CallStackDataSource();
|
||||
this.tree = new WorkbenchAsyncDataTree(treeContainer, new CallStackDelegate(), [
|
||||
new SessionsRenderer(),
|
||||
new ThreadsRenderer(),
|
||||
@@ -144,6 +144,9 @@ export class CallStackView extends ViewletPanel {
|
||||
}
|
||||
}, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService);
|
||||
|
||||
// TODO@isidor this is a promise
|
||||
this.tree.setInput(this.debugService.getModel());
|
||||
|
||||
const callstackNavigator = new TreeResourceNavigator2(this.tree);
|
||||
this.disposables.push(callstackNavigator);
|
||||
this.disposables.push(callstackNavigator.openResource(e => {
|
||||
@@ -176,12 +179,12 @@ export class CallStackView extends ViewletPanel {
|
||||
const thread = session && session.getThread(element.threadId);
|
||||
if (thread) {
|
||||
(<Thread>thread).fetchCallStack()
|
||||
.then(() => this.tree.refresh(null));
|
||||
.then(() => this.tree.refresh());
|
||||
}
|
||||
}
|
||||
if (element instanceof Array) {
|
||||
this.dataSource.deemphasizedStackFramesToShow.push(...element);
|
||||
this.tree.refresh(null);
|
||||
this.tree.refresh();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -571,19 +574,20 @@ class CallStackDelegate implements IListVirtualDelegate<CallStackItem> {
|
||||
}
|
||||
}
|
||||
|
||||
class CallStackDataSource implements IAsyncDataSource<CallStackItem> {
|
||||
function isDebugModel(obj: any): obj is IDebugModel {
|
||||
return typeof obj.getSessions === 'function';
|
||||
}
|
||||
|
||||
class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem> {
|
||||
deemphasizedStackFramesToShow: IStackFrame[];
|
||||
|
||||
constructor(private debugService: IDebugService) { }
|
||||
|
||||
hasChildren(element: CallStackItem | null): boolean {
|
||||
return element === null || element instanceof DebugSession || (element instanceof Thread && element.stopped);
|
||||
hasChildren(element: IDebugModel | CallStackItem): boolean {
|
||||
return isDebugModel(element) || element instanceof DebugSession || (element instanceof Thread && element.stopped);
|
||||
}
|
||||
|
||||
getChildren(element: CallStackItem | null): Promise<CallStackItem[]> {
|
||||
if (element === null) {
|
||||
const model = this.debugService.getModel();
|
||||
const sessions = model.getSessions();
|
||||
getChildren(element: IDebugModel | CallStackItem): Promise<CallStackItem[]> {
|
||||
if (isDebugModel(element)) {
|
||||
const sessions = element.getSessions();
|
||||
if (sessions.length === 0) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
@@ -594,12 +598,11 @@ class CallStackDataSource implements IAsyncDataSource<CallStackItem> {
|
||||
const threads = sessions[0].getAllThreads();
|
||||
// Only show the threads in the call stack if there is more than 1 thread.
|
||||
return threads.length === 1 ? this.getThreadChildren(<Thread>threads[0]) : Promise.resolve(threads);
|
||||
}
|
||||
if (element instanceof DebugSession) {
|
||||
} else if (element instanceof DebugSession) {
|
||||
return Promise.resolve(element.getAllThreads());
|
||||
} else {
|
||||
return this.getThreadChildren(<Thread>element);
|
||||
}
|
||||
|
||||
return this.getThreadChildren(<Thread>element);
|
||||
}
|
||||
|
||||
private getThreadChildren(thread: Thread): Promise<Array<IStackFrame | string | ThreadAndSessionIds>> {
|
||||
|
||||
@@ -45,7 +45,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
|
||||
private _isVisible: boolean;
|
||||
private domNode: HTMLElement;
|
||||
private tree: AsyncDataTree<IExpression>;
|
||||
private tree: AsyncDataTree<IExpression, IExpression>;
|
||||
private showAtPosition: Position;
|
||||
private highlightDecorations: string[];
|
||||
private complexValueContainer: HTMLElement;
|
||||
@@ -237,9 +237,8 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
|
||||
this.valueContainer.hidden = true;
|
||||
this.complexValueContainer.hidden = false;
|
||||
this.dataSource.expression = expression;
|
||||
|
||||
return this.tree.refresh(null).then(() => {
|
||||
return this.tree.setInput(expression).then(() => {
|
||||
this.complexValueTitle.textContent = expression.value;
|
||||
this.complexValueTitle.title = expression.value;
|
||||
this.layoutTreeAndContainer();
|
||||
@@ -291,19 +290,13 @@ class DebugHoverAccessibilityProvider implements IAccessibilityProvider<IExpress
|
||||
}
|
||||
}
|
||||
|
||||
class DebugHoverDataSource implements IAsyncDataSource<IExpression> {
|
||||
class DebugHoverDataSource implements IAsyncDataSource<IExpression, IExpression> {
|
||||
|
||||
expression: IExpression;
|
||||
|
||||
hasChildren(element: IExpression | null): boolean {
|
||||
return element === null || element.hasChildren;
|
||||
hasChildren(element: IExpression): boolean {
|
||||
return element.hasChildren;
|
||||
}
|
||||
|
||||
getChildren(element: IExpression | null): Promise<IExpression[]> {
|
||||
if (element === null) {
|
||||
element = this.expression;
|
||||
}
|
||||
|
||||
getChildren(element: IExpression): Promise<IExpression[]> {
|
||||
return element.getChildren();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ interface IPrivateReplService {
|
||||
clearRepl(): void;
|
||||
}
|
||||
|
||||
function revealLastElement<T>(tree: WorkbenchAsyncDataTree<T>) {
|
||||
function revealLastElement(tree: WorkbenchAsyncDataTree<any, any>) {
|
||||
tree.scrollTop = tree.scrollHeight - tree.renderHeight;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
private static readonly REPL_INPUT_MAX_HEIGHT = 170;
|
||||
|
||||
private history: HistoryNavigator<string>;
|
||||
private tree: WorkbenchAsyncDataTree<IReplElement>;
|
||||
private tree: WorkbenchAsyncDataTree<null, IReplElement>;
|
||||
private dataSource: ReplDataSource;
|
||||
private replDelegate: ReplDelegate;
|
||||
private container: HTMLElement;
|
||||
@@ -213,7 +213,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
|
||||
if (this.tree && this.dataSource.input !== session) {
|
||||
this.dataSource.input = session;
|
||||
this.tree.refresh(null).then(() => revealLastElement(this.tree));
|
||||
this.tree.refresh().then(() => revealLastElement(this.tree));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
private get refreshScheduler(): RunOnceScheduler {
|
||||
return new RunOnceScheduler(() => {
|
||||
const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight;
|
||||
this.tree.refresh(null).then(() => {
|
||||
this.tree.refresh().then(() => {
|
||||
if (lastElementVisible) {
|
||||
// Only scroll if we were scrolled all the way down before tree refreshed #10486
|
||||
revealLastElement(this.tree);
|
||||
@@ -363,6 +363,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }
|
||||
}, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService);
|
||||
|
||||
this.tree.setInput(null);
|
||||
|
||||
this.toDispose.push(this.tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
// Make sure to select the session if debugging is already active
|
||||
this.selectSession();
|
||||
@@ -750,7 +752,7 @@ class ReplDelegate implements IListVirtualDelegate<IReplElement> {
|
||||
}
|
||||
|
||||
|
||||
class ReplDataSource implements IAsyncDataSource<IReplElement> {
|
||||
class ReplDataSource implements IAsyncDataSource<null, IReplElement> {
|
||||
input: IDebugSession;
|
||||
|
||||
hasChildren(element: IReplElement | null): boolean {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { CollapseAction2 } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IViewModel } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Variable, Scope } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
@@ -37,7 +37,7 @@ export class VariablesView extends ViewletPanel {
|
||||
|
||||
private onFocusStackFrameScheduler: RunOnceScheduler;
|
||||
private needsRefresh: boolean;
|
||||
private tree: WorkbenchAsyncDataTree<IExpression | IScope>;
|
||||
private tree: WorkbenchAsyncDataTree<IViewModel, IExpression | IScope>;
|
||||
|
||||
constructor(
|
||||
options: IViewletViewOptions,
|
||||
@@ -55,7 +55,7 @@ export class VariablesView extends ViewletPanel {
|
||||
// Use scheduler to prevent unnecessary flashing
|
||||
this.onFocusStackFrameScheduler = new RunOnceScheduler(() => {
|
||||
this.needsRefresh = false;
|
||||
this.tree.refresh(null).then(() => {
|
||||
this.tree.refresh().then(() => {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (stackFrame) {
|
||||
stackFrame.getScopes().then(scopes => {
|
||||
@@ -75,18 +75,21 @@ export class VariablesView extends ViewletPanel {
|
||||
|
||||
this.tree = new WorkbenchAsyncDataTree(treeContainer, new VariablesDelegate(),
|
||||
[this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer()],
|
||||
new VariablesDataSource(this.debugService), {
|
||||
new VariablesDataSource(), {
|
||||
ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"),
|
||||
accessibilityProvider: new VariablesAccessibilityProvider(),
|
||||
identityProvider: { getId: element => element.getId() },
|
||||
keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }
|
||||
}, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService);
|
||||
|
||||
// TODO@isidor this is a promise
|
||||
this.tree.setInput(this.debugService.getViewModel());
|
||||
|
||||
CONTEXT_VARIABLES_FOCUSED.bindTo(this.contextKeyService.createScoped(treeContainer));
|
||||
|
||||
const collapseAction = new CollapseAction2(this.tree, true, 'explorer-action collapse-explorer');
|
||||
this.toolbar.setActions([collapseAction])();
|
||||
this.tree.refresh(null);
|
||||
this.tree.refresh();
|
||||
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(sf => {
|
||||
if (!this.isVisible() || !this.isExpanded()) {
|
||||
@@ -99,7 +102,7 @@ export class VariablesView extends ViewletPanel {
|
||||
const timeout = sf.explicit ? 0 : undefined;
|
||||
this.onFocusStackFrameScheduler.schedule(timeout);
|
||||
}));
|
||||
this.disposables.push(variableSetEmitter.event(() => this.tree.refresh(null)));
|
||||
this.disposables.push(variableSetEmitter.event(() => this.tree.refresh()));
|
||||
this.disposables.push(this.tree.onMouseDblClick(e => this.onMouseDblClick(e)));
|
||||
this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
}
|
||||
@@ -149,21 +152,23 @@ export class VariablesView extends ViewletPanel {
|
||||
}
|
||||
}
|
||||
|
||||
export class VariablesDataSource implements IAsyncDataSource<IExpression | IScope> {
|
||||
function isViewModel(obj: any): obj is IViewModel {
|
||||
return typeof obj.getSelectedExpression === 'function';
|
||||
}
|
||||
|
||||
constructor(private debugService: IDebugService) { }
|
||||
export class VariablesDataSource implements IAsyncDataSource<IViewModel, IExpression | IScope> {
|
||||
|
||||
hasChildren(element: IExpression | IScope | null): boolean {
|
||||
if (element === null || element instanceof Scope) {
|
||||
hasChildren(element: IViewModel | IExpression | IScope): boolean {
|
||||
if (isViewModel(element) || element instanceof Scope) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return element.hasChildren;
|
||||
}
|
||||
|
||||
getChildren(element: IExpression | IScope | null): Promise<Array<IExpression | IScope>> {
|
||||
if (element === null) {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
getChildren(element: IViewModel | IExpression | IScope): Promise<(IExpression | IScope)[]> {
|
||||
if (isViewModel(element)) {
|
||||
const stackFrame = element.focusedStackFrame;
|
||||
return stackFrame ? stackFrame.getScopes() : Promise.resolve([]);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
private needsRefresh: boolean;
|
||||
private tree: WorkbenchAsyncDataTree<IExpression>;
|
||||
private tree: WorkbenchAsyncDataTree<IDebugService, IExpression>;
|
||||
|
||||
constructor(
|
||||
options: IViewletViewOptions,
|
||||
@@ -51,7 +51,7 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
|
||||
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
|
||||
this.needsRefresh = false;
|
||||
this.tree.refresh(null);
|
||||
this.tree.refresh();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
@@ -63,14 +63,15 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
const expressionsRenderer = this.instantiationService.createInstance(WatchExpressionsRenderer);
|
||||
this.disposables.push(expressionsRenderer);
|
||||
this.tree = new WorkbenchAsyncDataTree(treeContainer, new WatchExpressionsDelegate(), [expressionsRenderer, this.instantiationService.createInstance(VariablesRenderer)],
|
||||
new WatchExpressionsDataSource(this.debugService), {
|
||||
new WatchExpressionsDataSource(), {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"),
|
||||
accessibilityProvider: new WatchExpressionsAccessibilityProvider(),
|
||||
identityProvider: { getId: element => element.getId() },
|
||||
keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }
|
||||
}, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService);
|
||||
|
||||
this.tree.refresh(null);
|
||||
// TODO@isidor this is a promise
|
||||
this.tree.setInput(this.debugService);
|
||||
|
||||
const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService);
|
||||
const collapseAction = new CollapseAction2(this.tree, true, 'explorer-action collapse-explorer');
|
||||
@@ -83,7 +84,7 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
if (!this.isExpanded() || !this.isVisible()) {
|
||||
this.needsRefresh = true;
|
||||
} else {
|
||||
this.tree.refresh(null);
|
||||
this.tree.refresh();
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => {
|
||||
@@ -96,7 +97,7 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
this.onWatchExpressionsUpdatedScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.disposables.push(variableSetEmitter.event(() => this.tree.refresh(null)));
|
||||
this.disposables.push(variableSetEmitter.event(() => this.tree.refresh()));
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
@@ -186,22 +187,21 @@ class WatchExpressionsDelegate implements IListVirtualDelegate<IExpression> {
|
||||
}
|
||||
}
|
||||
|
||||
class WatchExpressionsDataSource implements IAsyncDataSource<IExpression> {
|
||||
function isDebugService(element: any): element is IDebugService {
|
||||
return typeof element.getConfigurationManager === 'function';
|
||||
}
|
||||
|
||||
constructor(private debugService: IDebugService) { }
|
||||
class WatchExpressionsDataSource implements IAsyncDataSource<IDebugService, IExpression> {
|
||||
|
||||
hasChildren(element: IExpression | null): boolean {
|
||||
if (element === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return element.hasChildren;
|
||||
return isDebugService(element) || element.hasChildren;
|
||||
}
|
||||
|
||||
getChildren(element: IExpression | null): Promise<Array<IExpression>> {
|
||||
if (element === null) {
|
||||
const watchExpressions = this.debugService.getModel().getWatchExpressions();
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
getChildren(element: IDebugService | IExpression): Promise<Array<IExpression>> {
|
||||
if (isDebugService(element)) {
|
||||
const debugService = element as IDebugService;
|
||||
const watchExpressions = debugService.getModel().getWatchExpressions();
|
||||
const viewModel = debugService.getViewModel();
|
||||
return Promise.all(watchExpressions.map(we => !!we.name
|
||||
? we.evaluate(viewModel.focusedSession, viewModel.focusedStackFrame, 'watch').then(() => we)
|
||||
: Promise.resolve(we)));
|
||||
|
||||
Reference in New Issue
Block a user