mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 09:35:38 -05:00
Query Execution Plan Expensive Operator Highlighting (#20579)
* Boilerplate for new context menu options * Enables checkmarks for expensive operator context menu actions * Updates azdataGraph version to 0.0.44 * Adds clearing logic to actions and retrieves additional info from nodes * Removes unnecessary actions that aren't supported by other providers * Finishes setting up the rest of the context menu actions * Corrects context menu action label * Defines new widget type for finding expensive operations * Adds TODO for class icon name * Creates action to open the expensive operation widget * Adds escape and enter key shortcuts to findExpensiveOperation widget * Styles find expensive operation widget * Corrects class name for finding expensive operator action * Corrects import statement. * Code clean up * Bumps azdataGraph version to 0.0.45 * Adds an info box for when a metric doesn't find any nodes * Adds label to find expensive operator widget * Invokes dispose when widget controller removes widget * Implements disposable in all execution plan widgets. * Adds off action to clear highlighting * Adds a default setting and default prompt for highlighting metric * Fixes not all code paths return error * Removes shortcut key from text for widget actions * Adds enum description * Removes added dictionary type, and renames class name * Removes unnecessary null checks * Removes cost metric dictionary and adds corresponding properties * Code review changes * Removes incorrectly implemented key down event for widget. * Renames widget and action class names for highlighting expensive ops * File rename * Cleans up labels to better reflect highlight action * Removes hardcoded button width style and sets it to auto * More clean up * Clean up import statement * Code review changes * Drop down list only shows available metrics * Updates highlight expensive operation icon * Update STS version
This commit is contained in:
@@ -26,7 +26,7 @@ import { Progress } from 'vs/platform/progress/common/progress';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Action, Separator } from 'vs/base/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { customZoomIconClassNames, disableTooltipIconClassName, enableTooltipIconClassName, executionPlanCompareIconClassName, executionPlanTopOperations, openPlanFileIconClassNames, openPropertiesIconClassNames, openQueryIconClassNames, savePlanIconClassNames, searchIconClassNames, zoomInIconClassNames, zoomOutIconClassNames, zoomToFitIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import * as constants from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CustomZoomWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/customZoomWidget';
|
||||
@@ -37,6 +37,7 @@ import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ExecutionPlanComparisonInput } from 'sql/workbench/contrib/executionPlan/browser/compareExecutionPlanInput';
|
||||
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';
|
||||
|
||||
export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
|
||||
@@ -66,6 +67,9 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
// plan diagram
|
||||
public executionPlanDiagram: AzdataGraphView;
|
||||
|
||||
// previous expensive operator action selected
|
||||
public previousExpensiveOperatorAction: Action;
|
||||
|
||||
public actionBarToggleTopTip: Action;
|
||||
public contextMenuToggleTooltipAction: Action;
|
||||
constructor(
|
||||
@@ -169,6 +173,7 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
this._instantiationService.createInstance(SearchNodeAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(PropertiesAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(HighlightExpensiveOperationAction, 'ActionBar'),
|
||||
this.actionBarToggleTopTip
|
||||
];
|
||||
// Setting up context menu
|
||||
@@ -186,7 +191,9 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
this._instantiationService.createInstance(SearchNodeAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(PropertiesAction, 'ContextMenu'),
|
||||
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ContextMenu'),
|
||||
this.contextMenuToggleTooltipAction
|
||||
this._instantiationService.createInstance(HighlightExpensiveOperationAction, 'ContextMenu'),
|
||||
this.contextMenuToggleTooltipAction,
|
||||
new Separator(),
|
||||
];
|
||||
|
||||
if (this._queryResultsView) {
|
||||
@@ -320,7 +327,7 @@ export class OpenQueryAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(OpenQueryAction.ID, OpenQueryAction.LABEL, openQueryIconClassNames);
|
||||
super(OpenQueryAction.ID, OpenQueryAction.LABEL, constants.openQueryIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -340,7 +347,7 @@ export class PropertiesAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(PropertiesAction.ID, PropertiesAction.LABEL, openPropertiesIconClassNames);
|
||||
super(PropertiesAction.ID, PropertiesAction.LABEL, constants.openPropertiesIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -360,7 +367,7 @@ export class ZoomInAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(ZoomInAction.ID, ZoomInAction.LABEL, zoomInIconClassNames);
|
||||
super(ZoomInAction.ID, ZoomInAction.LABEL, constants.zoomInIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -380,7 +387,7 @@ export class ZoomOutAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(ZoomOutAction.ID, ZoomOutAction.LABEL, zoomOutIconClassNames);
|
||||
super(ZoomOutAction.ID, ZoomOutAction.LABEL, constants.zoomOutIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -400,7 +407,7 @@ export class ZoomToFitAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(ZoomToFitAction.ID, ZoomToFitAction.LABEL, zoomToFitIconClassNames);
|
||||
super(ZoomToFitAction.ID, ZoomToFitAction.LABEL, constants.zoomToFitIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -418,7 +425,7 @@ export class SavePlanFile extends Action {
|
||||
public static LABEL = localize('executionPlanSavePlanXML', "Save Plan File");
|
||||
|
||||
constructor() {
|
||||
super(SavePlanFile.ID, SavePlanFile.LABEL, savePlanIconClassNames);
|
||||
super(SavePlanFile.ID, SavePlanFile.LABEL, constants.savePlanIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -453,7 +460,7 @@ export class CustomZoomAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(CustomZoomAction.ID, CustomZoomAction.LABEL, customZoomIconClassNames);
|
||||
super(CustomZoomAction.ID, CustomZoomAction.LABEL, constants.customZoomIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -473,7 +480,7 @@ export class SearchNodeAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(SearchNodeAction.ID, SearchNodeAction.LABEL, searchIconClassNames);
|
||||
super(SearchNodeAction.ID, SearchNodeAction.LABEL, constants.searchIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -491,7 +498,7 @@ export class OpenPlanFile extends Action {
|
||||
public static Label = localize('executionPlanOpenGraphFile', "Show Query Plan XML"); //TODO: add a contribution point for providers to set this text
|
||||
|
||||
constructor() {
|
||||
super(OpenPlanFile.ID, OpenPlanFile.Label, openPlanFileIconClassNames);
|
||||
super(OpenPlanFile.ID, OpenPlanFile.Label, constants.openPlanFileIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -505,17 +512,17 @@ export class ActionBarToggleTooltip extends Action {
|
||||
public static WHEN_TOOLTIPS_DISABLED_LABEL = localize('executionPlanDisableTooltip', "Tooltips disabled");
|
||||
|
||||
constructor() {
|
||||
super(ActionBarToggleTooltip.ID, ActionBarToggleTooltip.WHEN_TOOLTIPS_ENABLED_LABEL, enableTooltipIconClassName);
|
||||
super(ActionBarToggleTooltip.ID, ActionBarToggleTooltip.WHEN_TOOLTIPS_ENABLED_LABEL, constants.enableTooltipIconClassName);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
const state = context.executionPlanDiagram.toggleTooltip();
|
||||
if (!state) {
|
||||
this.class = disableTooltipIconClassName;
|
||||
this.class = constants.disableTooltipIconClassName;
|
||||
this.label = ActionBarToggleTooltip.WHEN_TOOLTIPS_DISABLED_LABEL;
|
||||
context.contextMenuToggleTooltipAction.label = ContextMenuTooltipToggle.WHEN_TOOLTIPS_DISABLED_LABEL;
|
||||
} else {
|
||||
this.class = enableTooltipIconClassName;
|
||||
this.class = constants.enableTooltipIconClassName;
|
||||
this.label = ActionBarToggleTooltip.WHEN_TOOLTIPS_ENABLED_LABEL;
|
||||
context.contextMenuToggleTooltipAction.label = ContextMenuTooltipToggle.WHEN_TOOLTIPS_ENABLED_LABEL;
|
||||
}
|
||||
@@ -528,18 +535,18 @@ export class ContextMenuTooltipToggle extends Action {
|
||||
public static WHEN_TOOLTIPS_DISABLED_LABEL = localize('executionPlanContextMenuEnableTooltip', "Enable Tooltips");
|
||||
|
||||
constructor() {
|
||||
super(ContextMenuTooltipToggle.ID, ContextMenuTooltipToggle.WHEN_TOOLTIPS_ENABLED_LABEL, enableTooltipIconClassName);
|
||||
super(ContextMenuTooltipToggle.ID, ContextMenuTooltipToggle.WHEN_TOOLTIPS_ENABLED_LABEL, constants.enableTooltipIconClassName);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
const state = context.executionPlanDiagram.toggleTooltip();
|
||||
if (!state) {
|
||||
this.label = ContextMenuTooltipToggle.WHEN_TOOLTIPS_DISABLED_LABEL;
|
||||
context.actionBarToggleTopTip.class = disableTooltipIconClassName;
|
||||
context.actionBarToggleTopTip.class = constants.disableTooltipIconClassName;
|
||||
context.actionBarToggleTopTip.label = ActionBarToggleTooltip.WHEN_TOOLTIPS_DISABLED_LABEL;
|
||||
} else {
|
||||
this.label = ContextMenuTooltipToggle.WHEN_TOOLTIPS_ENABLED_LABEL;
|
||||
context.actionBarToggleTopTip.class = enableTooltipIconClassName;
|
||||
context.actionBarToggleTopTip.class = constants.enableTooltipIconClassName;
|
||||
context.actionBarToggleTopTip.label = ActionBarToggleTooltip.WHEN_TOOLTIPS_ENABLED_LABEL;
|
||||
}
|
||||
}
|
||||
@@ -552,7 +559,7 @@ export class CompareExecutionPlanAction extends Action {
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(CompareExecutionPlanAction.COMPARE_PLAN, CompareExecutionPlanAction.COMPARE_PLAN, executionPlanCompareIconClassName);
|
||||
super(CompareExecutionPlanAction.COMPARE_PLAN, CompareExecutionPlanAction.COMPARE_PLAN, constants.executionPlanCompareIconClassName);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
@@ -572,10 +579,30 @@ export class TopOperationsAction extends Action {
|
||||
|
||||
constructor
|
||||
() {
|
||||
super(TopOperationsAction.ID, TopOperationsAction.LABEL, executionPlanTopOperations);
|
||||
super(TopOperationsAction.ID, TopOperationsAction.LABEL, constants.executionPlanTopOperations);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
context.openTopOperations();
|
||||
}
|
||||
}
|
||||
|
||||
export class HighlightExpensiveOperationAction extends Action {
|
||||
public static ID = 'ep.highlightExpensiveOperation';
|
||||
public static LABEL = localize('executionPlanHighlightExpensiveOperationAction', 'Highlight Expensive Operation');
|
||||
|
||||
constructor(private source: ExecutionPlanActionSource,
|
||||
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
|
||||
) {
|
||||
super(HighlightExpensiveOperationAction.ID, HighlightExpensiveOperationAction.LABEL, constants.highlightExpensiveOperationClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanView): Promise<void> {
|
||||
this.telemetryService
|
||||
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.HighlightExpensiveOperation)
|
||||
.withAdditionalProperties({ source: this.source })
|
||||
.send();
|
||||
|
||||
context.widgetController.toggleWidget(context._instantiationService.createInstance(HighlightExpensiveOperationWidget, context.widgetController, context.executionPlanDiagram));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user