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:
Lewis Sanchez
2022-07-28 14:14:32 -07:00
committed by GitHub
parent 489f5f359f
commit 9fc251259c
3 changed files with 157 additions and 54 deletions

View File

@@ -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};
}
`);
}
}); });

View File

@@ -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%;
} }

View File

@@ -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;