diff --git a/src/sql/workbench/api/node/mainThreadQueryEditor.ts b/src/sql/workbench/api/node/mainThreadQueryEditor.ts index a825187f6b..a1024a6f3c 100644 --- a/src/sql/workbench/api/node/mainThreadQueryEditor.ts +++ b/src/sql/workbench/api/node/mainThreadQueryEditor.ts @@ -10,9 +10,9 @@ import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionT import { QueryEditor } from 'sql/workbench/parts/query/browser/queryEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IQueryModelService, IQueryEvent } from 'sql/platform/query/common/queryModel'; -import { IQueryManagementService } from 'sql/platform/query/common/queryManagement'; +import { IQueryModelService } from 'sql/platform/query/common/queryModel'; import * as azdata from 'azdata'; +import { IQueryManagementService } from 'sql/platform/query/common/queryManagement'; @extHostNamedCustomer(SqlMainContext.MainThreadQueryEditor) export class MainThreadQueryEditor implements MainThreadQueryEditorShape { @@ -23,9 +23,9 @@ export class MainThreadQueryEditor implements MainThreadQueryEditorShape { constructor( extHostContext: IExtHostContext, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @IQueryManagementService private _queryManagementService: IQueryManagementService, @IQueryModelService private _queryModelService: IQueryModelService, - @IEditorService private _editorService: IEditorService + @IEditorService private _editorService: IEditorService, + @IQueryManagementService private _queryManagementService: IQueryManagementService ) { if (extHostContext) { this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostQueryEditor); diff --git a/src/sql/workbench/parts/charts/browser/actions.ts b/src/sql/workbench/parts/charts/browser/actions.ts index d44906a2c6..f408f73920 100644 --- a/src/sql/workbench/parts/charts/browser/actions.ts +++ b/src/sql/workbench/parts/charts/browser/actions.ts @@ -5,7 +5,6 @@ import { IInsightOptions, IInsight } from './interfaces'; import { Graph } from './graphInsight'; -import { QueryEditor } from 'sql/workbench/parts/query/browser/queryEditor'; import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; import { IInsightsConfig } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces'; import { resolveCurrentDirectory, getRootPath } from 'sql/platform/node/pathUtilities'; @@ -20,6 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; export interface IChartActionContext { options: IInsightOptions; @@ -87,10 +87,9 @@ export class CreateInsightAction extends Action { } private getActiveUriString(): string { - let editor = this.editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - return queryEditor.uri; + let editor = this.editorService.activeEditor; + if (editor instanceof QueryInput) { + return editor.uri; } return undefined; } @@ -203,10 +202,9 @@ export class SaveImageAction extends Action { } private getActiveUriString(): string { - let editor = this.editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - return queryEditor.uri; + let editor = this.editorService.activeEditor; + if (editor instanceof QueryInput) { + return editor.uri; } return undefined; } diff --git a/src/sql/workbench/parts/query/browser/actions.ts b/src/sql/workbench/parts/query/browser/actions.ts index 3660c37c29..21c617c4ce 100644 --- a/src/sql/workbench/parts/query/browser/actions.ts +++ b/src/sql/workbench/parts/query/browser/actions.ts @@ -205,12 +205,8 @@ export class ChartDataAction extends Action { } public run(context: IGridActionContext): Promise { - let activeEditor = this.editorService.activeControl; - if (activeEditor instanceof QueryEditor) { - activeEditor.resultsEditor.chart({ batchId: context.batchId, resultId: context.resultId }); - return Promise.resolve(true); - } else { - return Promise.resolve(false); - } + const activeEditor = this.editorService.activeControl as QueryEditor; + activeEditor.chart({ batchId: context.batchId, resultId: context.resultId }); + return Promise.resolve(true); } } diff --git a/src/sql/workbench/parts/query/browser/flexibleSash.ts b/src/sql/workbench/parts/query/browser/flexibleSash.ts index 0db0ec9fbd..480a301aa6 100644 --- a/src/sql/workbench/parts/query/browser/flexibleSash.ts +++ b/src/sql/workbench/parts/query/browser/flexibleSash.ts @@ -7,7 +7,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { - IHorizontalSashLayoutProvider, IVerticalSashLayoutProvider, + IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; // There is no need to import the sash CSS - 'vs/base/browser/ui/sash/sash' already includes it @@ -40,121 +40,6 @@ export interface IFlexibleSash { onPositionChange: Event; } -/** - * A simple Vertical Sash that computes the position of the sash when it is moved between the given dimension. - * Triggers onPositionChange event when the position is changed. Implements IFlexibleSash to enable classes to be - * agnostic of the fact that this sash is vertical. - */ - - -export class VerticalFlexibleSash extends Disposable implements IVerticalSashLayoutProvider, IFlexibleSash { - - private sash: Sash; - private ratio: number; - private startPosition: number; - private position: number; - private dimension: Dimension; - private top: number; - - private _onPositionChange: Emitter = new Emitter(); - public get onPositionChange(): Event { return this._onPositionChange.event; } - - constructor(container: HTMLElement, private minWidth: number) { - super(); - this.ratio = 0.5; - this.top = 0; - this.sash = new Sash(container, this); - - this._register(this.sash.onDidStart(() => this.onSashDragStart())); - this._register(this.sash.onDidChange((e: ISashEvent) => this.onSashDrag(e))); - this._register(this.sash.onDidEnd(() => this.onSashDragEnd())); - this._register(this.sash.onDidReset(() => this.onSashReset())); - } - - public getSplitPoint(): number { - return this.getVerticalSashLeft(); - } - - public layout(): void { - this.sash.layout(); - } - - public show(): void { - this.sash.show(); - } - - public hide(): void { - this.sash.hide(); - } - - public getVerticalSashTop(): number { - return this.top; - } - - public getVerticalSashLeft(): number { - return this.position; - } - - public getVerticalSashHeight(): number { - return this.dimension.height; - } - - public setDimenesion(dimension: Dimension) { - this.dimension = dimension; - this.compute(this.ratio); - } - - public setEdge(edge: number) { - this.top = edge; - } - - private onSashDragStart(): void { - this.startPosition = this.position; - } - - private onSashDrag(e: ISashEvent): void { - this.compute((this.startPosition + (e.currentX - e.startX)) / this.dimension.width); - } - - private compute(ratio: number) { - this.computeSashPosition(ratio); - this.ratio = this.position / this.dimension.width; - this._onPositionChange.fire(this.position); - } - - private onSashDragEnd(): void { - this.sash.layout(); - } - - private onSashReset(): void { - this.ratio = 0.5; - this._onPositionChange.fire(this.position); - this.sash.layout(); - } - - private computeSashPosition(sashRatio: number = this.ratio) { - let contentWidth = this.dimension.width; - let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); - let midPoint = Math.floor(0.5 * contentWidth); - - if (contentWidth > this.minWidth * 2) { - if (sashPosition < this.minWidth) { - sashPosition = this.minWidth; - } - if (sashPosition > contentWidth - this.minWidth) { - sashPosition = contentWidth - this.minWidth; - } - } else { - sashPosition = midPoint; - } - if (this.position !== sashPosition) { - this.position = sashPosition; - this.sash.layout(); - } - } - -} - /** * A simple Horizontal Sash that computes the position of the sash when it is moved between the given dimension. * Triggers onPositionChange event when the position is changed. Implements IFlexibleSash to enable classes to be @@ -269,4 +154,4 @@ export class HorizontalFlexibleSash extends Disposable implements IHorizontalSas this.sash.layout(); } } -} \ No newline at end of file +} diff --git a/src/sql/workbench/parts/query/browser/keyboardQueryActions.ts b/src/sql/workbench/parts/query/browser/keyboardQueryActions.ts index a1247d8208..94665cee9e 100644 --- a/src/sql/workbench/parts/query/browser/keyboardQueryActions.ts +++ b/src/sql/workbench/parts/query/browser/keyboardQueryActions.ts @@ -20,14 +20,15 @@ import * as ConnectionConstants from 'sql/platform/connection/common/constants'; import { EditDataEditor } from 'sql/workbench/parts/editData/browser/editDataEditor'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; const singleQuote = '\''; function isConnected(editor: QueryEditor, connectionManagementService: IConnectionManagementService): boolean { - if (!editor || !editor.currentQueryInput) { + if (!editor || !editor.input) { return false; } - return connectionManagementService.isConnected(editor.currentQueryInput.uri); + return connectionManagementService.isConnected(editor.input.uri); } function runActionOnActiveQueryEditor(editorService: IEditorService, action: (QueryEditor) => void): void { @@ -71,10 +72,9 @@ export class FocusOnCurrentQueryKeyboardAction extends Action { } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - queryEditor.focus(); + const editor = this._editorService.activeControl; + if (editor instanceof QueryEditor) { + editor.focus(); } return Promise.resolve(null); } @@ -98,10 +98,9 @@ export class RunQueryKeyboardAction extends Action { } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && (editor instanceof QueryEditor || editor instanceof EditDataEditor)) { - let queryEditor: QueryEditor | EditDataEditor = editor; - queryEditor.runQuery(); + const editor = this._editorService.activeControl; + if (editor instanceof QueryEditor || editor instanceof EditDataEditor) { + editor.runQuery(); } return Promise.resolve(null); } @@ -124,10 +123,9 @@ export class RunCurrentQueryKeyboardAction extends Action { } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - queryEditor.runCurrentQuery(); + const editor = this._editorService.activeControl; + if (editor instanceof QueryEditor) { + editor.runCurrentQuery(); } return Promise.resolve(null); } @@ -147,10 +145,9 @@ export class RunCurrentQueryWithActualPlanKeyboardAction extends Action { } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - queryEditor.runCurrentQueryWithActualPlan(); + const editor = this._editorService.activeControl; + if (editor instanceof QueryEditor) { + editor.runCurrentQueryWithActualPlan(); } return Promise.resolve(null); } @@ -174,10 +171,9 @@ export class CancelQueryKeyboardAction extends Action { } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && (editor instanceof QueryEditor || editor instanceof EditDataEditor)) { - let queryEditor: QueryEditor | EditDataEditor = editor; - queryEditor.cancelQuery(); + const editor = this._editorService.activeControl; + if (editor instanceof QueryEditor || editor instanceof EditDataEditor) { + editor.cancelQuery(); } return Promise.resolve(null); } @@ -193,17 +189,17 @@ export class RefreshIntellisenseKeyboardAction extends Action { constructor( id: string, label: string, - @IEditorService private _editorService: IEditorService + @IConnectionManagementService private connectionManagementService: IConnectionManagementService, + @IEditorService private editorService: IEditorService ) { super(id, label); this.enabled = true; } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - queryEditor.rebuildIntelliSenseCache(); + const editor = this.editorService.activeEditor; + if (editor instanceof QueryInput) { + this.connectionManagementService.rebuildIntelliSenseCache(editor.uri); } return Promise.resolve(null); } @@ -227,10 +223,9 @@ export class ToggleQueryResultsKeyboardAction extends Action { } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - queryEditor.toggleResultsEditorVisibility(); + const editor = this._editorService.activeControl; + if (editor instanceof QueryEditor) { + editor.toggleResultsEditorVisibility(); } return Promise.resolve(null); } @@ -243,18 +238,18 @@ export class RunQueryShortcutAction extends Action { public static ID = 'runQueryShortcutAction'; constructor( - @IEditorService private _editorService: IEditorService, - @IQueryModelService protected _queryModelService: IQueryModelService, - @IQueryManagementService private _queryManagementService: IQueryManagementService, - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @IConfigurationService private _workspaceConfigurationService: IConfigurationService + @IEditorService private readonly editorService: IEditorService, + @IQueryModelService protected readonly queryModelService: IQueryModelService, + @IQueryManagementService private readonly queryManagementService: IQueryManagementService, + @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(RunQueryShortcutAction.ID); } public run(index: number): Promise { let promise: Thenable = Promise.resolve(null); - runActionOnActiveQueryEditor(this._editorService, (editor) => { + runActionOnActiveQueryEditor(this.editorService, (editor) => { promise = this.runQueryShortcut(editor, index); }); return new Promise((resolve, reject) => { @@ -273,7 +268,7 @@ export class RunQueryShortcutAction extends Action { throw new Error(nls.localize('queryShortcutNoEditor', 'Editor parameter is required for a shortcut to be executed')); } - if (isConnected(editor, this._connectionManagementService)) { + if (isConnected(editor, this.connectionManagementService)) { let shortcutText = this.getShortcutText(shortcutIndex); if (!shortcutText.trim()) { // no point going further @@ -285,7 +280,7 @@ export class RunQueryShortcutAction extends Action { let parameterText: string = editor.getSelectionText(); return this.escapeStringParamIfNeeded(editor, shortcutText, parameterText).then((escapedParam) => { let queryString = `${shortcutText} ${escapedParam}`; - editor.currentQueryInput.runQueryString(queryString); + editor.input.runQueryString(queryString); }).then(success => null, err => { // swallow errors for now return null; @@ -297,7 +292,7 @@ export class RunQueryShortcutAction extends Action { private getShortcutText(shortcutIndex: number) { let shortcutSetting = Constants.shortcutStart + shortcutIndex; - let querySettings = WorkbenchUtils.getSqlConfigSection(this._workspaceConfigurationService, Constants.querySection); + let querySettings = WorkbenchUtils.getSqlConfigSection(this.configurationService, Constants.querySection); let shortcutText = querySettings[shortcutSetting]; return shortcutText; } @@ -307,7 +302,7 @@ export class RunQueryShortcutAction extends Action { if (this.canQueryProcMetadata(editor)) { let dbName = this.getDatabaseName(editor); let query = `exec dbo.sp_sproc_columns @procedure_name = N'${escapeSqlString(shortcutText, singleQuote)}', @procedure_owner = null, @procedure_qualifier = N'${escapeSqlString(dbName, singleQuote)}'`; - return this._queryManagementService.runQueryAndReturn(editor.uri, query) + return this.queryManagementService.runQueryAndReturn(editor.input.uri, query) .then(result => { switch (this.isProcWithSingleArgument(result)) { case 1: @@ -377,12 +372,12 @@ export class RunQueryShortcutAction extends Action { } private canQueryProcMetadata(editor: QueryEditor): boolean { - let info = this._connectionManagementService.getConnectionInfo(editor.uri); + let info = this.connectionManagementService.getConnectionInfo(editor.input.uri); return (info && info.providerId === ConnectionConstants.mssqlProviderName); } private getDatabaseName(editor: QueryEditor): string { - let info = this._connectionManagementService.getConnectionInfo(editor.uri); + let info = this.connectionManagementService.getConnectionInfo(editor.input.uri); return info.connectionProfile.databaseName; } } @@ -398,39 +393,38 @@ export class ParseSyntaxAction extends Action { constructor( id: string, label: string, - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @IQueryManagementService private _queryManagementService: IQueryManagementService, - @IEditorService private _editorService: IEditorService, - @INotificationService private _notificationService: INotificationService + @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, + @IQueryManagementService private readonly queryManagementService: IQueryManagementService, + @IEditorService private readonly editorService: IEditorService, + @INotificationService private readonly notificationService: INotificationService ) { super(id, label); this.enabled = true; } public run(): Promise { - let editor = this._editorService.activeControl; - if (editor && editor instanceof QueryEditor) { - let queryEditor: QueryEditor = editor; - if (!queryEditor.isSelectionEmpty()) { - if (this.isConnected(queryEditor)) { - let text = queryEditor.getSelectionText(); + const editor = this.editorService.activeControl; + if (editor instanceof QueryEditor) { + if (!editor.isSelectionEmpty()) { + if (this.isConnected(editor)) { + let text = editor.getSelectionText(); if (text === '') { - text = queryEditor.getAllText(); + text = editor.getAllText(); } - this._queryManagementService.parseSyntax(queryEditor.connectedUri, text).then(result => { + this.queryManagementService.parseSyntax(editor.input.uri, text).then(result => { if (result && result.parseable) { - this._notificationService.notify({ + this.notificationService.notify({ severity: Severity.Info, message: nls.localize('queryActions.parseSyntaxSuccess', 'Commands completed successfully') }); } else if (result && result.errors.length > 0) { let errorMessage = nls.localize('queryActions.parseSyntaxFailure', 'Command failed: '); - this._notificationService.error(`${errorMessage}${result.errors[0]}`); + this.notificationService.error(`${errorMessage}${result.errors[0]}`); } }); } else { - this._notificationService.notify({ + this.notificationService.notify({ severity: Severity.Error, message: nls.localize('queryActions.notConnected', 'Please connect to a server') }); @@ -447,9 +441,9 @@ export class ParseSyntaxAction extends Action { * Public for testing only. */ private isConnected(editor: QueryEditor): boolean { - if (!editor || !editor.currentQueryInput) { + if (!editor || !editor.input) { return false; } - return this._connectionManagementService.isConnected(editor.currentQueryInput.uri); + return this.connectionManagementService.isConnected(editor.input.uri); } } diff --git a/src/sql/workbench/parts/query/browser/queryActions.ts b/src/sql/workbench/parts/query/browser/queryActions.ts index 911efdef30..db3d3b9a8b 100644 --- a/src/sql/workbench/parts/query/browser/queryActions.ts +++ b/src/sql/workbench/parts/query/browser/queryActions.ts @@ -37,8 +37,8 @@ export abstract class QueryTaskbarAction extends Action { private _classes: string[]; constructor( - protected _connectionManagementService: IConnectionManagementService, - protected editor: QueryEditor, + protected readonly connectionManagementService: IConnectionManagementService, + protected readonly editor: QueryEditor, id: string, enabledClass: string ) { @@ -75,10 +75,10 @@ export abstract class QueryTaskbarAction extends Action { * Public for testing only. */ public isConnected(editor: QueryEditor): boolean { - if (!editor || !editor.currentQueryInput) { + if (!editor || !editor.input) { return false; } - return this._connectionManagementService.isConnected(editor.currentQueryInput.uri); + return this.connectionManagementService.isConnected(editor.input.uri); } /** @@ -87,12 +87,12 @@ export abstract class QueryTaskbarAction extends Action { */ protected connectEditor(editor: QueryEditor, runQueryOnCompletion?: RunQueryOnConnectionMode, selection?: ISelectionData): void { let params: INewConnectionParams = { - input: editor.currentQueryInput, + input: editor.input, connectionType: ConnectionType.editor, runQueryOnCompletion: runQueryOnCompletion ? runQueryOnCompletion : RunQueryOnConnectionMode.none, querySelection: selection }; - this._connectionManagementService.showConnectionDialog(params); + this.connectionManagementService.showConnectionDialog(params); } } @@ -106,7 +106,7 @@ export class RunQueryAction extends QueryTaskbarAction { constructor( editor: QueryEditor, - @IQueryModelService protected _queryModelService: IQueryModelService, + @IQueryModelService protected readonly queryModelService: IQueryModelService, @IConnectionManagementService connectionManagementService: IConnectionManagementService ) { super(connectionManagementService, editor, RunQueryAction.ID, RunQueryAction.EnabledClass); @@ -151,11 +151,11 @@ export class RunQueryAction extends QueryTaskbarAction { // otherwise, either run the statement or the script depending on parameter let selection: ISelectionData = editor.getSelection(false); if (runCurrentStatement && selection && this.isCursorPosition(selection)) { - editor.currentQueryInput.runQueryStatement(selection); + editor.input.runQueryStatement(selection); } else { // get the selection again this time with trimming selection = editor.getSelection(); - editor.currentQueryInput.runQuery(selection); + editor.input.runQuery(selection); } } } @@ -176,7 +176,7 @@ export class CancelQueryAction extends QueryTaskbarAction { constructor( editor: QueryEditor, - @IQueryModelService private _queryModelService: IQueryModelService, + @IQueryModelService private readonly queryModelService: IQueryModelService, @IConnectionManagementService connectionManagementService: IConnectionManagementService ) { super(connectionManagementService, editor, CancelQueryAction.ID, CancelQueryAction.EnabledClass); @@ -186,7 +186,7 @@ export class CancelQueryAction extends QueryTaskbarAction { public run(): Promise { if (this.isConnected(this.editor)) { - this._queryModelService.cancelQuery(this.editor.currentQueryInput.uri); + this.queryModelService.cancelQuery(this.editor.input.uri); } return Promise.resolve(null); } @@ -202,7 +202,6 @@ export class EstimatedQueryPlanAction extends QueryTaskbarAction { constructor( editor: QueryEditor, - @IQueryModelService private _queryModelService: IQueryModelService, @IConnectionManagementService connectionManagementService: IConnectionManagementService ) { super(connectionManagementService, editor, EstimatedQueryPlanAction.ID, EstimatedQueryPlanAction.EnabledClass); @@ -229,7 +228,7 @@ export class EstimatedQueryPlanAction extends QueryTaskbarAction { } if (this.isConnected(editor)) { - editor.currentQueryInput.runQuery(editor.getSelection(), { + editor.input.runQuery(editor.getSelection(), { displayEstimatedQueryPlan: true }); } @@ -242,7 +241,6 @@ export class ActualQueryPlanAction extends QueryTaskbarAction { constructor( editor: QueryEditor, - @IQueryModelService private _queryModelService: IQueryModelService, @IConnectionManagementService connectionManagementService: IConnectionManagementService ) { super(connectionManagementService, editor, ActualQueryPlanAction.ID, ActualQueryPlanAction.EnabledClass); @@ -273,7 +271,7 @@ export class ActualQueryPlanAction extends QueryTaskbarAction { if (!selection) { selection = editor.getAllSelection(); } - editor.currentQueryInput.runQuery(selection, { + editor.input.runQuery(selection, { displayActualQueryPlan: true }); } @@ -299,7 +297,7 @@ export class DisconnectDatabaseAction extends QueryTaskbarAction { public run(): Promise { // Call disconnectEditor regardless of the connection state and let the ConnectionManagementService // determine if we need to disconnect, cancel an in-progress conneciton, or do nothing - this._connectionManagementService.disconnectEditor(this.editor.currentQueryInput); + this.connectionManagementService.disconnectEditor(this.editor.input); return Promise.resolve(null); } } @@ -350,22 +348,14 @@ export class ToggleConnectDatabaseAction extends QueryTaskbarAction { public static DisconnectClass = 'disconnect'; public static ID = 'toggleConnectDatabaseAction'; - private _connected: boolean; - private _connectLabel: string; - private _disconnectLabel: string; + private _connectLabel = nls.localize('connectDatabaseLabel', 'Connect'); + private _disconnectLabel = nls.localize('disconnectDatabaseLabel', 'Disconnect'); constructor( editor: QueryEditor, - isConnected: boolean, + private _connected: boolean, @IConnectionManagementService connectionManagementService: IConnectionManagementService ) { - let enabledClass: string; - - super(connectionManagementService, editor, ToggleConnectDatabaseAction.ID, enabledClass); - - this._connectLabel = nls.localize('connectDatabaseLabel', 'Connect'); - this._disconnectLabel = nls.localize('disconnectDatabaseLabel', 'Disconnect'); - - this.connected = isConnected; + super(connectionManagementService, editor, ToggleConnectDatabaseAction.ID, undefined); } public get connected(): boolean { @@ -393,7 +383,7 @@ export class ToggleConnectDatabaseAction extends QueryTaskbarAction { if (this.connected) { // Call disconnectEditor regardless of the connection state and let the ConnectionManagementService // determine if we need to disconnect, cancel an in-progress connection, or do nothing - this._connectionManagementService.disconnectEditor(this.editor.currentQueryInput); + this.connectionManagementService.disconnectEditor(this.editor.input); } else { this.connectEditor(this.editor); } @@ -444,14 +434,14 @@ export class ListDatabasesActionItem implements IActionViewItem { // CONSTRUCTOR ///////////////////////////////////////////////////////// constructor( private _editor: QueryEditor, - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @INotificationService private _notificationService: INotificationService, @IContextViewService contextViewProvider: IContextViewService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, + @INotificationService private readonly notificationService: INotificationService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { this._toDispose = []; this._databaseListDropdown = $('.databaseListDropdown'); - this._isInAccessibilityMode = this._configurationService.getValue('editor.accessibilitySupport') === 'on'; + this._isInAccessibilityMode = this.configurationService.getValue('editor.accessibilitySupport') === 'on'; if (this._isInAccessibilityMode) { this._databaseSelectBox = new SelectBox([this._selectDatabaseString], this._selectDatabaseString, contextViewProvider, undefined, { ariaLabel: this._selectDatabaseString }); @@ -467,12 +457,11 @@ export class ListDatabasesActionItem implements IActionViewItem { actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown') }); this._dropdown.onValueChange(s => this.databaseSelected(s)); - this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); })); + this._toDispose.push(this._dropdown.onFocus(() => this.onDropdownFocus())); } // Register event handlers - let self = this; - this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); })); + this._toDispose.push(this.connectionManagementService.onConnectionChanged(params => this.onConnectionChanged(params))); } // PUBLIC METHODS ////////////////////////////////////////////////////// @@ -546,22 +535,22 @@ export class ListDatabasesActionItem implements IActionViewItem { // PRIVATE HELPERS ///////////////////////////////////////////////////// private databaseSelected(dbName: string): void { - let uri = this._editor.connectedUri; + let uri = this._editor.input.uri; if (!uri) { return; } - let profile = this._connectionManagementService.getConnectionProfile(uri); + let profile = this.connectionManagementService.getConnectionProfile(uri); if (!profile) { return; } - this._connectionManagementService.changeDatabase(this._editor.uri, dbName) + this.connectionManagementService.changeDatabase(this._editor.input.uri, dbName) .then( result => { if (!result) { this.resetDatabaseName(); - this._notificationService.notify({ + this.notificationService.notify({ severity: Severity.Error, message: nls.localize('changeDatabase.failed', "Failed to change database") }); @@ -569,7 +558,7 @@ export class ListDatabasesActionItem implements IActionViewItem { }, error => { this.resetDatabaseName(); - this._notificationService.notify({ + this.notificationService.notify({ severity: Severity.Error, message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error) }); @@ -577,9 +566,9 @@ export class ListDatabasesActionItem implements IActionViewItem { } private getCurrentDatabaseName() { - let uri = this._editor.connectedUri; + let uri = this._editor.input.uri; if (uri) { - let profile = this._connectionManagementService.getConnectionProfile(uri); + let profile = this.connectionManagementService.getConnectionProfile(uri); if (profile) { return profile.databaseName; } @@ -600,7 +589,7 @@ export class ListDatabasesActionItem implements IActionViewItem { return; } - let uri = this._editor.connectedUri; + let uri = this._editor.input.uri; if (uri !== connParams.connectionUri) { return; } @@ -609,14 +598,12 @@ export class ListDatabasesActionItem implements IActionViewItem { } private onDropdownFocus(): void { - let self = this; - - let uri = self._editor.connectedUri; + let uri = this._editor.input.uri; if (!uri) { return; } - self._connectionManagementService.listDatabases(uri) + this.connectionManagementService.listDatabases(uri) .then(result => { if (result && result.databaseNames) { this._dropdown.values = result.databaseNames; @@ -630,12 +617,11 @@ export class ListDatabasesActionItem implements IActionViewItem { if (this._isInAccessibilityMode) { this._databaseSelectBox.enable(); - let self = this; - let uri = self._editor.connectedUri; + let uri = this._editor.input.uri; if (!uri) { return; } - self._connectionManagementService.listDatabases(uri) + this.connectionManagementService.listDatabases(uri) .then(result => { if (result && result.databaseNames) { this._databaseSelectBox.setOptions(result.databaseNames); diff --git a/src/sql/workbench/parts/query/browser/queryEditor.ts b/src/sql/workbench/parts/query/browser/queryEditor.ts index 137fa5a382..5f6aff27f3 100644 --- a/src/sql/workbench/parts/query/browser/queryEditor.ts +++ b/src/sql/workbench/parts/query/browser/queryEditor.ts @@ -4,49 +4,34 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/queryEditor'; -import * as strings from 'vs/base/common/strings'; + import * as DOM from 'vs/base/browser/dom'; -import * as types from 'vs/base/common/types'; - -import { EditorInput, EditorOptions, IEditorControl, IEditor, TextEditorOptions } from 'vs/workbench/common/editor'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorOptions, IEditorControl } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { VerticalFlexibleSash, HorizontalFlexibleSash, IFlexibleSash } from 'sql/workbench/parts/query/browser/flexibleSash'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; - import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; - -import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; - import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { Action } from 'vs/base/common/actions'; -import { ISelectionData } from 'azdata'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { IRange } from 'vs/editor/common/core/range'; - -import { QueryResultsInput } from 'sql/workbench/parts/query/common/queryResultsInput'; -import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; -import { QueryResultsEditor } from 'sql/workbench/parts/query/browser/queryResultsEditor'; -import * as queryContext from 'sql/workbench/parts/query/common/queryContext'; -import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar'; -import { - RunQueryAction, CancelQueryAction, ListDatabasesAction, ListDatabasesActionItem, - ConnectDatabaseAction, ToggleConnectDatabaseAction, EstimatedQueryPlanAction, - ActualQueryPlanAction -} from 'sql/workbench/parts/query/browser/queryActions'; -import { IQueryModelService } from 'sql/platform/query/common/queryModel'; -import { IEditorDescriptorService } from 'sql/workbench/services/queryEditor/common/editorDescriptorService'; -import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { Event } from 'vs/base/common/event'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { ISelectionData } from 'azdata'; +import { Action, IActionViewItem } from 'vs/base/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +import { QueryInput, IQueryEditorStateChange } from 'sql/workbench/parts/query/common/queryInput'; +import { QueryResultsEditor } from 'sql/workbench/parts/query/browser/queryResultsEditor'; +import * as queryContext from 'sql/workbench/parts/query/common/queryContext'; +import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar'; +import * as actions from 'sql/workbench/parts/query/browser/queryActions'; +import { IRange } from 'vs/editor/common/core/range'; /** * Editor that hosts 2 sub-editors: A TextResourceEditor for SQL file editing, and a QueryResultsEditor @@ -56,148 +41,239 @@ export class QueryEditor extends BaseEditor { public static ID: string = 'workbench.editor.queryEditor'; - // The height of the tabs above the editor - private readonly _tabHeight: number = 35; + private dimension: DOM.Dimension = new DOM.Dimension(0, 0); - // The minimum width/height of the editors hosted in the QueryEditor - private readonly _minEditorSize: number = 100; + private resultsEditor: QueryResultsEditor; - private _sash: IFlexibleSash; - private _editorTopOffset: number; - private _orientation: Orientation; - private _dimension: DOM.Dimension; + private resultsEditorContainer: HTMLElement; + // could be untitled or resource editor + private textEditor: TextResourceEditor; + private textEditorContainer: HTMLElement; - private _resultsEditor: QueryResultsEditor; - private _resultsEditorContainer: HTMLElement; + private taskbar: Taskbar; + private splitview: SplitView; - private _sqlEditor: TextResourceEditor; - private _sqlEditorContainer: HTMLElement; - - private _taskbar: Taskbar; - private _taskbarContainer: HTMLElement; - private _listDatabasesActionItem: ListDatabasesActionItem; + private inputDisposables: IDisposable[] = []; + private resultsVisible = false; private queryEditorVisible: IContextKey; - private _runQueryAction: RunQueryAction; - private _cancelQueryAction: CancelQueryAction; - private _toggleConnectDatabaseAction: ToggleConnectDatabaseAction; - private _changeConnectionAction: ConnectDatabaseAction; - private _listDatabasesAction: ListDatabasesAction; - private _estimatedQueryPlanAction: EstimatedQueryPlanAction; - private _actualQueryPlanAction: ActualQueryPlanAction; + //actions + private _runQueryAction: actions.RunQueryAction; + private _cancelQueryAction: actions.CancelQueryAction; + private _toggleConnectDatabaseAction: actions.ToggleConnectDatabaseAction; + private _changeConnectionAction: actions.ConnectDatabaseAction; + private _listDatabasesAction: actions.ListDatabasesAction; + private _estimatedQueryPlanAction: actions.EstimatedQueryPlanAction; + private _actualQueryPlanAction: actions.ActualQueryPlanAction; + private _listDatabasesActionItem: actions.ListDatabasesActionItem; constructor( - @ITelemetryService _telemetryService: ITelemetryService, + @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, - @IInstantiationService private _instantiationService: IInstantiationService, - @IEditorService private _editorService: IEditorService, - @IQueryModelService private _queryModelService: IQueryModelService, - @IEditorDescriptorService private _editorDescriptorService: IEditorDescriptorService, + @IStorageService storageService: IStorageService, @IContextKeyService contextKeyService: IContextKeyService, - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @IConfigurationService private _configurationService: IConfigurationService, - @IStorageService storageService: IStorageService + @IEditorService private readonly editorService: IEditorService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { - super(QueryEditor.ID, _telemetryService, themeService, storageService); + super(QueryEditor.ID, telemetryService, themeService, storageService); - this._orientation = Orientation.HORIZONTAL; - - if (contextKeyService) { - this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); - } - - if (_editorService) { - _editorService.overrideOpenEditor((editor, options, group) => { - if (this.isVisible() && (editor !== this.input || group !== this.group)) { - this.saveEditorViewState(); - } - return {}; - }); - } - } - - // PROPERTIES ////////////////////////////////////////////////////////// - /** - * Returns the URI of this editor if it is connected. - * @returns URI of the editor if connected, undefined otherwise - */ - public get connectedUri(): string { - return this._connectionManagementService.isConnected(this.uri) - ? this.uri - : undefined; - } - - /** - * Returns the URI of this editor if an input is associated with it - * @return URI of this if input is associated, undefined otherwise - */ - get uri(): string { - let input: QueryInput = this.input; - return input - ? input.getQueryResultsInputResource() - : undefined; + this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); } // PUBLIC METHODS //////////////////////////////////////////////////////////// - public get currentQueryInput(): QueryInput { - return this.input; + public get input(): QueryInput { + return this._input as QueryInput; } /** * Called to create the editor in the parent element. */ public createEditor(parent: HTMLElement): void { - const parentElement = parent; - DOM.addClass(parentElement, 'side-by-side-editor'); - this._createTaskbar(parentElement); + DOM.addClass(parent, 'query-editor'); + + let splitviewContainer = DOM.$('.query-editor-view'); + + this.createTaskbar(parent); + + parent.appendChild(splitviewContainer); + + this.splitview = new SplitView(splitviewContainer, { orientation: Orientation.VERTICAL }); + this._register(this.splitview); + this._register(this.splitview.onDidSashReset(() => this.splitview.distributeViewSizes())); + + this.textEditorContainer = DOM.$('.text-editor-container'); + this.textEditor = this._register(this.instantiationService.createInstance(TextResourceEditor)); + this.textEditor.create(this.textEditorContainer); + + this.splitview.addView({ + element: this.textEditorContainer, + layout: size => this.textEditor.layout(new DOM.Dimension(this.dimension.width, size)), + minimumSize: 220, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: Event.None + }, Sizing.Distribute); + + this.resultsEditorContainer = DOM.$('.results-editor-container'); + this.resultsEditor = this._register(this.instantiationService.createInstance(QueryResultsEditor)); + this.resultsEditor.create(this.resultsEditorContainer); } /** - * Sets the input data for this editor. + * Creates the query execution taskbar that appears at the top of the QueryEditor */ - public setInput(newInput: QueryInput, options?: EditorOptions): Promise { - const oldInput = this.input; + private createTaskbar(parentElement: HTMLElement): void { + // Create QueryTaskbar + let taskbarContainer = DOM.append(parentElement, DOM.$('div')); + this.taskbar = this._register(new Taskbar(taskbarContainer, { + actionViewItemProvider: (action: Action) => this._getActionItemForAction(action), + })); - if (newInput.matches(oldInput)) { - return Promise.resolve(undefined); + // Create Actions for the toolbar + this._runQueryAction = this.instantiationService.createInstance(actions.RunQueryAction, this); + this._cancelQueryAction = this.instantiationService.createInstance(actions.CancelQueryAction, this); + this._toggleConnectDatabaseAction = this.instantiationService.createInstance(actions.ToggleConnectDatabaseAction, this, false); + this._changeConnectionAction = this.instantiationService.createInstance(actions.ConnectDatabaseAction, this, true); + this._listDatabasesAction = this.instantiationService.createInstance(actions.ListDatabasesAction, this); + this._estimatedQueryPlanAction = this.instantiationService.createInstance(actions.EstimatedQueryPlanAction, this); + this._actualQueryPlanAction = this.instantiationService.createInstance(actions.ActualQueryPlanAction, this); + + this.setTaskbarContent(); + + this._toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) { + this.setTaskbarContent(); + } + })); + } + + /** + * Update the buttons on the taskbar to reflect the state of the current input. + */ + private updateState(stateChangeEvent: IQueryEditorStateChange): void { + if (stateChangeEvent.connectedChange) { + this._toggleConnectDatabaseAction.connected = this.input.state.connected; + this._changeConnectionAction.enabled = this.input.state.connected; + if (this.input.state.connected) { + this.listDatabasesActionItem.onConnected(); + } else { + this.listDatabasesActionItem.onDisconnect(); + } } - // Make sure all event callbacks will be sent to this QueryEditor in the case that this QueryInput was moved from - // another QueryEditor - let taskbarCallback: IDisposable = newInput.updateTaskbarEvent(() => this._updateTaskbar()); - let showResultsCallback: IDisposable = newInput.showQueryResultsEditorEvent(() => this._showQueryResultsEditor()); - let selectionCallback: IDisposable = newInput.updateSelectionEvent((selection) => this._setSelection(selection)); - newInput.setEventCallbacks([taskbarCallback, showResultsCallback, selectionCallback]); + if (stateChangeEvent.connectingChange) { + this._runQueryAction.enabled = !this.input.state.connecting; + this._estimatedQueryPlanAction.enabled = !this.input.state.connecting; - return super.setInput(newInput, options, CancellationToken.None) - .then(() => this._updateInput(oldInput, newInput, options)); + } + + if (stateChangeEvent.executingChange) { + this._runQueryAction.enabled = !this.input.state.executing; + this._estimatedQueryPlanAction.enabled = !this.input.state.executing; + this._cancelQueryAction.enabled = this.input.state.executing; + } + + if (stateChangeEvent.resultsVisibleChange) { + if (this.input.state.resultsVisible) { + this.addResultsEditor(); + } else { + this.removeResultsEditor(); + } + } + } + + /** + * Gets the IActionItem for the List Databases dropdown if provided the associated Action. + * Otherwise returns null. + */ + private _getActionItemForAction(action: Action): IActionViewItem { + if (action.id === actions.ListDatabasesAction.ID) { + return this.listDatabasesActionItem; + } + + return null; + } + + private get listDatabasesActionItem(): actions.ListDatabasesActionItem { + if (!this._listDatabasesActionItem) { + this._listDatabasesActionItem = this.instantiationService.createInstance(actions.ListDatabasesActionItem, this); + this._register(this._listDatabasesActionItem.attachStyler(this.themeService)); + } + return this._listDatabasesActionItem; + } + + private setTaskbarContent(): void { + // Create HTML Elements for the taskbar + let separator = Taskbar.createTaskbarSeparator(); + + // Set the content in the order we desire + let content: ITaskbarContent[] = [ + { action: this._runQueryAction }, + { action: this._cancelQueryAction }, + { element: separator }, + { action: this._toggleConnectDatabaseAction }, + { action: this._changeConnectionAction }, + { action: this._listDatabasesAction }, + { element: separator }, + { action: this._estimatedQueryPlanAction } + ]; + + // Remove the estimated query plan action if preview features are not enabled + let previewFeaturesEnabled = this.configurationService.getValue('workbench')['enablePreviewFeatures']; + if (!previewFeaturesEnabled) { + content = content.slice(0, -2); + } + + this.taskbar.setContent(content); + } + + public setInput(newInput: QueryInput, options: EditorOptions, token: CancellationToken): Promise { + const oldInput = this.input; + + if (newInput.matches(oldInput)) { + return Promise.resolve(); + } + + return Promise.all([ + super.setInput(newInput, options, token), + this.textEditor.setInput(newInput.sql, options, token), + this.resultsEditor.setInput(newInput.results, options) + ]).then(() => { + dispose(this.inputDisposables); + this.inputDisposables = []; + this.inputDisposables.push(this.input.state.onChange(c => this.updateState(c))); + this.updateState({ connectingChange: true, connectedChange: true, executingChange: true, resultsVisibleChange: true }); + }); + } + + public toggleResultsEditorVisibility() { + if (this.resultsVisible) { + this.removeResultsEditor(); + } else { + this.addResultsEditor(); + } } /** * Sets this editor and the 2 sub-editors to visible. */ public setEditorVisible(visible: boolean, group: IEditorGroup): void { - if (this._resultsEditor) { - this._resultsEditor.setVisible(visible, group); - } - if (this._sqlEditor) { - this._sqlEditor.setVisible(visible, group); - } + this.textEditor.setVisible(visible, group); + this.resultsEditor.setVisible(visible, group); super.setEditorVisible(visible, group); // Note: must update after calling super.setEditorVisible so that the accurate count is handled this.updateQueryEditorVisible(visible); } - private updateQueryEditorVisible(currentEditorIsVisible: boolean): void { if (this.queryEditorVisible) { let visible = currentEditorIsVisible; if (!currentEditorIsVisible) { // Current editor is closing but still tracked as visible. Check if any other editor is visible - const candidates = [...this._editorService.visibleControls].filter(e => { + const candidates = [...this.editorService.visibleControls].filter(e => { if (e && e.getId) { return e.getId() === QueryEditor.ID; } @@ -211,18 +287,14 @@ export class QueryEditor extends BaseEditor { } } + /** * Called to indicate to the editor that the input should be cleared and resources associated with the * input should be freed. */ public clearInput(): void { - if (this._resultsEditor) { - this._resultsEditor.clearInput(); - } - if (this._sqlEditor) { - this._sqlEditor.clearInput(); - } - this._disposeEditors(); + this.textEditor.clearInput(); + this.resultsEditor.clearInput(); super.clearInput(); } @@ -230,9 +302,7 @@ export class QueryEditor extends BaseEditor { * Sets focus on this editor. Specifically, it sets the focus on the hosted text editor. */ public focus(): void { - if (this._sqlEditor) { - this._sqlEditor.focus(); - } + this.textEditor.focus(); } /** @@ -240,37 +310,45 @@ export class QueryEditor extends BaseEditor { * To be called when the container of this editor changes size. */ public layout(dimension: DOM.Dimension): void { - this._dimension = dimension; - - if (this._sash) { - this._setSashDimension(); - this.sash.layout(); - } - - this._doLayout(); - this._resizeGridContents(); + this.dimension = dimension; + this.splitview.layout(dimension.height - 31); } /** * Returns the editor control for the text editor. */ public getControl(): IEditorControl { - if (this._sqlEditor) { - return this._sqlEditor.getControl(); + return this.textEditor.getControl(); + } + + public setOptions(options: EditorOptions): void { + this.textEditor.setOptions(options); + } + + private removeResultsEditor() { + if (this.resultsVisible) { + this.splitview.removeView(1, Sizing.Distribute); + this.resultsVisible = false; } - return null; } - public getQueryResultsEditor(): QueryResultsEditor { - return this._resultsEditor; - } - - public getSqlEditor(): TextResourceEditor { - return this._sqlEditor; + private addResultsEditor() { + if (!this.resultsVisible) { + this.splitview.addView({ + element: this.resultsEditorContainer, + layout: size => this.resultsEditor && this.resultsEditor.layout(new DOM.Dimension(this.dimension.width, size)), + minimumSize: 220, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: Event.None + }, Sizing.Distribute); + this.resultsVisible = true; + } } public dispose(): void { - this._disposeEditors(); + this.textEditor.dispose(); + this.resultsEditor.dispose(); + this.splitview.dispose(); super.dispose(); } @@ -280,44 +358,21 @@ export class QueryEditor extends BaseEditor { queryInput.results.close(); } - /** - * Makes visible the QueryResultsEditor for the current QueryInput (if it is not - * already visible). - */ - public _showQueryResultsEditor(): void { - if (this._isResultsEditorVisible()) { - return; + // helper functions + + public isSelectionEmpty(): boolean { + if (this.textEditor && this.textEditor.getControl()) { + let control = this.textEditor.getControl(); + let codeEditor: ICodeEditor = control; + + if (codeEditor) { + let value = codeEditor.getValue(); + if (value !== undefined && value.length > 0) { + return false; + } + } } - - const activeControl = this._editorService.activeControl; - activeControl.group.pinEditor(activeControl.input); - - let input = this.input; - this._createResultsEditorContainer(); - - this._createEditor(input.results, this._resultsEditorContainer, this.group) - .then(result => { - this._onResultsEditorCreated(result, input.results, this.options); - this.resultsEditorVisibility = true; - this.hideQueryResultsView = false; - this._doLayout(true); - }); - } - - private hideQueryResultsView = false; - - /** - * Toggle the visibility of the view state of results - */ - public toggleResultsEditorVisibility(): void { - let input = this.input; - let hideResults = this.hideQueryResultsView; - this.hideQueryResultsView = !this.hideQueryResultsView; - if (!input.results) { - return; - } - this.resultsEditorVisibility = hideResults; - this._doLayout(); + return true; } /** @@ -325,8 +380,8 @@ export class QueryEditor extends BaseEditor { * is no selected text. */ public getSelection(checkIfRange: boolean = true): ISelectionData { - if (this._sqlEditor && this._sqlEditor.getControl()) { - let vscodeSelection = this._sqlEditor.getControl().getSelection(); + if (this.textEditor && this.textEditor.getControl()) { + let vscodeSelection = this.textEditor.getControl().getSelection(); // If the selection is a range of characters rather than just a cursor position, return the range let isRange: boolean = @@ -347,40 +402,9 @@ export class QueryEditor extends BaseEditor { return undefined; } - public isSelectionEmpty(): boolean { - if (this._sqlEditor && this._sqlEditor.getControl()) { - let control = this._sqlEditor.getControl(); - let codeEditor: ICodeEditor = control; - - if (codeEditor) { - let value = codeEditor.getValue(); - if (value !== undefined && value.length > 0) { - return false; - } - } - } - return true; - } - - public getAllText(): string { - if (this._sqlEditor && this._sqlEditor.getControl()) { - let control = this._sqlEditor.getControl(); - let codeEditor: ICodeEditor = control; - if (codeEditor) { - let value = codeEditor.getValue(); - if (value !== undefined && value.length > 0) { - return value; - } else { - return ''; - } - } - } - return undefined; - } - public getAllSelection(): ISelectionData { - if (this._sqlEditor && this._sqlEditor.getControl()) { - let control = this._sqlEditor.getControl(); + if (this.textEditor && this.textEditor.getControl()) { + let control = this.textEditor.getControl(); let codeEditor: ICodeEditor = control; if (codeEditor) { let model = codeEditor.getModel(); @@ -398,9 +422,25 @@ export class QueryEditor extends BaseEditor { return undefined; } + public getAllText(): string { + if (this.textEditor && this.textEditor.getControl()) { + let control = this.textEditor.getControl(); + let codeEditor: ICodeEditor = control; + if (codeEditor) { + let value = codeEditor.getValue(); + if (value !== undefined && value.length > 0) { + return value; + } else { + return ''; + } + } + } + return undefined; + } + public getSelectionText(): string { - if (this._sqlEditor && this._sqlEditor.getControl()) { - let control = this._sqlEditor.getControl(); + if (this.textEditor && this.textEditor.getControl()) { + let control = this.textEditor.getControl(); let codeEditor: ICodeEditor = control; let vscodeSelection = control.getSelection(); @@ -415,13 +455,6 @@ export class QueryEditor extends BaseEditor { return ''; } - /** - * Calls the run method of this editor's RunQueryAction - */ - public runQuery(): void { - this._runQueryAction.run(); - } - /** * Calls the runCurrent method of this editor's RunQueryAction */ @@ -436,6 +469,13 @@ export class QueryEditor extends BaseEditor { this._actualQueryPlanAction.run(); } + /** + * Calls the run method of this editor's RunQueryAction + */ + public runQuery(): void { + this._runQueryAction.run(); + } + /** * Calls the run method of this editor's CancelQueryAction */ @@ -443,458 +483,12 @@ export class QueryEditor extends BaseEditor { this._cancelQueryAction.run(); } - public rebuildIntelliSenseCache(): void { - this._connectionManagementService.rebuildIntelliSenseCache(this.connectedUri); + public registerQueryModelViewTab(title: string, componentId: string): void { + this.resultsEditor.registerQueryModelViewTab(title, componentId); } - public setOptions(options: EditorOptions): void { - const textOptions = options; - if (textOptions && types.isFunction(textOptions.apply)) { - textOptions.apply(this.getControl() as editorCommon.IEditor, editorCommon.ScrollType.Smooth); - } - } - - // PRIVATE METHODS //////////////////////////////////////////////////////////// - - /** - * Creates the query execution taskbar that appears at the top of the QueryEditor - */ - private _createTaskbar(parentElement: HTMLElement): void { - // Create QueryTaskbar - this._taskbarContainer = DOM.append(parentElement, DOM.$('div')); - this._taskbar = new Taskbar(this._taskbarContainer, { - actionViewItemProvider: (action: Action) => this._getActionItemForAction(action), - }); - - // Create Actions for the toolbar - this._runQueryAction = this._instantiationService.createInstance(RunQueryAction, this); - this._cancelQueryAction = this._instantiationService.createInstance(CancelQueryAction, this); - this._toggleConnectDatabaseAction = this._instantiationService.createInstance(ToggleConnectDatabaseAction, this, false); - this._changeConnectionAction = this._instantiationService.createInstance(ConnectDatabaseAction, this, true); - this._listDatabasesAction = this._instantiationService.createInstance(ListDatabasesAction, this); - this._estimatedQueryPlanAction = this._instantiationService.createInstance(EstimatedQueryPlanAction, this); - this._actualQueryPlanAction = this._instantiationService.createInstance(ActualQueryPlanAction, this); - - this.setTaskbarContent(); - - this._toDispose.push(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) { - this.setTaskbarContent(); - } - })); - } - - private setTaskbarContent(): void { - // Create HTML Elements for the taskbar - let separator = Taskbar.createTaskbarSeparator(); - - // Set the content in the order we desire - let content: ITaskbarContent[] = [ - { action: this._runQueryAction }, - { action: this._cancelQueryAction }, - { element: separator }, - { action: this._toggleConnectDatabaseAction }, - { action: this._changeConnectionAction }, - { action: this._listDatabasesAction }, - { element: separator }, - { action: this._estimatedQueryPlanAction } - ]; - - // Remove the estimated query plan action if preview features are not enabled - let previewFeaturesEnabled = this._configurationService.getValue('workbench')['enablePreviewFeatures']; - if (!previewFeaturesEnabled) { - content = content.slice(0, -2); - } - - this._taskbar.setContent(content); - } - - /** - * Gets the IActionItem for the List Databases dropdown if provided the associated Action. - * Otherwise returns null. - */ - private _getActionItemForAction(action: Action): IActionViewItem { - if (action.id === ListDatabasesAction.ID) { - return this.listDatabasesActionItem; - } - - return null; - } - - /** - * Public for testing purposes only - */ - public get listDatabasesActionItem(): ListDatabasesActionItem { - if (!this._listDatabasesActionItem) { - this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this); - this._register(this._listDatabasesActionItem.attachStyler(this.themeService)); - } - return this._listDatabasesActionItem; - } - - /** - * Handles setting input for this editor. - */ - private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): Promise { - if (this._sqlEditor) { - this._sqlEditor.clearInput(); - } - - if (oldInput) { - this._disposeEditors(); - } - - this._createSqlEditorContainer(); - if (this._isResultsEditorVisible()) { - this._createResultsEditorContainer(); - - let uri: string = newInput.getQueryResultsInputResource(); - if (uri) { - this._queryModelService.refreshResultsets(uri); - } - } - - if (this._sash) { - if (this._isResultsEditorVisible()) { - this._sash.show(); - } else { - this._sash.hide(); - } - } - - this._updateTaskbar(); - return this._setNewInput(newInput, options); - } - - /** - * Handles setting input and creating editors when this QueryEditor is either: - * - Opened for the first time - * - Opened with a new QueryInput - * This will create only the SQL editor if the results editor does not yet exist for the - * given QueryInput. - */ - private _setNewInput(newInput: QueryInput, options?: EditorOptions): Promise { - - // Promises that will ensure proper ordering of editor creation logic - let createEditors: () => Promise; - let onEditorsCreated: (result) => Promise; - - // If both editors exist, create joined promises - one for each editor - if (this._isResultsEditorVisible()) { - createEditors = () => { - return Promise.all([ - this._createEditor(newInput.results, this._resultsEditorContainer, this.group), - this._createEditor(newInput.sql, this._sqlEditorContainer, this.group) - ]); - }; - onEditorsCreated = (result: IEditor[]) => { - return Promise.all([ - this._onResultsEditorCreated(result[0], newInput.results, options), - this._onSqlEditorCreated(result[1], newInput.sql, options) - ]); - }; - - // If only the sql editor exists, create a promise and wait for the sql editor to be created - } else { - createEditors = () => { - return this._createEditor(newInput.sql, this._sqlEditorContainer, this.group); - }; - onEditorsCreated = (result: TextResourceEditor) => { - return Promise.all([ - this._onSqlEditorCreated(result, newInput.sql, options) - ]); - }; - } - - // Create a promise to re render the layout after the editor creation logic - let doLayout: () => Promise = () => { - this._doLayout(); - return Promise.resolve(undefined); - }; - - // Run all three steps synchronously - return createEditors() - .then(onEditorsCreated) - .then(doLayout) - .then(() => { - if (newInput.results) { - newInput.results.onRestoreViewStateEmitter.fire(); - } - if (newInput.savedViewState) { - this._sqlEditor.getControl().restoreViewState(newInput.savedViewState); - } - }); - } - - /** - * Create a single editor based on the type of the given EditorInput. - */ - private _createEditor(editorInput: EditorInput, container: HTMLElement, group: IEditorGroup): Promise { - const descriptor = this._editorDescriptorService.getEditor(editorInput); - if (!descriptor) { - return Promise.reject(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); - } - - let editor = descriptor.instantiate(this._instantiationService); - editor.create(container); - editor.setVisible(this.isVisible(), group); - return Promise.resolve(editor); - } - - /** - * Sets input for the SQL editor after it has been created. - */ - private _onSqlEditorCreated(sqlEditor: TextResourceEditor, sqlInput: UntitledEditorInput, options: EditorOptions): Thenable { - this._sqlEditor = sqlEditor; - return this._sqlEditor.setInput(sqlInput, options, CancellationToken.None); - } - - /** - * Sets input for the results editor after it has been created. - */ - private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): Promise { - this._resultsEditor = resultsEditor; - return this._resultsEditor.setInput(resultsInput, options); - } - - /** - * Appends the HTML for the SQL editor. Creates new HTML every time. - */ - private _createSqlEditorContainer() { - const parentElement = this.getContainer(); - this._sqlEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container')); - this._sqlEditorContainer.style.position = 'absolute'; - } - - /** - * Appends the HTML for the QueryResultsEditor to the QueryEditor. If the HTML has not yet been - * created, it creates it and appends it. If it has already been created, it locates it and - * appends it. - */ - private _createResultsEditorContainer() { - this._createSash(); - - const parentElement = this.getContainer(); - let input = this.input; - - if (!input.results.container) { - let cssClass: string = '.master-editor-container'; - if (this._orientation === Orientation.HORIZONTAL) { - cssClass = '.master-editor-container-horizontal'; - } - - this._resultsEditorContainer = DOM.append(parentElement, DOM.$(cssClass)); - this._resultsEditorContainer.style.position = 'absolute'; - - input.results.container = this._resultsEditorContainer; - } else { - this._resultsEditorContainer = DOM.append(parentElement, input.results.container); - } - } - - /** - * Creates the sash with the requested orientation and registers sash callbacks - */ - private _createSash(): void { - if (!this._sash) { - let parentElement: HTMLElement = this.getContainer(); - - if (this._orientation === Orientation.HORIZONTAL) { - this._sash = this._register(new HorizontalFlexibleSash(parentElement, this._minEditorSize)); - } else { - this._sash = this._register(new VerticalFlexibleSash(parentElement, this._minEditorSize)); - this._sash.setEdge(this.getTaskBarHeight() + this._tabHeight); - } - this._setSashDimension(); - - this._register(this._sash.onPositionChange(position => this._doLayout())); - } - - this.sash.show(); - } - - private _setSashDimension(): void { - if (!this._dimension) { - return; - } - if (this._orientation === Orientation.HORIZONTAL) { - this._sash.setDimenesion(this._dimension); - } else { - this._sash.setDimenesion(new DOM.Dimension(this._dimension.width, this._dimension.height - this.getTaskBarHeight())); - } - - } - - /** - * Updates the size of the 2 sub-editors. Uses agnostic dimensions due to the fact that - * the IFlexibleSash could be horizontal or vertical. The same logic is used for horizontal - * and vertical sashes. - */ - private _doLayout(skipResizeGridContent: boolean = false): void { - if (!this._isResultsEditorVisible() && this._sqlEditor) { - this._doLayoutSql(); - return; - } - if (!this._sqlEditor || !this._resultsEditor || !this._dimension || !this._sash) { - return; - } - - if (this._orientation === Orientation.HORIZONTAL) { - this._doLayoutHorizontal(); - } else { - this._doLayoutVertical(); - } - - if (!skipResizeGridContent) { - this._resizeGridContents(); - } - } - - private getTaskBarHeight(): number { - let taskBarElement = this.taskbar.getContainer(); - return DOM.getContentHeight(taskBarElement); - } - - private _doLayoutHorizontal(): void { - let splitPointTop: number = this._sash.getSplitPoint(); - let parent: ClientRect = this.getContainer().getBoundingClientRect(); - - let sqlEditorHeight = splitPointTop - (parent.top + this.getTaskBarHeight()); - - let titleBar = document.getElementById('workbench.parts.titlebar'); - if (titleBar) { - sqlEditorHeight += DOM.getContentHeight(titleBar); - } - - let queryResultsEditorHeight = parent.bottom - splitPointTop; - this._resultsEditorContainer.hidden = false; - this._sqlEditorContainer.style.height = `${sqlEditorHeight}px`; - this._sqlEditorContainer.style.width = `${this._dimension.width}px`; - this._sqlEditorContainer.style.top = `${this._editorTopOffset}px`; - - this._resultsEditorContainer.style.height = `${queryResultsEditorHeight}px`; - this._resultsEditorContainer.style.width = `${this._dimension.width}px`; - this._resultsEditorContainer.style.top = `${splitPointTop}px`; - - this._sqlEditor.layout(new DOM.Dimension(this._dimension.width, sqlEditorHeight)); - this._resultsEditor.layout(new DOM.Dimension(this._dimension.width, queryResultsEditorHeight)); - } - - private _doLayoutVertical(): void { - let splitPointLeft: number = this._sash.getSplitPoint(); - let parent: ClientRect = this.getContainer().getBoundingClientRect(); - - let sqlEditorWidth = splitPointLeft; - let queryResultsEditorWidth = parent.width - splitPointLeft; - - let taskbarHeight = this.getTaskBarHeight(); - this._sqlEditorContainer.style.width = `${sqlEditorWidth}px`; - this._sqlEditorContainer.style.height = `${this._dimension.height - taskbarHeight}px`; - this._sqlEditorContainer.style.left = `0px`; - - this._resultsEditorContainer.hidden = false; - this._resultsEditorContainer.style.width = `${queryResultsEditorWidth}px`; - this._resultsEditorContainer.style.height = `${this._dimension.height - taskbarHeight}px`; - this._resultsEditorContainer.style.left = `${splitPointLeft}px`; - - this._sqlEditor.layout(new DOM.Dimension(sqlEditorWidth, this._dimension.height - taskbarHeight)); - this._resultsEditor.layout(new DOM.Dimension(queryResultsEditorWidth, this._dimension.height - taskbarHeight)); - } - - private _doLayoutSql() { - if (this._resultsEditorContainer) { - this._resultsEditorContainer.style.width = '0px'; - this._resultsEditorContainer.style.height = '0px'; - this._resultsEditorContainer.style.left = '0px'; - this._resultsEditorContainer.hidden = true; - } - - if (this._dimension) { - this._sqlEditor.layout(new DOM.Dimension(this._dimension.width, this._dimension.height - this.getTaskBarHeight())); - } - } - - private _resizeGridContents(): void { - if (this._isResultsEditorVisible()) { - let queryInput: QueryInput = this.input; - let uri: string = queryInput.getQueryResultsInputResource(); - if (uri) { - this._queryModelService.resizeResultsets(uri); - } - } - } - - private _disposeEditors(): void { - if (this._sqlEditor) { - this._sqlEditor.dispose(); - this._sqlEditor = null; - } - if (this._resultsEditor) { - this._resultsEditor.dispose(); - this._resultsEditor = null; - } - - let thisEditorParent: HTMLElement = this.getContainer(); - - if (this._sqlEditorContainer) { - let sqlEditorParent: HTMLElement = this._sqlEditorContainer.parentElement; - if (sqlEditorParent && sqlEditorParent === thisEditorParent) { - this._sqlEditorContainer.parentElement.removeChild(this._sqlEditorContainer); - } - this._sqlEditorContainer = null; - } - - if (this._resultsEditorContainer) { - let resultsEditorParent: HTMLElement = this._resultsEditorContainer.parentElement; - if (resultsEditorParent && resultsEditorParent === thisEditorParent) { - this._resultsEditorContainer.parentElement.removeChild(this._resultsEditorContainer); - } - this._resultsEditorContainer = null; - this.hideQueryResultsView = false; - } - } - - /** - * Returns true if the QueryResultsInput has denoted that the results editor - * should be visible. - * Public for testing only. - */ - public _isResultsEditorVisible(): boolean { - let input: QueryInput = this.input; - - if (!input) { - return false; - } - return input.results.visible; - } - - set resultsEditorVisibility(isVisible: boolean) { - let input: QueryInput = this.input; - input.results.visible = isVisible; - } - - /** - * Update the buttons on the taskbar to reflect the state of the current input. - */ - private _updateTaskbar(): void { - let queryInput: QueryInput = this.input; - - if (queryInput) { - this._cancelQueryAction.enabled = queryInput.cancelQueryEnabled; - this._changeConnectionAction.enabled = queryInput.changeConnectionEnabled; - - // For the toggle database action, it should always be enabled since it's a toggle. - // We use inverse of connect enabled state for now, should refactor queryInput in the future to - // define connected as a boolean instead of using the enabled flag - this._toggleConnectDatabaseAction.enabled = true; - this._toggleConnectDatabaseAction.connected = !queryInput.connectEnabled; - this._runQueryAction.enabled = queryInput.runQueryEnabled; - if (queryInput.listDatabasesConnected) { - this.listDatabasesActionItem.onConnected(); - } else { - this.listDatabasesActionItem.onDisconnect(); - } - } + public chart(dataId: { batchId: number, resultId: number }) { + this.resultsEditor.chart(dataId); } /** @@ -907,67 +501,9 @@ export class QueryEditor extends BaseEditor { endLineNumber: selection.endLine + 1, endColumn: selection.endColumn + 1 }; - let editor = this._sqlEditor.getControl(); + let editor = this.textEditor.getControl(); editor.revealRange(rangeConversion); editor.setSelection(rangeConversion); editor.focus(); } - - private saveEditorViewState(): void { - let queryInput = this.input as QueryInput; - if (queryInput) { - if (this._sqlEditor) { - queryInput.savedViewState = this._sqlEditor.getControl().saveViewState(); - } - if (queryInput.results) { - queryInput.results.onSaveViewStateEmitter.fire(); - } - } - } - - // TESTING PROPERTIES //////////////////////////////////////////////////////////// - - public get resultsEditor(): QueryResultsEditor { - return this._resultsEditor; - } - - public get sqlEditor(): TextResourceEditor { - return this._sqlEditor; - } - - public get taskbar(): Taskbar { - return this._taskbar; - } - - public get sash(): IFlexibleSash { - return this._sash; - } - - public get resultsEditorContainer(): HTMLElement { - return this._resultsEditorContainer; - } - - public get sqlEditorContainer(): HTMLElement { - return this._sqlEditorContainer; - } - - public get taskbarContainer(): HTMLElement { - return this._taskbarContainer; - } - - public get runQueryAction(): RunQueryAction { - return this._runQueryAction; - } - - public get cancelQueryAction(): CancelQueryAction { - return this._cancelQueryAction; - } - - public get changeConnectionAction(): ConnectDatabaseAction { - return this._changeConnectionAction; - } - - public registerQueryModelViewTab(title: string, componentId: string): void { - this._resultsEditor.registerQueryModelViewTab(title, componentId); - } } diff --git a/src/sql/workbench/parts/query/browser/queryResultsEditor.ts b/src/sql/workbench/parts/query/browser/queryResultsEditor.ts index 272d4f992a..ad1a99c5dd 100644 --- a/src/sql/workbench/parts/query/browser/queryResultsEditor.ts +++ b/src/sql/workbench/parts/query/browser/queryResultsEditor.ts @@ -16,7 +16,6 @@ import * as types from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { QueryResultsInput } from 'sql/workbench/parts/query/common/queryResultsInput'; -import { IQueryModelService } from 'sql/platform/query/common/queryModel'; import { QueryResultsView } from 'sql/workbench/parts/query/browser/queryResultsView'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -95,7 +94,6 @@ export class QueryResultsEditor extends BaseEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, - @IQueryModelService private _queryModelService: IQueryModelService, @IConfigurationService private _configurationService: IConfigurationService, @IInstantiationService private _instantiationService: IInstantiationService, @IStorageService storageService: IStorageService diff --git a/src/sql/workbench/parts/query/common/queryInput.ts b/src/sql/workbench/parts/query/common/queryInput.ts index 8cdf076ca8..2a8ee3f6cd 100644 --- a/src/sql/workbench/parts/query/common/queryInput.ts +++ b/src/sql/workbench/parts/query/common/queryInput.ts @@ -8,19 +8,21 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; -import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor'; +import { EditorInput, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement'; import { QueryResultsInput } from 'sql/workbench/parts/query/common/queryResultsInput'; import { IQueryModelService } from 'sql/platform/query/common/queryModel'; -import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; import { ISelectionData, ExecutionPlanOptions } from 'azdata'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; +import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; const MAX_SIZE = 13; +type PublicPart = { [K in keyof T]: T[K] }; + function trimTitle(title: string): string { const length = title.length; const diff = length - MAX_SIZE; @@ -33,30 +35,81 @@ function trimTitle(title: string): string { } } +export interface IQueryEditorStateChange { + connectedChange?: boolean; + resultsVisibleChange?: boolean; + executingChange?: boolean; + connectingChange?: boolean; +} + +export class QueryEditorState { + private _connected = false; + private _resultsVisible = false; + private _executing = false; + private _connecting = false; + + private _onChange = new Emitter(); + public onChange = this._onChange.event; + + public set connected(val: boolean) { + if (val !== this._connected) { + this._connected = val; + this._onChange.fire({ connectedChange: true }); + } + } + + public get connected(): boolean { + return this._connected; + } + + public set connecting(val: boolean) { + if (val !== this._connecting) { + this._connecting = val; + this._onChange.fire({ connectingChange: true }); + } + } + + public get connecting(): boolean { + return this._connecting; + } + + public set resultsVisible(val: boolean) { + if (val !== this._resultsVisible) { + this._resultsVisible = val; + this._onChange.fire({ resultsVisibleChange: true }); + } + } + + public get resultsVisible(): boolean { + return this._resultsVisible; + } + + public set executing(val: boolean) { + if (val !== this._executing) { + this._executing = val; + this._onChange.fire({ executingChange: true }); + } + } + + public get executing(): boolean { + return this._executing; + } +} + /** * Input for the QueryEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor * and a UntitledEditorInput for the SQL File Editor. */ -export class QueryInput extends EditorInput implements IEncodingSupport, IConnectableInput, IDisposable { +export class QueryInput extends EditorInput implements IEncodingSupport, IConnectableInput, PublicPart, IDisposable { public static ID: string = 'workbench.editorinputs.queryInput'; public static SCHEMA: string = 'sql'; - private _runQueryEnabled: boolean; - private _cancelQueryEnabled: boolean; - private _connectEnabled: boolean; - private _disconnectEnabled: boolean; - private _changeConnectionEnabled: boolean; - private _listDatabasesConnected: boolean; + private _state = new QueryEditorState(); + public get state(): QueryEditorState { return this._state; } - private _updateTaskbar: Emitter; - private _showQueryResultsEditor: Emitter; private _updateSelection: Emitter; - private _currentEventCallbacks: IDisposable[]; - - public savedViewState: IEditorViewState; - constructor( private _description: string, private _sql: UntitledEditorInput, @@ -64,16 +117,12 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec private _connectionProviderName: string, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IQueryModelService private _queryModelService: IQueryModelService, - @IQueryEditorService private _queryEditorService: IQueryEditorService, @IConfigurationService private _configurationService: IConfigurationService ) { super(); - this._updateTaskbar = new Emitter(); - this._showQueryResultsEditor = new Emitter(); this._updateSelection = new Emitter(); this._toDispose = []; - this._currentEventCallbacks = []; // re-emit sql editor events through this editor if it exists if (this._sql) { this._toDispose.push(this._sql.onDidChangeDirty(() => this._onDidChangeDirty.fire())); @@ -130,17 +179,6 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec public get uri(): string { return this.getResource().toString(); } public get sql(): UntitledEditorInput { return this._sql; } public get results(): QueryResultsInput { return this._results; } - public get updateTaskbarEvent(): Event { return this._updateTaskbar.event; } - public get showQueryResultsEditorEvent(): Event { return this._showQueryResultsEditor.event; } - public get updateSelectionEvent(): Event { return this._updateSelection.event; } - public get runQueryEnabled(): boolean { return this._runQueryEnabled; } - public get cancelQueryEnabled(): boolean { return this._cancelQueryEnabled; } - public get connectEnabled(): boolean { return this._connectEnabled; } - public get disconnectEnabled(): boolean { return this._disconnectEnabled; } - public get changeConnectionEnabled(): boolean { return this._changeConnectionEnabled; } - public get listDatabasesConnected(): boolean { return this._listDatabasesConnected; } - public getQueryResultsInputResource(): string { return this._results.uri; } - public showQueryResultsEditor(): void { this._showQueryResultsEditor.fire(); } public updateSelection(selection: ISelectionData): void { this._updateSelection.fire(selection); } public getTypeId(): string { return QueryInput.ID; } // Description is shown beside the tab name in the combobox of open editors @@ -163,7 +201,7 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec // Forwarding resource functions to the inline sql file editor public get onDidModelChangeContent(): Event { return this._sql.onDidModelChangeContent; } public get onDidModelChangeEncoding(): Event { return this._sql.onDidModelChangeEncoding; } - public resolve(refresh?: boolean): Promise { return this._sql.resolve(); } + public resolve(): Promise { return this._sql.resolve(); } public save(): Promise { return this._sql.save(); } public isDirty(): boolean { return this._sql.isDirty(); } public confirmSave(): Promise { return this._sql.confirmSave(); } @@ -207,43 +245,37 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec // State update funtions public runQuery(selection: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void { this._queryModelService.runQuery(this.uri, selection, this, executePlanOptions); - this.showQueryResultsEditor(); + this.state.executing = true; } public runQueryStatement(selection: ISelectionData): void { this._queryModelService.runQueryStatement(this.uri, selection, this); - this.showQueryResultsEditor(); + this.state.executing = true; } public runQueryString(text: string): void { this._queryModelService.runQueryString(this.uri, text, this); - this.showQueryResultsEditor(); + this.state.executing = true; } public onConnectStart(): void { - this._runQueryEnabled = false; - this._cancelQueryEnabled = false; - this._connectEnabled = false; - this._disconnectEnabled = true; - this._changeConnectionEnabled = false; - this._listDatabasesConnected = false; - this._updateTaskbar.fire(); + this.state.connecting = true; + this.state.connected = false; } public onConnectReject(): void { - this.onDisconnect(); - this._updateTaskbar.fire(); + this.state.connecting = false; + this.state.connected = false; } public onConnectCanceled(): void { + this.state.connecting = false; + this.state.connected = false; } public onConnectSuccess(params?: INewConnectionParams): void { - this._runQueryEnabled = true; - this._connectEnabled = false; - this._disconnectEnabled = true; - this._changeConnectionEnabled = true; - this._listDatabasesConnected = true; + this.state.connected = true; + this.state.connecting = false; let isRunningQuery = this._queryModelService.isRunningQuery(this.uri); if (!isRunningQuery && params && params.runQueryOnCompletion) { @@ -258,30 +290,19 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec this.runQuery(selection, { displayActualQueryPlan: true }); } } - this._updateTaskbar.fire(); - this._onDidChangeLabel.fire(); } public onDisconnect(): void { - this._runQueryEnabled = true; - this._cancelQueryEnabled = false; - this._connectEnabled = true; - this._disconnectEnabled = false; - this._changeConnectionEnabled = false; - this._listDatabasesConnected = false; - this._updateTaskbar.fire(); + this.state.connected = false; } public onRunQuery(): void { - this._runQueryEnabled = false; - this._cancelQueryEnabled = true; - this._updateTaskbar.fire(); + this.state.executing = true; + this.state.resultsVisible = true; } public onQueryComplete(): void { - this._runQueryEnabled = true; - this._cancelQueryEnabled = false; - this._updateTaskbar.fire(); + this.state.executing = false; } // Clean up functions @@ -289,7 +310,6 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec this._sql.dispose(); this._results.dispose(); this._toDispose = dispose(this._toDispose); - this._currentEventCallbacks = dispose(this._currentEventCallbacks); super.dispose(); } @@ -301,18 +321,6 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec this._results.close(); } - /** - * Unsubscribe all events in _currentEventCallbacks and set the new callbacks - * to be unsubscribed the next time this method is called. - * - * This method is used to ensure that all callbacks point to the current QueryEditor - * in the case that this QueryInput is moved between different QueryEditors. - */ - public setEventCallbacks(callbacks: IDisposable[]): void { - this._currentEventCallbacks = dispose(this._currentEventCallbacks); - this._currentEventCallbacks = callbacks; - } - /** * Get the color that should be displayed */ diff --git a/src/sql/workbench/services/commandLine/common/commandLineService.ts b/src/sql/workbench/services/commandLine/common/commandLineService.ts index b97b5e4d53..2558f0fcdd 100644 --- a/src/sql/workbench/services/commandLine/common/commandLineService.ts +++ b/src/sql/workbench/services/commandLine/common/commandLineService.ts @@ -142,7 +142,7 @@ export class CommandLineService implements ICommandLineProcessing { let activeEditor = this._editorService.editors.filter(v => v.getResource().toString() === uriString).pop(); if (activeEditor) { let queryInput = activeEditor as QueryInput; - if (queryInput && queryInput.connectEnabled) { + if (queryInput && queryInput.state.connected) { let options: IConnectionCompletionOptions = { params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none, input: queryInput }, saveTheConnection: false, diff --git a/src/sqltest/parts/commandLine/commandLineService.test.ts b/src/sqltest/parts/commandLine/commandLineService.test.ts index d8103a144f..b49fdff1fa 100644 --- a/src/sqltest/parts/commandLine/commandLineService.test.ts +++ b/src/sqltest/parts/commandLine/commandLineService.test.ts @@ -23,7 +23,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { assertThrowsAsync } from 'sqltest/utils/testUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestEditorService, TestLogService } from 'vs/workbench/test/workbenchTestServices'; -import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; +import { QueryInput, QueryEditorState } from 'sql/workbench/parts/query/common/queryInput'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; @@ -374,7 +374,9 @@ suite('commandLineService tests', () => { const configurationService = getConfigurationServiceMock(true); const queryInput: TypeMoq.Mock = TypeMoq.Mock.ofType(QueryInput); let uri = URI.file(args._[0]); - queryInput.setup(q => q.connectEnabled).returns(() => 1 === 1).verifiable(TypeMoq.Times.once()); + const queryState = new QueryEditorState(); + queryState.connected = true; + queryInput.setup(q => q.state).returns(() => queryState); queryInput.setup(q => q.getResource()).returns(() => uri).verifiable(TypeMoq.Times.once()); const editorService: TypeMoq.Mock = TypeMoq.Mock.ofType(TestEditorService, TypeMoq.MockBehavior.Strict); editorService.setup(e => e.editors).returns(() => [queryInput.object]); diff --git a/src/sqltest/parts/query/editor/queryActions.test.ts b/src/sqltest/parts/query/editor/queryActions.test.ts index 9b5d3d06b1..ee1ff74a69 100644 --- a/src/sqltest/parts/query/editor/queryActions.test.ts +++ b/src/sqltest/parts/query/editor/queryActions.test.ts @@ -30,7 +30,8 @@ import { ConfigurationService } from 'vs/platform/configuration/node/configurati import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; -import { TestStorageService, TestLayoutService } from 'vs/workbench/test/workbenchTestServices'; +import { TestStorageService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; let none: void; @@ -48,12 +49,12 @@ suite('SQL QueryAction Tests', () => { testQueryInput.setup(x => x.uri).returns(() => testUri); testQueryInput.setup(x => x.runQuery(undefined)).callback(() => { calledRunQueryOnInput = true; }); + const contextkeyservice = new MockContextKeyService(); + // Setup a reusable mock QueryEditor - editor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService(), undefined, undefined, undefined, undefined, - undefined, undefined, undefined, new TestStorageService()); - editor.setup(x => x.connectedUri).returns(() => testUri); - editor.setup(x => x.currentQueryInput).returns(() => testQueryInput.object); - editor.setup(x => x.uri).returns(() => testUri); + editor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService(), new TestStorageService(), contextkeyservice, undefined, undefined, + undefined); + editor.setup(x => x.input).returns(() => testQueryInput.object); editor.setup(x => x.getSelection()).returns(() => undefined); editor.setup(x => x.getSelection(false)).returns(() => undefined); @@ -85,10 +86,12 @@ suite('SQL QueryAction Tests', () => { let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService()); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnectedReturnValue); - // ... Create an editor - let editor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Loose, undefined, new TestThemeService(), undefined, undefined, undefined, undefined, - undefined, undefined, undefined, new TestStorageService()); - editor.setup(x => x.currentQueryInput).returns(() => testQueryInput.object); + const contextkeyservice = new MockContextKeyService(); + + // Setup a reusable mock QueryEditor + editor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService(), new TestStorageService(), contextkeyservice, undefined, undefined, + undefined); + editor.setup(x => x.input).returns(() => testQueryInput.object); // If I create a QueryTaskbarAction and I pass a non-connected editor to _getConnectedQueryEditorUri let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, connectionManagementService.object); @@ -148,7 +151,7 @@ suite('SQL QueryAction Tests', () => { assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); assert.equal(connectionParams.runQueryOnCompletion, RunQueryOnConnectionMode.executeQuery, 'runQueryOnCompletion should be true`'); assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); - assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + assert.equal(connectionParams.input, editor.object.input, 'Editor should be set to the mock editor'); // If I call run on RunQueryAction when I am connected isConnected = true; @@ -174,9 +177,12 @@ suite('SQL QueryAction Tests', () => { queryInput.setup(x => x.runQuery(undefined)).callback(() => { countCalledRunQuery++; }); - let queryEditor: TypeMoq.Mock = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService(), undefined, - undefined, undefined, undefined, undefined, undefined, undefined, new TestStorageService()); - queryEditor.setup(x => x.currentQueryInput).returns(() => queryInput.object); + const contextkeyservice = new MockContextKeyService(); + + // Setup a reusable mock QueryEditor + let queryEditor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService(), new TestStorageService(), contextkeyservice, undefined, undefined, + undefined); + queryEditor.setup(x => x.input).returns(() => queryInput.object); queryEditor.setup(x => x.getSelection()).returns(() => undefined); queryEditor.setup(x => x.getSelection(false)).returns(() => undefined); queryEditor.setup(x => x.isSelectionEmpty()).returns(() => isSelectionEmpty); @@ -206,7 +212,7 @@ suite('SQL QueryAction Tests', () => { done(); }); - test('ISelectionData is properly passed when queries are run', (done) => { + test('ISelectionData is properly passed when queries are run', () => { /// Setup Test /// @@ -239,14 +245,19 @@ suite('SQL QueryAction Tests', () => { runQuerySelection = selection; countCalledRunQuery++; }); + const contextkeyservice = new MockContextKeyService(); - // ... Mock "getSelection" in QueryEditor - let queryEditor: TypeMoq.Mock = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Loose, undefined, new TestThemeService(), undefined, - undefined, undefined, undefined, undefined, undefined, undefined, new TestStorageService()); - queryEditor.setup(x => x.currentQueryInput).returns(() => queryInput.object); + // Setup a reusable mock QueryEditor + let queryEditor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService(), new TestStorageService(), contextkeyservice, undefined, undefined, + undefined); + queryEditor.setup(x => x.input).returns(() => queryInput.object); + queryEditor.setup(x => x.isSelectionEmpty()).returns(() => false); queryEditor.setup(x => x.getSelection()).returns(() => { return selectionToReturnInGetSelection; }); + queryEditor.setup(x => x.getSelection(TypeMoq.It.isAny())).returns(() => { + return selectionToReturnInGetSelection; + }); // ... Mock "isConnected" in ConnectionManagementService let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object); @@ -305,8 +316,6 @@ suite('SQL QueryAction Tests', () => { assert.equal(runQuerySelection.startColumn, selectionToReturnInGetSelection.startColumn, 'startColumn should match'); assert.equal(runQuerySelection.endLine, selectionToReturnInGetSelection.endLine, 'endLine should match'); assert.equal(runQuerySelection.endColumn, selectionToReturnInGetSelection.endColumn, 'endColumn should match'); - - done(); }); test('CancelQueryAction calls cancelQuery() only if URI is connected', (done) => { @@ -401,7 +410,7 @@ suite('SQL QueryAction Tests', () => { assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); - assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + assert.equal(connectionParams.input, editor.object.input, 'Editor should be set to the mock editor'); // If I call run on ConnectDatabaseAction when I am connected isConnected = true; @@ -412,7 +421,7 @@ suite('SQL QueryAction Tests', () => { assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); - assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + assert.equal(connectionParams.input, editor.object.input, 'Editor should be set to the mock editor'); done(); }); @@ -447,7 +456,7 @@ suite('SQL QueryAction Tests', () => { assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); - assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + assert.equal(connectionParams.input, editor.object.input, 'Editor should be set to the mock editor'); // Then if I call run on ChangeConnectionAction when I am connected isConnected = true; queryAction.run(); @@ -457,7 +466,7 @@ suite('SQL QueryAction Tests', () => { assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); - assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + assert.equal(connectionParams.input, editor.object.input, 'Editor should be set to the mock editor'); done(); }); @@ -476,7 +485,7 @@ suite('SQL QueryAction Tests', () => { }); // If I query without having initialized anything, state should be clear - listItem = new ListDatabasesActionItem(editor.object, connectionManagementService.object, undefined, undefined, configurationService.object); + listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, configurationService.object); assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected'); assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected'); @@ -511,7 +520,7 @@ suite('SQL QueryAction Tests', () => { cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => { databaseName: databaseName }); // ... Create a database dropdown that has been connected - let listItem = new ListDatabasesActionItem(editor.object, cms.object, null, null, configurationService.object); + let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, undefined, configurationService.object); listItem.onConnected(); // If: I raise a connection changed event @@ -535,7 +544,7 @@ suite('SQL QueryAction Tests', () => { cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => { databaseName: databaseName }); // ... Create a database dropdown that has been connected - let listItem = new ListDatabasesActionItem(editor.object, cms.object, null, null, configurationService.object); + let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, undefined, configurationService.object); listItem.onConnected(); // If: I raise a connection changed event for the 'wrong' URI @@ -562,14 +571,14 @@ suite('SQL QueryAction Tests', () => { cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); // ... Create a database dropdown - let listItem = new ListDatabasesActionItem(editor.object, cms.object, null, null, configurationService.object); + let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, undefined, configurationService.object); // If: I raise a connection changed event let eventParams = { connectionProfile: { databaseName: 'foobarbaz' }, - connectionUri: editor.object.uri + connectionUri: editor.object.input.uri }; dbChangedEmitter.fire(eventParams); diff --git a/src/sqltest/parts/query/editor/queryEditor.test.ts b/src/sqltest/parts/query/editor/queryEditor.test.ts index 725d141448..e30bedc495 100644 --- a/src/sqltest/parts/query/editor/queryEditor.test.ts +++ b/src/sqltest/parts/query/editor/queryEditor.test.ts @@ -6,12 +6,10 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEditorDescriptor } from 'vs/workbench/browser/editor'; import { URI } from 'vs/base/common/uri'; -import * as DOM from 'vs/base/browser/dom'; import { Memento } from 'vs/workbench/common/memento'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { QueryResultsInput } from 'sql/workbench/parts/query/common/queryResultsInput'; -import { QueryEditor } from 'sql/workbench/parts/query/browser/queryEditor'; import { QueryModelService } from 'sql/platform/query/common/queryModelService'; import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; import { INewConnectionParams, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement'; @@ -19,8 +17,6 @@ import { ConnectionManagementService } from 'sql/platform/connection/common/conn import { RunQueryAction, ListDatabasesActionItem } from 'sql/workbench/parts/query/browser/queryActions'; import { EditorDescriptorService } from 'sql/workbench/services/queryEditor/common/editorDescriptorService'; -import { TestThemeService } from 'sqltest/stubs/themeTestService'; - import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -30,7 +26,6 @@ import { TestStorageService } from 'vs/workbench/test/workbenchTestServices'; suite('SQL QueryEditor Tests', () => { let instantiationService: TypeMoq.Mock; - let themeService: TestThemeService = new TestThemeService(); let editorDescriptorService: TypeMoq.Mock; let connectionManagementService: TypeMoq.Mock; let configurationService: TypeMoq.Mock; @@ -38,20 +33,6 @@ suite('SQL QueryEditor Tests', () => { let mockEditor: any; - let getQueryEditor = function (): QueryEditor { - return new QueryEditor( - undefined, - themeService, - instantiationService.object, - undefined, - undefined, - editorDescriptorService.object, - undefined, - undefined, - configurationService.object, - new TestStorageService()); - }; - setup(() => { // Create object to mock the Editor classes // QueryResultsEditor fails at runtime due to the way we are importing Angualar, @@ -79,7 +60,7 @@ suite('SQL QueryEditor Tests', () => { instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => { if (classDef.ID) { if (classDef.ID === 'listDatabaseQueryActionItem') { - return new ListDatabasesActionItem(editor, connectionManagementService.object, undefined, undefined, configurationService.object); + return new ListDatabasesActionItem(editor, undefined, connectionManagementService.object, undefined, configurationService.object); } } // Default @@ -114,26 +95,6 @@ suite('SQL QueryEditor Tests', () => { connectionManagementService.setup(x => x.ensureDefaultLanguageFlavor(TypeMoq.It.isAnyString())).returns(() => void 0); }); - test('createEditor creates only the taskbar', (done) => { - // If I call createEditor - let editor: QueryEditor = getQueryEditor(); - editor.createEditor(DOM.$('queryEditorParent')); - - // The taskbar should be created - assert.equal(!!editor.taskbar, true); - assert.equal(!!editor.taskbarContainer, true); - - // But Nothing else should be created - assert.equal(!!editor.getContainer(), false); - assert.equal(!!editor.sqlEditor, false); - assert.equal(!!editor.sqlEditorContainer, false); - assert.equal(!!editor.resultsEditor, false); - assert.equal(!!editor.resultsEditorContainer, false); - assert.equal(!!editor.sash, false); - assert.equal(!!editor._isResultsEditorVisible(), false); - done(); - }); - /* test('setInput creates SQL components', (done) => { let assertInput = function () { @@ -315,7 +276,7 @@ suite('SQL QueryEditor Tests', () => { queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((definition, editor, action, selectBox) => { if (definition.ID === 'listDatabaseQueryActionItem') { - let item = new ListDatabasesActionItem(editor, queryConnectionService.object, undefined, undefined, configurationService.object); + let item = new ListDatabasesActionItem(editor, undefined, queryConnectionService.object, undefined, configurationService.object); return item; } // Default @@ -333,43 +294,33 @@ suite('SQL QueryEditor Tests', () => { undefined, connectionManagementService.object, queryModelService.object, - undefined, undefined ); }); - test('Taskbar buttons are set correctly upon standard load', (done) => { + test('Taskbar buttons are set correctly upon standard load', () => { queryConnectionService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false); queryModelService.setup(x => x.isRunningQuery(TypeMoq.It.isAny())).returns(() => false); // If I use the created QueryEditor with no changes since creation // Buttons should be set as if disconnected - assert.equal(queryInput.runQueryEnabled, true, 'runQueryAction button should be enabled'); - assert.equal(queryInput.cancelQueryEnabled, false, 'cancelQueryAction button should not be enabled'); - assert.equal(queryInput.connectEnabled, true, 'connectDatabaseAction button should be enabled'); - assert.equal(queryInput.disconnectEnabled, false, 'disconnectDatabaseAction button should not be enabled'); - assert.equal(queryInput.changeConnectionEnabled, false, 'changeConnectionAction button should not be enabled'); - assert.equal(queryInput.listDatabasesConnected, false); - done(); + assert.equal(queryInput.state.connected, false, 'query state should be not connected'); + assert.equal(queryInput.state.executing, false, 'query state should be not executing'); + assert.equal(queryInput.state.connecting, false, 'query state should be not connecting'); }); - test('Taskbar buttons are set correctly upon connect', (done) => { + test('Taskbar buttons are set correctly upon connect', () => { let params: INewConnectionParams = { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none }; queryInput.onConnectSuccess(params); queryModelService.setup(x => x.isRunningQuery(TypeMoq.It.isAny())).returns(() => false); - assert.equal(queryInput.runQueryEnabled, true, 'runQueryAction button should be enabled'); - assert.equal(queryInput.cancelQueryEnabled, false, 'cancelQueryAction button should not be enabled'); - assert.equal(queryInput.connectEnabled, false, 'connectDatabaseAction button should not be enabled'); - assert.equal(queryInput.disconnectEnabled, true, 'disconnectDatabaseAction button should be enabled'); - assert.equal(queryInput.changeConnectionEnabled, true, 'changeConnectionAction button should be enabled'); - assert.equal(queryInput.listDatabasesConnected, true); - done(); + assert.equal(queryInput.state.connected, true, 'query state should be not connected'); + assert.equal(queryInput.state.executing, false, 'query state should be not executing'); + assert.equal(queryInput.state.connecting, false, 'query state should be not connecting'); }); - test('Test that we attempt to dispose query when the queryInput is disposed', (done) => { + test('Test that we attempt to dispose query when the queryInput is disposed', () => { let queryResultsInput = new QueryResultsInput('testUri', configurationService.object); queryInput['_results'] = queryResultsInput; queryInput.close(); queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); - done(); }); }); }); diff --git a/src/sqltest/workbench/common/taskUtilities.test.ts b/src/sqltest/workbench/common/taskUtilities.test.ts index a148c30ca8..3d5a3b23d9 100644 --- a/src/sqltest/workbench/common/taskUtilities.test.ts +++ b/src/sqltest/workbench/common/taskUtilities.test.ts @@ -76,7 +76,7 @@ suite('TaskUtilities', function () { // Mock the workbench service to return the active tab connection let tabConnectionUri = 'file://test_uri'; let editorInput = new UntitledEditorInput(URI.parse(tabConnectionUri), false, undefined, undefined, undefined, undefined, undefined, undefined); - let queryInput = new QueryInput(undefined, editorInput, undefined, undefined, undefined, undefined, undefined, undefined); + let queryInput = new QueryInput(undefined, editorInput, undefined, undefined, undefined, undefined, undefined); mockConnectionManagementService.setup(x => x.getConnectionProfile(tabConnectionUri)).returns(() => tabProfile); // If I call getCurrentGlobalConnection, it should return the expected profile from the active tab diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index bf981ce16a..99d30e973c 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -160,10 +160,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs } - // {{SQL CARBON EDIT}} - // turn-off custom menus to avoid bug calculating size of SQL editor - // const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom'; - const useCustomTitleStyle = false; + const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom'; if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; this.hiddenTitleBarStyle = true; diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index c5118746f0..d9843090f2 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -300,16 +300,13 @@ export function getTitleBarStyle(configurationService: IConfigurationService, en return 'native'; // simple fullscreen does not work well with custom title style (https://github.com/Microsoft/vscode/issues/63291) } - // {{SQL CARBON EDIT}} - Always use native toolbar - // const style = configuration.titleBarStyle; - // if (style === 'native' || style === 'custom') { - // return style; - // } + const style = configuration.titleBarStyle; + if (style === 'native' || style === 'custom') { + return style; + } } - // {{SQL CARBON EDIT}} - Always use native toolbar - return 'native'; - // return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows + return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows } export const enum OpenContext { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 453dc6c75c..62ebb0ac85 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -219,7 +219,6 @@ export class MenubarControl extends Disposable { // TODO@sbatten remove after feb19 private notifyExistingLinuxUser(): void { - /*// {{SQL CARBON EDIT}} - Disable the custom titlebar recommendation if (!isLinux) { return; } @@ -246,7 +245,6 @@ export class MenubarControl extends Disposable { } } ]); - */ } private notifyUserOfCustomMenubarAccessibility(): void { diff --git a/src/vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution.ts index 3da9444617..c0c1a006c9 100644 --- a/src/vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution.ts @@ -55,12 +55,11 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private onConfigurationChange(config: IConfiguration, notify: boolean): void { let changed = false; - // {{SQL CARBON EDIT}} // Titlebar style - // if (config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) { - // this.titleBarStyle = 'native'; //config.window.titleBarStyle; - // changed = true; - // } + if (config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) { + this.titleBarStyle = config.window.titleBarStyle; + changed = true; + } // macOS: Native tabs if (isMacintosh && config.window && typeof config.window.nativeTabs === 'boolean' && config.window.nativeTabs !== this.nativeTabs) {