mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 01:25:36 -05:00
Adding properties grid enhancements in execution plan (#20208)
* init push * Fixing properties in plan comparison * Add long Text Cell viewer * Disabling auto edit by default * Removing text editor
This commit is contained in:
@@ -352,7 +352,7 @@ export class ExecutionPlanComparisonEditorView {
|
||||
|
||||
if (this.activeBottomPlanDiagram) {
|
||||
const element = this.activeBottomPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
||||
if (similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||
if (this.activeBottomPlanDiagram.getSelectedElement() && similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,7 +392,7 @@ export class ExecutionPlanComparisonEditorView {
|
||||
|
||||
if (this.activeTopPlanDiagram) {
|
||||
const element = this.activeTopPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
||||
if (similarNode.matchingNodesId.find(m => this.activeTopPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||
if (this.activeTopPlanDiagram.getSelectedElement() && similarNode.matchingNodesId.find(m => this.activeTopPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import { InternalExecutionPlanElement } from 'sql/workbench/contrib/executionPla
|
||||
import { executionPlanComparisonPropertiesDifferent, executionPlanComparisonPropertiesUpArrow, executionPlanComparisonPropertiesDownArrow } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { TextWithIconColumn } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export enum ExecutionPlanCompareOrientation {
|
||||
Horizontal = 'horizontal',
|
||||
@@ -37,8 +39,10 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
public constructor(
|
||||
parentContainer: HTMLElement,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService
|
||||
) {
|
||||
super(parentContainer, themeService);
|
||||
super(parentContainer, themeService, instantiationService, contextMenuService);
|
||||
this._model = <ExecutionPlanComparisonPropertiesViewModel>{};
|
||||
this._parentContainer.style.display = 'none';
|
||||
const header = DOM.$('.compare-operation-name');
|
||||
@@ -128,30 +132,27 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
name: localize('nodePropertyViewNameNameColumnHeader', "Name"),
|
||||
field: 'name',
|
||||
width: 200,
|
||||
editor: Slick.Editors.Text,
|
||||
headerCssClass: 'prop-table-header',
|
||||
formatter: textFormatter
|
||||
});
|
||||
columns.push({
|
||||
id: 'value',
|
||||
id: 'value1',
|
||||
name: getPropertyViewNameValueColumnTopHeaderForOrientation(this._orientation),
|
||||
field: 'primary',
|
||||
width: 150,
|
||||
editor: Slick.Editors.Text,
|
||||
headerCssClass: 'prop-table-header',
|
||||
formatter: textFormatter
|
||||
});
|
||||
}
|
||||
if (this._model.bottomElement) {
|
||||
columns.push(new TextWithIconColumn({
|
||||
id: 'value',
|
||||
id: 'value2',
|
||||
name: getPropertyViewNameValueColumnBottomHeaderForOrientation(this._orientation),
|
||||
field: 'secondary',
|
||||
width: 150,
|
||||
headerCssClass: 'prop-table-header',
|
||||
}).definition);
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
@@ -313,6 +314,7 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,16 @@ export class ExecutionPlanEditor extends EditorPane {
|
||||
public layout(dimension: DOM.Dimension): void {
|
||||
}
|
||||
|
||||
override clearInput(): void {
|
||||
const currentInput = this.input as ExecutionPlanInput;
|
||||
// clearing old input view if present in the editor
|
||||
if (currentInput?._executionPlanFileViewUUID) {
|
||||
const oldView = this._viewCache.executionPlanFileViewMap.get(currentInput._executionPlanFileViewUUID);
|
||||
oldView.onHide(this._parentContainer);
|
||||
}
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
public override async setInput(newInput: ExecutionPlanInput, options: IEditorOptions, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
|
||||
const oldInput = this.input as ExecutionPlanInput;
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import { removeLineBreaks } from 'sql/base/common/strings';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { textFormatter } from 'sql/base/browser/ui/table/formatters';
|
||||
import { ExecutionPlanPropertiesViewBase, PropertiesSortType } from 'sql/workbench/contrib/executionPlan/browser/executionPlanPropertiesViewBase';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase {
|
||||
// Div that holds the name of the element selected
|
||||
@@ -19,9 +21,11 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
|
||||
public constructor(
|
||||
parentContainer: HTMLElement,
|
||||
themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
) {
|
||||
super(parentContainer, themeService);
|
||||
super(parentContainer, themeService, instantiationService, contextMenuService);
|
||||
this._model = <ExecutionPlanPropertiesView>{};
|
||||
this._operationName = DOM.$('h3');
|
||||
this._operationName.classList.add('operation-name');
|
||||
@@ -91,7 +95,6 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
name: localize('nodePropertyViewNameNameColumnHeader', "Name"),
|
||||
field: 'name',
|
||||
width: 250,
|
||||
editor: Slick.Editors.Text,
|
||||
headerCssClass: 'prop-table-header',
|
||||
formatter: textFormatter
|
||||
},
|
||||
@@ -100,7 +103,6 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
name: localize('nodePropertyViewNameValueColumnHeader', "Value"),
|
||||
field: 'value',
|
||||
width: 250,
|
||||
editor: Slick.Editors.Text,
|
||||
headerCssClass: 'prop-table-header',
|
||||
formatter: textFormatter
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ import { RESULTS_GRID_DEFAULTS } from 'sql/workbench/common/constants';
|
||||
import { contrastBorder, listHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { TreeGrid } from 'sql/base/browser/ui/table/treeGrid';
|
||||
import { ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
|
||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
|
||||
export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLayoutProvider {
|
||||
// Title bar with close button action
|
||||
@@ -42,9 +48,13 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
|
||||
public resizeSash: Sash;
|
||||
|
||||
private _selectionModel: CellSelectionModel<Slick.SlickData>;
|
||||
|
||||
constructor(
|
||||
public _parentContainer: HTMLElement,
|
||||
private _themeService: IThemeService
|
||||
private _themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService
|
||||
) {
|
||||
|
||||
const sashContainer = DOM.$('.properties-sash');
|
||||
@@ -103,14 +113,38 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
const table = DOM.$('.table');
|
||||
this._tableContainer.appendChild(table);
|
||||
|
||||
this._selectionModel = new CellSelectionModel<Slick.SlickData>();
|
||||
|
||||
this._tableComponent = new TreeGrid(table, {
|
||||
columns: []
|
||||
}, {
|
||||
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
||||
forceFitColumns: true,
|
||||
defaultColumnWidth: 120
|
||||
defaultColumnWidth: 120,
|
||||
editable: true,
|
||||
autoEdit: false
|
||||
});
|
||||
attachTableStyler(this._tableComponent, this._themeService);
|
||||
this._tableComponent.setSelectionModel(this._selectionModel);
|
||||
|
||||
const contextMenuAction = [
|
||||
this._instantiationService.createInstance(CopyTableData),
|
||||
];
|
||||
|
||||
this._tableComponent.onContextMenu(e => {
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => contextMenuAction,
|
||||
getActionsContext: () => this.getCopyString()
|
||||
});
|
||||
});
|
||||
|
||||
let copyHandler = new CopyKeybind<any>();
|
||||
this._tableComponent.registerPlugin(copyHandler);
|
||||
|
||||
copyHandler.onCopy(e => {
|
||||
this._instantiationService.createInstance(CopyTableData).run(this.getCopyString());
|
||||
});
|
||||
|
||||
new ResizeObserver((e) => {
|
||||
this.resizeSash.layout();
|
||||
@@ -118,6 +152,33 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
}).observe(_parentContainer);
|
||||
}
|
||||
|
||||
|
||||
public getCopyString(): string {
|
||||
const selectedDataRange = this._selectionModel.getSelectedRanges()[0];
|
||||
let csvString = '';
|
||||
if (selectedDataRange) {
|
||||
const data = [];
|
||||
|
||||
for (let rowIndex = selectedDataRange.fromRow; rowIndex <= selectedDataRange.toRow; rowIndex++) {
|
||||
const dataRow = this._tableComponent.getData().getItem(rowIndex);
|
||||
const row = [];
|
||||
for (let colIndex = selectedDataRange.fromCell; colIndex <= selectedDataRange.toCell; colIndex++) {
|
||||
const dataItem = dataRow[this._tableComponent.grid.getColumns()[colIndex].field];
|
||||
if (dataItem) {
|
||||
row.push(isString(dataItem) ? dataItem : dataItem.displayText ?? dataItem.text ?? dataItem.title);
|
||||
} else {
|
||||
row.push('');
|
||||
}
|
||||
}
|
||||
data.push(row);
|
||||
}
|
||||
csvString = data.map(row =>
|
||||
row.map(x => `${x}`).join('\t')
|
||||
).join('\n');
|
||||
}
|
||||
return csvString;
|
||||
}
|
||||
|
||||
getVerticalSashLeft(sash: Sash): number {
|
||||
return 0;
|
||||
}
|
||||
@@ -273,3 +334,19 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export class CopyTableData extends Action {
|
||||
public static ID = 'ep.CopyTableData';
|
||||
public static LABEL = localize('ep.topOperationsCopyTableData', "Copy");
|
||||
|
||||
constructor(
|
||||
@IClipboardService private _clipboardService: IClipboardService
|
||||
) {
|
||||
super(CopyTableData.ID, CopyTableData.LABEL, '');
|
||||
}
|
||||
|
||||
public override async run(context: string): Promise<void> {
|
||||
|
||||
this._clipboardService.writeText(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
@@ -75,7 +74,6 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
private _executionPlanFileView: ExecutionPlanFileView,
|
||||
private _queryResultsView: QueryResultsView,
|
||||
@IInstantiationService public readonly _instantiationService: IInstantiationService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@IContextViewService public readonly contextViewService: IContextViewService,
|
||||
@IUntitledTextEditorService private readonly _untitledEditorService: IUntitledTextEditorService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@@ -144,7 +142,7 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
// container properties
|
||||
this._propContainer = DOM.$('.properties');
|
||||
this.container.appendChild(this._propContainer);
|
||||
this.propertiesView = new ExecutionPlanPropertiesView(this._propContainer, this._themeService);
|
||||
this.propertiesView = this._instantiationService.createInstance(ExecutionPlanPropertiesView, this._propContainer);
|
||||
|
||||
this._widgetContainer = DOM.$('.plan-action-container');
|
||||
this._planContainer.appendChild(this._widgetContainer);
|
||||
@@ -197,7 +195,7 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
this._actionBar.pushAction(actionBarActions, { icon: true, label: false });
|
||||
|
||||
const self = this;
|
||||
this.container.oncontextmenu = (e: MouseEvent) => {
|
||||
this._planContainer.oncontextmenu = (e: MouseEvent) => {
|
||||
if (contextMenuAction) {
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => {
|
||||
|
||||
Reference in New Issue
Block a user