diff --git a/extensions/query-history/README.md b/extensions/query-history/README.md index 071222365a..950806d5c2 100644 --- a/extensions/query-history/README.md +++ b/extensions/query-history/README.md @@ -1,13 +1,15 @@ # Query History *(preview)* -Adds a Query History panel for viewing and running past executed queries. +Adds a Query History view for viewing and running past executed queries. ### How do I view the history? -Query History is displayed as a tab in the tab panel, which is toggled by the **View: Toggle Panel** command. +Query History is initially as a tab in the tab panel, which is toggled by the **View: Toggle Panel** or **Query History: Focus on Query History View** commands. ![Query History tab](https://raw.githubusercontent.com/microsoft/azuredatastudio/main/extensions/query-history/images/QueryHistoryTab.PNG) +This view can be moved similar to other views into other view containers by dragging and dropping into the desired view container. + Initially this view will be empty but once you execute a query editor that will be captured in the window - with a separate row displayed for every query executed. ![Query History tab with queries](https://raw.githubusercontent.com/microsoft/azuredatastudio/main/extensions/query-history/images/QueryHistoryTabWithQueries.PNG) @@ -18,6 +20,12 @@ Each row consists of 4 parts : - Connection Info : The Server and Database the query was executed against - Timestamp : The date and time the query was executed +### Disabling/Enabling Query History + +Query History capture can be enabled/disabled in one of two ways : + +1. Through the action button on the view container - this will be a ⏸️ when capture is enabled and clicking it will disable capture until re-enabled. When capture is disabled this button will be a ▶ and clicking it will enable capture until disabled. +2. By running the **Query History: Pause Query History Capture** or **Query History: Start Query History Capture** commands from the command palette. ### Query History row actions Right clicking on a history row will bring up a menu with a number of actions available. @@ -42,16 +50,9 @@ This will permanently delete **ALL** history rows. This action is also available from the command palette (**Query History: Clear All History**) and as an action button on the panel. -#### Pause/Start Query History Capture - -This will Pause or Start Query History Capture. While paused no data will be stored for queries run. - -This action is also available from the command palette (**Query History: Toggle Query History Capture**) and as an action button -on the panel. - ### Data Storage -Currently all information is stored in-memory and not persisted upon application exit. +Currently all information is stored in-memory and not persisted upon application exit. There is no limit to the number of entries stored, new entries will be continuously added as long as capture is enabled unless entries are deleted or the entire history is cleared. ## Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/extensions/query-history/images/history.svg b/extensions/query-history/images/history.svg new file mode 100644 index 0000000000..2116cbf95c --- /dev/null +++ b/extensions/query-history/images/history.svg @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/query-history/package.json b/extensions/query-history/package.json index f8b51b67d5..1578f1cc1c 100644 --- a/extensions/query-history/package.json +++ b/extensions/query-history/package.json @@ -1,61 +1,187 @@ { - "name": "query-history", - "displayName": "%queryHistory.displayName%", - "description": "%queryHistory.description%", - "version": "0.2.0", - "publisher": "Microsoft", - "preview": true, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt", - "icon": "images/extension.png", - "aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e", - "engines": { - "vscode": "^1.30.1", - "azdata": ">=1.12.0" - }, - "activationEvents": [ - "*" - ], - "main": "./out/main", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "capabilities": { - "virtualWorkspaces": false, - "untrustedWorkspaces": { - "supported": true - } - }, - "extensionDependencies": [], - "contributes": { - "commands": [ - { - "command": "queryHistory.clear", - "title": "%queryHistory.clear%", - "category": "%queryHistory.displayName%" - }, - { - "command": "queryHistory.toggleCapture", - "title": "%queryHistory.toggleCapture%", - "category": "%queryHistory.displayName%" - } - ], - "menus": { - "commandPalette": [ - { - "command": "queryHistory.clear" - }, - { - "command": "queryHistory.toggleCapture" - } - ] - } - }, - "dependencies": {}, - "devDependencies": { }, - "__metadata": { - "id": "55", - "publisherDisplayName": "Microsoft", - "publisherId": "Microsoft" - } + "name": "query-history", + "displayName": "%queryHistory.displayName%", + "description": "%queryHistory.description%", + "version": "0.3.0", + "publisher": "Microsoft", + "preview": true, + "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt", + "icon": "images/extension.png", + "aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e", + "engines": { + "vscode": "*", + "azdata": ">=1.38.0" + }, + "activationEvents": [ + "*" + ], + "main": "./out/main", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "capabilities": { + "virtualWorkspaces": false, + "untrustedWorkspaces": { + "supported": true + } + }, + "extensionDependencies": [ + "Microsoft.mssql" + ], + "contributes": { + "configuration": [ + { + "type": "object", + "title": "%queryHistory.displayName%", + "properties": { + "queryHistory.captureEnabled": { + "type": "boolean", + "default": true, + "description": "%queryHistory.captureEnabledDesc%" + } + } + } + ], + "commands": [ + { + "command": "queryHistory.open", + "title": "%queryHistory.open%", + "category": "%queryHistory.displayName%" + }, + { + "command": "queryHistory.run", + "title": "%queryHistory.run%", + "category": "%queryHistory.displayName%" + }, + { + "command": "queryHistory.delete", + "title": "%queryHistory.delete%", + "category": "%queryHistory.displayName%" + }, + { + "command": "queryHistory.clear", + "title": "%queryHistory.clear%", + "category": "%queryHistory.displayName%", + "icon": "$(clear-all)" + }, + { + "command": "queryHistory.disableCapture", + "title": "%queryHistory.disableCapture%", + "category": "%queryHistory.displayName%", + "icon": "$(debug-pause)" + }, + { + "command": "queryHistory.enableCapture", + "title": "%queryHistory.enableCapture%", + "category": "%queryHistory.displayName%", + "icon": "$(play)" + } + ], + "menus": { + "commandPalette": [ + { + "command": "queryHistory.open", + "when": "false" + }, + { + "command": "queryHistory.run", + "when": "false" + }, + { + "command": "queryHistory.delete", + "when": "false" + }, + { + "command": "queryHistory.disableCapture", + "when": "config.queryHistory.captureEnabled" + }, + { + "command": "queryHistory.enableCapture", + "when": "!config.queryHistory.captureEnabled" + } + ], + "view/title": [ + { + "command": "queryHistory.clear", + "when": "view == queryHistory", + "group": "navigation" + }, + { + "command": "queryHistory.disableCapture", + "when": "view == queryHistory && config.queryHistory.captureEnabled", + "group": "navigation" + }, + { + "command": "queryHistory.enableCapture", + "when": "view == queryHistory && !config.queryHistory.captureEnabled", + "group": "navigation" + } + ], + "view/item/context": [ + { + "command": "queryHistory.open", + "when": "view == queryHistory", + "group": "QueryHistory@1" + }, + { + "command": "queryHistory.run", + "when": "view == queryHistory", + "group": "QueryHistory@2" + }, + { + "command": "queryHistory.delete", + "when": "view == queryHistory", + "group": "QueryHistory@3" + }, + { + "command": "queryHistory.clear", + "when": "view == queryHistory", + "group": "QueryHistory@4" + }, + { + "command": "queryHistory.disableCapture", + "when": "view == queryHistory && config.queryHistory.capatureEnabled", + "group": "QueryHistory@5" + }, + { + "command": "queryHistory.enableCapture", + "when": "view == queryHistory && !config.queryHistory.captureEnabled", + "group": "QueryHistory@5" + } + ] + }, + "views": { + "queryHistory": [ + { + "id": "queryHistory", + "name": "%queryHistory.displayName%" + } + ] + }, + "viewsWelcome": [ + { + "view": "queryHistory", + "contents": "%queryHistory.noEntries%" + } + ], + "viewsContainers": { + "panel": [ + { + "id": "queryHistory", + "title": "%queryHistory.displayName%", + "icon": "./images/history.png" + } + ] + } + }, + "dependencies": {}, + "devDependencies": { + "@types/node": "^12.11.7" + }, + "__metadata": { + "id": "55", + "publisherDisplayName": "Microsoft", + "publisherId": "Microsoft" + } } diff --git a/extensions/query-history/package.nls.json b/extensions/query-history/package.nls.json index 05b12e3800..c5addc4cd8 100644 --- a/extensions/query-history/package.nls.json +++ b/extensions/query-history/package.nls.json @@ -1,6 +1,12 @@ { "queryHistory.displayName": "Query History", "queryHistory.description": "View and run previously executed queries", + "queryHistory.captureEnabledDesc": "Whether Query History capture is enabled. If false queries executed will not be captured.", + "queryHistory.open": "Open Query", + "queryHistory.run": "Run Query", + "queryHistory.delete": "Delete", "queryHistory.clear": "Clear All History", - "queryHistory.toggleCapture": "Toggle Query History Capture" + "queryHistory.disableCapture": "Pause Query History Capture", + "queryHistory.enableCapture": "Start Query History Capture", + "queryHistory.noEntries": "No queries to display" } diff --git a/extensions/query-history/src/main.ts b/extensions/query-history/src/main.ts index e7cba0ccaa..2ca3c57506 100644 --- a/extensions/query-history/src/main.ts +++ b/extensions/query-history/src/main.ts @@ -3,15 +3,39 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as azdata from 'azdata'; import * as vscode from 'vscode'; +import { QueryHistoryNode } from './queryHistoryNode'; +import { QueryHistoryProvider } from './queryHistoryProvider'; export async function activate(context: vscode.ExtensionContext): Promise { - // Currently all the functionality for this is contained within the core ADS - // code as the extensibility API doesn't currently support all the required - // functionality (such as contributing tab panels) - void vscode.commands.executeCommand('queryHistory.enableQueryHistory'); -} - -export async function deactivate(): Promise { - + const provider = new QueryHistoryProvider(); + context.subscriptions.push(provider); + context.subscriptions.push(vscode.window.registerTreeDataProvider('queryHistory', provider)); + context.subscriptions.push(vscode.commands.registerCommand('queryHistory.open', async (node: QueryHistoryNode) => { + return azdata.queryeditor.openQueryDocument( + { + content: node.queryText + }, node.connectionProfile?.providerId); + })); + context.subscriptions.push(vscode.commands.registerCommand('queryHistory.run', async (node: QueryHistoryNode) => { + const doc = await azdata.queryeditor.openQueryDocument( + { + content: node.queryText + }, node.connectionProfile?.providerId); + await azdata.queryeditor.connect(doc.uri, node.connectionProfile?.connectionId || ''); + azdata.queryeditor.runQuery(doc.uri); + })); + context.subscriptions.push(vscode.commands.registerCommand('queryHistory.delete', (node: QueryHistoryNode) => { + provider.deleteNode(node); + })); + context.subscriptions.push(vscode.commands.registerCommand('queryHistory.clear', () => { + provider.clearAll(); + })); + context.subscriptions.push(vscode.commands.registerCommand('queryHistory.disableCapture', async () => { + return provider.setCaptureEnabled(false); + })); + context.subscriptions.push(vscode.commands.registerCommand('queryHistory.enableCapture', async () => { + return provider.setCaptureEnabled(true); + })); } diff --git a/extensions/query-history/src/queryHistoryNode.ts b/extensions/query-history/src/queryHistoryNode.ts new file mode 100644 index 0000000000..08d4fdbd7b --- /dev/null +++ b/extensions/query-history/src/queryHistoryNode.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as azdata from 'azdata'; +import * as vscode from 'vscode'; +import { removeNewLines } from './utils'; + +export class QueryHistoryNode extends vscode.TreeItem { + constructor( + public readonly queryText: string, + public readonly connectionProfile: azdata.connection.ConnectionProfile | undefined, + timestamp: Date, + isSuccess: boolean + ) { + super(removeNewLines(queryText), vscode.TreeItemCollapsibleState.None); + this.iconPath = isSuccess ? new vscode.ThemeIcon('check', new vscode.ThemeColor('testing.iconPassed')) : new vscode.ThemeIcon('error', new vscode.ThemeColor('testing.iconFailed')); + this.tooltip = queryText; + this.description = connectionProfile ? `${connectionProfile.serverName}|${connectionProfile.databaseName} ${timestamp.toLocaleString()}` : timestamp.toLocaleString(); + } +} diff --git a/extensions/query-history/src/queryHistoryProvider.ts b/extensions/query-history/src/queryHistoryProvider.ts new file mode 100644 index 0000000000..f951d96f25 --- /dev/null +++ b/extensions/query-history/src/queryHistoryProvider.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as azdata from 'azdata'; +import { QueryHistoryNode } from './queryHistoryNode'; + +const QUERY_HISTORY_CONFIG_SECTION = 'queryHistory'; +const CAPTURE_ENABLED_CONFIG_SECTION = 'captureEnabled'; +const DEFAULT_CAPTURE_ENABLED = true; + +export class QueryHistoryProvider implements vscode.TreeDataProvider, vscode.Disposable { + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + private _queryHistoryNodes: QueryHistoryNode[] = []; + private _captureEnabled: boolean = true; + + private _disposables: vscode.Disposable[] = []; + + constructor() { + this._disposables.push(azdata.queryeditor.registerQueryEventListener({ + onQueryEvent: async (type: azdata.queryeditor.QueryEventType, document: azdata.queryeditor.QueryDocument, args: azdata.ResultSetSummary | string | undefined, queryInfo?: azdata.queryeditor.IQueryInfo) => { + if (this._captureEnabled && queryInfo && type === 'queryStop') { + const queryText = queryInfo.queryText ?? ''; + const connProfile = await azdata.connection.getConnection(document.uri); + const isError = queryInfo.messages.find(m => m.isError) ? false : true; + // Add to the front of the list so the new item appears at the top + this._queryHistoryNodes.unshift(new QueryHistoryNode(queryText, connProfile, new Date(), isError)); + this._onDidChangeTreeData.fire(undefined); + } + } + })); + this.updateCaptureEnabled(); + this._disposables.push(vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(QUERY_HISTORY_CONFIG_SECTION)) { + this.updateCaptureEnabled(); + } + })); + } + + public clearAll(): void { + this._queryHistoryNodes = []; + this._onDidChangeTreeData.fire(undefined); + } + + public deleteNode(node: QueryHistoryNode): void { + this._queryHistoryNodes = this._queryHistoryNodes.filter(n => n !== node); + this._onDidChangeTreeData.fire(undefined); + } + public getTreeItem(node: QueryHistoryNode): vscode.TreeItem { + return node; + } + + public getChildren(element?: QueryHistoryNode): QueryHistoryNode[] { + // We only have top level items + return this._queryHistoryNodes; + } + + public dispose(): void { + this._disposables.forEach(d => d.dispose()); + } + + private updateCaptureEnabled(): void { + this._captureEnabled = vscode.workspace.getConfiguration(QUERY_HISTORY_CONFIG_SECTION).get(CAPTURE_ENABLED_CONFIG_SECTION) ?? DEFAULT_CAPTURE_ENABLED; + } + + /** + * Set whether query history capture is currently enabled + * @param enabled Whether capture is currently enabled + * @returns A promise that resolves when the value is updated and persisted to configuration + */ + public async setCaptureEnabled(enabled: boolean): Promise { + this._captureEnabled = enabled; + return vscode.workspace.getConfiguration(QUERY_HISTORY_CONFIG_SECTION).update(CAPTURE_ENABLED_CONFIG_SECTION, this._captureEnabled, vscode.ConfigurationTarget.Global); + } +} diff --git a/extensions/query-history/src/typings/ref.d.ts b/extensions/query-history/src/typings/ref.d.ts index e0aee1c0fd..87aa0da20a 100644 --- a/extensions/query-history/src/typings/ref.d.ts +++ b/extensions/query-history/src/typings/ref.d.ts @@ -4,3 +4,5 @@ *--------------------------------------------------------------------------------------------*/ /// +/// +/// diff --git a/src/sql/workbench/contrib/queryHistory/common/constants.ts b/extensions/query-history/src/utils.ts similarity index 56% rename from src/sql/workbench/contrib/queryHistory/common/constants.ts rename to extensions/query-history/src/utils.ts index 92ed2c8d08..ee8f9fe324 100644 --- a/src/sql/workbench/contrib/queryHistory/common/constants.ts +++ b/extensions/query-history/src/utils.ts @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ /** - * Query History panel id + * Removes all newlines from the given string, replacing them with spaces + * @param str The original string + * @returns The string with all newlines replaced by spaces */ -export const QUERY_HISTORY_CONTAINER_ID = 'workbench.panel.queryHistory'; -export const QUERY_HISTORY_VIEW_ID = 'workbench.panel.queryHistory.view'; +export function removeNewLines(str: string): string { + return str.replace(/\r\n/g, ' ').replace(/\n/g, ' '); +} diff --git a/extensions/query-history/yarn.lock b/extensions/query-history/yarn.lock index fb57ccd13a..34d0c15605 100644 --- a/extensions/query-history/yarn.lock +++ b/extensions/query-history/yarn.lock @@ -2,3 +2,7 @@ # yarn lockfile v1 +"@types/node@^12.11.7": + version "12.20.52" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.52.tgz#2fd2dc6bfa185601b15457398d4ba1ef27f81251" + integrity sha512-cfkwWw72849SNYp3Zx0IcIs25vABmFh73xicxhCkTcvtZQeIez15PpwQN8fY3RD7gv1Wrxlc9MEtfMORZDEsGw== diff --git a/src/sql/workbench/contrib/queryHistory/browser/media/queryHistoryPanel.css b/src/sql/workbench/contrib/queryHistory/browser/media/queryHistoryPanel.css deleted file mode 100644 index bd7ed01aed..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/media/queryHistoryPanel.css +++ /dev/null @@ -1,56 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -#workbench\.panel\.queryHistory .no-queries-message { - padding: 10px 22px 0 22px; -} - -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item { - line-height:22px; - display:flex; -} - -/* query history label and description */ -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item > .label { - text-overflow: ellipsis; - overflow: hidden; - font-weight: bold; -} - -/* style for server name | database name */ -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item > .connection-info { - text-overflow: ellipsis; - overflow: hidden; - padding-left: 12px -} - -/* style for timing */ -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item > .time { - padding-left: 12px; - opacity: .6; - text-overflow: ellipsis; - font-style: italic -} - -/* query history icon status */ -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item > .query-history-icon { - background-size: 16px; - background-position: left center; - background-repeat: no-repeat; - padding-right: 6px; - width: 22px; - height: 22px; - display: inline-block; -} - - -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item > .query-history-icon.success { - background-image: url("status_success.svg"); -} - -#workbench\.panel\.queryHistory .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .query-history-item > .query-history-icon.error { - background-image: url("status_error.svg"); -} - diff --git a/src/sql/workbench/contrib/queryHistory/browser/media/status_error.svg b/src/sql/workbench/contrib/queryHistory/browser/media/status_error.svg deleted file mode 100644 index 309574a48e..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/media/status_error.svg +++ /dev/null @@ -1 +0,0 @@ -globalerror_red \ No newline at end of file diff --git a/src/sql/workbench/contrib/queryHistory/browser/media/status_success.svg b/src/sql/workbench/contrib/queryHistory/browser/media/status_success.svg deleted file mode 100644 index 776e1fd909..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/media/status_success.svg +++ /dev/null @@ -1 +0,0 @@ -success_16x16 \ No newline at end of file diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryActionProvider.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryActionProvider.ts deleted file mode 100644 index d00b823141..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryActionProvider.ts +++ /dev/null @@ -1,62 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { DeleteAction, OpenQueryAction, RunQueryAction, ClearHistoryAction, ToggleQueryHistoryCaptureAction } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryActions'; -import { ITree } from 'vs/base/parts/tree/browser/tree'; -import { IAction } from 'vs/base/common/actions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode'; - -/** - * Provides query history actions - */ -export class QueryHistoryActionProvider { - - private _actions: { - openQueryAction: IAction, - runQueryAction: IAction, - deleteAction: IAction, - clearAction: IAction, - toggleCaptureAction: IAction - }; - - constructor( - @IInstantiationService instantiationService: IInstantiationService - ) { - this._actions = { - openQueryAction: instantiationService.createInstance(OpenQueryAction, OpenQueryAction.ID, OpenQueryAction.LABEL), - runQueryAction: instantiationService.createInstance(RunQueryAction, RunQueryAction.ID, RunQueryAction.LABEL), - deleteAction: instantiationService.createInstance(DeleteAction, DeleteAction.ID, DeleteAction.LABEL), - clearAction: instantiationService.createInstance(ClearHistoryAction, ClearHistoryAction.ID, ClearHistoryAction.LABEL), - toggleCaptureAction: instantiationService.createInstance(ToggleQueryHistoryCaptureAction, ToggleQueryHistoryCaptureAction.ID, ToggleQueryHistoryCaptureAction.LABEL) - }; - } - - public hasActions(element: any): boolean { - return element instanceof QueryHistoryNode; - } - - /** - * Return actions for a selected node - or the default actions if no node is selected - */ - public getActions(element: any): IAction[] { - const actions: IAction[] = []; - // Actions we only want to display if we're on a valid QueryHistoryNode - if (element instanceof QueryHistoryNode && element.info) { - if (element.info && element.info.queryText && element.info.queryText !== '') { - actions.push(this._actions.openQueryAction); - actions.push(this._actions.runQueryAction); - } - actions.push(this._actions.deleteAction); - } - // Common actions we want to always display - actions.push(this._actions.clearAction, this._actions.toggleCaptureAction); - return actions; - } - - public hasSecondaryActions(tree: ITree, element: any): boolean { - return false; - } -} diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryActions.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryActions.ts deleted file mode 100644 index 63dffbe898..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryActions.ts +++ /dev/null @@ -1,141 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -import { QUERY_HISTORY_VIEW_ID } from 'sql/workbench/contrib/queryHistory/common/constants'; -import { RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { localize } from 'vs/nls'; -import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService'; -import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { openNewQuery } from 'sql/workbench/contrib/query/browser/queryActions'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; -import { ToggleViewAction } from 'sql/workbench/browser/actions/layoutActions'; - -export class ToggleQueryHistoryAction extends ToggleViewAction { - - public static readonly ID = 'workbench.action.tasks.toggleQueryHistory'; - public static readonly LABEL = localize('toggleQueryHistory', "Toggle Query History"); - - constructor( - id: string, label: string, - @IViewsService viewsService: IViewsService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService - ) { - super(id, label, QUERY_HISTORY_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService); - } -} - -export class DeleteAction extends Action { - public static ID = 'queryHistory.delete'; - public static LABEL = localize('queryHistory.delete', "Delete"); - - constructor( - id: string, - label: string, - @IQueryHistoryService private _queryHistoryService: IQueryHistoryService - ) { - super(id, label); - } - - public override async run(element: QueryHistoryNode): Promise { - if (element instanceof QueryHistoryNode && element.info) { - this._queryHistoryService.deleteQueryHistoryInfo(element.info); - } - } -} - -export class ClearHistoryAction extends Action { - public static ID = 'queryHistory.clear'; - public static LABEL = localize('queryHistory.clearLabel', "Clear All History"); - - constructor( - id: string, - label: string, - @ICommandService private _commandService: ICommandService - ) { - super(id, label, 'clear-query-history-action codicon-clear-all'); - } - - public override async run(): Promise { - return this._commandService.executeCommand('queryHistory.clear'); - } -} - - -export class OpenQueryAction extends Action { - public static ID = 'queryHistory.openQuery'; - public static LABEL = localize('queryHistory.openQuery', "Open Query"); - - constructor( - id: string, - label: string, - @IInstantiationService private _instantiationService: IInstantiationService - ) { - super(id, label); - } - - public override async run(element: QueryHistoryNode): Promise { - if (element instanceof QueryHistoryNode && element.info) { - return this._instantiationService.invokeFunction(openNewQuery, element.info.connectionProfile, element.info.queryText, RunQueryOnConnectionMode.none).then(); - } - } -} - -export class RunQueryAction extends Action { - public static ID = 'queryHistory.runQuery'; - public static LABEL = localize('queryHistory.runQuery', "Run Query"); - - constructor( - id: string, - label: string, - @IInstantiationService private _instantiationService: IInstantiationService - ) { - super(id, label); - } - - public override async run(element: QueryHistoryNode): Promise { - if (element instanceof QueryHistoryNode && element.info) { - return this._instantiationService.invokeFunction(openNewQuery, element.info.connectionProfile, element.info.queryText, RunQueryOnConnectionMode.executeQuery).then(); - } - } -} - -export class ToggleQueryHistoryCaptureAction extends Action { - public static ID = 'queryHistory.toggleCapture'; - public static LABEL = localize('queryHistory.toggleCaptureLabel', "Toggle Query History capture"); - - constructor( - id: string, - label: string, - @ICommandService private _commandService: ICommandService, - @IQueryHistoryService queryHistoryService: IQueryHistoryService - ) { - super(id, label); - this.setClassAndLabel(queryHistoryService.captureEnabled); - this._register(queryHistoryService.onQueryHistoryCaptureChanged((captureEnabled: boolean) => { this.setClassAndLabel(captureEnabled); })); - } - - public override async run(): Promise { - return this._commandService.executeCommand('queryHistory.toggleCapture'); - } - - private setClassAndLabel(enabled: boolean) { - if (enabled) { - this.class = 'toggle-query-history-capture-action codicon-debug-pause'; - this.label = localize('queryHistory.disableCapture', "Pause Query History Capture"); - } else { - this.class = 'toggle-query-history-capture-action codicon-play'; - this.label = localize('queryHistory.enableCapture', "Start Query History Capture"); - } - } -} - diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryController.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryController.ts deleted file mode 100644 index ce70acbba1..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryController.ts +++ /dev/null @@ -1,80 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ITree, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; -import * as treeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { QueryHistoryActionProvider } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryActionProvider'; - -/** - * Extends the tree controller to handle clicks on the tree elements for a Query History node - */ -export class QueryHistoryController extends treeDefaults.DefaultController { - - constructor( - private actionProvider: QueryHistoryActionProvider, - @IContextMenuService private contextMenuService: IContextMenuService, - @IKeybindingService private keybindingService: IKeybindingService - ) { - super({ clickBehavior: treeDefaults.ClickBehavior.ON_MOUSE_DOWN }); - } - - public override onClick(tree: ITree, element: any, event: IMouseEvent): boolean { - return super.onClick(tree, element, event); - } - - protected override onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { - return super.onLeftClick(tree, element, event, origin); - } - - // Do not allow left / right to expand and collapse groups #7848 - protected override onLeft(tree: ITree, event: IKeyboardEvent): boolean { - return true; - } - - protected override onRight(tree: ITree, event: IKeyboardEvent): boolean { - return true; - } - - protected override onEnter(tree: ITree, event: IKeyboardEvent): boolean { - return super.onEnter(tree, event); - } - - /** - * Return actions in the context menu - */ - public override onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean { - if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return false; - } - // Check if clicked on some element - if (element === tree.getInput()) { - return false; - } - - event.preventDefault(); - event.stopPropagation(); - - tree.setFocus(element); - - let anchor = { x: event.posx + 1, y: event.posy }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => this.actionProvider.getActions(element), - getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id), - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - tree.domFocus(); - } - }, - getActionsContext: () => (element) - }); - - return true; - } -} diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryDataSource.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryDataSource.ts deleted file mode 100644 index 7f989a51c2..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryDataSource.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; -import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode'; - -/** - * Implements the DataSource (that returns a parent/children of an element) for the query history - */ -export class QueryHistoryDataSource implements IDataSource { - - /** - * Returns the unique identifier of the given element. - * No more than one element may use a given identifier. - */ - public getId(tree: ITree, element: any): string { - if (element instanceof QueryHistoryNode && element.info) { - return element.info.id; - } - return ''; - } - - /** - * Returns a boolean value indicating whether the element has children. - */ - public hasChildren(tree: ITree, element: any): boolean { - if (element instanceof QueryHistoryNode) { - return element.hasChildren; - } - return false; - } - - /** - * Returns the element's children as an array - */ - public async getChildren(tree: ITree, element: any): Promise { - if (element instanceof QueryHistoryNode) { - return element.children; - } - return undefined; - } - - /** - * Returns the element's parent - */ - public async getParent(tree: ITree, element: any): Promise { - return undefined; - } -} diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryNode.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryNode.ts deleted file mode 100644 index b38c164e71..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryNode.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { QueryHistoryInfo } from 'sql/workbench/services/queryHistory/common/queryHistoryInfo'; - -/** - * Wrapper around a QueryHistoryInfo for displaying in the panel TreeView - */ -export class QueryHistoryNode { - - public hasChildren: boolean = false; - - public children: QueryHistoryNode[] = []; - - constructor( - public info: QueryHistoryInfo | undefined) { } -} diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryRenderer.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryRenderer.ts deleted file mode 100644 index 7e3609a229..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryRenderer.ts +++ /dev/null @@ -1,96 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/queryHistoryPanel'; -import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; -import { QueryStatus } from 'sql/workbench/services/queryHistory/common/queryHistoryInfo'; -import * as dom from 'vs/base/browser/dom'; -import { localize } from 'vs/nls'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode'; - -const $ = dom.$; - -export interface IQueryHistoryItemTemplateData { - root: HTMLElement; - icon: HTMLElement; - label: HTMLSpanElement; - connectionInfo: HTMLSpanElement; - time: HTMLSpanElement; - disposables: Array; -} - -/** - * Renders the tree items. - * Uses the dom template to render task history. - */ -export class QueryHistoryRenderer implements IRenderer { - - public static readonly QUERYHISTORYOBJECT_HEIGHT = 22; - private static readonly FAIL_CLASS = 'error'; - private static readonly SUCCESS_CLASS = 'success'; - /** - * Returns the element's height in the tree, in pixels. - */ - public getHeight(tree: ITree, element: QueryHistoryNode): number { - return QueryHistoryRenderer.QUERYHISTORYOBJECT_HEIGHT; - } - - /** - * Returns a template ID for a given element. - */ - public getTemplateId(tree: ITree, element: QueryHistoryNode): string { - return element.info?.id || ''; - } - - /** - * Render template in a dom element based on template id - */ - public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { - const taskTemplate: IQueryHistoryItemTemplateData = Object.create(null); - taskTemplate.root = dom.append(container, $('.query-history-item')); - taskTemplate.icon = dom.append(taskTemplate.root, $('.codicon.query-history-icon')); - taskTemplate.label = dom.append(taskTemplate.root, $('.label')); - taskTemplate.connectionInfo = dom.append(taskTemplate.root, $('.connection-info')); - taskTemplate.time = dom.append(taskTemplate.root, $('.time')); - taskTemplate.disposables = []; - return taskTemplate; - } - - /** - * Render a element, given an object bag returned by the template - */ - public renderElement(tree: ITree, element: QueryHistoryNode, templateId: string, templateData: IQueryHistoryItemTemplateData): void { - let taskStatus = ''; - if (element && element.info) { - templateData.icon.className = 'query-history-icon'; - if (element.info.status === QueryStatus.Succeeded) { - templateData.icon.classList.add(QueryHistoryRenderer.SUCCESS_CLASS); - taskStatus = localize('succeeded', "succeeded"); - } - else if (element.info.status === QueryStatus.Failed) { - templateData.icon.classList.add(QueryHistoryRenderer.FAIL_CLASS); - taskStatus = localize('failed', "failed"); - } - - templateData.icon.title = taskStatus; - - templateData.label.textContent = element.info.queryText; - templateData.label.title = templateData.label.textContent; - - // Determine the target name and set hover text equal to that - const connectionInfo = `${element.info.connectionProfile.serverName}|${element.info.database}`; - templateData.connectionInfo.textContent = connectionInfo; - templateData.connectionInfo.title = templateData.connectionInfo.textContent; - templateData.time.textContent = element.info.startTime.toLocaleString(); - templateData.time.title = templateData.time.textContent; - - } - } - - public disposeTemplate(tree: ITree, templateId: string, templateData: IQueryHistoryItemTemplateData): void { - dispose(templateData.disposables); - } -} diff --git a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryView.ts b/src/sql/workbench/contrib/queryHistory/browser/queryHistoryView.ts deleted file mode 100644 index c8a1bc9c09..0000000000 --- a/src/sql/workbench/contrib/queryHistory/browser/queryHistoryView.ts +++ /dev/null @@ -1,153 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as errors from 'vs/base/common/errors'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { attachListStyler } from 'vs/platform/theme/common/styler'; -import { ITree } from 'vs/base/parts/tree/browser/tree'; -import { DefaultFilter, DefaultDragAndDrop, DefaultAccessibilityProvider } from 'vs/base/parts/tree/browser/treeDefaults'; -import { localize } from 'vs/nls'; -import { hide, $, append, show } from 'vs/base/browser/dom'; -import { QueryHistoryRenderer } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryRenderer'; -import { QueryHistoryDataSource } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryDataSource'; -import { QueryHistoryController } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryController'; -import { QueryHistoryActionProvider } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryActionProvider'; -import { IExpandableTree } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils'; -import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService'; -import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode'; -import { QueryHistoryInfo } from 'sql/workbench/services/queryHistory/common/queryHistoryInfo'; -import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; - -/** - * QueryHistoryView implements the dynamic tree view for displaying Query History - */ -export class QueryHistoryView extends ViewPane { - private _messages!: HTMLElement; - private _tree!: ITree; - private _actionProvider: QueryHistoryActionProvider; - - constructor( - options: IViewPaneOptions, - @IInstantiationService instantiationService: IInstantiationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService telemetryService: ITelemetryService, - @IContextKeyService contextKeyService: IContextKeyService, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, - @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, - @IQueryHistoryService private readonly queryHistoryService: IQueryHistoryService - ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this._actionProvider = this.instantiationService.createInstance(QueryHistoryActionProvider); - } - - /** - * Render the view body - */ - public override renderBody(container: HTMLElement): void { - // Add div to display no task executed message - this._messages = append(container, $('div.no-queries-message')); - - const noQueriesMessage = localize('noQueriesMessage', "No queries to display."); - append(this._messages, $('span')).innerText = noQueriesMessage; - - this._tree = this._register(this.createQueryHistoryTree(container, this.instantiationService)); - - // Theme styler - this._register(attachListStyler(this._tree, this.themeService)); - - this.queryHistoryService.onInfosUpdated((nodes: QueryHistoryInfo[]) => { - this.refreshTree(); - }); - - // Refresh the tree so we correctly update if there were already existing history items - this.refreshTree(); - } - - /** - * Create a task history tree - */ - public createQueryHistoryTree(treeContainer: HTMLElement, instantiationService: IInstantiationService): Tree { - const dataSource = instantiationService.createInstance(QueryHistoryDataSource); - const renderer = instantiationService.createInstance(QueryHistoryRenderer); - const controller = instantiationService.createInstance(QueryHistoryController, this._actionProvider); - const dnd = new DefaultDragAndDrop(); - const filter = new DefaultFilter(); - const accessibilityProvider = new DefaultAccessibilityProvider(); - - return new Tree(treeContainer, { - dataSource, renderer, controller, dnd, filter, sorter: undefined, accessibilityProvider - }, { - indentPixels: 10, - twistiePixels: 20, - ariaLabel: localize({ key: 'queryHistory.regTreeAriaLabel', comment: ['QueryHistory'] }, "Query History") - }); - } - - public refreshTree(): void { - let selectedElement: any; - let targetsToExpand: any[]; - - const selection = this._tree.getSelection(); - if (selection && selection.length === 1) { - selectedElement = selection[0]; - } - // convert to old VS Code tree interface with expandable methods - const expandableTree: IExpandableTree = this._tree; - targetsToExpand = expandableTree.getExpandedElements(); - - const nodes: QueryHistoryNode[] = this.queryHistoryService.getQueryHistoryInfos().map(i => new QueryHistoryNode(i)); - - if (nodes.length > 0) { - hide(this._messages); - } else { - show(this._messages); - } - - // Set the tree input - root node is just an empty container node - const rootNode = new QueryHistoryNode(undefined); - rootNode.children = nodes; - rootNode.hasChildren = true; - - this._tree.setInput(rootNode).then(() => { - // Make sure to expand all folders that were expanded in the previous session - if (targetsToExpand) { - this._tree.expandAll(targetsToExpand); - } - if (selectedElement) { - this._tree.select(selectedElement); - } - }, errors.onUnexpectedError); - } - - /** - * set the layout of the view - */ - public override layout(height: number): void { - this._tree.layout(height); - } - - /** - * set the visibility of the view - */ - public override setVisible(visible: boolean): void { - if (visible) { - this._tree.onVisible(); - } else { - this._tree.onHidden(); - } - } -} diff --git a/src/sql/workbench/contrib/queryHistory/electron-browser/queryHistory.contribution.ts b/src/sql/workbench/contrib/queryHistory/electron-browser/queryHistory.contribution.ts deleted file mode 100644 index 1e9a40280f..0000000000 --- a/src/sql/workbench/contrib/queryHistory/electron-browser/queryHistory.contribution.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { QueryHistoryWorkbenchContribution } from 'sql/workbench/contrib/queryHistory/electron-browser/queryHistory'; - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(QueryHistoryWorkbenchContribution, LifecyclePhase.Restored); diff --git a/src/sql/workbench/contrib/queryHistory/electron-browser/queryHistory.ts b/src/sql/workbench/contrib/queryHistory/electron-browser/queryHistory.ts deleted file mode 100644 index b22b117fab..0000000000 --- a/src/sql/workbench/contrib/queryHistory/electron-browser/queryHistory.ts +++ /dev/null @@ -1,155 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { localize } from 'vs/nls'; -import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ToggleQueryHistoryAction } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryActions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IConfigurationRegistry, ConfigurationScope, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsRegistry, ViewContainerLocation } from 'vs/workbench/common/views'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { QUERY_HISTORY_CONTAINER_ID, QUERY_HISTORY_VIEW_ID } from 'sql/workbench/contrib/queryHistory/common/constants'; -import { QueryHistoryView } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryView'; -import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { Codicon } from 'vs/base/common/codicons'; - -export class QueryHistoryWorkbenchContribution implements IWorkbenchContribution { - - private queryHistoryEnabled: boolean = false; - - constructor( - @IQueryHistoryService _queryHistoryService: IQueryHistoryService - ) { - // This feature is in preview so for now hide it behind a flag. We expose this as a command - // so that the query-history extension can call it. We eventually want to move all this into - // the extension itself so this should be a temporary workaround - CommandsRegistry.registerCommand({ - id: 'queryHistory.enableQueryHistory', - handler: () => { - // This should never be called more than once, but just in case - // we don't want to try and register multiple times - if (!this.queryHistoryEnabled) { - this.queryHistoryEnabled = true; - - const configurationRegistry = Registry.as(Extensions.Configuration); - configurationRegistry.registerConfiguration({ - id: 'queryHistory', - title: localize('queryHistoryConfigurationTitle', "QueryHistory"), - type: 'object', - properties: { - 'queryHistory.captureEnabled': { - type: 'boolean', - default: true, - scope: ConfigurationScope.APPLICATION, - description: localize('queryHistoryCaptureEnabled', "Whether Query History capture is enabled. If false queries executed will not be captured.") - } - } - }); - - // We need this to be running in the background even if the Panel (which is currently the only thing using it) - // isn't shown yet. Otherwise the service won't be initialized until the Panel is which means we might miss out - // on some events - _queryHistoryService.start(); - - registerAction2(class extends Action2 { - constructor() { - super({ - id: `queryHistory.clear`, - title: { value: localize('queryHistory.clearLabel', "Clear All History"), original: 'Clear All History' }, - icon: Codicon.clearAll, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - when: ContextKeyEqualsExpr.create('view', QUERY_HISTORY_VIEW_ID), - order: 10 - } - }); - } - - run(accessor: ServicesAccessor) { - const queryHistoryService = accessor.get(IQueryHistoryService); - queryHistoryService.clearQueryHistory(); - } - }); - - const PAUSE_QUERY_HISTORY_CAPTURE = localize('queryHistory.disableCapture', "Pause Query History Capture"); - const START_QUERY_HISTORY_CAPTURE = localize('queryHistory.enableCapture', "Start Query History Capture"); - - registerAction2(class extends Action2 { - constructor() { - super({ - id: `queryHistory.toggleCapture`, - title: { value: START_QUERY_HISTORY_CAPTURE, original: 'Start Query History Capture' }, - tooltip: START_QUERY_HISTORY_CAPTURE, - icon: Codicon.play, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - when: ContextKeyEqualsExpr.create('view', QUERY_HISTORY_VIEW_ID), - order: 20 - }, - toggled: { - condition: ContextKeyEqualsExpr.create('config.queryHistory.captureEnabled', true), - title: { value: PAUSE_QUERY_HISTORY_CAPTURE, original: 'Pause Query History Capture' }, - tooltip: PAUSE_QUERY_HISTORY_CAPTURE, - icon: { id: 'debug-pause' } - } - }); - } - - run(accessor: ServicesAccessor) { - const queryHistoryService = accessor.get(IQueryHistoryService); - queryHistoryService.toggleCaptureEnabled(); - } - }); - - const registry = Registry.as(ActionExtensions.WorkbenchActions); - registry.registerWorkbenchAction( - SyncActionDescriptor.create( - ToggleQueryHistoryAction, - ToggleQueryHistoryAction.ID, - ToggleQueryHistoryAction.LABEL, - { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), - 'View: Toggle Query history', - localize('viewCategory', "View") - ); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: ToggleQueryHistoryAction.ID, - title: localize({ key: 'miViewQueryHistory', comment: ['&& denotes a mnemonic'] }, "&&Query History") - }, - order: 2 - }); - - const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ - id: QUERY_HISTORY_CONTAINER_ID, - title: localize('queryHistory', "Query History"), - hideIfEmpty: true, - order: 20, - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [QUERY_HISTORY_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), - storageId: `${QUERY_HISTORY_CONTAINER_ID}.storage`, - }, ViewContainerLocation.Panel); - - Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ - id: QUERY_HISTORY_VIEW_ID, - name: localize('queryHistory', "Query History"), - canToggleVisibility: false, - canMoveView: false, - ctorDescriptor: new SyncDescriptor(QueryHistoryView), - }], VIEW_CONTAINER); - } - } - }); - } -} diff --git a/src/sql/workbench/services/query/common/queryModelService.ts b/src/sql/workbench/services/query/common/queryModelService.ts index 9fe1911551..fa2b5ee073 100644 --- a/src/sql/workbench/services/query/common/queryModelService.ts +++ b/src/sql/workbench/services/query/common/queryModelService.ts @@ -287,7 +287,6 @@ export class QueryModelService implements IQueryModelService { }); queryRunner.onQueryEnd(totalMilliseconds => { this._onRunQueryComplete.fire(queryRunner.uri); - // fire extensibility API event let event: IQueryEvent = { type: 'queryStop', diff --git a/src/sql/workbench/services/queryHistory/common/queryHistoryInfo.ts b/src/sql/workbench/services/queryHistory/common/queryHistoryInfo.ts deleted file mode 100644 index bbb5d8a212..0000000000 --- a/src/sql/workbench/services/queryHistory/common/queryHistoryInfo.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { generateUuid } from 'vs/base/common/uuid'; -import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; - -export enum QueryStatus { - Succeeded = 0, - Failed = 1, - Nothing = 2 -} - -/** - * Contains information about a query that was ran - */ -export class QueryHistoryInfo { - - public database: string; - - public status?: QueryStatus; - - public readonly id = generateUuid(); - - constructor( - public queryText: string, - public connectionProfile: IConnectionProfile, - public startTime: Date, - status?: QueryStatus) { - this.database = connectionProfile?.databaseName ?? ''; - this.status = status; - } -} diff --git a/src/sql/workbench/services/queryHistory/common/queryHistoryService.ts b/src/sql/workbench/services/queryHistory/common/queryHistoryService.ts deleted file mode 100644 index 972c98a2a4..0000000000 --- a/src/sql/workbench/services/queryHistory/common/queryHistoryService.ts +++ /dev/null @@ -1,55 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { QueryHistoryInfo } from 'sql/workbench/services/queryHistory/common/queryHistoryInfo'; -import { Event } from 'vs/base/common/event'; - -export const SERVICE_ID = 'queryHistoryService'; - -export const IQueryHistoryService = createDecorator(SERVICE_ID); - -/** - * Service that collects the results of executed queries - */ -export interface IQueryHistoryService { - _serviceBrand: any; - - /** - * Event fired whenever the collection of stored QueryHistoryInfo's is updated - */ - onInfosUpdated: Event; - /** - * Event fired whenever the Query History capture state has changed - */ - onQueryHistoryCaptureChanged: Event; - - /** - * Whether Query History capture is currently enabled - */ - readonly captureEnabled: boolean; - - /** - * Gets the current list of Query History Info objects that have been collected - */ - getQueryHistoryInfos(): QueryHistoryInfo[]; - /** - * Deletes all QueryHistoryInfo's from the collection that have the same id as the specified one - * @param info The QueryHistoryInfo to delete - */ - deleteQueryHistoryInfo(info: QueryHistoryInfo): void; - /** - * Clears all Query History - removing all collected items - */ - clearQueryHistory(): void; - /** - * Toggles whether Query History capture is enabled - */ - toggleCaptureEnabled(): Promise; - /** - * Starts the Query History Service - */ - start(): void; -} diff --git a/src/sql/workbench/services/queryHistory/common/queryHistoryServiceImpl.ts b/src/sql/workbench/services/queryHistory/common/queryHistoryServiceImpl.ts deleted file mode 100644 index ca2c97b422..0000000000 --- a/src/sql/workbench/services/queryHistory/common/queryHistoryServiceImpl.ts +++ /dev/null @@ -1,135 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService'; - -import { IQueryModelService, IQueryEvent } from 'sql/workbench/services/query/common/queryModel'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { URI } from 'vs/base/common/uri'; -import { Range } from 'vs/editor/common/core/range'; -import { QueryHistoryInfo, QueryStatus } from 'sql/workbench/services/queryHistory/common/queryHistoryInfo'; -import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; - -/** - * Service that collects the results of executed queries - */ -export class QueryHistoryService extends Disposable implements IQueryHistoryService { - _serviceBrand: any; - - // MEMBER VARIABLES //////////////////////////////////////////////////// - private _infos: QueryHistoryInfo[] = []; - private _onInfosUpdated: Emitter = new Emitter(); - private _onQueryHistoryCaptureChanged: Emitter = new Emitter(); - private _captureEnabled: boolean; - private _started: boolean = false; - - // EVENTS ////////////////////////////////////////////////////////////// - public get onInfosUpdated(): Event { return this._onInfosUpdated.event; } - public get onQueryHistoryCaptureChanged(): Event { return this._onQueryHistoryCaptureChanged.event; } - // CONSTRUCTOR ///////////////////////////////////////////////////////// - constructor( - @IQueryModelService _queryModelService: IQueryModelService, - @IModelService _modelService: IModelService, - @IConnectionManagementService _connectionManagementService: IConnectionManagementService, - @IConfigurationService private _configurationService: IConfigurationService - ) { - super(); - - this.updateCaptureEnabled(); - - this._register(this._configurationService.onDidChangeConfiguration((e: IConfigurationChangeEvent) => { - if (e.affectedKeys.find(x => x === 'queryHistory.captureEnabled')) { - this.updateCaptureEnabled(); - } - })); - - this._register(_queryModelService.onQueryEvent((e: IQueryEvent) => { - if (this._captureEnabled && e.type === 'queryStop') { - const uri: URI = URI.parse(e.uri); - const model = _modelService.getModel(uri); - if (model) { - // VS Range is 1 based so offset values by 1. The endLine we get back from SqlToolsService is incremented - // by 1 from the original input range sent in as well so take that into account and don't modify - const text: string = e.queryInfo.range && e.queryInfo.range.length > 0 ? - model.getValueInRange(new Range( - e.queryInfo.range[0].startLineNumber, - e.queryInfo.range[0].startColumn, - e.queryInfo.range[0].endLineNumber, - e.queryInfo.range[0].endColumn)) : - // If no specific selection get the entire text - model.getValue(); - - const newInfo = new QueryHistoryInfo(text, _connectionManagementService.getConnectionProfile(e.uri), new Date(), QueryStatus.Succeeded); - - // icon as required (for now logic is if any message has error query has error) - let error: boolean = false; - e.queryInfo.messages.forEach(x => error = error || x.isError); - if (error) { - newInfo.status = QueryStatus.Failed; - } - - // Append new node to beginning of array so the newest ones are at the top - this._infos.unshift(newInfo); - this._onInfosUpdated.fire(this._infos); - } - } - })); - } - - /** - * Whether Query History capture is currently enabled - */ - public get captureEnabled(): boolean { - return this._captureEnabled; - } - - /** - * Gets all the current query history infos - */ - public getQueryHistoryInfos(): QueryHistoryInfo[] { - return this._infos; - } - - /** - * Deletes infos from the cache with the same ID as the given QueryHistoryInfo - * @param info The QueryHistoryInfo to delete - */ - public deleteQueryHistoryInfo(info: QueryHistoryInfo): void { - this._infos = this._infos.filter(i => i.id !== info.id); - this._onInfosUpdated.fire(this._infos); - } - - /** - * Clears all infos from the cache - */ - public clearQueryHistory(): void { - this._infos = []; - this._onInfosUpdated.fire(this._infos); - } - - public async toggleCaptureEnabled(): Promise { - const captureEnabled = !!this._configurationService.getValue('queryHistory.captureEnabled'); - await this._configurationService.updateValue('queryHistory.captureEnabled', !captureEnabled); - } - - private updateCaptureEnabled(): void { - const currentCaptureEnabled = this._captureEnabled; - this._captureEnabled = !!this._configurationService.getValue('queryHistory.captureEnabled') && this._started; - if (currentCaptureEnabled !== this._captureEnabled) { - this._onQueryHistoryCaptureChanged.fire(this._captureEnabled); - } - } - - /** - * Method to force initialization of the service so that it can start tracking query events - */ - public start(): void { - this._started = true; - this.updateCaptureEnabled(); - } -} diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 106648cf2b..48da8d2f53 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -82,8 +82,6 @@ import { ISqlOAuthService } from 'sql/platform/oAuth/common/sqlOAuthService'; import { SqlOAuthService } from 'sql/platform/oAuth/electron-browser/sqlOAuthServiceImpl'; import { IClipboardService as sqlIClipboardService } from 'sql/platform/clipboard/common/clipboardService'; import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/electron-browser/clipboardService'; -import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService'; -import { QueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryServiceImpl'; import { IAzureBlobService } from 'sql/platform/azureBlob/common/azureBlobService'; import { AzureBlobService } from 'sql/workbench/services/azureBlob/browser/azureBlobService'; import { IAzureAccountService } from 'sql/platform/azureAccount/common/azureAccountService'; @@ -91,7 +89,6 @@ import { AzureAccountService } from 'sql/workbench/services/azureAccount/browser registerSingleton(ISqlOAuthService, SqlOAuthService); registerSingleton(sqlIClipboardService, sqlClipboardService); -registerSingleton(IQueryHistoryService, QueryHistoryService); registerSingleton(IAzureBlobService, AzureBlobService); registerSingleton(IAzureAccountService, AzureAccountService); // {{SQL CARBON EDIT}} - End @@ -146,9 +143,6 @@ import 'vs/workbench/contrib/extensions/electron-browser/extensions.contribution // getting started import 'sql/workbench/update/electron-browser/gettingStarted.contribution'; -// query history -import 'sql/workbench/contrib/queryHistory/electron-browser/queryHistory.contribution'; - // CLI import 'sql/workbench/contrib/commandLine/electron-browser/commandLine.contribution';