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:
Anthony Dresser
2020-02-15 01:54:23 -06:00
committed by GitHub
parent 873c6a39fe
commit 506c6a5e5f
338 changed files with 815 additions and 724 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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