mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-23 09:35:39 -05:00
Adding caching to execution plan and refactoring code and some other fixes (#18913)
* Making ep code modular for easy swithcing in and out * Changing to innerText * Fixing renames * Fixing var name in one file
This commit is contained in:
@@ -6,89 +6,84 @@
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import type * as azdata from 'azdata';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachTableStyler } from 'sql/platform/theme/common/styler';
|
||||
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { RESULTS_GRID_DEFAULTS } from 'sql/workbench/common/constants';
|
||||
import { ActionBar } from 'sql/base/browser/ui/taskbar/actionbar';
|
||||
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { removeLineBreaks } from 'sql/base/common/strings';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { sortAlphabeticallyIconClassNames, sortByDisplayOrderIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import { textFormatter } from 'sql/base/browser/ui/table/formatters';
|
||||
import { ExecutionPlanPropertiesViewBase, PropertiesSortType } from 'sql/workbench/contrib/executionPlan/browser/executionPlanPropertiesViewBase';
|
||||
|
||||
|
||||
export class ExecutionPlanPropertiesView {
|
||||
|
||||
// Title bar with close button action
|
||||
private _propertiesTitle!: HTMLElement;
|
||||
private _titleText!: HTMLElement;
|
||||
private _titleActionBarContainer!: HTMLElement;
|
||||
private _titleActionBar: ActionBar;
|
||||
|
||||
export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase {
|
||||
// Div that holds the name of the element selected
|
||||
private _operationName!: HTMLElement;
|
||||
|
||||
// Action bar that contains sorting option for the table
|
||||
private _tableActionBarContainer!: HTMLElement;
|
||||
private _tableActionBar!: ActionBar;
|
||||
|
||||
// Properties table
|
||||
private _table: Table<Slick.SlickData>;
|
||||
private _dataView: TableDataView<Slick.SlickData>;
|
||||
private _data: { [key: string]: string }[];
|
||||
private _tableContainer!: HTMLElement;
|
||||
private _actualTable!: HTMLElement;
|
||||
|
||||
// Table dimensions.
|
||||
private _tableWidth = 485;
|
||||
private _tableHeight;
|
||||
private _model: ExecutionPlanPropertiesViewModel;
|
||||
|
||||
public constructor(
|
||||
private _parentContainer: HTMLElement,
|
||||
private _themeService: IThemeService,
|
||||
private _model: GraphElementPropertyViewData = <GraphElementPropertyViewData>{}
|
||||
parentContainer: HTMLElement,
|
||||
themeService: IThemeService
|
||||
) {
|
||||
this._parentContainer.style.display = 'none';
|
||||
|
||||
this._propertiesTitle = DOM.$('.title');
|
||||
this._parentContainer.appendChild(this._propertiesTitle);
|
||||
|
||||
this._titleText = DOM.$('h3');
|
||||
this._titleText.classList.add('text');
|
||||
this._titleText.innerText = localize('nodePropertyViewTitle', "Properties");
|
||||
this._propertiesTitle.appendChild(this._titleText);
|
||||
|
||||
this._titleActionBarContainer = DOM.$('.action-bar');
|
||||
this._propertiesTitle.appendChild(this._titleActionBarContainer);
|
||||
this._titleActionBar = new ActionBar(this._titleActionBarContainer, {
|
||||
orientation: ActionsOrientation.HORIZONTAL, context: this
|
||||
});
|
||||
this._titleActionBar.pushAction([new ClosePropertyViewAction()], { icon: true, label: false });
|
||||
|
||||
super(parentContainer, themeService);
|
||||
this._model = <ExecutionPlanPropertiesView>{};
|
||||
this._operationName = DOM.$('h3');
|
||||
this._operationName.classList.add('operation-name');
|
||||
this._parentContainer.appendChild(this._operationName);
|
||||
this.setHeader(this._operationName);
|
||||
|
||||
this._tableActionBarContainer = DOM.$('.table-action-bar');
|
||||
this._parentContainer.appendChild(this._tableActionBarContainer);
|
||||
this._tableActionBar = new ActionBar(this._tableActionBarContainer, {
|
||||
orientation: ActionsOrientation.HORIZONTAL, context: this
|
||||
this._parentContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
public set graphElement(element: azdata.executionPlan.ExecutionPlanNode | azdata.executionPlan.ExecutionPlanEdge) {
|
||||
this._model.graphElement = element;
|
||||
this.renderView();
|
||||
}
|
||||
|
||||
public sortPropertiesAlphabetically(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): azdata.executionPlan.ExecutionPlanGraphElementProperty[] {
|
||||
return props.sort((a, b) => {
|
||||
if (!a?.name && !b?.name) {
|
||||
return 0;
|
||||
} else if (!a?.name) {
|
||||
return -1;
|
||||
} else if (!b?.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
});
|
||||
this._tableActionBar.pushAction([new SortPropertiesByDisplayOrderAction(), new SortPropertiesAlphabeticallyAction()], { icon: true, label: false });
|
||||
}
|
||||
|
||||
public sortPropertiesReverseAlphabetically(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): azdata.executionPlan.ExecutionPlanGraphElementProperty[] {
|
||||
return props.sort((a, b) => {
|
||||
if (!a?.name && !b?.name) {
|
||||
return 0;
|
||||
} else if (!a?.name) {
|
||||
return -1;
|
||||
} else if (!b?.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return b.name.localeCompare(a.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this._tableContainer = DOM.$('.table-container');
|
||||
this._parentContainer.appendChild(this._tableContainer);
|
||||
public sortPropertiesByImportance(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): azdata.executionPlan.ExecutionPlanGraphElementProperty[] {
|
||||
return props.sort((a, b) => {
|
||||
if (!a?.displayOrder && !b?.displayOrder) {
|
||||
return 0;
|
||||
} else if (!a?.displayOrder) {
|
||||
return -1;
|
||||
} else if (!b?.displayOrder) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.displayOrder - b.displayOrder;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._actualTable = DOM.$('.table');
|
||||
this._tableContainer.appendChild(this._actualTable);
|
||||
|
||||
this._dataView = new TableDataView();
|
||||
this._data = [];
|
||||
public renderView(): void {
|
||||
if (this._model.graphElement) {
|
||||
const nodeName = (<azdata.executionPlan.ExecutionPlanNode>this._model.graphElement).name;
|
||||
this._operationName.innerText = nodeName ? removeLineBreaks(nodeName) : localize('executionPlanPropertiesEdgeOperationName', "Edge"); //since edges do not have names like node, we set the operation name to 'Edge'
|
||||
}
|
||||
|
||||
const columns: Slick.Column<Slick.SlickData>[] = [
|
||||
{
|
||||
@@ -111,100 +106,26 @@ export class ExecutionPlanPropertiesView {
|
||||
}
|
||||
];
|
||||
|
||||
this._table = new Table(this._actualTable, {
|
||||
dataProvider: this._dataView, columns: columns
|
||||
}, {
|
||||
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
||||
forceFitColumns: true,
|
||||
defaultColumnWidth: 120
|
||||
});
|
||||
|
||||
new ResizeObserver((e) => {
|
||||
this.tableHeight = (this._parentContainer.getBoundingClientRect().height - 80);
|
||||
}).observe(this._parentContainer);
|
||||
|
||||
attachTableStyler(this._table, this._themeService);
|
||||
this.populateTable(columns, this.convertModelToTableRows(this._model.graphElement.properties, -1, 0));
|
||||
}
|
||||
|
||||
public set graphElement(element: azdata.executionPlan.ExecutionPlanNode | azdata.executionPlan.ExecutionPlanEdge) {
|
||||
this._model.graphElement = element;
|
||||
this.sortPropertiesByImportance();
|
||||
this.renderView();
|
||||
}
|
||||
|
||||
public sortPropertiesAlphabetically(): void {
|
||||
this._model.graphElement.properties = this._model.graphElement.properties.sort((a, b) => {
|
||||
if (!a?.name && !b?.name) {
|
||||
return 0;
|
||||
} else if (!a?.name) {
|
||||
return -1;
|
||||
} else if (!b?.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
});
|
||||
this.renderView();
|
||||
}
|
||||
|
||||
public sortPropertiesByImportance(): void {
|
||||
this._model.graphElement.properties = this._model.graphElement.properties.sort((a, b) => {
|
||||
if (!a?.displayOrder && !b?.displayOrder) {
|
||||
return 0;
|
||||
} else if (!a?.displayOrder) {
|
||||
return -1;
|
||||
} else if (!b?.displayOrder) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.displayOrder - b.displayOrder;
|
||||
}
|
||||
});
|
||||
this.renderView();
|
||||
}
|
||||
|
||||
public set tableHeight(value: number) {
|
||||
if (this.tableHeight !== value) {
|
||||
this._tableHeight = value;
|
||||
this.renderView();
|
||||
}
|
||||
}
|
||||
|
||||
public get tableHeight(): number {
|
||||
return this._tableHeight;
|
||||
}
|
||||
|
||||
public set tableWidth(value: number) {
|
||||
if (this._tableWidth !== value) {
|
||||
this._tableWidth = value;
|
||||
this.renderView();
|
||||
}
|
||||
}
|
||||
|
||||
public get tableWidth(): number {
|
||||
return this._tableWidth;
|
||||
}
|
||||
|
||||
private renderView(): void {
|
||||
if (this._model.graphElement) {
|
||||
const nodeName = (<azdata.executionPlan.ExecutionPlanNode>this._model.graphElement).name;
|
||||
this._operationName.innerText = nodeName ? removeLineBreaks(nodeName) : localize('executionPlanPropertiesEdgeOperationName', "Edge"); //since edges do not have names like node, we set the operation name to 'Edge'
|
||||
}
|
||||
this._tableContainer.scrollTo(0, 0);
|
||||
this._dataView.clear();
|
||||
this._data = this.convertPropertiesToTableRows(this._model.graphElement.properties, -1, 0);
|
||||
this._dataView.push(this._data);
|
||||
this._table.setData(this._dataView);
|
||||
this._table.autosizeColumns();
|
||||
this._table.updateRowCount();
|
||||
this.tableHeight = (this._parentContainer.getBoundingClientRect().height - 80); //80px is the space taken by the title and toolbar
|
||||
this._table.layout(new DOM.Dimension(this._tableWidth, this._tableHeight));
|
||||
this._table.resizeCanvas();
|
||||
}
|
||||
|
||||
private convertPropertiesToTableRows(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[], parentIndex: number, indent: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
||||
private convertModelToTableRows(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[], parentIndex: number, indent: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
||||
if (!props) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
switch (this.sortType) {
|
||||
case PropertiesSortType.DisplayOrder:
|
||||
props = this.sortPropertiesByImportance(props);
|
||||
break;
|
||||
case PropertiesSortType.Alphabetical:
|
||||
props = this.sortPropertiesAlphabetically(props);
|
||||
break;
|
||||
case PropertiesSortType.ReverseAlphabetical:
|
||||
props = this.sortPropertiesReverseAlphabetically(props);
|
||||
break;
|
||||
}
|
||||
|
||||
props.forEach((p, i) => {
|
||||
let row = {};
|
||||
rows.push(row);
|
||||
@@ -212,7 +133,7 @@ export class ExecutionPlanPropertiesView {
|
||||
row['parent'] = parentIndex;
|
||||
if (!isString(p.value)) {
|
||||
row['value'] = removeLineBreaks(p.displayValue, ' ');
|
||||
this.convertPropertiesToTableRows(p.value, rows.length - 1, indent + 2, rows);
|
||||
this.convertModelToTableRows(p.value, rows.length - 1, indent + 2, rows);
|
||||
} else {
|
||||
row['value'] = removeLineBreaks(p.value, ' ');
|
||||
row['tooltip'] = p.value;
|
||||
@@ -220,52 +141,8 @@ export class ExecutionPlanPropertiesView {
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
|
||||
public toggleVisibility(): void {
|
||||
this._parentContainer.style.display = this._parentContainer.style.display === 'none' ? 'block' : 'none';
|
||||
this.renderView();
|
||||
}
|
||||
}
|
||||
|
||||
export interface GraphElementPropertyViewData {
|
||||
export interface ExecutionPlanPropertiesViewModel {
|
||||
graphElement: azdata.executionPlan.ExecutionPlanNode | azdata.executionPlan.ExecutionPlanEdge;
|
||||
}
|
||||
|
||||
export class ClosePropertyViewAction extends Action {
|
||||
public static ID = 'ep.propertiesView.close';
|
||||
public static LABEL = localize('executionPlanPropertyViewClose', "Close");
|
||||
|
||||
constructor() {
|
||||
super(ClosePropertyViewAction.ID, ClosePropertyViewAction.LABEL, Codicon.close.classNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanPropertiesView): Promise<void> {
|
||||
context.toggleVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
export class SortPropertiesAlphabeticallyAction extends Action {
|
||||
public static ID = 'ep.propertiesView.sortByAlphabet';
|
||||
public static LABEL = localize('executionPlanPropertyViewSortAlphabetically', "Alphabetical");
|
||||
|
||||
constructor() {
|
||||
super(SortPropertiesAlphabeticallyAction.ID, SortPropertiesAlphabeticallyAction.LABEL, sortAlphabeticallyIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanPropertiesView): Promise<void> {
|
||||
context.sortPropertiesAlphabetically();
|
||||
}
|
||||
}
|
||||
|
||||
export class SortPropertiesByDisplayOrderAction extends Action {
|
||||
public static ID = 'ep.propertiesView.sortByDisplayOrder';
|
||||
public static LABEL = localize('executionPlanPropertyViewSortByDisplayOrder', "Categorized");
|
||||
|
||||
constructor() {
|
||||
super(SortPropertiesByDisplayOrderAction.ID, SortPropertiesByDisplayOrderAction.LABEL, sortByDisplayOrderIconClassNames);
|
||||
}
|
||||
|
||||
public override async run(context: ExecutionPlanPropertiesView): Promise<void> {
|
||||
context.sortPropertiesByImportance();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user