diff --git a/extensions/mssql/config.json b/extensions/mssql/config.json index 5b07c11816..e0f3bf0188 100644 --- a/extensions/mssql/config.json +++ b/extensions/mssql/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "3.0.0-release.204", + "version": "3.0.0-release.206", "downloadFileNames": { "Windows_86": "win-x86-net6.0.zip", "Windows_64": "win-x64-net6.0.zip", diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 7f2cb73070..3bbbe56c9a 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1425,7 +1425,7 @@ declare module 'azdata' { /** * Error messages of current state, and the property the caused the error. */ - errors?: { message: string, property?: DesignerEditPath }[]; + errors?: { message: string, propertyPath?: DesignerEditPath }[]; } /** diff --git a/src/sql/base/browser/ui/panel/panel.ts b/src/sql/base/browser/ui/panel/panel.ts index 90bc1ba0b2..b55c65ef14 100644 --- a/src/sql/base/browser/ui/panel/panel.ts +++ b/src/sql/base/browser/ui/panel/panel.ts @@ -120,8 +120,8 @@ export class TabbedPanel extends Disposable { this._styleElement.remove(); } - public contains(tab: IPanelTab): boolean { - return this._tabMap.has(tab.identifier); + public contains(tabId: string): boolean { + return this._tabMap.has(tabId); } public pushTab(tab: IPanelTab, index?: number, destroyTabBody?: boolean): PanelTabIdentifier { diff --git a/src/sql/workbench/browser/designer/designer.ts b/src/sql/workbench/browser/designer/designer.ts index 4e59c03415..d723126ac4 100644 --- a/src/sql/workbench/browser/designer/designer.ts +++ b/src/sql/workbench/browser/designer/designer.ts @@ -6,7 +6,7 @@ import { DesignerComponentInput, DesignerEditType, DesignerTab, DesignerEdit, DesignerEditPath, DesignerViewModel, DesignerDataPropertyInfo, DesignerTableComponentRowData, DesignerTableProperties, InputBoxProperties, DropDownProperties, CheckBoxProperties, - DesignerEditProcessedEventArgs, DesignerStateChangedEventArgs, DesignerAction, DesignerUIState, DesignerTextEditor, ScriptProperty, DesignerRootObjectPath + DesignerEditProcessedEventArgs, DesignerStateChangedEventArgs, DesignerAction, DesignerUIState, ScriptProperty, DesignerRootObjectPath } from 'sql/workbench/browser/designer/interfaces'; import { IPanelTab, ITabbedPanelStyles, TabbedPanel } from 'sql/base/browser/ui/panel/panel'; @@ -34,9 +34,10 @@ import { Color } from 'vs/base/common/color'; import { LoadingSpinner } from 'sql/base/browser/ui/loadingSpinner/loadingSpinner'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { DesignerScriptEditor } from 'sql/workbench/browser/designer/designerScriptEditor'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { DesignerMessagesTabPanelView } from 'sql/workbench/browser/designer/designerMessagesTabPanelView'; +import { DesignerScriptEditorTabPanelView } from 'sql/workbench/browser/designer/designerScriptEditorTabPanelView'; export interface IDesignerStyle { tabbedPanelStyles?: ITabbedPanelStyles; @@ -56,6 +57,8 @@ export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, compo const TableRowHeight = 25; const TableHeaderRowHeight = 28; +const ScriptTabId = 'scripts'; +const MessagesTabId = 'messages'; type DesignerUIArea = 'PropertiesView' | 'ScriptView' | 'TopContentView' | 'TabsView'; @@ -67,7 +70,8 @@ export class Designer extends Disposable implements IThemable { private _editorContainer: HTMLElement; private _horizontalSplitView: SplitView; private _verticalSplitView: SplitView; - private _tabbedPanel: TabbedPanel; + private _contentTabbedPanel: TabbedPanel; + private _scriptTabbedPannel: TabbedPanel; private _contentContainer: HTMLElement; private _topContentContainer: HTMLElement; private _propertiesPaneContainer: HTMLElement; @@ -81,7 +85,8 @@ export class Designer extends Disposable implements IThemable { private _inputDisposable: DisposableStore; private _loadingTimeoutHandle: any; private _groupHeaders: HTMLElement[] = []; - private _textEditor: DesignerTextEditor; + private _messagesView: DesignerMessagesTabPanelView; + private _scriptEditorView: DesignerScriptEditorTabPanelView; constructor(private readonly _container: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -119,7 +124,7 @@ export class Designer extends Disposable implements IThemable { this._propertiesPaneContainer = DOM.$('.properties-container'); this._verticalSplitView = new SplitView(this._verticalSplitViewContainer, { orientation: Orientation.VERTICAL }); this._horizontalSplitView = new SplitView(this._horizontalSplitViewContainer, { orientation: Orientation.HORIZONTAL }); - this._tabbedPanel = new TabbedPanel(this._tabbedPanelContainer); + this._contentTabbedPanel = new TabbedPanel(this._tabbedPanelContainer); this._container.appendChild(this._verticalSplitViewContainer); this._contentContainer.appendChild(this._topContentContainer); this._contentContainer.appendChild(this._tabbedPanelContainer); @@ -132,11 +137,18 @@ export class Designer extends Disposable implements IThemable { maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None }, Sizing.Distribute); - this._textEditor = this._instantiationService.createInstance(DesignerScriptEditor, this._editorContainer); + this._scriptTabbedPannel = new TabbedPanel(this._editorContainer); + this._messagesView = new DesignerMessagesTabPanelView(); + this._scriptEditorView = new DesignerScriptEditorTabPanelView(this._instantiationService); + this._scriptTabbedPannel.pushTab({ + title: localize('designer.scriptTabTitle', "Scripts"), + identifier: ScriptTabId, + view: this._scriptEditorView + }); this._verticalSplitView.addView({ element: this._editorContainer, layout: size => { - this._textEditor.layout(new DOM.Dimension(this._editorContainer.clientWidth, size)); + this._scriptTabbedPannel.layout(new DOM.Dimension(this._editorContainer.clientWidth, size - this._scriptTabbedPannel.headersize)); }, minimumSize: 100, maximumSize: Number.POSITIVE_INFINITY, @@ -163,6 +175,7 @@ export class Designer extends Disposable implements IThemable { onDidChange: Event.None }, Sizing.Distribute); + this._propertiesPane = new DesignerPropertiesPane(this._propertiesPaneContainer, (container, components, parentPath) => { return this.createComponents(container, components, this._propertiesPane.componentMap, this._propertiesPane.groupHeaders, parentPath, 'PropertiesView'); }, (definition, component, viewModel) => { @@ -244,7 +257,7 @@ export class Designer extends Disposable implements IThemable { this._buttons = []; this._componentMap.clear(); DOM.clearNode(this._topContentContainer); - this._tabbedPanel.clearTabs(); + this._contentTabbedPanel.clearTabs(); this._propertiesPane.clear(); this._inputDisposable?.dispose(); this._groupHeaders = []; @@ -287,7 +300,7 @@ export class Designer extends Disposable implements IThemable { this.createComponents(this._topContentContainer, view.components, this._componentMap, this._groupHeaders, DesignerRootObjectPath, 'TopContentView'); } view.tabs.forEach(tab => { - this._tabbedPanel.pushTab(this.createTabView(tab)); + this._contentTabbedPanel.pushTab(this.createTabView(tab)); }); this.layoutTabbedPanel(); this.updateComponentValues(); @@ -297,42 +310,37 @@ export class Designer extends Disposable implements IThemable { private handleEditProcessedEvent(args: DesignerEditProcessedEventArgs): void { const edit = args.edit; - const result = args.result; - if (result.isValid) { - this._supressEditProcessing = true; - try { - this.updateComponentValues(); - if (edit.type === DesignerEditType.Add) { - // For tables in the main view, move focus to the first cell of the newly added row, and the properties pane will be showing the new object. - if (edit.path.length === 1) { - const propertyName = edit.path[0] as string; - const tableData = this._input.viewModel[propertyName] as DesignerTableProperties; - const table = this._componentMap.get(propertyName).component as Table; - try { - table.setActiveCell(tableData.data.length - 1, 0); - } - catch { - // Ignore the slick grid error when setting active cell. - } - } else { - this.updatePropertiesPane(this._propertiesPane.objectPath); + this._supressEditProcessing = true; + try { + this.updateComponentValues(); + if (edit.type === DesignerEditType.Add) { + // For tables in the main view, move focus to the first cell of the newly added row, and the properties pane will be showing the new object. + if (edit.path.length === 1) { + const propertyName = edit.path[0] as string; + const tableData = this._input.viewModel[propertyName] as DesignerTableProperties; + const table = this._componentMap.get(propertyName).component as Table; + try { + table.setActiveCell(tableData.data.length - 1, 0); } - } else if (edit.type === DesignerEditType.Update) { - // for edit, update the properties pane with new values of current object. + catch { + // Ignore the slick grid error when setting active cell. + } + } else { + this.updatePropertiesPane(this._propertiesPane.objectPath); + } + } else if (edit.type === DesignerEditType.Update) { + // for edit, update the properties pane with new values of current object. + this.updatePropertiesPane(this._propertiesPane.objectPath); + } else if (edit.type === DesignerEditType.Remove) { + // removing the secondary level entities, the properties pane needs to be updated to reflect the changes. + if (edit.path.length === 4) { this.updatePropertiesPane(this._propertiesPane.objectPath); - } else if (edit.type === DesignerEditType.Remove) { - // removing the secondary level entities, the properties pane needs to be updated to reflect the changes. - if (edit.path.length === 4) { - this.updatePropertiesPane(this._propertiesPane.objectPath); - } } - } catch (err) { - this._notificationService.error(err); } - this._supressEditProcessing = false; - } else { - this._notificationService.error(result.errors.map(e => e.message)); + } catch (err) { + this._notificationService.error(err); } + this._supressEditProcessing = false; } private handleInputStateChangedEvent(args: DesignerStateChangedEventArgs): void { @@ -378,7 +386,7 @@ export class Designer extends Disposable implements IThemable { } private layoutTabbedPanel() { - this._tabbedPanel.layout(new DOM.Dimension(this._tabbedPanelContainer.clientWidth, this._tabbedPanelContainer.clientHeight)); + this._contentTabbedPanel.layout(new DOM.Dimension(this._tabbedPanelContainer.clientWidth, this._tabbedPanelContainer.clientHeight)); } private layoutPropertiesPane() { @@ -431,16 +439,36 @@ export class Designer extends Disposable implements IThemable { } private updateComponentValues(): void { + this.updateMessagesTab(); const viewModel = this._input.viewModel; const scriptProperty = viewModel[ScriptProperty] as InputBoxProperties; if (scriptProperty) { - this._textEditor.content = scriptProperty.value || ''; + this._scriptEditorView.content = scriptProperty.value || ''; } this._componentMap.forEach((value) => { this.setComponentValue(value.defintion, value.component, viewModel); }); } + private updateMessagesTab(): void { + if (!this._input) { + return; + } + if (this._scriptTabbedPannel.contains(MessagesTabId)) { + this._scriptTabbedPannel.removeTab(MessagesTabId); + } + if (this._input.validationErrors === undefined || this._input.validationErrors.length === 0) { + return; + } + this._scriptTabbedPannel.pushTab({ + title: localize('designer.messagesTabTitle', "Errors ({0})", this._input.validationErrors.length), + identifier: MessagesTabId, + view: this._messagesView + }); + this._scriptTabbedPannel.showTab(MessagesTabId); + this._messagesView.updateMessages(this._input.validationErrors); + } + private handleEdit(edit: DesignerEdit): void { if (this._supressEditProcessing) { return; @@ -791,13 +819,13 @@ export class Designer extends Disposable implements IThemable { private getUIState(): DesignerUIState { return { - activeTabId: this._tabbedPanel.activeTabId + activeTabId: this._contentTabbedPanel.activeTabId }; } private restoreUIState(): void { if (this._input.designerUIState) { - this._tabbedPanel.showTab(this._input.designerUIState.activeTabId); + this._contentTabbedPanel.showTab(this._input.designerUIState.activeTabId); } } } diff --git a/src/sql/workbench/browser/designer/designerMessagesTabPanelView.ts b/src/sql/workbench/browser/designer/designerMessagesTabPanelView.ts new file mode 100644 index 0000000000..f7d0a4b938 --- /dev/null +++ b/src/sql/workbench/browser/designer/designerMessagesTabPanelView.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IPanelView } from 'sql/base/browser/ui/panel/panel'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as DOM from 'vs/base/browser/dom'; +import { DesignerValidationError } from 'sql/workbench/browser/designer/interfaces'; + +export class DesignerMessagesTabPanelView extends Disposable implements IPanelView { + private _container: HTMLElement; + + render(container: HTMLElement): void { + this._container = container.appendChild(DOM.$('.messages-container')); + } + + layout(dimension: DOM.Dimension): void { + } + + updateMessages(errors: DesignerValidationError[]) { + if (this._container) { + DOM.clearNode(this._container); + errors?.forEach(error => { + const messageItem = this._container.appendChild(DOM.$('.message-item.codicon.error')); + messageItem.innerText = error.message; + }); + } + } +} diff --git a/src/sql/workbench/browser/designer/designerScriptEditor.ts b/src/sql/workbench/browser/designer/designerScriptEditor.ts index 18d383d83b..eb5bbbbbda 100644 --- a/src/sql/workbench/browser/designer/designerScriptEditor.ts +++ b/src/sql/workbench/browser/designer/designerScriptEditor.ts @@ -84,9 +84,6 @@ export class DesignerScriptEditor extends BaseTextEditor implements DesignerText options.renderIndentGuides = false; options.rulers = []; options.glyphMargin = true; - options.minimap = { - enabled: true - }; } return options; } diff --git a/src/sql/workbench/browser/designer/designerScriptEditorTabPanelView.ts b/src/sql/workbench/browser/designer/designerScriptEditorTabPanelView.ts new file mode 100644 index 0000000000..5c2e3ab033 --- /dev/null +++ b/src/sql/workbench/browser/designer/designerScriptEditorTabPanelView.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IPanelView } from 'sql/base/browser/ui/panel/panel'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as DOM from 'vs/base/browser/dom'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DesignerScriptEditor } from 'sql/workbench/browser/designer/designerScriptEditor'; + +export class DesignerScriptEditorTabPanelView extends Disposable implements IPanelView { + private _textEditor: DesignerScriptEditor; + + constructor(private _instantiationService: IInstantiationService) { + super(); + } + + render(container: HTMLElement): void { + this._textEditor = this._instantiationService.createInstance(DesignerScriptEditor, container); + } + + layout(dimension: DOM.Dimension): void { + this._textEditor.layout(dimension); + } + + set content(content: string) { + if (this._textEditor) { + this._textEditor.content = content; + } + } +} diff --git a/src/sql/workbench/browser/designer/interfaces.ts b/src/sql/workbench/browser/designer/interfaces.ts index da30165968..aec40c127d 100644 --- a/src/sql/workbench/browser/designer/interfaces.ts +++ b/src/sql/workbench/browser/designer/interfaces.ts @@ -43,6 +43,11 @@ export interface DesignerComponentInput { */ readonly viewModel: DesignerViewModel; + /** + * Gets the validation errors. + */ + readonly validationErrors: DesignerValidationError[] | undefined; + /** * Start initilizing the designer input object. */ @@ -210,9 +215,11 @@ export interface DesignerEdit { export type DesignerEditPath = (string | number)[]; export const DesignerRootObjectPath: DesignerEditPath = []; +export type DesignerValidationError = { message: string, property?: DesignerEditPath }; + export interface DesignerEditResult { isValid: boolean; - errors?: { message: string, property?: DesignerEditPath }[]; + errors?: DesignerValidationError[]; } export interface DesignerTextEditor { diff --git a/src/sql/workbench/browser/designer/media/designer.css b/src/sql/workbench/browser/designer/media/designer.css index b0c6481a3c..2eac8241b4 100644 --- a/src/sql/workbench/browser/designer/media/designer.css +++ b/src/sql/workbench/browser/designer/media/designer.css @@ -29,6 +29,20 @@ height: 100%; } +.designer-component .messages-container { + overflow: scroll; + height: 100%; + width: 100%; +} + +.designer-component .messages-container .message-item { + padding: 0px 5px 0px 25px; + background-position: 5px center; + background-size: 16px 16px; + user-select: text; + line-height: 25px; +} + .designer-component .tabbed-panel-container { flex: 1 1 auto; overflow: hidden; diff --git a/src/sql/workbench/contrib/query/browser/queryResultsView.ts b/src/sql/workbench/contrib/query/browser/queryResultsView.ts index 8043cefb08..f9460643df 100644 --- a/src/sql/workbench/contrib/query/browser/queryResultsView.ts +++ b/src/sql/workbench/contrib/query/browser/queryResultsView.ts @@ -257,27 +257,27 @@ export class QueryResultsView extends Disposable { } })); - if (this.input?.state.visibleTabs.has(this.chartTab.identifier) && !this._panelView.contains(this.chartTab)) { + if (this.input?.state.visibleTabs.has(this.chartTab.identifier) && !this._panelView.contains(this.chartTab.identifier)) { this._panelView.pushTab(this.chartTab); - } else if (!this.input?.state.visibleTabs.has(this.chartTab.identifier) && this._panelView.contains(this.chartTab)) { + } else if (!this.input?.state.visibleTabs.has(this.chartTab.identifier) && this._panelView.contains(this.chartTab.identifier)) { this._panelView.removeTab(this.chartTab.identifier); } - if (this.input?.state.visibleTabs.has(this.qp2Tab.identifier) && !this._panelView.contains(this.qp2Tab)) { + if (this.input?.state.visibleTabs.has(this.qp2Tab.identifier) && !this._panelView.contains(this.qp2Tab.identifier)) { this._panelView.pushTab(this.qp2Tab); - } else if (!this.input?.state.visibleTabs.has(this.qp2Tab.identifier) && this._panelView.contains(this.qp2Tab)) { + } else if (!this.input?.state.visibleTabs.has(this.qp2Tab.identifier) && this._panelView.contains(this.qp2Tab.identifier)) { this._panelView.removeTab(this.qp2Tab.identifier); } - if (this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && !this._panelView.contains(this.topOperationsTab)) { + if (this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && !this._panelView.contains(this.topOperationsTab.identifier)) { this._panelView.pushTab(this.topOperationsTab); - } else if (!this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && this._panelView.contains(this.topOperationsTab)) { + } else if (!this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && this._panelView.contains(this.topOperationsTab.identifier)) { this._panelView.removeTab(this.topOperationsTab.identifier); } // restore query model view tabs this.dynamicModelViewTabs.forEach(tab => { - if (this._panelView.contains(tab)) { + if (this._panelView.contains(tab.identifier)) { this._panelView.removeTab(tab.identifier); } }); @@ -291,7 +291,7 @@ export class QueryResultsView extends Disposable { let tab = this._register(new QueryModelViewTab(parts[1], this.instantiationService)); tab.view.componentId = parts[2]; this.dynamicModelViewTabs.push(tab); - if (!this._panelView.contains(tab)) { + if (!this._panelView.contains(tab.identifier)) { this._panelView.pushTab(tab, undefined, true); } } @@ -381,7 +381,7 @@ export class QueryResultsView extends Disposable { public chartData(dataId: { resultId: number, batchId: number }): void { this.input?.state.visibleTabs.add(this.chartTab.identifier); - if (!this._panelView.contains(this.chartTab)) { + if (!this._panelView.contains(this.chartTab.identifier)) { this._panelView.pushTab(this.chartTab); } @@ -390,19 +390,19 @@ export class QueryResultsView extends Disposable { } public hideChart() { - if (this._panelView.contains(this.chartTab)) { + if (this._panelView.contains(this.chartTab.identifier)) { this._panelView.removeTab(this.chartTab.identifier); } } public hideResults() { - if (this._panelView.contains(this.resultsTab)) { + if (this._panelView.contains(this.resultsTab.identifier)) { this._panelView.removeTab(this.resultsTab.identifier); } } public showResults() { - if (!this._panelView.contains(this.resultsTab)) { + if (!this._panelView.contains(this.resultsTab.identifier)) { this._panelView.pushTab(this.resultsTab, 0); } this._panelView.showTab(this.resultsTab.identifier); @@ -410,16 +410,16 @@ export class QueryResultsView extends Disposable { public showTopOperations(xml: string) { this.input?.state.visibleTabs.add(this.topOperationsTab.identifier); - if (!this._panelView.contains(this.topOperationsTab)) { + if (!this._panelView.contains(this.topOperationsTab.identifier)) { this._panelView.pushTab(this.topOperationsTab); } this.topOperationsTab.view.showPlan(xml); } public showPlan2() { - if (!this._panelView.contains(this.qp2Tab)) { + if (!this._panelView.contains(this.qp2Tab.identifier)) { this.input?.state.visibleTabs.add(this.qp2Tab.identifier); - if (!this._panelView.contains(this.qp2Tab)) { + if (!this._panelView.contains(this.qp2Tab.identifier)) { this._panelView.pushTab(this.qp2Tab); } this._panelView.showTab(this.qp2Tab.identifier); @@ -427,13 +427,13 @@ export class QueryResultsView extends Disposable { } public hideTopOperations() { - if (this._panelView.contains(this.topOperationsTab)) { + if (this._panelView.contains(this.topOperationsTab.identifier)) { this._panelView.removeTab(this.topOperationsTab.identifier); } } public hidePlan2() { - if (this._panelView.contains(this.qp2Tab)) { + if (this._panelView.contains(this.qp2Tab.identifier)) { this.qp2Tab.clear(); this.input.state.queryPlan2State.clearQueryPlan2State(); this._panelView.removeTab(this.qp2Tab.identifier); @@ -442,7 +442,7 @@ export class QueryResultsView extends Disposable { public hideDynamicViewModelTabs() { this.dynamicModelViewTabs.forEach(tab => { - if (this._panelView.contains(tab)) { + if (this._panelView.contains(tab.identifier)) { this._panelView.removeTab(tab.identifier); } }); @@ -462,7 +462,7 @@ export class QueryResultsView extends Disposable { this.dynamicModelViewTabs.push(tab); this.input?.state.visibleTabs.add('querymodelview;' + title + ';' + componentId); - if (!this._panelView.contains(tab)) { + if (!this._panelView.contains(tab.identifier)) { this._panelView.pushTab(tab, undefined, true); } diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts index b70376fb54..23fbf7ed3f 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; -import { DesignerViewModel, DesignerEdit, DesignerComponentInput, DesignerView, DesignerTab, DesignerDataPropertyInfo, DropDownProperties, DesignerTableProperties, DesignerEditProcessedEventArgs, DesignerAction, DesignerStateChangedEventArgs, DesignerEditPath } from 'sql/workbench/browser/designer/interfaces'; +import { DesignerViewModel, DesignerEdit, DesignerComponentInput, DesignerView, DesignerTab, DesignerDataPropertyInfo, DropDownProperties, DesignerTableProperties, DesignerEditProcessedEventArgs, DesignerAction, DesignerStateChangedEventArgs, DesignerEditPath, DesignerValidationError } from 'sql/workbench/browser/designer/interfaces'; import { TableDesignerProvider } from 'sql/workbench/services/tableDesigner/common/interface'; import { localize } from 'vs/nls'; import { designers } from 'sql/workbench/api/common/sqlExtHostTypes'; @@ -22,6 +22,7 @@ const ErrorDialogTitle: string = localize('tableDesigner.ErrorDialogTitle', "Tab export class TableDesignerComponentInput implements DesignerComponentInput { private _viewModel: DesignerViewModel; + private _validationErrors?: DesignerValidationError[]; private _view: DesignerView; private _valid: boolean = true; private _dirty: boolean = false; @@ -75,6 +76,10 @@ export class TableDesignerComponentInput implements DesignerComponentInput { return this._viewModel; } + get validationErrors(): DesignerValidationError[] | undefined { + return this._validationErrors; + } + processEdit(edit: DesignerEdit): void { const telemetryInfo = this.createTelemetryInfo(); telemetryInfo.tableObjectType = this.getObjectTypeFromPath(edit.path); @@ -85,6 +90,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { this._provider.processTableEdit(this.tableInfo, edit).then( result => { this._viewModel = result.viewModel; + this._validationErrors = result.errors; this.updateState(result.isValid, !equals(this._viewModel, this._originalViewModel), undefined); this._onEditProcessed.fire({