mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Execution Plan Filtering and UI improvements (#20631)
* Fixing execution plan stuff * Adding filter to top operations * Flipped custom zoom icon * changing keys * Moving constants to a file * Search properties * Making logic concise
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import 'vs/css!./media/actionBar';
|
||||||
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
|
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||||
|
|||||||
10
src/sql/base/browser/ui/taskbar/media/actionBar.css
Normal file
10
src/sql/base/browser/ui/taskbar/media/actionBar.css
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.monaco-action-bar.vertical .action-item .action-label.separator {
|
||||||
|
width: 20px;
|
||||||
|
height: 1px;
|
||||||
|
margin: 5px 2px !important;
|
||||||
|
}
|
||||||
@@ -70,10 +70,13 @@ export class AzdataGraphView {
|
|||||||
this.onElementSelected = this._onElementSelectedEmitter.event;
|
this.onElementSelected = this._onElementSelectedEmitter.event;
|
||||||
this._diagram.graph.getSelectionModel().addListener('change', (sender, evt) => {
|
this._diagram.graph.getSelectionModel().addListener('change', (sender, evt) => {
|
||||||
if (evt.properties?.removed) {
|
if (evt.properties?.removed) {
|
||||||
if (this._cellInFocus?.id === evt.properties.removed[0].id) {
|
if (this._cellInFocus?.id === evt.properties.removed[0]?.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newSelection = evt.properties.removed[0];
|
const newSelection = evt.properties.removed[0];
|
||||||
|
if (!newSelection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._onElementSelectedEmitter.fire(this.getElementById(newSelection.id));
|
this._onElementSelectedEmitter.fire(this.getElementById(newSelection.id));
|
||||||
this.centerElement(this.getElementById(newSelection.id));
|
this.centerElement(this.getElementById(newSelection.id));
|
||||||
this._cellInFocus = evt.properties.removed[0];
|
this._cellInFocus = evt.properties.removed[0];
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
|
||||||
let imageBasePath = require.toUrl('./images/icons/');
|
let imageBasePath = require.toUrl('./images/icons/');
|
||||||
export let executionPlanNodeIconPaths =
|
export let executionPlanNodeIconPaths =
|
||||||
{
|
{
|
||||||
@@ -366,3 +368,8 @@ export const polygonFillColor: string[] = [
|
|||||||
`rgba(70, 104, 197, 0.1)`, // "f2 blue purple"
|
`rgba(70, 104, 197, 0.1)`, // "f2 blue purple"
|
||||||
`rgba(226, 229, 132, 0.1)`, // "j2 khaki"
|
`rgba(226, 229, 132, 0.1)`, // "j2 khaki"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
//constant strings
|
||||||
|
export const propertiesSearchDescription = localize('ep.propertiesSearchDescription', 'Search properties table');
|
||||||
|
export const topOperationsSearchDescription = localize('ep.topOperationsSearchDescription', 'Search top operations');
|
||||||
|
export const searchPlaceholder = localize('ep.searchPlaceholder', 'Filter for any field...');
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ export class ExecutionPlanComparisonEditorView {
|
|||||||
|
|
||||||
if (this.activeBottomPlanDiagram) {
|
if (this.activeBottomPlanDiagram) {
|
||||||
const element = this.activeBottomPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
const element = this.activeBottomPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
||||||
if (this.activeBottomPlanDiagram.getSelectedElement() && similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
if (similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { InternalExecutionPlanElement } from 'sql/workbench/contrib/executionPla
|
|||||||
import { executionPlanComparisonPropertiesDifferent, executionPlanComparisonPropertiesUpArrow, executionPlanComparisonPropertiesDownArrow } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
import { executionPlanComparisonPropertiesDifferent, executionPlanComparisonPropertiesUpArrow, executionPlanComparisonPropertiesDownArrow } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||||
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { TextWithIconColumn } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
|
import { TextWithIconColumn } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
export enum ExecutionPlanCompareOrientation {
|
export enum ExecutionPlanCompareOrientation {
|
||||||
@@ -56,9 +56,10 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
parentContainer: HTMLElement,
|
parentContainer: HTMLElement,
|
||||||
@IThemeService themeService: IThemeService,
|
@IThemeService themeService: IThemeService,
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@IContextMenuService contextMenuService: IContextMenuService
|
@IContextMenuService contextMenuService: IContextMenuService,
|
||||||
|
@IContextViewService contextViewService: IContextViewService
|
||||||
) {
|
) {
|
||||||
super(parentContainer, themeService, instantiationService, contextMenuService);
|
super(parentContainer, themeService, instantiationService, contextMenuService, contextViewService);
|
||||||
this._model = <ExecutionPlanComparisonPropertiesViewModel>{};
|
this._model = <ExecutionPlanComparisonPropertiesViewModel>{};
|
||||||
this._parentContainer.style.display = 'none';
|
this._parentContainer.style.display = 'none';
|
||||||
const header = DOM.$('.compare-operation-name');
|
const header = DOM.$('.compare-operation-name');
|
||||||
@@ -133,8 +134,7 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
if (this._model.secondaryElement?.properties) {
|
if (this._model.secondaryElement?.properties) {
|
||||||
secondaryProps = this._model.secondaryElement.properties;
|
secondaryProps = this._model.secondaryElement.properties;
|
||||||
}
|
}
|
||||||
|
this.populateTable(columns, this.convertPropertiesToTableRows(primaryProps, secondaryProps));
|
||||||
this.populateTable(columns, this.convertPropertiesToTableRows(primaryProps, secondaryProps, -1, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPropertyTableColumns() {
|
private getPropertyTableColumns() {
|
||||||
@@ -212,7 +212,8 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertPropertiesToTableRows(primaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[], secondaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[], parentIndex: number, indent: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
private convertPropertiesToTableRows(primaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[], secondaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): { [key: string]: string }[] {
|
||||||
|
const rows: { [key: string]: string }[] = [];
|
||||||
let propertiesMap: Map<string, TablePropertiesMapEntry> = new Map();
|
let propertiesMap: Map<string, TablePropertiesMapEntry> = new Map();
|
||||||
|
|
||||||
if (primaryNode) {
|
if (primaryNode) {
|
||||||
@@ -258,7 +259,6 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
row['name'] = {
|
row['name'] = {
|
||||||
text: k
|
text: k
|
||||||
};
|
};
|
||||||
row['parent'] = parentIndex;
|
|
||||||
|
|
||||||
const primaryProp = v.primaryProp;
|
const primaryProp = v.primaryProp;
|
||||||
const secondaryProp = v.secondaryProp;
|
const secondaryProp = v.secondaryProp;
|
||||||
@@ -294,13 +294,10 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
row['secondary'].iconCssClass += ` parent-row-styling`;
|
row['secondary'].iconCssClass += ` parent-row-styling`;
|
||||||
}
|
}
|
||||||
rows.push(row);
|
rows.push(row);
|
||||||
if (!isString(primaryProp.value) && !isString(secondaryProp.value)) {
|
const topPropValue = isString(primaryProp.value) ? undefined : primaryProp.value;
|
||||||
this.convertPropertiesToTableRows(primaryProp.value, secondaryProp.value, rows.length - 1, indent + 2, rows);
|
const bottomPropValue = isString(secondaryProp.value) ? undefined : secondaryProp.value;
|
||||||
} else if (isString(primaryProp?.value) && !isString(secondaryProp.value)) {
|
row['treeGridChildren'] = this.convertPropertiesToTableRows(topPropValue, bottomPropValue);
|
||||||
this.convertPropertiesToTableRows(undefined, secondaryProp.value, rows.length - 1, indent + 2, rows);
|
|
||||||
} else if (!isString(primaryProp.value) && !isString(secondaryProp.value)) {
|
|
||||||
this.convertPropertiesToTableRows(primaryProp.value, undefined, rows.length - 1, indent + 2, rows);
|
|
||||||
}
|
|
||||||
} else if (primaryProp && !secondaryProp) {
|
} else if (primaryProp && !secondaryProp) {
|
||||||
row['displayOrder'] = v.primaryProp.displayOrder;
|
row['displayOrder'] = v.primaryProp.displayOrder;
|
||||||
row['primary'] = {
|
row['primary'] = {
|
||||||
@@ -310,7 +307,7 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
if (!isString(primaryProp.value)) {
|
if (!isString(primaryProp.value)) {
|
||||||
row['name'].iconCssClass += ` parent-row-styling`;
|
row['name'].iconCssClass += ` parent-row-styling`;
|
||||||
row['primary'].iconCssClass += ` parent-row-styling`;
|
row['primary'].iconCssClass += ` parent-row-styling`;
|
||||||
this.convertPropertiesToTableRows(primaryProp.value, undefined, rows.length - 1, indent + 2, rows);
|
row['treeGridChildren'] = this.convertPropertiesToTableRows(primaryProp.value, undefined);
|
||||||
}
|
}
|
||||||
} else if (!primaryProp && secondaryProp) {
|
} else if (!primaryProp && secondaryProp) {
|
||||||
row['displayOrder'] = v.secondaryProp.displayOrder;
|
row['displayOrder'] = v.secondaryProp.displayOrder;
|
||||||
@@ -322,12 +319,11 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
|||||||
if (!isString(secondaryProp.value)) {
|
if (!isString(secondaryProp.value)) {
|
||||||
row['name'].iconCssClass += ` parent-row-styling`;
|
row['name'].iconCssClass += ` parent-row-styling`;
|
||||||
row['secondary'].iconCssClass += ` parent-row-styling`;
|
row['secondary'].iconCssClass += ` parent-row-styling`;
|
||||||
this.convertPropertiesToTableRows(undefined, secondaryProp.value, rows.length - 1, indent + 2, rows);
|
row['treeGridChildren'] = this.convertPropertiesToTableRows(undefined, secondaryProp.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { removeLineBreaks } from 'sql/base/common/strings';
|
|||||||
import { isString } from 'vs/base/common/types';
|
import { isString } from 'vs/base/common/types';
|
||||||
import { textFormatter } from 'sql/base/browser/ui/table/formatters';
|
import { textFormatter } from 'sql/base/browser/ui/table/formatters';
|
||||||
import { ExecutionPlanPropertiesViewBase, PropertiesSortType } from 'sql/workbench/contrib/executionPlan/browser/executionPlanPropertiesViewBase';
|
import { ExecutionPlanPropertiesViewBase, PropertiesSortType } from 'sql/workbench/contrib/executionPlan/browser/executionPlanPropertiesViewBase';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase {
|
export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase {
|
||||||
@@ -24,8 +24,9 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
|||||||
@IThemeService themeService: IThemeService,
|
@IThemeService themeService: IThemeService,
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@IContextMenuService contextMenuService: IContextMenuService,
|
@IContextMenuService contextMenuService: IContextMenuService,
|
||||||
|
@IContextViewService contextViewService: IContextViewService
|
||||||
) {
|
) {
|
||||||
super(parentContainer, themeService, instantiationService, contextMenuService);
|
super(parentContainer, themeService, instantiationService, contextMenuService, contextViewService);
|
||||||
this._model = <ExecutionPlanPropertiesView>{};
|
this._model = <ExecutionPlanPropertiesView>{};
|
||||||
this._operationName = DOM.$('h3');
|
this._operationName = DOM.$('h3');
|
||||||
this._operationName.classList.add('operation-name');
|
this._operationName.classList.add('operation-name');
|
||||||
@@ -108,14 +109,38 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
this.populateTable(columns, this.convertModelToTableRows(this._model.graphElement.properties, -1, 0));
|
this.populateTable(columns, this.convertPropertiesToTableRows(this._model.graphElement?.properties));
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertModelToTableRows(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[], parentIndex: number, indent: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
private convertPropertiesToTableRows(properties: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): Slick.SlickData[] {
|
||||||
if (!props) {
|
if (!properties) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const sortedProperties = this.sortProperties(properties);
|
||||||
|
const rows: Slick.SlickData[] = [];
|
||||||
|
sortedProperties.forEach((property, index) => {
|
||||||
|
let row = {};
|
||||||
|
row['name'] = property.name;
|
||||||
|
if (!isString(property.value)) {
|
||||||
|
// Styling values in the parent row differently to make them more apparent and standout compared to the rest of the cells.
|
||||||
|
row['name'] = {
|
||||||
|
text: row['name']
|
||||||
|
};
|
||||||
|
row['value'] = {
|
||||||
|
text: removeLineBreaks(property.displayValue, ' ')
|
||||||
|
};
|
||||||
|
row['tootltip'] = property.displayValue;
|
||||||
|
row['treeGridChildren'] = this.convertPropertiesToTableRows(property.value);
|
||||||
|
} else {
|
||||||
|
row['value'] = removeLineBreaks(property.displayValue, ' ');
|
||||||
|
row['tooltip'] = property.displayValue;
|
||||||
|
}
|
||||||
|
rows.push(row);
|
||||||
|
});
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sortProperties(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): azdata.executionPlan.ExecutionPlanGraphElementProperty[] {
|
||||||
switch (this.sortType) {
|
switch (this.sortType) {
|
||||||
case PropertiesSortType.DisplayOrder:
|
case PropertiesSortType.DisplayOrder:
|
||||||
props = this.sortPropertiesByImportance(props);
|
props = this.sortPropertiesByImportance(props);
|
||||||
@@ -127,6 +152,13 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
|||||||
props = this.sortPropertiesReverseAlphabetically(props);
|
props = this.sortPropertiesReverseAlphabetically(props);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
private convertModelToTableRows(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[] | undefined, parentIndex: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
||||||
|
if (!props) {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
props.forEach((p, i) => {
|
props.forEach((p, i) => {
|
||||||
let row = {};
|
let row = {};
|
||||||
@@ -142,9 +174,11 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
|||||||
text: removeLineBreaks(p.displayValue, ' ')
|
text: removeLineBreaks(p.displayValue, ' ')
|
||||||
};
|
};
|
||||||
row['tootltip'] = p.displayValue;
|
row['tootltip'] = p.displayValue;
|
||||||
this.convertModelToTableRows(p.value, rows.length - 1, indent + 2, rows);
|
this.convertModelToTableRows(p.value, rows.length - 1, rows);
|
||||||
} else {
|
} else {
|
||||||
row['value'] = removeLineBreaks(p.displayValue, ' ');
|
row['value'] = {
|
||||||
|
text: removeLineBreaks(p.displayValue, ' ')
|
||||||
|
};
|
||||||
row['tooltip'] = p.displayValue;
|
row['tooltip'] = p.displayValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,26 +10,36 @@ import { localize } from 'vs/nls';
|
|||||||
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
import { Action } from 'vs/base/common/actions';
|
import { Action } from 'vs/base/common/actions';
|
||||||
import { Codicon } from 'vs/base/common/codicons';
|
import { Codicon } from 'vs/base/common/codicons';
|
||||||
import { sortAlphabeticallyIconClassNames, sortByDisplayOrderIconClassNames, sortReverseAlphabeticallyIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
import { propertiesSearchDescription, searchIconClassNames, searchPlaceholder, sortAlphabeticallyIconClassNames, sortByDisplayOrderIconClassNames, sortReverseAlphabeticallyIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||||
import { attachTableStyler } from 'sql/platform/theme/common/styler';
|
import { attachTableStyler } from 'sql/platform/theme/common/styler';
|
||||||
import { RESULTS_GRID_DEFAULTS } from 'sql/workbench/common/constants';
|
import { RESULTS_GRID_DEFAULTS } from 'sql/workbench/common/constants';
|
||||||
import { contrastBorder, listHoverBackground, listInactiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry';
|
import { contrastBorder, inputBackground, listHoverBackground, listInactiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||||
import { TreeGrid } from 'sql/base/browser/ui/table/treeGrid';
|
import { TreeGrid } from 'sql/base/browser/ui/table/treeGrid';
|
||||||
import { ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
|
import { ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
|
||||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
import { isString } from 'vs/base/common/types';
|
import { isString } from 'vs/base/common/types';
|
||||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||||
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLayoutProvider {
|
export abstract class ExecutionPlanPropertiesViewBase extends Disposable implements IVerticalSashLayoutProvider {
|
||||||
// Title bar with close button action
|
// Title bar with close button action
|
||||||
private _titleBarContainer!: HTMLElement;
|
private _titleBarContainer!: HTMLElement;
|
||||||
private _titleBarTextContainer!: HTMLElement;
|
private _titleBarTextContainer!: HTMLElement;
|
||||||
private _titleBarActionsContainer!: HTMLElement;
|
private _titleBarActionsContainer!: HTMLElement;
|
||||||
private _titleActions: ActionBar;
|
private _titleActions: ActionBar;
|
||||||
|
|
||||||
|
|
||||||
|
// search bar and functrion
|
||||||
|
private _propertiesSearchInput: InputBox;
|
||||||
|
private _propertiesSearchInputContainer: HTMLElement;
|
||||||
|
|
||||||
|
private _searchAndActionBarContainer: HTMLElement;
|
||||||
|
|
||||||
// Header container
|
// Header container
|
||||||
private _headerContainer: HTMLElement;
|
private _headerContainer: HTMLElement;
|
||||||
|
|
||||||
@@ -50,22 +60,25 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
|
|
||||||
private _selectionModel: CellSelectionModel<Slick.SlickData>;
|
private _selectionModel: CellSelectionModel<Slick.SlickData>;
|
||||||
|
|
||||||
|
private _tableData: Slick.SlickData[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public _parentContainer: HTMLElement,
|
public _parentContainer: HTMLElement,
|
||||||
private _themeService: IThemeService,
|
private _themeService: IThemeService,
|
||||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||||
@IContextMenuService private _contextMenuService: IContextMenuService
|
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||||
|
@IContextViewService private _contextViewService: IContextViewService
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
const sashContainer = DOM.$('.properties-sash');
|
const sashContainer = DOM.$('.properties-sash');
|
||||||
this._parentContainer.appendChild(sashContainer);
|
this._parentContainer.appendChild(sashContainer);
|
||||||
|
|
||||||
this.resizeSash = new Sash(sashContainer, this, { orientation: Orientation.VERTICAL, size: 3 });
|
this.resizeSash = this._register(new Sash(sashContainer, this, { orientation: Orientation.VERTICAL, size: 3 }));
|
||||||
let originalWidth = 0;
|
let originalWidth = 0;
|
||||||
this.resizeSash.onDidStart((e: ISashEvent) => {
|
this._register(this.resizeSash.onDidStart((e: ISashEvent) => {
|
||||||
originalWidth = this._parentContainer.clientWidth;
|
originalWidth = this._parentContainer.clientWidth;
|
||||||
});
|
}));
|
||||||
this.resizeSash.onDidChange((evt: ISashEvent) => {
|
this._register(this.resizeSash.onDidChange((evt: ISashEvent) => {
|
||||||
const change = evt.startX - evt.currentX;
|
const change = evt.startX - evt.currentX;
|
||||||
const newWidth = originalWidth + change;
|
const newWidth = originalWidth + change;
|
||||||
if (newWidth < 200) {
|
if (newWidth < 200) {
|
||||||
@@ -73,9 +86,7 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
}
|
}
|
||||||
this._parentContainer.style.flex = `0 0 ${newWidth}px`;
|
this._parentContainer.style.flex = `0 0 ${newWidth}px`;
|
||||||
propertiesContent.style.width = `${newWidth}px`;
|
propertiesContent.style.width = `${newWidth}px`;
|
||||||
});
|
}));
|
||||||
this.resizeSash.onDidEnd(() => {
|
|
||||||
});
|
|
||||||
|
|
||||||
const propertiesContent = DOM.$('.properties-content');
|
const propertiesContent = DOM.$('.properties-content');
|
||||||
this._parentContainer.appendChild(propertiesContent);
|
this._parentContainer.appendChild(propertiesContent);
|
||||||
@@ -91,21 +102,35 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
|
|
||||||
this._titleBarActionsContainer = DOM.$('.action-bar');
|
this._titleBarActionsContainer = DOM.$('.action-bar');
|
||||||
this._titleBarContainer.appendChild(this._titleBarActionsContainer);
|
this._titleBarContainer.appendChild(this._titleBarActionsContainer);
|
||||||
this._titleActions = new ActionBar(this._titleBarActionsContainer, {
|
this._titleActions = this._register(new ActionBar(this._titleBarActionsContainer, {
|
||||||
orientation: ActionsOrientation.HORIZONTAL, context: this
|
orientation: ActionsOrientation.HORIZONTAL, context: this
|
||||||
});
|
}));
|
||||||
this._titleActions.pushAction([new ClosePropertyViewAction()], { icon: true, label: false });
|
this._titleActions.pushAction([new ClosePropertyViewAction()], { icon: true, label: false });
|
||||||
|
|
||||||
this._headerContainer = DOM.$('.header');
|
this._headerContainer = DOM.$('.header');
|
||||||
propertiesContent.appendChild(this._headerContainer);
|
propertiesContent.appendChild(this._headerContainer);
|
||||||
|
|
||||||
|
this._searchAndActionBarContainer = DOM.$('.search-action-bar-container');
|
||||||
|
propertiesContent.appendChild(this._searchAndActionBarContainer);
|
||||||
|
|
||||||
this._headerActionsContainer = DOM.$('.table-action-bar');
|
this._headerActionsContainer = DOM.$('.table-action-bar');
|
||||||
propertiesContent.appendChild(this._headerActionsContainer);
|
this._searchAndActionBarContainer.appendChild(this._headerActionsContainer);
|
||||||
this._headerActions = new ActionBar(this._headerActionsContainer, {
|
this._headerActions = this._register(new ActionBar(this._headerActionsContainer, {
|
||||||
orientation: ActionsOrientation.HORIZONTAL, context: this
|
orientation: ActionsOrientation.HORIZONTAL, context: this
|
||||||
});
|
}));
|
||||||
this._headerActions.pushAction([new SortPropertiesByDisplayOrderAction(), new SortPropertiesAlphabeticallyAction(), new SortPropertiesReverseAlphabeticallyAction()], { icon: true, label: false });
|
this._headerActions.pushAction([new SortPropertiesByDisplayOrderAction(), new SortPropertiesAlphabeticallyAction(), new SortPropertiesReverseAlphabeticallyAction()], { icon: true, label: false });
|
||||||
|
|
||||||
|
this._propertiesSearchInputContainer = DOM.$('.table-search');
|
||||||
|
this._propertiesSearchInputContainer.classList.add('codicon', searchIconClassNames);
|
||||||
|
this._propertiesSearchInput = this._register(new InputBox(this._propertiesSearchInputContainer, this._contextViewService, {
|
||||||
|
ariaDescription: propertiesSearchDescription,
|
||||||
|
placeholder: searchPlaceholder
|
||||||
|
}));
|
||||||
|
this._propertiesSearchInput.element.classList.add('codicon', searchIconClassNames);
|
||||||
|
this._searchAndActionBarContainer.appendChild(this._propertiesSearchInputContainer);
|
||||||
|
this._register(this._propertiesSearchInput.onDidChange(e => {
|
||||||
|
this.searchTable(e);
|
||||||
|
}));
|
||||||
|
|
||||||
this._tableContainer = DOM.$('.table-container');
|
this._tableContainer = DOM.$('.table-container');
|
||||||
propertiesContent.appendChild(this._tableContainer);
|
propertiesContent.appendChild(this._tableContainer);
|
||||||
@@ -115,7 +140,7 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
|
|
||||||
this._selectionModel = new CellSelectionModel<Slick.SlickData>();
|
this._selectionModel = new CellSelectionModel<Slick.SlickData>();
|
||||||
|
|
||||||
this._tableComponent = new TreeGrid(table, {
|
this._tableComponent = this._register(new TreeGrid(table, {
|
||||||
columns: []
|
columns: []
|
||||||
}, {
|
}, {
|
||||||
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
||||||
@@ -123,7 +148,7 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
defaultColumnWidth: 120,
|
defaultColumnWidth: 120,
|
||||||
editable: true,
|
editable: true,
|
||||||
autoEdit: false
|
autoEdit: false
|
||||||
});
|
}));
|
||||||
attachTableStyler(this._tableComponent, this._themeService);
|
attachTableStyler(this._tableComponent, this._themeService);
|
||||||
this._tableComponent.setSelectionModel(this._selectionModel);
|
this._tableComponent.setSelectionModel(this._selectionModel);
|
||||||
|
|
||||||
@@ -131,20 +156,20 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
this._instantiationService.createInstance(CopyTableData),
|
this._instantiationService.createInstance(CopyTableData),
|
||||||
];
|
];
|
||||||
|
|
||||||
this._tableComponent.onContextMenu(e => {
|
this._register(this._tableComponent.onContextMenu(e => {
|
||||||
this._contextMenuService.showContextMenu({
|
this._contextMenuService.showContextMenu({
|
||||||
getAnchor: () => e.anchor,
|
getAnchor: () => e.anchor,
|
||||||
getActions: () => contextMenuAction,
|
getActions: () => contextMenuAction,
|
||||||
getActionsContext: () => this.getCopyString()
|
getActionsContext: () => this.getCopyString()
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
|
|
||||||
let copyHandler = new CopyKeybind<any>();
|
let copyHandler = new CopyKeybind<any>();
|
||||||
this._tableComponent.registerPlugin(copyHandler);
|
this._tableComponent.registerPlugin(copyHandler);
|
||||||
|
|
||||||
copyHandler.onCopy(e => {
|
this._register(copyHandler.onCopy(e => {
|
||||||
this._instantiationService.createInstance(CopyTableData).run(this.getCopyString());
|
this._instantiationService.createInstance(CopyTableData).run(this.getCopyString());
|
||||||
});
|
}));
|
||||||
|
|
||||||
new ResizeObserver((e) => {
|
new ResizeObserver((e) => {
|
||||||
this.resizeSash.layout();
|
this.resizeSash.layout();
|
||||||
@@ -226,7 +251,9 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
public populateTable(columns: Slick.Column<Slick.SlickData>[], data: { [key: string]: string }[]) {
|
public populateTable(columns: Slick.Column<Slick.SlickData>[], data: { [key: string]: string }[]) {
|
||||||
this._tableComponent.columns = columns;
|
this._tableComponent.columns = columns;
|
||||||
this._tableContainer.scrollTo(0, 0);
|
this._tableContainer.scrollTo(0, 0);
|
||||||
this._tableComponent.setData(data);
|
this._tableData = data;
|
||||||
|
this._propertiesSearchInput.value = '';
|
||||||
|
this._tableComponent.setData(this.flattenTableData(data, -1));
|
||||||
this.resizeTable();
|
this.resizeTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +272,68 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
|||||||
this._tableComponent.resizeCanvas();
|
this._tableComponent.resizeCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private searchTable(searchString: string): void {
|
||||||
|
if (searchString === '') {
|
||||||
|
this._tableComponent.setData(this.flattenTableData(this._tableData, -1));
|
||||||
|
} else {
|
||||||
|
this._tableComponent.setData(
|
||||||
|
this.flattenTableData(
|
||||||
|
this.searchNestedTableData(searchString, this._tableData).data,
|
||||||
|
-1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this._tableComponent.rerenderGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private searchNestedTableData(search: string, data: Slick.SlickData[]): { include: boolean, data: Slick.SlickData[] } {
|
||||||
|
let resultData: Slick.SlickData[] = [];
|
||||||
|
data.forEach(dataRow => {
|
||||||
|
let includeRow = false;
|
||||||
|
const columns = this._tableComponent.grid.getColumns();
|
||||||
|
for (let i = 0; i < columns.length; i++) {
|
||||||
|
let dataValue = '';
|
||||||
|
let rawDataValue = dataRow[columns[i].field];
|
||||||
|
if (isString(rawDataValue)) {
|
||||||
|
dataValue = rawDataValue;
|
||||||
|
} else if (rawDataValue !== undefined) {
|
||||||
|
dataValue = rawDataValue.text ?? rawDataValue.title;
|
||||||
|
}
|
||||||
|
if (dataValue.toLowerCase().includes(search.toLowerCase())) {
|
||||||
|
includeRow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowClone = deepClone(dataRow);
|
||||||
|
if (rowClone['treeGridChildren'] !== undefined) {
|
||||||
|
const result = this.searchNestedTableData(search, rowClone['treeGridChildren']);
|
||||||
|
rowClone['treeGridChildren'] = result.data;
|
||||||
|
includeRow = includeRow || result.include;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeRow) {
|
||||||
|
if (rowClone['treeGridChildren'] !== undefined) {
|
||||||
|
rowClone['expanded'] = true;
|
||||||
|
}
|
||||||
|
resultData.push(rowClone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { include: resultData.length > 0, data: resultData };
|
||||||
|
}
|
||||||
|
|
||||||
|
private flattenTableData(nestedData: Slick.SlickData[], parentIndex: number, rows: Slick.SlickData[] = []): Slick.SlickData[] {
|
||||||
|
if (nestedData === undefined || nestedData.length === 0) {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
nestedData.forEach((dataRow) => {
|
||||||
|
rows.push(dataRow);
|
||||||
|
dataRow['parent'] = parentIndex;
|
||||||
|
if (dataRow['treeGridChildren']) {
|
||||||
|
this.flattenTableData(dataRow['treeGridChildren'], rows.length - 1, rows);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -342,6 +431,15 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchBarBackground = theme.getColor(inputBackground);
|
||||||
|
if (inputBackground) {
|
||||||
|
collector.addRule(`
|
||||||
|
.eps-container .properties .search-action-bar-container .table-search {
|
||||||
|
background-color: ${searchBarBackground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -160,10 +160,12 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
|||||||
new SavePlanFile(),
|
new SavePlanFile(),
|
||||||
new OpenPlanFile(),
|
new OpenPlanFile(),
|
||||||
this._instantiationService.createInstance(OpenQueryAction, 'ActionBar'),
|
this._instantiationService.createInstance(OpenQueryAction, 'ActionBar'),
|
||||||
|
new Separator(),
|
||||||
this._instantiationService.createInstance(ZoomInAction, 'ActionBar'),
|
this._instantiationService.createInstance(ZoomInAction, 'ActionBar'),
|
||||||
this._instantiationService.createInstance(ZoomOutAction, 'ActionBar'),
|
this._instantiationService.createInstance(ZoomOutAction, 'ActionBar'),
|
||||||
this._instantiationService.createInstance(ZoomToFitAction, 'ActionBar'),
|
this._instantiationService.createInstance(ZoomToFitAction, 'ActionBar'),
|
||||||
this._instantiationService.createInstance(CustomZoomAction, 'ActionBar'),
|
this._instantiationService.createInstance(CustomZoomAction, 'ActionBar'),
|
||||||
|
new Separator(),
|
||||||
this._instantiationService.createInstance(SearchNodeAction, 'ActionBar'),
|
this._instantiationService.createInstance(SearchNodeAction, 'ActionBar'),
|
||||||
this._instantiationService.createInstance(PropertiesAction, 'ActionBar'),
|
this._instantiationService.createInstance(PropertiesAction, 'ActionBar'),
|
||||||
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ActionBar'),
|
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ActionBar'),
|
||||||
|
|||||||
@@ -1,23 +1,65 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
<svg
|
||||||
<style type="text/css">
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
style="enable-background:new 0 0 16 16;"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="customZoom.svg"
|
||||||
|
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs13" /><sodipodi:namedview
|
||||||
|
id="namedview11"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="14.75"
|
||||||
|
inkscape:cx="8"
|
||||||
|
inkscape:cy="8"
|
||||||
|
inkscape:window-width="1792"
|
||||||
|
inkscape:window-height="1049"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Layer_1" />
|
||||||
|
<style
|
||||||
|
type="text/css"
|
||||||
|
id="style2">
|
||||||
.st0{fill:#F6F6F6;fill-opacity:0;}
|
.st0{fill:#F6F6F6;fill-opacity:0;}
|
||||||
.st1{fill:#F6F6F6;}
|
.st1{fill:#F6F6F6;}
|
||||||
.st2{fill:#424242;}
|
.st2{fill:#424242;}
|
||||||
.st3{fill:#F0EFF1;}
|
.st3{fill:#F0EFF1;}
|
||||||
</style>
|
</style>
|
||||||
<g id="canvas">
|
<g
|
||||||
<path class="st0" d="M16,16H0V0h16V16z"/>
|
id="canvas"
|
||||||
|
transform="matrix(-1,0,0,1,15.95,0)">
|
||||||
|
<path
|
||||||
|
class="st0"
|
||||||
|
d="M 16,16 H 0 V 0 h 16 z"
|
||||||
|
id="path4" />
|
||||||
</g>
|
</g>
|
||||||
<path id="outline" class="st1" d="M16,5.8c0-3.2-2.6-5.7-5.8-5.8C7,0,4.4,2.6,4.3,5.8c0,1,0.3,2,0.8,2.9l-4.7,4.7
|
<path
|
||||||
c-0.6,0.6-0.6,1.5,0,2.1c0.6,0.6,1.5,0.6,2.1,0l4.7-4.7c0.4,0.3,0.9,0.4,1.3,0.6l-0.8,0.8L7,16h1.4l2.4-0.8l5.2-5.1l-1-1
|
id="outline"
|
||||||
C15.6,8.1,16,7,16,5.8z"/>
|
class="st1"
|
||||||
<path id="iconBg" class="st2" d="M10.4,9.6c-0.1,0-0.2,0-0.2,0C8,9.7,6.3,8,6.3,5.8C6.3,3.7,8,2,10.2,2C12.3,2,14,3.7,14,5.8v0
|
d="m -0.05,5.8 c 0,-3.2 2.6,-5.7 5.8,-5.8 3.2,0 5.8,2.6 5.9,5.8 0,1 -0.3,2 -0.8,2.9 l 4.7,4.7 c 0.6,0.6 0.6,1.5 0,2.1 -0.6,0.6 -1.5,0.6 -2.1,0 l -4.7,-4.7 c -0.4,0.3 -0.9,0.4 -1.3,0.6 l 0.8,0.8 0.7,3.8 h -1.4 l -2.4,-0.8 -5.2,-5.1 1,-1 c -0.6,-1 -1,-2.1 -1,-3.3 z" />
|
||||||
c0,0.6-0.2,1.2-0.5,1.8l0.7,0.7c0.4-0.7,0.7-1.6,0.7-2.5c0-2.6-2.1-4.7-4.8-4.8v0C7.5,1,5.3,3.2,5.3,5.8c0,1.1,0.4,2.2,1.1,3
|
<path
|
||||||
l-5.3,5.3c-0.2,0.2-0.2,0.5,0,0.7c0.2,0.2,0.5,0.2,0.7,0l5.3-5.3c0.7,0.6,1.5,0.9,2.3,1L10.4,9.6z M8.2,15l2.1-0.7l-1.6-1.6L8.2,15z
|
id="iconBg"
|
||||||
M12.3,9.2l1.6,1.6l0.7-0.7L13,8.5L12.3,9.2z M9.5,12l1.6,1.6l2.1-2.1l-1.6-1.6L9.5,12z"/>
|
class="st2"
|
||||||
<path id="iconFg" class="st3" d="M10.2,9.7c0.1,0,0.2,0,0.2,0L13,7.1l0.6,0.6C13.8,7.1,14,6.5,14,5.8C14,3.7,12.3,2,10.2,2
|
d="m 5.55,9.6 c 0.1,0 0.2,0 0.2,0 C 7.95,9.7 9.65,8 9.65,5.8 9.65,3.7 7.95,2 5.75,2 3.65,2 1.95,3.7 1.95,5.8 v 0 c 0,0.6 0.2,1.2 0.5,1.8 L 1.75,8.3 C 1.35,7.6 1.05,6.7 1.05,5.8 1.05,3.2 3.15,1.1 5.85,1 v 0 c 2.6,0 4.8,2.2 4.8,4.8 0,1.1 -0.4,2.2 -1.1,3 l 5.3,5.3 c 0.2,0.2 0.2,0.5 0,0.7 -0.2,0.2 -0.5,0.2 -0.7,0 L 8.85,9.5 c -0.7,0.6 -1.5,0.9 -2.3,1 z m 2.2,5.4 -2.1,-0.7 1.6,-1.6 z m -4.1,-5.8 -1.6,1.6 -0.7,-0.7 1.6,-1.6 z m 2.8,2.8 -1.6,1.6 -2.1,-2.1 1.6,-1.6 z" />
|
||||||
c0,0,0,0,0,0C8,2,6.3,3.7,6.3,5.8C6.3,8,8,9.7,10.2,9.7z"/>
|
<path
|
||||||
|
id="iconFg"
|
||||||
|
class="st3"
|
||||||
|
d="m 5.75,9.7 c -0.1,0 -0.2,0 -0.2,0 L 2.95,7.1 2.35,7.7 C 2.15,7.1 1.95,6.5 1.95,5.8 1.95,3.7 3.65,2 5.75,2 c 0,0 0,0 0,0 2.2,0 3.9,1.7 3.9,3.8 0,2.2 -1.7,3.9 -3.9,3.9 z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,23 +1,65 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
<svg
|
||||||
<style type="text/css">
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
style="enable-background:new 0 0 16 16;"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="customZoomDark.svg"
|
||||||
|
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs148" /><sodipodi:namedview
|
||||||
|
id="namedview146"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="14.75"
|
||||||
|
inkscape:cx="8"
|
||||||
|
inkscape:cy="8"
|
||||||
|
inkscape:window-width="1792"
|
||||||
|
inkscape:window-height="1049"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Layer_1" />
|
||||||
|
<style
|
||||||
|
type="text/css"
|
||||||
|
id="style137">
|
||||||
.st0{fill:#F6F6F6;fill-opacity:0;}
|
.st0{fill:#F6F6F6;fill-opacity:0;}
|
||||||
.st1{fill:#0000;}
|
.st1{fill:#0000;}
|
||||||
.st2{fill:#fff;}
|
.st2{fill:#fff;}
|
||||||
.st3{fill:#0000;}
|
.st3{fill:#0000;}
|
||||||
</style>
|
</style>
|
||||||
<g id="canvas">
|
<g
|
||||||
<path class="st0" d="M16,16H0V0h16V16z"/>
|
id="canvas"
|
||||||
|
transform="matrix(-1,0,0,1,15.95,0)">
|
||||||
|
<path
|
||||||
|
class="st0"
|
||||||
|
d="M 16,16 H 0 V 0 h 16 z"
|
||||||
|
id="path139" />
|
||||||
</g>
|
</g>
|
||||||
<path id="outline" class="st1" d="M16,5.8c0-3.2-2.6-5.7-5.8-5.8C7,0,4.4,2.6,4.3,5.8c0,1,0.3,2,0.8,2.9l-4.7,4.7
|
<path
|
||||||
c-0.6,0.6-0.6,1.5,0,2.1c0.6,0.6,1.5,0.6,2.1,0l4.7-4.7c0.4,0.3,0.9,0.4,1.3,0.6l-0.8,0.8L7,16h1.4l2.4-0.8l5.2-5.1l-1-1
|
id="outline"
|
||||||
C15.6,8.1,16,7,16,5.8z"/>
|
class="st1"
|
||||||
<path id="iconBg" class="st2" d="M10.4,9.6c-0.1,0-0.2,0-0.2,0C8,9.7,6.3,8,6.3,5.8C6.3,3.7,8,2,10.2,2C12.3,2,14,3.7,14,5.8v0
|
d="m -0.05,5.8 c 0,-3.2 2.6,-5.7 5.8,-5.8 3.2,0 5.8,2.6 5.9,5.8 0,1 -0.3,2 -0.8,2.9 l 4.7,4.7 c 0.6,0.6 0.6,1.5 0,2.1 -0.6,0.6 -1.5,0.6 -2.1,0 l -4.7,-4.7 c -0.4,0.3 -0.9,0.4 -1.3,0.6 l 0.8,0.8 0.7,3.8 h -1.4 l -2.4,-0.8 -5.2,-5.1 1,-1 c -0.6,-1 -1,-2.1 -1,-3.3 z" />
|
||||||
c0,0.6-0.2,1.2-0.5,1.8l0.7,0.7c0.4-0.7,0.7-1.6,0.7-2.5c0-2.6-2.1-4.7-4.8-4.8v0C7.5,1,5.3,3.2,5.3,5.8c0,1.1,0.4,2.2,1.1,3
|
<path
|
||||||
l-5.3,5.3c-0.2,0.2-0.2,0.5,0,0.7c0.2,0.2,0.5,0.2,0.7,0l5.3-5.3c0.7,0.6,1.5,0.9,2.3,1L10.4,9.6z M8.2,15l2.1-0.7l-1.6-1.6L8.2,15z
|
id="iconBg"
|
||||||
M12.3,9.2l1.6,1.6l0.7-0.7L13,8.5L12.3,9.2z M9.5,12l1.6,1.6l2.1-2.1l-1.6-1.6L9.5,12z"/>
|
class="st2"
|
||||||
<path id="iconFg" class="st3" d="M10.2,9.7c0.1,0,0.2,0,0.2,0L13,7.1l0.6,0.6C13.8,7.1,14,6.5,14,5.8C14,3.7,12.3,2,10.2,2
|
d="m 5.55,9.6 c 0.1,0 0.2,0 0.2,0 C 7.95,9.7 9.65,8 9.65,5.8 9.65,3.7 7.95,2 5.75,2 3.65,2 1.95,3.7 1.95,5.8 v 0 c 0,0.6 0.2,1.2 0.5,1.8 L 1.75,8.3 C 1.35,7.6 1.05,6.7 1.05,5.8 1.05,3.2 3.15,1.1 5.85,1 v 0 c 2.6,0 4.8,2.2 4.8,4.8 0,1.1 -0.4,2.2 -1.1,3 l 5.3,5.3 c 0.2,0.2 0.2,0.5 0,0.7 -0.2,0.2 -0.5,0.2 -0.7,0 L 8.85,9.5 c -0.7,0.6 -1.5,0.9 -2.3,1 z m 2.2,5.4 -2.1,-0.7 1.6,-1.6 z m -4.1,-5.8 -1.6,1.6 -0.7,-0.7 1.6,-1.6 z m 2.8,2.8 -1.6,1.6 -2.1,-2.1 1.6,-1.6 z" />
|
||||||
c0,0,0,0,0,0C8,2,6.3,3.7,6.3,5.8C6.3,8,8,9.7,10.2,9.7z"/>
|
<path
|
||||||
|
id="iconFg"
|
||||||
|
class="st3"
|
||||||
|
d="m 5.75,9.7 c -0.1,0 -0.2,0 -0.2,0 L 2.95,7.1 2.35,7.7 C 2.15,7.1 1.95,6.5 1.95,5.8 1.95,3.7 3.65,2 5.75,2 c 0,0 0,0 0,0 2.2,0 3.9,1.7 3.9,3.8 0,2.2 -1.7,3.9 -3.9,3.9 z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -187,6 +187,31 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.eps-container .properties .search-action-bar-container .table-action-bar {
|
||||||
|
flex: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.eps-container .properties .search-action-bar-container .table-search {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eps-container .properties .search-action-bar-container .table-search>div,
|
||||||
|
.top-operations-tab .top-operations-container .top-operations-header-search-bar>div {
|
||||||
|
background-position-y: center;
|
||||||
|
background-position-x: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eps-container .properties .search-action-bar-container .table-search input,
|
||||||
|
.top-operations-tab .top-operations-container .top-operations-header-search-bar input {
|
||||||
|
margin-left: 20px;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eps-container .properties .search-action-bar-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
/* Operation name styling in the properties view. */
|
/* Operation name styling in the properties view. */
|
||||||
.eps-container .properties .operation-name {
|
.eps-container .properties .operation-name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -316,7 +341,8 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.eps-container .ep-search-icon {
|
.eps-container .ep-search-icon,
|
||||||
|
.top-operations-tab .ep-search-icon {
|
||||||
background-image: url(../images/actionIcons/search.svg);
|
background-image: url(../images/actionIcons/search.svg);
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -324,7 +350,9 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark .eps-container .ep-search-icon,
|
.vs-dark .eps-container .ep-search-icon,
|
||||||
.hc-black .eps-container .ep-search-icon {
|
.hc-black .eps-container .ep-search-icon,
|
||||||
|
.vs-dark .top-operations-tab .ep-search-icon,
|
||||||
|
.hc-black .top-operations-tab .ep-search-icon {
|
||||||
background-image: url(../images/actionIcons/searchDark.svg);
|
background-image: url(../images/actionIcons/searchDark.svg);
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -799,6 +827,19 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-operations-tab .top-operations-container .top-operations-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-operations-tab .top-operations-container .top-operations-header .top-operations-header-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-operations-tab .top-operations-container .top-operations-header .top-operations-header-search-bar {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
.top-operations-tab .top-operations-container .table-container {
|
.top-operations-tab .top-operations-container .table-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
@@ -846,9 +887,10 @@ However we always want it to be the width of the container it is resizing.
|
|||||||
.graph-cell .graph-cell-row-count {
|
.graph-cell .graph-cell-row-count {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 30%;
|
top: 30%;
|
||||||
left: calc(50% - 60px);
|
right: calc(50% + 17px);
|
||||||
|
margin-right: 3px;
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
text-align: right;
|
text-align: left;
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugi
|
|||||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||||
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { listHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
import { listHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { Action } from 'vs/base/common/actions';
|
import { Action } from 'vs/base/common/actions';
|
||||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
import { ITableKeyboardEvent } from 'sql/base/browser/ui/table/interfaces';
|
import { ITableKeyboardEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||||
|
import { searchIconClassNames, searchPlaceholder, topOperationsSearchDescription } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||||
|
|
||||||
const TABLE_SORT_COLUMN_KEY = 'tableCostColumnForSorting';
|
const TABLE_SORT_COLUMN_KEY = 'tableCostColumnForSorting';
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
|||||||
@IThemeService private _themeService: IThemeService,
|
@IThemeService private _themeService: IThemeService,
|
||||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||||
|
@IContextViewService private _contextViewService: IContextViewService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -145,13 +148,30 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
|||||||
name: c,
|
name: c,
|
||||||
field: c.toString(),
|
field: c.toString(),
|
||||||
formatter: i === 0 ? hyperLinkFormatter : textFormatter,
|
formatter: i === 0 ? hyperLinkFormatter : textFormatter,
|
||||||
sortable: true,
|
sortable: true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const topOperationContainer = DOM.$('.top-operations-container');
|
const topOperationContainer = DOM.$('.top-operations-container');
|
||||||
this._container.appendChild(topOperationContainer);
|
this._container.appendChild(topOperationContainer);
|
||||||
const header = this._instantiationService.createInstance(ExecutionPlanViewHeader, topOperationContainer, {
|
|
||||||
|
const headerContainer = DOM.$('.top-operations-header');
|
||||||
|
topOperationContainer.appendChild(headerContainer);
|
||||||
|
|
||||||
|
const headerInfoContainer = DOM.$('.top-operations-header-info');
|
||||||
|
headerContainer.appendChild(headerInfoContainer);
|
||||||
|
|
||||||
|
const headerSearchBarContainer = DOM.$('.top-operations-header-search-bar');
|
||||||
|
headerContainer.appendChild(headerSearchBarContainer);
|
||||||
|
headerContainer.classList.add('codicon', searchIconClassNames);
|
||||||
|
|
||||||
|
const topOperationsSearchInput = new InputBox(headerSearchBarContainer, this._contextViewService, {
|
||||||
|
ariaDescription: topOperationsSearchDescription,
|
||||||
|
placeholder: searchPlaceholder
|
||||||
|
});
|
||||||
|
topOperationsSearchInput.element.classList.add('codicon', searchIconClassNames);
|
||||||
|
|
||||||
|
const header = this._instantiationService.createInstance(ExecutionPlanViewHeader, headerInfoContainer, {
|
||||||
planIndex: index,
|
planIndex: index,
|
||||||
});
|
});
|
||||||
header.query = graph.query;
|
header.query = graph.query;
|
||||||
@@ -207,7 +227,7 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
|||||||
columns: columns,
|
columns: columns,
|
||||||
sorter: (args) => {
|
sorter: (args) => {
|
||||||
const column = args.sortCol.field;
|
const column = args.sortCol.field;
|
||||||
dataMap.sort((a, b) => {
|
const sortedData = table.getData().getItems().sort((a, b) => {
|
||||||
let result = -1;
|
let result = -1;
|
||||||
|
|
||||||
if (!a[column]) {
|
if (!a[column]) {
|
||||||
@@ -237,7 +257,7 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
|||||||
}
|
}
|
||||||
return args.sortAsc ? result : -result;
|
return args.sortAsc ? result : -result;
|
||||||
});
|
});
|
||||||
table.setData(dataMap);
|
table.setData(sortedData);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
||||||
@@ -268,6 +288,29 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
|||||||
this._instantiationService.createInstance(SelectAll)
|
this._instantiationService.createInstance(SelectAll)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
this._register(topOperationsSearchInput.onDidChange(e => {
|
||||||
|
const filter = e.toLowerCase();
|
||||||
|
if (filter) {
|
||||||
|
const filteredData = dataMap.filter(row => {
|
||||||
|
let includeRow = false;
|
||||||
|
for (let i = 0; i < columns.length; i++) {
|
||||||
|
const columnField = columns[i].field;
|
||||||
|
if (row[columnField]) {
|
||||||
|
const text = row[columnField].displayText ?? row[columnField].text;
|
||||||
|
if (text.toLowerCase().includes(filter)) {
|
||||||
|
includeRow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return includeRow;
|
||||||
|
});
|
||||||
|
table.setData(filteredData);
|
||||||
|
} else {
|
||||||
|
table.setData(dataMap);
|
||||||
|
}
|
||||||
|
table.rerenderGrid();
|
||||||
|
}));
|
||||||
|
|
||||||
this._register(table.onKeyDown((evt: ITableKeyboardEvent) => {
|
this._register(table.onKeyDown((evt: ITableKeyboardEvent) => {
|
||||||
if (evt.event.ctrlKey && (evt.event.key === 'a' || evt.event.key === 'A')) {
|
if (evt.event.ctrlKey && (evt.event.key === 'a' || evt.event.key === 'A')) {
|
||||||
selectionModel.setSelectedRanges([new Slick.Range(0, 1, table.getData().getLength() - 1, table.columns.length - 1)]);
|
selectionModel.setSelectedRanges([new Slick.Range(0, 1, table.getData().getLength() - 1, table.columns.length - 1)]);
|
||||||
|
|||||||
Reference in New Issue
Block a user