mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 011858832762aaff245b2336fb1c38166e7a10fb (#4663)
This commit is contained in:
@@ -25,7 +25,7 @@ export class ContextSubMenu extends SubmenuAction {
|
||||
export interface IContextMenuDelegate {
|
||||
getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; };
|
||||
getActions(): Array<IAction | ContextSubMenu>;
|
||||
getActionItem?(action: IAction): IActionItem | null;
|
||||
getActionItem?(action: IAction): IActionItem | undefined;
|
||||
getActionsContext?(event?: IContextMenuEvent): any;
|
||||
getKeyBinding?(action: IAction): ResolvedKeybinding | undefined;
|
||||
getMenuClassName?(): string;
|
||||
|
||||
@@ -364,7 +364,7 @@ export interface ActionTrigger {
|
||||
}
|
||||
|
||||
export interface IActionItemProvider {
|
||||
(action: IAction): IActionItem | null;
|
||||
(action: IAction): IActionItem | undefined;
|
||||
}
|
||||
|
||||
export interface IActionBarOptions {
|
||||
@@ -609,7 +609,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
e.stopPropagation();
|
||||
}));
|
||||
|
||||
let item: IActionItem | null = null;
|
||||
let item: IActionItem | undefined;
|
||||
|
||||
if (this.options.actionItemProvider) {
|
||||
item = this.options.actionItemProvider(action);
|
||||
|
||||
@@ -118,7 +118,7 @@ export class ContextView extends Disposable {
|
||||
this._register(toDisposable(() => this.setContainer(null)));
|
||||
}
|
||||
|
||||
public setContainer(container: HTMLElement | null): void {
|
||||
setContainer(container: HTMLElement | null): void {
|
||||
if (this.container) {
|
||||
this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer);
|
||||
this.container.removeChild(this.view);
|
||||
@@ -146,7 +146,7 @@ export class ContextView extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public show(delegate: IDelegate): void {
|
||||
show(delegate: IDelegate): void {
|
||||
if (this.isVisible()) {
|
||||
this.hide();
|
||||
}
|
||||
@@ -173,7 +173,7 @@ export class ContextView extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
layout(): void {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
@@ -258,7 +258,7 @@ export class ContextView extends Disposable {
|
||||
this.view.style.width = 'initial';
|
||||
}
|
||||
|
||||
public hide(data?: any): void {
|
||||
hide(data?: any): void {
|
||||
if (this.delegate && this.delegate.onHide) {
|
||||
this.delegate.onHide(data);
|
||||
}
|
||||
@@ -288,7 +288,7 @@ export class ContextView extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.hide();
|
||||
|
||||
super.dispose();
|
||||
|
||||
@@ -247,7 +247,7 @@ export class DropdownMenu extends BaseDropdown {
|
||||
getAnchor: () => this.element,
|
||||
getActions: () => this.actions,
|
||||
getActionsContext: () => this.menuOptions ? this.menuOptions.context : null,
|
||||
getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null,
|
||||
getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : undefined,
|
||||
getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : undefined,
|
||||
getMenuClassName: () => this.menuClassName,
|
||||
onHide: () => this.onHide(),
|
||||
|
||||
@@ -63,7 +63,7 @@ const DefaultOptions = {
|
||||
setRowLineHeight: true,
|
||||
supportDynamicHeights: false,
|
||||
dnd: {
|
||||
getDragElements(e) { return [e]; },
|
||||
getDragElements<T>(e: T) { return [e]; },
|
||||
getDragURI() { return null; },
|
||||
onDragStart(): void { },
|
||||
onDragOver() { return false; },
|
||||
|
||||
@@ -860,8 +860,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate<I
|
||||
|
||||
this.selectionDetailsPane.innerText = '';
|
||||
const selectedIndex = e.indexes[0];
|
||||
const description = this.options[selectedIndex].description || null;
|
||||
const descriptionIsMarkdown = this.options[selectedIndex].descriptionIsMarkdown || null;
|
||||
const description = this.options[selectedIndex].description;
|
||||
const descriptionIsMarkdown = this.options[selectedIndex].descriptionIsMarkdown;
|
||||
|
||||
if (description) {
|
||||
if (descriptionIsMarkdown) {
|
||||
|
||||
@@ -75,10 +75,10 @@ export class ToolBar extends Disposable {
|
||||
);
|
||||
this.toggleMenuActionItem!.setActionContext(this.actionBar.context);
|
||||
|
||||
return this.toggleMenuActionItem || null;
|
||||
return this.toggleMenuActionItem;
|
||||
}
|
||||
|
||||
return options.actionItemProvider ? options.actionItemProvider(action) : null;
|
||||
return options.actionItemProvider ? options.actionItemProvider(action) : undefined;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -805,7 +805,7 @@ class Trait<T> {
|
||||
onDidModelSplice({ insertedNodes, deletedNodes }: ITreeModelSpliceEvent<T, any>): void {
|
||||
if (!this.identityProvider) {
|
||||
const set = this.createNodeSet();
|
||||
const visit = node => set.delete(node);
|
||||
const visit = (node: ITreeNode<T, any>) => set.delete(node);
|
||||
deletedNodes.forEach(node => dfs(node, visit));
|
||||
this.set(values(set));
|
||||
return;
|
||||
@@ -816,8 +816,8 @@ class Trait<T> {
|
||||
this.nodes.forEach(node => nodesByIdentity.set(identityProvider.getId(node.element).toString(), node));
|
||||
|
||||
const toDeleteByIdentity = new Map<string, ITreeNode<T, any>>();
|
||||
const toRemoveSetter = node => toDeleteByIdentity.set(identityProvider.getId(node.element).toString(), node);
|
||||
const toRemoveDeleter = node => toDeleteByIdentity.delete(identityProvider.getId(node.element).toString());
|
||||
const toRemoveSetter = (node: ITreeNode<T, any>) => toDeleteByIdentity.set(identityProvider.getId(node.element).toString(), node);
|
||||
const toRemoveDeleter = (node: { element: T; }) => toDeleteByIdentity.delete(identityProvider.getId(node.element).toString());
|
||||
deletedNodes.forEach(node => dfs(node, toRemoveSetter));
|
||||
insertedNodes.forEach(node => dfs(node, toRemoveDeleter));
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
|
||||
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { toggleClass } from 'vs/base/browser/dom';
|
||||
import { values } from 'vs/base/common/map';
|
||||
|
||||
interface IAsyncDataTreeNode<TInput, T> {
|
||||
element: TInput | T;
|
||||
@@ -26,7 +27,6 @@ interface IAsyncDataTreeNode<TInput, T> {
|
||||
hasChildren: boolean;
|
||||
stale: boolean;
|
||||
slow: boolean;
|
||||
disposed: boolean;
|
||||
}
|
||||
|
||||
interface IAsyncDataTreeNodeRequiredProps<TInput, T> extends Partial<IAsyncDataTreeNode<TInput, T>> {
|
||||
@@ -41,7 +41,6 @@ function createAsyncDataTreeNode<TInput, T>(props: IAsyncDataTreeNodeRequiredPro
|
||||
children: [],
|
||||
loading: false,
|
||||
stale: true,
|
||||
disposed: false,
|
||||
slow: false
|
||||
};
|
||||
}
|
||||
@@ -274,6 +273,11 @@ interface IAsyncDataTreeViewStateContext<TInput, T> {
|
||||
readonly focus: IAsyncDataTreeNode<TInput, T>[];
|
||||
}
|
||||
|
||||
function dfs<TInput, T>(node: IAsyncDataTreeNode<TInput, T>, fn: (node: IAsyncDataTreeNode<TInput, T>) => void): void {
|
||||
fn(node);
|
||||
node.children.forEach(child => dfs(child, fn));
|
||||
}
|
||||
|
||||
export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable {
|
||||
|
||||
private readonly tree: ObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
|
||||
@@ -629,11 +633,6 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
}
|
||||
|
||||
private async refreshNode(node: IAsyncDataTreeNode<TInput, T>, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): Promise<void> {
|
||||
if (node.disposed) {
|
||||
console.error('Async data tree node is disposed');
|
||||
return;
|
||||
}
|
||||
|
||||
let result: Promise<void> | undefined;
|
||||
|
||||
this.subTreeRefreshPromises.forEach((refreshPromise, refreshNode) => {
|
||||
@@ -748,37 +747,47 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
return [];
|
||||
}
|
||||
|
||||
let nodeChildren: Map<string, IAsyncDataTreeNode<TInput, T>> | undefined;
|
||||
const nodesToForget = new Map<T, IAsyncDataTreeNode<TInput, T>>();
|
||||
const childrenTreeNodesById = new Map<string, ITreeNode<IAsyncDataTreeNode<TInput, T> | null, TFilterData>>();
|
||||
|
||||
if (this.identityProvider) {
|
||||
nodeChildren = new Map();
|
||||
for (const child of node.children) {
|
||||
nodesToForget.set(child.element as T, child);
|
||||
|
||||
for (const child of node.children) {
|
||||
nodeChildren.set(child.id!, child);
|
||||
if (this.identityProvider) {
|
||||
childrenTreeNodesById.set(child.id!, this.tree.getNode(child));
|
||||
}
|
||||
}
|
||||
|
||||
let childrenToRefresh: IAsyncDataTreeNode<TInput, T>[] = [];
|
||||
const childrenToRefresh: IAsyncDataTreeNode<TInput, T>[] = [];
|
||||
|
||||
const children = childrenElements.map<IAsyncDataTreeNode<TInput, T>>(element => {
|
||||
if (!this.identityProvider) {
|
||||
return createAsyncDataTreeNode({
|
||||
element,
|
||||
parent: node,
|
||||
hasChildren: !!this.dataSource.hasChildren(element),
|
||||
hasChildren: !!this.dataSource.hasChildren(element)
|
||||
});
|
||||
}
|
||||
|
||||
const id = this.identityProvider.getId(element).toString();
|
||||
const asyncDataTreeNode = nodeChildren!.get(id);
|
||||
const childNode = childrenTreeNodesById.get(id);
|
||||
|
||||
if (childNode) {
|
||||
const asyncDataTreeNode = childNode.element!;
|
||||
|
||||
nodesToForget.delete(asyncDataTreeNode.element as T);
|
||||
this.nodes.delete(asyncDataTreeNode.element as T);
|
||||
this.nodes.set(element, asyncDataTreeNode);
|
||||
|
||||
if (asyncDataTreeNode) {
|
||||
asyncDataTreeNode.element = element;
|
||||
asyncDataTreeNode.stale = asyncDataTreeNode.stale || recursive;
|
||||
asyncDataTreeNode.hasChildren = !!this.dataSource.hasChildren(element);
|
||||
|
||||
if (recursive && !this.tree.isCollapsed(asyncDataTreeNode)) {
|
||||
childrenToRefresh.push(asyncDataTreeNode);
|
||||
if (recursive) {
|
||||
if (childNode.collapsed) {
|
||||
dfs(asyncDataTreeNode, node => node.stale = true);
|
||||
} else {
|
||||
childrenToRefresh.push(asyncDataTreeNode);
|
||||
}
|
||||
}
|
||||
|
||||
return asyncDataTreeNode;
|
||||
@@ -806,33 +815,22 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
return childAsyncDataTreeNode;
|
||||
});
|
||||
|
||||
for (const node of values(nodesToForget)) {
|
||||
dfs(node, node => this.nodes.delete(node.element as T));
|
||||
}
|
||||
|
||||
for (const child of children) {
|
||||
this.nodes.set(child.element as T, child);
|
||||
}
|
||||
|
||||
node.children.splice(0, node.children.length, ...children);
|
||||
|
||||
return childrenToRefresh;
|
||||
}
|
||||
|
||||
private render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
|
||||
const insertedElements = new Set<T>();
|
||||
|
||||
const onDidCreateNode = (treeNode: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>) => {
|
||||
if (treeNode.element.element) {
|
||||
insertedElements.add(treeNode.element.element as T);
|
||||
this.nodes.set(treeNode.element.element as T, treeNode.element);
|
||||
}
|
||||
};
|
||||
|
||||
const onDidDeleteNode = (treeNode: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>) => {
|
||||
if (treeNode.element.element) {
|
||||
if (!insertedElements.has(treeNode.element.element as T)) {
|
||||
treeNode.element.disposed = true;
|
||||
this.nodes.delete(treeNode.element.element as T);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const children = node.children.map(c => asTreeElement(c, viewStateContext));
|
||||
this.tree.setChildren(node === this.root ? null : node, children, onDidCreateNode, onDidDeleteNode);
|
||||
|
||||
this.tree.setChildren(node === this.root ? null : node, children);
|
||||
this._onDidRender.fire();
|
||||
}
|
||||
|
||||
|
||||
@@ -198,8 +198,17 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
|
||||
}
|
||||
|
||||
getNode(element: T | null = null): ITreeNode<T | null, TFilterData> {
|
||||
const location = this.getElementLocation(element);
|
||||
return this.model.getNode(location);
|
||||
if (element === null) {
|
||||
return this.model.getNode(this.model.rootRef);
|
||||
}
|
||||
|
||||
const node = this.nodes.get(element);
|
||||
|
||||
if (!node) {
|
||||
throw new Error(`Tree element not found: ${element}`);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
getNodeLocation(node: ITreeNode<T, TFilterData>): T {
|
||||
|
||||
Reference in New Issue
Block a user