mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Adds Ability to Search for Nodes in Compared Execution Plans (#20168)
* Adds find node button to comparison plans. * Can search multiple nodes (improve widget UI and initialization) * Adjusts how second plan is added to the node search widget * Adds styling to the find node action bar * Removes unused code * Minor clean up * Cleans up CSS redundancy * Adjusts property names according to access specifiers * Corrects find node behavior to match SSMS * Dependency injects instantiation service * Adds additional property to telemetry event. * Adds undefined to getter return signatures for plans * Adds checks around active execution plan properties * Code review change * Code review changes
This commit is contained in:
@@ -19,18 +19,20 @@ import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticip
|
|||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { addIconClassName, openPropertiesIconClassNames, polygonBorderColor, polygonFillColor, resetZoomIconClassName, splitScreenHorizontallyIconClassName, splitScreenVerticallyIconClassName, zoomInIconClassNames, zoomOutIconClassNames, zoomToFitIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
import { addIconClassName, openPropertiesIconClassNames, polygonBorderColor, polygonFillColor, resetZoomIconClassName, searchIconClassNames, splitScreenHorizontallyIconClassName, splitScreenVerticallyIconClassName, zoomInIconClassNames, zoomOutIconClassNames, zoomToFitIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||||
import { extname } from 'vs/base/common/path';
|
import { extname } from 'vs/base/common/path';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import { InfoBox } from 'sql/workbench/browser/ui/infoBox/infoBox';
|
import { InfoBox } from 'sql/workbench/browser/ui/infoBox/infoBox';
|
||||||
import { LoadingSpinner } from 'sql/base/browser/ui/loadingSpinner/loadingSpinner';
|
import { LoadingSpinner } from 'sql/base/browser/ui/loadingSpinner/loadingSpinner';
|
||||||
import { errorForeground, listHoverBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
import { contrastBorder, editorWidgetBackground, errorForeground, listHoverBackground, textLinkForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||||
import { ExecutionPlanViewHeader } from 'sql/workbench/contrib/executionPlan/browser/executionPlanViewHeader';
|
import { ExecutionPlanViewHeader } from 'sql/workbench/contrib/executionPlan/browser/executionPlanViewHeader';
|
||||||
import { attachSelectBoxStyler } from 'sql/platform/theme/common/styler';
|
import { attachSelectBoxStyler } from 'sql/platform/theme/common/styler';
|
||||||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||||
|
import { ExecutionPlanWidgetController } from 'sql/workbench/contrib/executionPlan/browser/executionPlanWidgetController';
|
||||||
|
import { NodeSearchWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/nodeSearchWidget';
|
||||||
|
|
||||||
export class ExecutionPlanComparisonEditorView {
|
export class ExecutionPlanComparisonEditorView {
|
||||||
|
|
||||||
@@ -45,6 +47,8 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
private _resetZoomAction: Action;
|
private _resetZoomAction: Action;
|
||||||
private _propertiesAction: Action;
|
private _propertiesAction: Action;
|
||||||
private _toggleOrientationAction: Action;
|
private _toggleOrientationAction: Action;
|
||||||
|
private _searchNodeAction: Action;
|
||||||
|
private _searchNodeActionForAddedPlan: Action;
|
||||||
|
|
||||||
private _planComparisonContainer: HTMLElement;
|
private _planComparisonContainer: HTMLElement;
|
||||||
|
|
||||||
@@ -58,6 +62,11 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
private _verticalSash: Sash;
|
private _verticalSash: Sash;
|
||||||
private _orientation: ExecutionPlanCompareOrientation = ExecutionPlanCompareOrientation.Horizontal;
|
private _orientation: ExecutionPlanCompareOrientation = ExecutionPlanCompareOrientation.Horizontal;
|
||||||
|
|
||||||
|
private _topWidgetContainer: HTMLElement;
|
||||||
|
public topWidgetController: ExecutionPlanWidgetController;
|
||||||
|
private _bottomWidgetContainer: HTMLElement;
|
||||||
|
public bottomWidgetController: ExecutionPlanWidgetController;
|
||||||
|
|
||||||
private _placeholderContainer: HTMLElement;
|
private _placeholderContainer: HTMLElement;
|
||||||
private _placeholderInfoboxContainer: HTMLElement;
|
private _placeholderInfoboxContainer: HTMLElement;
|
||||||
private _placeholderInfobox: InfoBox;
|
private _placeholderInfobox: InfoBox;
|
||||||
@@ -77,10 +86,11 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
bottomPolygon: azdata.executionPlan.ExecutionGraphComparisonResult
|
bottomPolygon: azdata.executionPlan.ExecutionGraphComparisonResult
|
||||||
}> = new Map();
|
}> = new Map();
|
||||||
|
|
||||||
private get _activeTopPlanDiagram(): AzdataGraphView {
|
public get activeTopPlanDiagram(): AzdataGraphView | undefined {
|
||||||
if (this.topPlanDiagrams.length > 0) {
|
if (this.topPlanDiagrams.length > 0) {
|
||||||
return this.topPlanDiagrams[this._activeTopPlanIndex];
|
return this.topPlanDiagrams[this._activeTopPlanIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,10 +105,11 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
private _bottomSimilarNode: Map<string, azdata.executionPlan.ExecutionGraphComparisonResult> = new Map();
|
private _bottomSimilarNode: Map<string, azdata.executionPlan.ExecutionGraphComparisonResult> = new Map();
|
||||||
private _latestRequestUuid: string;
|
private _latestRequestUuid: string;
|
||||||
|
|
||||||
private get _activeBottomPlanDiagram(): AzdataGraphView {
|
public get activeBottomPlanDiagram(): AzdataGraphView | undefined {
|
||||||
if (this.bottomPlanDiagrams.length > 0) {
|
if (this.bottomPlanDiagrams.length > 0) {
|
||||||
return this.bottomPlanDiagrams[this._activeBottomPlanIndex];
|
return this.bottomPlanDiagrams[this._activeBottomPlanIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +119,7 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parentContainer: HTMLElement,
|
parentContainer: HTMLElement,
|
||||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||||
@IThemeService private themeService: IThemeService,
|
@IThemeService private themeService: IThemeService,
|
||||||
@IExecutionPlanService private _executionPlanService: IExecutionPlanService,
|
@IExecutionPlanService private _executionPlanService: IExecutionPlanService,
|
||||||
@IFileDialogService private _fileDialogService: IFileDialogService,
|
@IFileDialogService private _fileDialogService: IFileDialogService,
|
||||||
@@ -139,6 +150,8 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this._zoomToFitAction = new ZoomToFitAction();
|
this._zoomToFitAction = new ZoomToFitAction();
|
||||||
this._propertiesAction = this._instantiationService.createInstance(PropertiesAction);
|
this._propertiesAction = this._instantiationService.createInstance(PropertiesAction);
|
||||||
this._toggleOrientationAction = new ToggleOrientation();
|
this._toggleOrientationAction = new ToggleOrientation();
|
||||||
|
this._searchNodeAction = this._instantiationService.createInstance(SearchNodeAction, PlanIdentifier.Primary);
|
||||||
|
this._searchNodeActionForAddedPlan = this._instantiationService.createInstance(SearchNodeAction, PlanIdentifier.Added);
|
||||||
this._resetZoomAction = new ZoomReset();
|
this._resetZoomAction = new ZoomReset();
|
||||||
const content: ITaskbarContent[] = [
|
const content: ITaskbarContent[] = [
|
||||||
{ action: this._addExecutionPlanAction },
|
{ action: this._addExecutionPlanAction },
|
||||||
@@ -147,7 +160,9 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
{ action: this._zoomToFitAction },
|
{ action: this._zoomToFitAction },
|
||||||
{ action: this._resetZoomAction },
|
{ action: this._resetZoomAction },
|
||||||
{ action: this._toggleOrientationAction },
|
{ action: this._toggleOrientationAction },
|
||||||
{ action: this._propertiesAction }
|
{ action: this._propertiesAction },
|
||||||
|
{ action: this._searchNodeAction },
|
||||||
|
{ action: this._searchNodeActionForAddedPlan }
|
||||||
];
|
];
|
||||||
this._taskbar.setContent(content);
|
this._taskbar.setContent(content);
|
||||||
this.container.appendChild(this._taskbarContainer);
|
this.container.appendChild(this._taskbarContainer);
|
||||||
@@ -158,6 +173,7 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this.container.appendChild(this._planComparisonContainer);
|
this.container.appendChild(this._planComparisonContainer);
|
||||||
this.initializeSplitView();
|
this.initializeSplitView();
|
||||||
this.initializeProperties();
|
this.initializeProperties();
|
||||||
|
this.initializeWidgetControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeSplitView(): void {
|
private initializeSplitView(): void {
|
||||||
@@ -185,10 +201,9 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this._topPlanDropdown = new SelectBox(['option 1', 'option2'], 'option1', this.contextViewService, this._topPlanDropdownContainer);
|
this._topPlanDropdown = new SelectBox(['option 1', 'option2'], 'option1', this.contextViewService, this._topPlanDropdownContainer);
|
||||||
this._topPlanDropdown.render(this._topPlanDropdownContainer);
|
this._topPlanDropdown.render(this._topPlanDropdownContainer);
|
||||||
this._topPlanDropdown.onDidSelect(async (e) => {
|
this._topPlanDropdown.onDidSelect(async (e) => {
|
||||||
if (this._activeBottomPlanDiagram) {
|
this.activeBottomPlanDiagram?.clearSubtreePolygon();
|
||||||
this._activeBottomPlanDiagram.clearSubtreePolygon();
|
this.activeTopPlanDiagram?.clearSubtreePolygon();
|
||||||
}
|
|
||||||
this._activeTopPlanDiagram.clearSubtreePolygon();
|
|
||||||
this._topPlanDiagramContainers.forEach(c => {
|
this._topPlanDiagramContainers.forEach(c => {
|
||||||
c.style.display = 'none';
|
c.style.display = 'none';
|
||||||
});
|
});
|
||||||
@@ -211,10 +226,9 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this._bottomPlanDropdown = new SelectBox(['option 1', 'option2'], 'option1', this.contextViewService, this._bottomPlanDropdownContainer);
|
this._bottomPlanDropdown = new SelectBox(['option 1', 'option2'], 'option1', this.contextViewService, this._bottomPlanDropdownContainer);
|
||||||
this._bottomPlanDropdown.render(this._bottomPlanDropdownContainer);
|
this._bottomPlanDropdown.render(this._bottomPlanDropdownContainer);
|
||||||
this._bottomPlanDropdown.onDidSelect(async (e) => {
|
this._bottomPlanDropdown.onDidSelect(async (e) => {
|
||||||
this._activeBottomPlanDiagram.clearSubtreePolygon();
|
this.activeBottomPlanDiagram?.clearSubtreePolygon();
|
||||||
if (this._activeTopPlanDiagram) {
|
this.activeTopPlanDiagram?.clearSubtreePolygon();
|
||||||
this._activeTopPlanDiagram.clearSubtreePolygon();
|
|
||||||
}
|
|
||||||
this._bottomPlanDiagramContainers.forEach(c => {
|
this._bottomPlanDiagramContainers.forEach(c => {
|
||||||
c.style.display = 'none';
|
c.style.display = 'none';
|
||||||
});
|
});
|
||||||
@@ -273,6 +287,16 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this._planComparisonContainer.appendChild(this._propertiesContainer);
|
this._planComparisonContainer.appendChild(this._propertiesContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initializeWidgetControllers(): void {
|
||||||
|
this._topWidgetContainer = DOM.$('.plan-action-container');
|
||||||
|
this._topPlanContainer.appendChild(this._topWidgetContainer);
|
||||||
|
this.topWidgetController = new ExecutionPlanWidgetController(this._topWidgetContainer);
|
||||||
|
|
||||||
|
this._bottomWidgetContainer = DOM.$('.plan-action-container');
|
||||||
|
this._bottomPlanContainer.appendChild(this._bottomWidgetContainer);
|
||||||
|
this.bottomWidgetController = new ExecutionPlanWidgetController(this._bottomWidgetContainer);
|
||||||
|
}
|
||||||
|
|
||||||
public async openAndAddExecutionPlanFile(): Promise<void> {
|
public async openAndAddExecutionPlanFile(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const openedFileUris = await this._fileDialogService.showOpenDialog({
|
const openedFileUris = await this._fileDialogService.showOpenDialog({
|
||||||
@@ -325,11 +349,15 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
const id = e.id.replace(`element-`, '');
|
const id = e.id.replace(`element-`, '');
|
||||||
if (this._topSimilarNode.has(id)) {
|
if (this._topSimilarNode.has(id)) {
|
||||||
const similarNode = this._topSimilarNode.get(id);
|
const similarNode = this._topSimilarNode.get(id);
|
||||||
const element = this._activeBottomPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
|
||||||
if (similarNode.matchingNodesId.find(m => this._activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
if (this.activeBottomPlanDiagram) {
|
||||||
return;
|
const element = this.activeBottomPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
||||||
|
if (similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeBottomPlanDiagram.selectElement(element);
|
||||||
}
|
}
|
||||||
this._activeBottomPlanDiagram.selectElement(element);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.topPlanDiagrams.push(diagram);
|
this.topPlanDiagrams.push(diagram);
|
||||||
@@ -343,6 +371,7 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this._resetZoomAction.enabled = true;
|
this._resetZoomAction.enabled = true;
|
||||||
this._zoomToFitAction.enabled = true;
|
this._zoomToFitAction.enabled = true;
|
||||||
this._toggleOrientationAction.enabled = true;
|
this._toggleOrientationAction.enabled = true;
|
||||||
|
this._searchNodeAction.enabled = true;
|
||||||
} else {
|
} else {
|
||||||
this._bottomPlanDiagramModels = executionPlanGraphs;
|
this._bottomPlanDiagramModels = executionPlanGraphs;
|
||||||
this._bottomPlanDropdown.setOptions(executionPlanGraphs.map((e, index) => {
|
this._bottomPlanDropdown.setOptions(executionPlanGraphs.map((e, index) => {
|
||||||
@@ -360,11 +389,15 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
const id = e.id.replace(`element-`, '');
|
const id = e.id.replace(`element-`, '');
|
||||||
if (this._bottomSimilarNode.has(id)) {
|
if (this._bottomSimilarNode.has(id)) {
|
||||||
const similarNode = this._bottomSimilarNode.get(id);
|
const similarNode = this._bottomSimilarNode.get(id);
|
||||||
const element = this._activeTopPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
|
||||||
if (similarNode.matchingNodesId.find(m => this._activeTopPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
if (this.activeTopPlanDiagram) {
|
||||||
return;
|
const element = this.activeTopPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
||||||
|
if (similarNode.matchingNodesId.find(m => this.activeTopPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeTopPlanDiagram.selectElement(element);
|
||||||
}
|
}
|
||||||
this._activeTopPlanDiagram.selectElement(element);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.bottomPlanDiagrams.push(diagram);
|
this.bottomPlanDiagrams.push(diagram);
|
||||||
@@ -373,15 +406,17 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this._bottomPlanDropdown.select(preSelectIndex);
|
this._bottomPlanDropdown.select(preSelectIndex);
|
||||||
this._propertiesView.setBottomElement(executionPlanGraphs[0].root);
|
this._propertiesView.setBottomElement(executionPlanGraphs[0].root);
|
||||||
this._addExecutionPlanAction.enabled = false;
|
this._addExecutionPlanAction.enabled = false;
|
||||||
|
this._searchNodeActionForAddedPlan.enabled = true;
|
||||||
}
|
}
|
||||||
this.refreshSplitView();
|
this.refreshSplitView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSkeletonNodes(): Promise<void> {
|
private async getSkeletonNodes(): Promise<void> {
|
||||||
if (!this._activeBottomPlanDiagram) {
|
if (!this.activeBottomPlanDiagram) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._progressService.withProgress(
|
|
||||||
|
await this._progressService.withProgress(
|
||||||
{
|
{
|
||||||
location: ProgressLocation.Notification,
|
location: ProgressLocation.Notification,
|
||||||
title: localize('epCompare.comparisonProgess', "Loading similar areas in compared plans"),
|
title: localize('epCompare.comparisonProgess', "Loading similar areas in compared plans"),
|
||||||
@@ -406,9 +441,11 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
this.getSimilarSubtrees(result.secondComparisonResult, true);
|
this.getSimilarSubtrees(result.secondComparisonResult, true);
|
||||||
let colorIndex = 0;
|
let colorIndex = 0;
|
||||||
this._polygonRootsMap.forEach((v, k) => {
|
this._polygonRootsMap.forEach((v, k) => {
|
||||||
this._activeTopPlanDiagram.drawSubtreePolygon(v.topPolygon.baseNode.id, polygonFillColor[colorIndex], polygonBorderColor[colorIndex]);
|
if (this.activeTopPlanDiagram && this.activeBottomPlanDiagram) {
|
||||||
this._activeBottomPlanDiagram.drawSubtreePolygon(v.bottomPolygon.baseNode.id, polygonFillColor[colorIndex], polygonBorderColor[colorIndex]);
|
this.activeTopPlanDiagram.drawSubtreePolygon(v.topPolygon.baseNode.id, polygonFillColor[colorIndex], polygonBorderColor[colorIndex]);
|
||||||
colorIndex += 1;
|
this.activeBottomPlanDiagram.drawSubtreePolygon(v.bottomPolygon.baseNode.id, polygonFillColor[colorIndex], polygonBorderColor[colorIndex]);
|
||||||
|
colorIndex += 1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -500,37 +537,40 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public zoomIn(): void {
|
public zoomIn(): void {
|
||||||
this._activeTopPlanDiagram.zoomIn();
|
this.activeTopPlanDiagram?.zoomIn();
|
||||||
this._activeBottomPlanDiagram.zoomIn();
|
this.activeBottomPlanDiagram?.zoomIn();
|
||||||
|
|
||||||
this.syncZoom();
|
this.syncZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
public zoomOut(): void {
|
public zoomOut(): void {
|
||||||
this._activeTopPlanDiagram.zoomOut();
|
this.activeTopPlanDiagram?.zoomOut();
|
||||||
this._activeBottomPlanDiagram.zoomOut();
|
this.activeBottomPlanDiagram?.zoomOut();
|
||||||
|
|
||||||
this.syncZoom();
|
this.syncZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
public zoomToFit(): void {
|
public zoomToFit(): void {
|
||||||
this._activeTopPlanDiagram.zoomToFit();
|
this.activeTopPlanDiagram?.zoomToFit();
|
||||||
this._activeBottomPlanDiagram.zoomToFit();
|
this.activeBottomPlanDiagram.zoomToFit();
|
||||||
|
|
||||||
this.syncZoom();
|
this.syncZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetZoom(): void {
|
public resetZoom(): void {
|
||||||
if (this._activeTopPlanDiagram) {
|
this.activeTopPlanDiagram?.setZoomLevel(100);
|
||||||
this._activeTopPlanDiagram.setZoomLevel(100);
|
this.activeBottomPlanDiagram?.setZoomLevel(100);
|
||||||
}
|
|
||||||
if (this._activeBottomPlanDiagram) {
|
|
||||||
this._activeBottomPlanDiagram.setZoomLevel(100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private syncZoom(): void {
|
private syncZoom(): void {
|
||||||
if (this._activeTopPlanDiagram.getZoomLevel() < this._activeBottomPlanDiagram.getZoomLevel()) {
|
if (this.activeTopPlanDiagram === undefined && this.activeBottomPlanDiagram === undefined) {
|
||||||
this._activeBottomPlanDiagram.setZoomLevel(this._activeTopPlanDiagram.getZoomLevel());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.activeTopPlanDiagram.getZoomLevel() < this.activeBottomPlanDiagram.getZoomLevel()) {
|
||||||
|
this.activeBottomPlanDiagram.setZoomLevel(this.activeTopPlanDiagram.getZoomLevel());
|
||||||
} else {
|
} else {
|
||||||
this._activeTopPlanDiagram.setZoomLevel(this._activeBottomPlanDiagram.getZoomLevel());
|
this.activeTopPlanDiagram.setZoomLevel(this.activeBottomPlanDiagram.getZoomLevel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -640,6 +680,41 @@ class PropertiesAction extends Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PlanIdentifier {
|
||||||
|
Primary = 0,
|
||||||
|
Added = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
class SearchNodeAction extends Action {
|
||||||
|
public static ID = 'epCompare.searchNodeAction';
|
||||||
|
public static LABEL = localize('epCompare.searchNodeAction', 'Find Node');
|
||||||
|
public static LABEL_FOR_ADDED_PLAN = localize('epCompare.searchNodeActionAddedPlan', 'Find Node - Added Plan');
|
||||||
|
|
||||||
|
constructor(private readonly _planIdentifier: PlanIdentifier, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IAdsTelemetryService private readonly _telemetryService: IAdsTelemetryService) {
|
||||||
|
const getLabelForAction = () => {
|
||||||
|
return _planIdentifier === PlanIdentifier.Added ? SearchNodeAction.LABEL_FOR_ADDED_PLAN : SearchNodeAction.LABEL;
|
||||||
|
};
|
||||||
|
|
||||||
|
super(SearchNodeAction.ID, getLabelForAction(), searchIconClassNames);
|
||||||
|
this.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async run(context: ExecutionPlanComparisonEditorView): Promise<void> {
|
||||||
|
let executionPlan = this._planIdentifier === PlanIdentifier.Added ? context.activeBottomPlanDiagram : context.activeTopPlanDiagram;
|
||||||
|
let widgetController = this._planIdentifier === PlanIdentifier.Added ? context.bottomWidgetController : context.topWidgetController;
|
||||||
|
|
||||||
|
if (executionPlan) {
|
||||||
|
this._telemetryService
|
||||||
|
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.FindNode)
|
||||||
|
.withAdditionalProperties({ source: 'ComparisonView' })
|
||||||
|
.send();
|
||||||
|
|
||||||
|
let nodeSearchWidget = this._instantiationService.createInstance(NodeSearchWidget, widgetController, executionPlan);
|
||||||
|
widgetController.toggleWidget(nodeSearchWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HorizontalSash implements IHorizontalSashLayoutProvider {
|
class HorizontalSash implements IHorizontalSashLayoutProvider {
|
||||||
constructor(private _context: ExecutionPlanComparisonEditorView) {
|
constructor(private _context: ExecutionPlanComparisonEditorView) {
|
||||||
}
|
}
|
||||||
@@ -695,4 +770,28 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
const shadow = theme.getColor(widgetShadow);
|
||||||
|
if (shadow) {
|
||||||
|
collector.addRule(`
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .child {
|
||||||
|
box-shadow: 0 0 8px 2px ${shadow};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
const widgetBackgroundColor = theme.getColor(editorWidgetBackground);
|
||||||
|
if (widgetBackgroundColor) {
|
||||||
|
collector.addRule(`
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .child {
|
||||||
|
background-color: ${widgetBackgroundColor};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
const widgetBorderColor = theme.getColor(contrastBorder);
|
||||||
|
if (widgetBorderColor) {
|
||||||
|
collector.addRule(`
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .child {
|
||||||
|
border: 1px solid ${widgetBorderColor};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,13 +55,15 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* views created by the action-bar actions */
|
/* views created by the action-bar actions */
|
||||||
.eps-container .execution-plan .plan .plan-action-container .child {
|
.eps-container .execution-plan .plan .plan-action-container .child,
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .child {
|
||||||
flex: 0 0 25px;
|
flex: 0 0 25px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search node action view */
|
/* Search node action view */
|
||||||
.eps-container .execution-plan .plan .plan-action-container .search-node-widget {
|
.eps-container .execution-plan .plan .plan-action-container .search-node-widget,
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .search-node-widget {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -70,12 +72,14 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* input bar styling in search node action view */
|
/* input bar styling in search node action view */
|
||||||
.eps-container .execution-plan .plan .plan-action-container .search-node-widget .select-container {
|
.eps-container .execution-plan .plan .plan-action-container .search-node-widget .select-container,
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .search-node-widget .select-container {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* styling for select element in search node action view */
|
/* styling for select element in search node action view */
|
||||||
.eps-container .execution-plan .plan .plan-action-container .search-node-widget .select-container>select {
|
.eps-container .execution-plan .plan .plan-action-container .search-node-widget .select-container>select,
|
||||||
|
.eps-container .comparison-editor .plan-comparison-container .split-view-container .plan-container .plan-action-container .search-node-widget .select-container>select {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,17 +44,16 @@ export class NodeSearchWidget extends ExecutionPlanWidgetBase {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly planActionView: ExecutionPlanWidgetController,
|
public readonly planActionView: ExecutionPlanWidgetController,
|
||||||
public readonly executionPlanDiagram: AzdataGraphView,
|
private readonly _executionPlanDiagram: AzdataGraphView,
|
||||||
@IContextViewService public readonly contextViewService: IContextViewService,
|
@IContextViewService public readonly contextViewService: IContextViewService,
|
||||||
@IThemeService public readonly themeService: IThemeService
|
@IThemeService public readonly themeService: IThemeService
|
||||||
|
|
||||||
) {
|
) {
|
||||||
super(DOM.$('.search-node-widget'), 'searchWidget');
|
super(DOM.$('.search-node-widget'), 'searchWidget');
|
||||||
|
|
||||||
// property name dropdown
|
// property name dropdown
|
||||||
this._propertyNameSelectBoxContainer = DOM.$('.search-widget-property-name-select-box .dropdown-container');
|
this._propertyNameSelectBoxContainer = DOM.$('.search-widget-property-name-select-box .dropdown-container');
|
||||||
this.container.appendChild(this._propertyNameSelectBoxContainer);
|
this.container.appendChild(this._propertyNameSelectBoxContainer);
|
||||||
const propDropdownOptions = executionPlanDiagram.getUniqueElementProperties();
|
const propDropdownOptions = this._executionPlanDiagram.getUniqueElementProperties();
|
||||||
this._propertyNameSelectBox = new SelectBox(propDropdownOptions, propDropdownOptions[0], this.contextViewService, this._propertyNameSelectBoxContainer);
|
this._propertyNameSelectBox = new SelectBox(propDropdownOptions, propDropdownOptions[0], this.contextViewService, this._propertyNameSelectBoxContainer);
|
||||||
attachSelectBoxStyler(this._propertyNameSelectBox, this.themeService);
|
attachSelectBoxStyler(this._propertyNameSelectBox, this.themeService);
|
||||||
this._propertyNameSelectBoxContainer.style.width = '150px';
|
this._propertyNameSelectBoxContainer.style.width = '150px';
|
||||||
@@ -140,11 +139,12 @@ export class NodeSearchWidget extends ExecutionPlanWidgetBase {
|
|||||||
|
|
||||||
public searchNodes(): void {
|
public searchNodes(): void {
|
||||||
this._currentSearchResultIndex = 0;
|
this._currentSearchResultIndex = 0;
|
||||||
this._searchResults = this.executionPlanDiagram.searchNodes({
|
this._searchResults = this._executionPlanDiagram.searchNodes({
|
||||||
propertyName: this._propertyNameSelectBox.value,
|
propertyName: this._propertyNameSelectBox.value,
|
||||||
value: this._searchTextInputBox.value,
|
value: this._searchTextInputBox.value,
|
||||||
searchType: this._selectedSearchType
|
searchType: this._selectedSearchType
|
||||||
});
|
});
|
||||||
|
|
||||||
this._usePreviousSearchResult = true;
|
this._usePreviousSearchResult = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,8 +153,8 @@ export class NodeSearchWidget extends ExecutionPlanWidgetBase {
|
|||||||
this.searchNodes();
|
this.searchNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executionPlanDiagram.centerElement(this._searchResults[this._currentSearchResultIndex]);
|
this._executionPlanDiagram.centerElement(this._searchResults[this._currentSearchResultIndex]);
|
||||||
this.executionPlanDiagram.selectElement(this._searchResults[this._currentSearchResultIndex]);
|
this._executionPlanDiagram.selectElement(this._searchResults[this._currentSearchResultIndex]);
|
||||||
this._currentSearchResultIndex = this._currentSearchResultIndex === this._searchResults.length - 1 ?
|
this._currentSearchResultIndex = this._currentSearchResultIndex === this._searchResults.length - 1 ?
|
||||||
this._currentSearchResultIndex = 0 :
|
this._currentSearchResultIndex = 0 :
|
||||||
this._currentSearchResultIndex = ++this._currentSearchResultIndex;
|
this._currentSearchResultIndex = ++this._currentSearchResultIndex;
|
||||||
@@ -165,8 +165,8 @@ export class NodeSearchWidget extends ExecutionPlanWidgetBase {
|
|||||||
this.searchNodes();
|
this.searchNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executionPlanDiagram.centerElement(this._searchResults[this._currentSearchResultIndex]);
|
this._executionPlanDiagram.centerElement(this._searchResults[this._currentSearchResultIndex]);
|
||||||
this.executionPlanDiagram.selectElement(this._searchResults[this._currentSearchResultIndex]);
|
this._executionPlanDiagram.selectElement(this._searchResults[this._currentSearchResultIndex]);
|
||||||
this._currentSearchResultIndex = this._currentSearchResultIndex === 0 ?
|
this._currentSearchResultIndex = this._currentSearchResultIndex === 0 ?
|
||||||
this._currentSearchResultIndex = this._searchResults.length - 1 :
|
this._currentSearchResultIndex = this._searchResults.length - 1 :
|
||||||
this._currentSearchResultIndex = --this._currentSearchResultIndex;
|
this._currentSearchResultIndex = --this._currentSearchResultIndex;
|
||||||
|
|||||||
Reference in New Issue
Block a user