mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
More Layering (#9139)
* move handling generated files to the serilization classes * remove unneeded methods * add more folders to strictire compile, add more strict compile options * update ci * wip * add more layering and fix issues * add more strictness * remove unnecessary assertion * add missing checks * fix indentation * wip * remove jsdoc * fix layering * fix compile * fix compile errors * wip * wip * finish layering * fix css * more layering * rip * reworking results serializer * move some files around * move capabilities to platform wip * implement capabilities register provider * fix capabilities service * fix usage of the regist4ry * add contribution * wip * wip * wip * remove no longer good parts * fix strict-nulls * fix issues with startup * another try * fix startup * fix imports * fix tests * fix tests * fix more tests * fix tests * fix more tests * fix broken test * fix tabbing * fix naming * wip * finished layering * fix imports * fix valid layers * fix layers
This commit is contained in:
@@ -4,11 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ProfilerInput } from 'sql/workbench/contrib/profiler/browser/profilerInput';
|
||||
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as azdata from 'azdata';
|
||||
import { INewProfilerState } from 'sql/workbench/contrib/profiler/common/profilerState';
|
||||
import { INewProfilerState } from 'sql/workbench/common/editor/profiler/profilerState';
|
||||
|
||||
const PROFILER_SERVICE_ID = 'profilerService';
|
||||
export const IProfilerService = createDecorator<IProfilerService>(PROFILER_SERVICE_ID);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.065 13H15v2H2.056v-2h5.009zm3.661-12H7.385L8.44 2.061 7.505 3H15V1h-4.274zM3.237 9H2.056v2H15V9H3.237zm4.208-4l.995 1-.995 1H15V5H7.445z" fill="#424242"/><path d="M5.072 4.03L7.032 6 5.978 7.061l-1.96-1.97-1.961 1.97L1 6l1.96-1.97L1 2.061 2.056 1l1.96 1.97L5.977 1l1.057 1.061L5.072 4.03z" fill="#A1260D"/></svg>
|
||||
|
After Width: | Height: | Size: 419 B |
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.065 13H15v2H2.056v-2h5.009zm3.661-12H7.385L8.44 2.061 7.505 3H15V1h-4.274zM3.237 9H2.056v2H15V9H3.237zm4.208-4l.995 1-.995 1H15V5H7.445z" fill="#C5C5C5"/><path d="M5.072 4.03L7.032 6 5.978 7.061l-1.96-1.97-1.961 1.97L1 6l1.96-1.97L1 2.061 2.056 1l1.96 1.97L5.977 1l1.057 1.061L5.072 4.03z" fill="#F48771"/></svg>
|
||||
|
After Width: | Height: | Size: 419 B |
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.profiler-column-tree {
|
||||
height: calc(100% - 25px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-row > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.vs .codicon.clear-results {
|
||||
background-image: url('clear.svg');
|
||||
}
|
||||
|
||||
.vs-dark .codicon.clear-results {
|
||||
background-image: url('clear_inverse.svg');
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.profiler-filter-dialog {
|
||||
height: 300px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.profiler-filter-clause-table {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.clause-table-container{
|
||||
max-height: 270px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.profiler-filter-remove-condition {
|
||||
width:20px;
|
||||
height:20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profiler-filter-clause-table-action {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
padding: 2px;
|
||||
text-decoration: underline;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/profiler';
|
||||
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
|
||||
class EventItem {
|
||||
|
||||
constructor(
|
||||
private _name: string,
|
||||
private _parent: SessionItem,
|
||||
private _columns?: Array<ColumnItem>,
|
||||
) {
|
||||
if (!_columns) {
|
||||
this._columns = new Array<ColumnItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public hasChildren(): boolean {
|
||||
return this._columns && this._columns.length > 0;
|
||||
}
|
||||
|
||||
public getChildren(): Array<ColumnItem> {
|
||||
return this._columns;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public addColumn(...columns: Array<ColumnItem>) {
|
||||
this._columns = this._columns.concat(columns);
|
||||
}
|
||||
|
||||
public get parent(): SessionItem {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public get selected(): boolean {
|
||||
return this._columns.every(i => i.selected);
|
||||
}
|
||||
|
||||
public set selected(val: boolean) {
|
||||
this._columns.forEach(i => i.selected = val);
|
||||
}
|
||||
|
||||
public get indeterminate(): boolean {
|
||||
return this._columns.some(i => i.selected) && !this.selected;
|
||||
}
|
||||
}
|
||||
|
||||
class ColumnItem {
|
||||
public selected: boolean;
|
||||
public readonly indeterminate = false;
|
||||
constructor(
|
||||
private _name: string,
|
||||
private _parent: EventItem
|
||||
) { }
|
||||
|
||||
public get id(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get parent(): EventItem {
|
||||
return this._parent;
|
||||
}
|
||||
}
|
||||
|
||||
class ColumnSortedColumnItem {
|
||||
constructor(
|
||||
private _name: string,
|
||||
private _parent: SessionItem
|
||||
) { }
|
||||
|
||||
public get id(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get parent(): SessionItem {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public get selected(): boolean {
|
||||
return this._parent.getUnsortedChildren()
|
||||
.every(e => e.getChildren().filter(c => c.id === this.id)
|
||||
.every(c => c.selected));
|
||||
}
|
||||
|
||||
public set selected(val: boolean) {
|
||||
this._parent.getUnsortedChildren()
|
||||
.forEach(e => e.getChildren()
|
||||
.filter(c => c.id === this.id)
|
||||
.forEach(c => c.selected = val));
|
||||
}
|
||||
|
||||
public get indeterminate(): boolean {
|
||||
return this._parent.getUnsortedChildren()
|
||||
.some(e => e.getChildren()
|
||||
.filter(c => c.id === this.id)
|
||||
.some(c => c.selected))
|
||||
&& !this.selected;
|
||||
}
|
||||
}
|
||||
|
||||
class SessionItem {
|
||||
private _sortedColumnItems: Array<ColumnSortedColumnItem> = [];
|
||||
constructor(
|
||||
private _name: string,
|
||||
private _sort: 'event' | 'column',
|
||||
private _events?: Array<EventItem>
|
||||
) {
|
||||
if (!_events) {
|
||||
this._events = new Array<EventItem>();
|
||||
} else {
|
||||
_events.forEach(e => {
|
||||
e.getChildren().forEach(c => {
|
||||
if (!this._sortedColumnItems.some(i => i.id === c.id)) {
|
||||
this._sortedColumnItems.push(new ColumnSortedColumnItem(c.id, this));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public hasChildren(): boolean {
|
||||
if (this._sort === 'event') {
|
||||
return this._events && this._events.length > 0;
|
||||
} else {
|
||||
return this._events && this._events.some(i => i.hasChildren());
|
||||
}
|
||||
}
|
||||
|
||||
public getUnsortedChildren(): Array<EventItem> {
|
||||
return this._events;
|
||||
}
|
||||
|
||||
public getChildren(): Array<EventItem | ColumnSortedColumnItem> {
|
||||
if (this._sort === 'event') {
|
||||
return this._events;
|
||||
} else {
|
||||
return this._sortedColumnItems;
|
||||
}
|
||||
}
|
||||
|
||||
public addEvents(...events: Array<EventItem>) {
|
||||
this._events = this._events.concat(events);
|
||||
events.forEach(e => {
|
||||
e.getChildren().forEach(c => {
|
||||
if (!this._sortedColumnItems.some(i => i.id === c.id)) {
|
||||
this._sortedColumnItems.push(new ColumnSortedColumnItem(c.id, this));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public changeSort(type: 'event' | 'column'): void {
|
||||
this._sort = type;
|
||||
}
|
||||
}
|
||||
|
||||
class TreeRenderer implements IRenderer {
|
||||
|
||||
private _onSelectedChange = new Emitter<any>();
|
||||
public onSelectedChange: Event<any> = this._onSelectedChange.event;
|
||||
|
||||
getHeight(tree: ITree, element: any): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(tree: ITree, element: any): string {
|
||||
if (element instanceof SessionItem) {
|
||||
return 'session';
|
||||
} else if (element instanceof EventItem) {
|
||||
return 'event';
|
||||
} else if (element instanceof ColumnItem) {
|
||||
return 'column';
|
||||
} else if (element instanceof ColumnSortedColumnItem) {
|
||||
return 'columnSorted';
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): RenderTemplate {
|
||||
const data = Object.create(null);
|
||||
const row = document.createElement('div');
|
||||
row.className = 'tree-row';
|
||||
DOM.append(container, row);
|
||||
data.toDispose = [];
|
||||
data.checkbox = document.createElement('input');
|
||||
DOM.append(row, data.checkbox);
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDispose.push(DOM.addStandardDisposableListener(data.checkbox, 'change', () => {
|
||||
data.context.selected = !data.context.selected;
|
||||
this._onSelectedChange.fire(data.context);
|
||||
}));
|
||||
data.label = document.createElement('div');
|
||||
DOM.append(row, data.label);
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: any, templateId: string, templateData: RenderTemplate): void {
|
||||
templateData.context = element;
|
||||
templateData.label.innerText = element.id;
|
||||
templateData.checkbox.checked = element.selected;
|
||||
templateData.checkbox.indeterminate = element.indeterminate;
|
||||
}
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, templateData: RenderTemplate): void {
|
||||
dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
interface RenderTemplate {
|
||||
label: HTMLElement;
|
||||
toDispose: Array<IDisposable>;
|
||||
checkbox: HTMLInputElement;
|
||||
context?: any;
|
||||
}
|
||||
|
||||
class TreeDataSource implements IDataSource {
|
||||
|
||||
getId(tree: ITree, element: any): string {
|
||||
if (element instanceof EventItem) {
|
||||
return element.parent.id + element.id;
|
||||
} else if (element instanceof ColumnItem) {
|
||||
return element.parent.parent.id + element.parent.id + element.id;
|
||||
} else if (element instanceof SessionItem) {
|
||||
return element.id;
|
||||
} else if (element instanceof ColumnSortedColumnItem) {
|
||||
return element.id;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
hasChildren(tree: ITree, element: any): boolean {
|
||||
if (element instanceof SessionItem) {
|
||||
return element.hasChildren();
|
||||
} else if (element instanceof EventItem) {
|
||||
return element.hasChildren();
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, element: any): Promise<Array<any>> {
|
||||
if (element instanceof EventItem) {
|
||||
return Promise.resolve(element.getChildren());
|
||||
} else if (element instanceof SessionItem) {
|
||||
return Promise.resolve(element.getChildren());
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
getParent(tree: ITree, element: any): Promise<any> {
|
||||
if (element instanceof ColumnItem) {
|
||||
return Promise.resolve(element.parent);
|
||||
} else if (element instanceof EventItem) {
|
||||
return Promise.resolve(element.parent);
|
||||
} else if (element instanceof ColumnSortedColumnItem) {
|
||||
return Promise.resolve(element.parent);
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
shouldAutoexpand?(tree: ITree, element: any): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProfilerColumnEditorDialog extends Modal {
|
||||
|
||||
private _selectBox: SelectBox;
|
||||
private readonly _options = [
|
||||
{ text: nls.localize('eventSort', "Sort by event") },
|
||||
{ text: nls.localize('nameColumn', "Sort by column") }
|
||||
];
|
||||
private _tree: Tree;
|
||||
private _element: SessionItem;
|
||||
private _treeContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super(nls.localize('profilerColumnDialog.profiler', "Profiler"), TelemetryKeys.Profiler, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService);
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
super.render();
|
||||
this._register(attachModalDialogStyler(this, this._themeService));
|
||||
this.addFooterButton(nls.localize('profilerColumnDialog.ok', "OK"), () => this.onAccept(undefined));
|
||||
this.addFooterButton(nls.localize('profilerColumnDialog.cancel', "Cancel"), () => this.onClose(undefined));
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
const body = DOM.append(container, DOM.$(''));
|
||||
this._selectBox = new SelectBox(this._options, 0, this._contextViewService);
|
||||
this._selectBox.render(body);
|
||||
this._register(this._selectBox.onDidSelect(e => {
|
||||
this._element.changeSort(e.index === 0 ? 'event' : 'column');
|
||||
this._tree.refresh(this._element, true);
|
||||
}));
|
||||
this._treeContainer = DOM.append(body, DOM.$('.profiler-column-tree'));
|
||||
const renderer = new TreeRenderer();
|
||||
this._tree = new Tree(this._treeContainer, { dataSource: new TreeDataSource(), renderer });
|
||||
this._register(renderer.onSelectedChange(e => this._tree.refresh(e, true)));
|
||||
this._register(attachListStyler(this._tree, this._themeService));
|
||||
}
|
||||
|
||||
public open(input: ProfilerInput): void {
|
||||
super.show();
|
||||
this._updateList();
|
||||
}
|
||||
|
||||
protected onAccept(e: StandardKeyboardEvent): void {
|
||||
this._updateInput();
|
||||
super.onAccept(e);
|
||||
}
|
||||
|
||||
// currently not used, this dialog is a work in progress
|
||||
// tracked in issue #1545 https://github.com/Microsoft/azuredatastudio/issues/1545
|
||||
private _updateInput(): void {
|
||||
/*
|
||||
this._element.getUnsortedChildren().forEach(e => {
|
||||
let origEvent = this._input.sessionTemplate.view.events.find(i => i.name === e.id);
|
||||
if (e.indeterminate) {
|
||||
e.getChildren().forEach(c => {
|
||||
if (origEvent.columns.includes(c.id) && !c.selected) {
|
||||
origEvent.columns = origEvent.columns.filter(i => i !== c.id);
|
||||
} else if (!origEvent.columns.includes(c.id) && c.selected) {
|
||||
origEvent.columns.push(c.id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
origEvent.columns = e.getChildren()
|
||||
.filter(c => c.selected)
|
||||
.map(c => c.id);
|
||||
}
|
||||
});
|
||||
let newColumns = this._input.sessionTemplate.view.events.reduce<Array<string>>((p, e) => {
|
||||
e.columns.forEach(c => {
|
||||
if (!p.includes(c)) {
|
||||
p.push(c);
|
||||
}
|
||||
});
|
||||
return p;
|
||||
}, []);
|
||||
newColumns.unshift('EventClass');
|
||||
this._input.setColumns(newColumns);
|
||||
*/
|
||||
}
|
||||
|
||||
// currently not used, this dialog is a work in progress
|
||||
// tracked in issue #1545 https://github.com/Microsoft/azuredatastudio/issues/1545
|
||||
private _updateList(): void {
|
||||
/*
|
||||
this._element = new SessionItem(this._input.sessionTemplate.name, this._selectedValue === 0 ? 'event' : 'column');
|
||||
this._input.sessionTemplate.events.forEach(item => {
|
||||
let event = new EventItem(item.name, this._element);
|
||||
item.optionalColumns.forEach(col => {
|
||||
let column = new ColumnItem(col, event);
|
||||
column.selected = this._input.sessionTemplate.view.events.find(i => i.name === event.id).columns.includes(col);
|
||||
event.addColumn(column);
|
||||
});
|
||||
this._element.addEvents(event);
|
||||
});
|
||||
this._tree.setInput(this._element);
|
||||
this._tree.layout(DOM.getTotalHeight(this._treeContainer));
|
||||
*/
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
this._tree.layout(DOM.getContentHeight(this._treeContainer));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/profilerFilterDialog';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { attachButtonStyler, attachInputBoxStyler } from 'sql/platform/theme/common/styler';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ProfilerFilter, ProfilerFilterClause, ProfilerFilterClauseOperator, IProfilerService } from 'sql/workbench/services/profiler/browser/interfaces';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { find, firstIndex } from 'vs/base/common/arrays';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
|
||||
|
||||
const ClearText: string = localize('profilerFilterDialog.clear', "Clear all");
|
||||
const ApplyText: string = localize('profilerFilterDialog.apply', "Apply");
|
||||
const OkText: string = localize('profilerFilterDialog.ok', "OK");
|
||||
const CancelText: string = localize('profilerFilterDialog.cancel', "Cancel");
|
||||
const DialogTitle: string = localize('profilerFilterDialog.title', "Filters");
|
||||
const RemoveText: string = localize('profilerFilterDialog.remove', "Remove this clause");
|
||||
const SaveFilterText: string = localize('profilerFilterDialog.saveFilter', "Save Filter");
|
||||
const LoadFilterText: string = localize('profilerFilterDialog.loadFilter', "Load Filter");
|
||||
const AddClauseText: string = localize('profilerFilterDialog.addClauseText', "Add a clause");
|
||||
const TitleIconClass: string = 'icon filterLabel';
|
||||
|
||||
const FieldText: string = localize('profilerFilterDialog.fieldColumn', "Field");
|
||||
const OperatorText: string = localize('profilerFilterDialog.operatorColumn', "Operator");
|
||||
const ValueText: string = localize('profilerFilterDialog.valueColumn', "Value");
|
||||
|
||||
const Equals: string = '=';
|
||||
const NotEquals: string = '<>';
|
||||
const LessThan: string = '<';
|
||||
const LessThanOrEquals: string = '<=';
|
||||
const GreaterThan: string = '>';
|
||||
const GreaterThanOrEquals: string = '>=';
|
||||
const IsNull: string = localize('profilerFilterDialog.isNullOperator', "Is Null");
|
||||
const IsNotNull: string = localize('profilerFilterDialog.isNotNullOperator', "Is Not Null");
|
||||
const Contains: string = localize('profilerFilterDialog.containsOperator', "Contains");
|
||||
const NotContains: string = localize('profilerFilterDialog.notContainsOperator', "Not Contains");
|
||||
const StartsWith: string = localize('profilerFilterDialog.startsWithOperator', "Starts With");
|
||||
const NotStartsWith: string = localize('profilerFilterDialog.notStartsWithOperator', "Not Starts With");
|
||||
|
||||
const Operators = [Equals, NotEquals, LessThan, LessThanOrEquals, GreaterThan, GreaterThanOrEquals, GreaterThan, GreaterThanOrEquals, IsNull, IsNotNull, Contains, NotContains, StartsWith, NotStartsWith];
|
||||
|
||||
export class ProfilerFilterDialog extends Modal {
|
||||
|
||||
private _clauseBuilder: HTMLElement;
|
||||
private _okButton: Button;
|
||||
private _cancelButton: Button;
|
||||
private _applyButton: Button;
|
||||
private _loadFilterButton: Button;
|
||||
private _saveFilterButton: Button;
|
||||
private _input: ProfilerInput;
|
||||
private _clauseRows: ClauseRowUI[] = [];
|
||||
|
||||
|
||||
constructor(
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ILogService logService: ILogService,
|
||||
@IContextViewService private contextViewService: IContextViewService,
|
||||
@IProfilerService private profilerService: IProfilerService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super('', TelemetryKeys.ProfilerFilter, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { isFlyout: false, hasTitleIcon: true });
|
||||
}
|
||||
|
||||
public open(input: ProfilerInput) {
|
||||
this._input = input;
|
||||
this.render();
|
||||
this.show();
|
||||
this._okButton.focus();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
this.title = DialogTitle;
|
||||
this.titleIconClassName = TitleIconClass;
|
||||
this._register(attachModalDialogStyler(this, this._themeService));
|
||||
this._saveFilterButton = this.addFooterButton(SaveFilterText, () => this.saveFilter(), 'left');
|
||||
this._loadFilterButton = this.addFooterButton(LoadFilterText, () => this.loadSavedFilter(), 'left');
|
||||
this._applyButton = this.addFooterButton(ApplyText, () => this.filterSession());
|
||||
this._okButton = this.addFooterButton(OkText, () => this.handleOkButtonClick());
|
||||
this._cancelButton = this.addFooterButton(CancelText, () => this.hide());
|
||||
this._register(attachButtonStyler(this._okButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._cancelButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._applyButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._saveFilterButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._loadFilterButton, this._themeService));
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
const body = DOM.append(container, DOM.$('.profiler-filter-dialog'));
|
||||
const clauseTableContainer = DOM.append(body, DOM.$('.clause-table-container'));
|
||||
this._clauseBuilder = DOM.append(clauseTableContainer, DOM.$('table.profiler-filter-clause-table'));
|
||||
const headerRow = DOM.append(this._clauseBuilder, DOM.$('tr'));
|
||||
DOM.append(headerRow, DOM.$('td')).innerText = FieldText;
|
||||
DOM.append(headerRow, DOM.$('td')).innerText = OperatorText;
|
||||
DOM.append(headerRow, DOM.$('td')).innerText = ValueText;
|
||||
DOM.append(headerRow, DOM.$('td')).innerText = '';
|
||||
|
||||
this._input.filter.clauses.forEach(clause => {
|
||||
this.addClauseRow(true, clause.field, this.convertToOperatorString(clause.operator), clause.value);
|
||||
});
|
||||
|
||||
this.createClauseTableActionLink(AddClauseText, body, () => { this.addClauseRow(false); });
|
||||
this.createClauseTableActionLink(ClearText, body, () => { this.handleClearButtonClick(); });
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
// Nothing to re-layout
|
||||
}
|
||||
|
||||
/* espace key */
|
||||
protected onClose() {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/* enter key */
|
||||
protected onAccept() {
|
||||
this.handleOkButtonClick();
|
||||
}
|
||||
|
||||
private handleOkButtonClick(): void {
|
||||
this.filterSession();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
private handleClearButtonClick() {
|
||||
this._clauseRows.forEach(clause => {
|
||||
clause.row.remove();
|
||||
});
|
||||
this._clauseRows = [];
|
||||
}
|
||||
|
||||
private createClauseTableActionLink(text: string, parent: HTMLElement, handler: () => void): void {
|
||||
const actionLink = DOM.append(parent, DOM.$('.profiler-filter-clause-table-action', {
|
||||
'tabIndex': '0',
|
||||
'role': 'button'
|
||||
}));
|
||||
actionLink.innerText = text;
|
||||
DOM.addDisposableListener(actionLink, DOM.EventType.CLICK, handler);
|
||||
DOM.addStandardDisposableListener(actionLink, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
||||
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
|
||||
handler();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private createSelectBox(container: HTMLElement, options: string[], selectedOption: string, ariaLabel: string): SelectBox {
|
||||
const dropdown = new SelectBox(options, selectedOption, this.contextViewService, undefined, { ariaLabel: ariaLabel });
|
||||
dropdown.render(container);
|
||||
this._register(attachSelectBoxStyler(dropdown, this._themeService));
|
||||
return dropdown;
|
||||
}
|
||||
|
||||
private filterSession(): void {
|
||||
this._input.filterSession(this.getFilter());
|
||||
}
|
||||
|
||||
private saveFilter(): void {
|
||||
this.profilerService.saveFilter(this.getFilter()).catch(e => onUnexpectedError(e));
|
||||
}
|
||||
|
||||
private loadSavedFilter(): void {
|
||||
// for now we only have one saved filter, this is enough for what user asked for so far.
|
||||
const savedFilters = this.profilerService.getFilters();
|
||||
if (savedFilters && savedFilters.length > 0) {
|
||||
const savedFilter = savedFilters[0];
|
||||
this._clauseRows.forEach(clause => {
|
||||
clause.row.remove();
|
||||
});
|
||||
this._clauseRows = [];
|
||||
savedFilter.clauses.forEach(clause => {
|
||||
this.addClauseRow(true, clause.field, this.convertToOperatorString(clause.operator), clause.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getFilter(): ProfilerFilter {
|
||||
const clauses: ProfilerFilterClause[] = [];
|
||||
|
||||
this._clauseRows.forEach(row => {
|
||||
clauses.push({
|
||||
field: row.field.value,
|
||||
operator: this.convertToOperatorEnum(row.operator.value),
|
||||
value: row.value.value
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'default',
|
||||
clauses: clauses
|
||||
};
|
||||
}
|
||||
|
||||
private addClauseRow(setInitialValue: boolean, field?: string, operator?: string, value?: string): void {
|
||||
const columns = this._input.columns.map(column => column.name);
|
||||
if (field && !find(columns, x => x === field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const row = DOM.append(this._clauseBuilder, DOM.$('tr'));
|
||||
const clauseId = generateUuid();
|
||||
|
||||
const fieldDropDown = this.createSelectBox(DOM.append(row, DOM.$('td')), columns, columns[0], FieldText);
|
||||
|
||||
const operatorDropDown = this.createSelectBox(DOM.append(row, DOM.$('td')), Operators, Operators[0], OperatorText);
|
||||
|
||||
const valueText = new InputBox(DOM.append(row, DOM.$('td')), undefined, {});
|
||||
this._register(attachInputBoxStyler(valueText, this._themeService));
|
||||
|
||||
const removeCell = DOM.append(row, DOM.$('td'));
|
||||
const removeClauseButton = DOM.append(removeCell, DOM.$('.profiler-filter-remove-condition.codicon.remove', {
|
||||
'tabIndex': '0',
|
||||
'aria-label': RemoveText,
|
||||
'title': RemoveText,
|
||||
'role': 'button'
|
||||
}));
|
||||
|
||||
DOM.addStandardDisposableListener(removeClauseButton, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
||||
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
|
||||
this.removeRow(clauseId);
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
DOM.addDisposableListener(removeClauseButton, DOM.EventType.CLICK, (e: MouseEvent) => {
|
||||
this.removeRow(clauseId);
|
||||
});
|
||||
|
||||
if (setInitialValue) {
|
||||
fieldDropDown.selectWithOptionName(field);
|
||||
operatorDropDown.selectWithOptionName(operator);
|
||||
valueText.value = value;
|
||||
}
|
||||
|
||||
this._clauseRows.push({
|
||||
id: clauseId,
|
||||
row,
|
||||
field: fieldDropDown,
|
||||
operator: operatorDropDown,
|
||||
value: valueText
|
||||
});
|
||||
}
|
||||
|
||||
private removeRow(clauseId: string) {
|
||||
const idx = firstIndex(this._clauseRows, (entry) => { return entry.id === clauseId; });
|
||||
if (idx !== -1) {
|
||||
this._clauseRows[idx].row.remove();
|
||||
this._clauseRows.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
private convertToOperatorEnum(operator: string): ProfilerFilterClauseOperator {
|
||||
switch (operator) {
|
||||
case Equals:
|
||||
return ProfilerFilterClauseOperator.Equals;
|
||||
case NotEquals:
|
||||
return ProfilerFilterClauseOperator.NotEquals;
|
||||
case LessThan:
|
||||
return ProfilerFilterClauseOperator.LessThan;
|
||||
case LessThanOrEquals:
|
||||
return ProfilerFilterClauseOperator.LessThanOrEquals;
|
||||
case GreaterThan:
|
||||
return ProfilerFilterClauseOperator.GreaterThan;
|
||||
case GreaterThanOrEquals:
|
||||
return ProfilerFilterClauseOperator.GreaterThanOrEquals;
|
||||
case IsNull:
|
||||
return ProfilerFilterClauseOperator.IsNull;
|
||||
case IsNotNull:
|
||||
return ProfilerFilterClauseOperator.IsNotNull;
|
||||
case Contains:
|
||||
return ProfilerFilterClauseOperator.Contains;
|
||||
case NotContains:
|
||||
return ProfilerFilterClauseOperator.NotContains;
|
||||
case StartsWith:
|
||||
return ProfilerFilterClauseOperator.StartsWith;
|
||||
case NotStartsWith:
|
||||
return ProfilerFilterClauseOperator.NotStartsWith;
|
||||
default:
|
||||
throw new Error(`Not a valid operator: ${operator}`);
|
||||
}
|
||||
}
|
||||
|
||||
private convertToOperatorString(operator: ProfilerFilterClauseOperator): string {
|
||||
switch (operator) {
|
||||
case ProfilerFilterClauseOperator.Equals:
|
||||
return Equals;
|
||||
case ProfilerFilterClauseOperator.NotEquals:
|
||||
return NotEquals;
|
||||
case ProfilerFilterClauseOperator.LessThan:
|
||||
return LessThan;
|
||||
case ProfilerFilterClauseOperator.LessThanOrEquals:
|
||||
return LessThanOrEquals;
|
||||
case ProfilerFilterClauseOperator.GreaterThan:
|
||||
return GreaterThan;
|
||||
case ProfilerFilterClauseOperator.GreaterThanOrEquals:
|
||||
return GreaterThanOrEquals;
|
||||
case ProfilerFilterClauseOperator.IsNull:
|
||||
return IsNull;
|
||||
case ProfilerFilterClauseOperator.IsNotNull:
|
||||
return IsNotNull;
|
||||
case ProfilerFilterClauseOperator.Contains:
|
||||
return Contains;
|
||||
case ProfilerFilterClauseOperator.NotContains:
|
||||
return NotContains;
|
||||
case ProfilerFilterClauseOperator.StartsWith:
|
||||
return StartsWith;
|
||||
case ProfilerFilterClauseOperator.NotStartsWith:
|
||||
return NotStartsWith;
|
||||
default:
|
||||
throw new Error(`Not a valid operator: ${operator}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ClauseRowUI {
|
||||
id: string;
|
||||
row: HTMLElement;
|
||||
field: SelectBox;
|
||||
operator: SelectBox;
|
||||
value: InputBox;
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings, EngineType, ProfilerFilter, PROFILER_FILTER_SETTINGS } from './interfaces';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ProfilerInput } from 'sql/workbench/contrib/profiler/browser/profilerInput';
|
||||
import { ProfilerColumnEditorDialog } from 'sql/workbench/contrib/profiler/browser/profilerColumnEditorDialog';
|
||||
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
|
||||
import { ProfilerColumnEditorDialog } from 'sql/workbench/services/profiler/browser/profilerColumnEditorDialog';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { ProfilerFilterDialog } from 'sql/workbench/contrib/profiler/browser/profilerFilterDialog';
|
||||
import { ProfilerFilterDialog } from 'sql/workbench/services/profiler/browser/profilerFilterDialog';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
class TwoWayMap<T, K> {
|
||||
|
||||
Reference in New Issue
Block a user