mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 17:22:48 -05:00
Registers all disposable items for query execution plans (#20851)
This commit is contained in:
@@ -38,8 +38,9 @@ import { ExecutionPlanComparisonInput } from 'sql/workbench/contrib/executionPla
|
||||
import { ExecutionPlanFileView } from 'sql/workbench/contrib/executionPlan/browser/executionPlanFileView';
|
||||
import { QueryResultsView } from 'sql/workbench/contrib/query/browser/queryResultsView';
|
||||
import { HighlightExpensiveOperationWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/highlightExpensiveNodeWidget';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
export class ExecutionPlanView extends Disposable implements ISashLayoutProvider {
|
||||
|
||||
// Underlying execution plan displayed in the view
|
||||
private _model?: azdata.executionPlan.ExecutionPlanGraph;
|
||||
@@ -87,6 +88,8 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
@IWorkspaceContextService public workspaceContextService: IWorkspaceContextService,
|
||||
@IEditorService private _editorService: IEditorService
|
||||
) {
|
||||
super();
|
||||
|
||||
// parent container for query plan.
|
||||
this.container = DOM.$('.execution-plan');
|
||||
this._parent.appendChild(this.container);
|
||||
@@ -94,19 +97,20 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
this._parent.appendChild(sashContainer);
|
||||
|
||||
// resizing sash for the query plan.
|
||||
const sash = new Sash(sashContainer, this, { orientation: Orientation.HORIZONTAL, size: 3 });
|
||||
const sash = this._register(new Sash(sashContainer, this, { orientation: Orientation.HORIZONTAL, size: 3 }));
|
||||
let originalHeight = this.container.offsetHeight;
|
||||
let originalTableHeight = 0;
|
||||
let change = 0;
|
||||
sash.onDidStart((e: ISashEvent) => {
|
||||
|
||||
this._register(sash.onDidStart((e: ISashEvent) => {
|
||||
originalHeight = this.container.offsetHeight;
|
||||
originalTableHeight = this.propertiesView.tableHeight;
|
||||
});
|
||||
}));
|
||||
|
||||
/**
|
||||
* Using onDidChange for the smooth resizing of the graph diagram
|
||||
*/
|
||||
sash.onDidChange((evt: ISashEvent) => {
|
||||
this._register(sash.onDidChange((evt: ISashEvent) => {
|
||||
change = evt.startY - evt.currentY;
|
||||
const newHeight = originalHeight - change;
|
||||
if (newHeight < 200) {
|
||||
@@ -118,14 +122,14 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
*/
|
||||
this.container.style.minHeight = '200px';
|
||||
this.container.style.flex = `0 0 ${newHeight}px`;
|
||||
});
|
||||
}));
|
||||
|
||||
/**
|
||||
* Resizing properties window table only once at the end as it is a heavy operation and worsens the smooth resizing experience
|
||||
*/
|
||||
sash.onDidEnd(() => {
|
||||
this._register(sash.onDidEnd(() => {
|
||||
this.propertiesView.tableHeight = originalTableHeight - change;
|
||||
});
|
||||
}));
|
||||
|
||||
this._planContainer = DOM.$('.plan');
|
||||
this.container.appendChild(this._planContainer);
|
||||
@@ -139,14 +143,14 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
this._planHeaderContainer.style.fontWeight = EDITOR_FONT_DEFAULTS.fontWeight;
|
||||
|
||||
this._planContainer.appendChild(this._planHeaderContainer);
|
||||
this.planHeader = this._instantiationService.createInstance(ExecutionPlanViewHeader, this._planHeaderContainer, {
|
||||
this.planHeader = this._register(this._instantiationService.createInstance(ExecutionPlanViewHeader, this._planHeaderContainer, {
|
||||
planIndex: this._graphIndex,
|
||||
});
|
||||
}));
|
||||
|
||||
// container properties
|
||||
this._propContainer = DOM.$('.properties');
|
||||
this.container.appendChild(this._propContainer);
|
||||
this.propertiesView = this._instantiationService.createInstance(ExecutionPlanPropertiesView, this._propContainer);
|
||||
this.propertiesView = this._register(this._instantiationService.createInstance(ExecutionPlanPropertiesView, this._propContainer));
|
||||
|
||||
this._widgetContainer = DOM.$('.plan-action-container');
|
||||
this._planContainer.appendChild(this._widgetContainer);
|
||||
@@ -155,56 +159,56 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
// container that holds action bar icons
|
||||
this._actionBarContainer = DOM.$('.action-bar-container');
|
||||
this.container.appendChild(this._actionBarContainer);
|
||||
this._actionBar = new ActionBar(this._actionBarContainer, {
|
||||
this._actionBar = this._register(new ActionBar(this._actionBarContainer, {
|
||||
orientation: ActionsOrientation.VERTICAL, context: this
|
||||
});
|
||||
}));
|
||||
|
||||
this.actionBarToggleTopTip = new ActionBarToggleTooltip();
|
||||
this.actionBarToggleTopTip = this._register(new ActionBarToggleTooltip());
|
||||
const actionBarActions = [
|
||||
new SavePlanFile(),
|
||||
new OpenPlanFile(),
|
||||
this._instantiationService.createInstance(OpenQueryAction, 'ActionBar'),
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(ZoomInAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(ZoomOutAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(ZoomToFitAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(CustomZoomAction, 'ActionBar'),
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(SearchNodeAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(PropertiesAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(HighlightExpensiveOperationAction, 'ActionBar'),
|
||||
this._register(new SavePlanFile()),
|
||||
this._register(new OpenPlanFile()),
|
||||
this._register(this._instantiationService.createInstance(OpenQueryAction, 'ActionBar')),
|
||||
this._register(new Separator()),
|
||||
this._register(this._instantiationService.createInstance(ZoomInAction, 'ActionBar')),
|
||||
this._register(this._instantiationService.createInstance(ZoomOutAction, 'ActionBar')),
|
||||
this._register(this._instantiationService.createInstance(ZoomToFitAction, 'ActionBar')),
|
||||
this._register(this._instantiationService.createInstance(CustomZoomAction, 'ActionBar')),
|
||||
this._register(new Separator()),
|
||||
this._register(this._instantiationService.createInstance(SearchNodeAction, 'ActionBar')),
|
||||
this._register(this._instantiationService.createInstance(PropertiesAction, 'ActionBar')),
|
||||
this._register(this._instantiationService.createInstance(CompareExecutionPlanAction, 'ActionBar')),
|
||||
this._register(this._instantiationService.createInstance(HighlightExpensiveOperationAction, 'ActionBar')),
|
||||
this.actionBarToggleTopTip
|
||||
];
|
||||
// Setting up context menu
|
||||
this.contextMenuToggleTooltipAction = new ContextMenuTooltipToggle();
|
||||
this.contextMenuToggleTooltipAction = this._register(new ContextMenuTooltipToggle());
|
||||
const contextMenuAction = [
|
||||
new SavePlanFile(),
|
||||
new OpenPlanFile(),
|
||||
this._instantiationService.createInstance(OpenQueryAction, 'ContextMenu'),
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(ZoomInAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(ZoomOutAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(ZoomToFitAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(CustomZoomAction, 'ContextMenu'),
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(SearchNodeAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(PropertiesAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(HighlightExpensiveOperationAction, 'ContextMenu'),
|
||||
this._register(new SavePlanFile()),
|
||||
this._register(new OpenPlanFile()),
|
||||
this._register(this._instantiationService.createInstance(OpenQueryAction, 'ContextMenu')),
|
||||
this._register(new Separator()),
|
||||
this._register(this._instantiationService.createInstance(ZoomInAction, 'ContextMenu')),
|
||||
this._register(this._instantiationService.createInstance(ZoomOutAction, 'ContextMenu')),
|
||||
this._register(this._instantiationService.createInstance(ZoomToFitAction, 'ContextMenu')),
|
||||
this._register(this._instantiationService.createInstance(CustomZoomAction, 'ContextMenu')),
|
||||
this._register(new Separator()),
|
||||
this._register(this._instantiationService.createInstance(SearchNodeAction, 'ContextMenu')),
|
||||
this._register(this._instantiationService.createInstance(PropertiesAction, 'ContextMenu')),
|
||||
this._register(this._instantiationService.createInstance(CompareExecutionPlanAction, 'ContextMenu')),
|
||||
this._register(this._instantiationService.createInstance(HighlightExpensiveOperationAction, 'ContextMenu')),
|
||||
this.contextMenuToggleTooltipAction,
|
||||
new Separator(),
|
||||
this._register(new Separator()),
|
||||
];
|
||||
|
||||
if (this._queryResultsView) {
|
||||
actionBarActions.push(this._instantiationService.createInstance(TopOperationsAction));
|
||||
contextMenuAction.push(this._instantiationService.createInstance(TopOperationsAction));
|
||||
actionBarActions.push(this._register(this._instantiationService.createInstance(TopOperationsAction)));
|
||||
contextMenuAction.push(this._register(this._instantiationService.createInstance(TopOperationsAction)));
|
||||
}
|
||||
|
||||
this._actionBar.pushAction(actionBarActions, { icon: true, label: false });
|
||||
|
||||
const self = this;
|
||||
this._planContainer.oncontextmenu = (e: MouseEvent) => {
|
||||
this._register(DOM.addDisposableListener(this._planContainer, DOM.EventType.CONTEXT_MENU, (e: MouseEvent) => {
|
||||
if (contextMenuAction) {
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => {
|
||||
@@ -217,34 +221,37 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
getActionsContext: () => (self)
|
||||
});
|
||||
}
|
||||
};
|
||||
}));
|
||||
|
||||
this.container.onkeydown = (e: KeyboardEvent) => {
|
||||
this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
||||
let searchNodeAction = self._instantiationService.createInstance(SearchNodeAction, 'HotKey');
|
||||
let searchNodeAction = self._register(self._instantiationService.createInstance(SearchNodeAction, 'HotKey'));
|
||||
searchNodeAction.run(self);
|
||||
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
getHorizontalSashTop(sash: Sash): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
getHorizontalSashLeft?(sash: Sash): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
getHorizontalSashWidth?(sash: Sash): number {
|
||||
return this.container.clientWidth;
|
||||
}
|
||||
|
||||
private createPlanDiagram(container: HTMLElement) {
|
||||
this.executionPlanDiagram = this._instantiationService.createInstance(AzdataGraphView, container, this._model);
|
||||
this.executionPlanDiagram.onElementSelected(e => {
|
||||
this.executionPlanDiagram = this._register(this._instantiationService.createInstance(AzdataGraphView, container, this._model));
|
||||
|
||||
this._register(this.executionPlanDiagram.onElementSelected(e => {
|
||||
container.focus();
|
||||
this.propertiesView.graphElement = e;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -253,9 +260,11 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
if (this._model) {
|
||||
this.planHeader.graphIndex = this._graphIndex;
|
||||
this.planHeader.query = graph.query;
|
||||
|
||||
if (graph.recommendations) {
|
||||
this.planHeader.recommendations = graph.recommendations;
|
||||
}
|
||||
|
||||
let diagramContainer = DOM.$('.diagram');
|
||||
this.createPlanDiagram(diagramContainer);
|
||||
|
||||
@@ -266,13 +275,13 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
* the graph control. To scroll the individual graphs, users should
|
||||
* use the scroll bars.
|
||||
*/
|
||||
diagramContainer.addEventListener('wheel', e => {
|
||||
this._register(DOM.addDisposableListener(diagramContainer, DOM.EventType.WHEEL, (e: WheelEvent) => {
|
||||
//Hiding all tooltips when we scroll.
|
||||
const element = document.getElementsByClassName('mxTooltip');
|
||||
for (let i = 0; i < element.length; i++) {
|
||||
(<HTMLElement>element[i]).style.visibility = 'hidden';
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this._planContainer.appendChild(diagramContainer);
|
||||
|
||||
@@ -301,10 +310,10 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
}
|
||||
|
||||
public compareCurrentExecutionPlan() {
|
||||
this._editorService.openEditor(this._instantiationService.createInstance(ExecutionPlanComparisonInput, {
|
||||
this._editorService.openEditor(this._register(this._instantiationService.createInstance(ExecutionPlanComparisonInput, {
|
||||
topExecutionPlan: this._executionPlanFileView.graphs,
|
||||
topPlanIndex: this._graphIndex - 1
|
||||
}), {
|
||||
})), {
|
||||
pinned: true
|
||||
});
|
||||
}
|
||||
@@ -466,7 +475,7 @@ export class CustomZoomAction extends Action {
|
||||
.withAdditionalProperties({ source: this.source })
|
||||
.send();
|
||||
|
||||
context.widgetController.toggleWidget(context._instantiationService.createInstance(CustomZoomWidget, context.widgetController, context.executionPlanDiagram));
|
||||
context.widgetController.toggleWidget(this._register(context._instantiationService.createInstance(CustomZoomWidget, context.widgetController, context.executionPlanDiagram)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +495,7 @@ export class SearchNodeAction extends Action {
|
||||
.withAdditionalProperties({ source: this.source })
|
||||
.send();
|
||||
|
||||
context.widgetController.toggleWidget(context._instantiationService.createInstance(NodeSearchWidget, context.widgetController, context.executionPlanDiagram));
|
||||
context.widgetController.toggleWidget(this._register(context._instantiationService.createInstance(NodeSearchWidget, context.widgetController, context.executionPlanDiagram)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,6 +609,6 @@ export class HighlightExpensiveOperationAction extends Action {
|
||||
.withAdditionalProperties({ source: this.source })
|
||||
.send();
|
||||
|
||||
context.widgetController.toggleWidget(context._instantiationService.createInstance(HighlightExpensiveOperationWidget, context.widgetController, context.executionPlanDiagram));
|
||||
context.widgetController.toggleWidget(this._register(context._instantiationService.createInstance(HighlightExpensiveOperationWidget, context.widgetController, context.executionPlanDiagram)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user