mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Move query history into extension (#19794)
* initial * more * Remove connectionId * cleanup * cleanup * Remove core contributions, add to panel by default * Add enabled state * Update config * cleanup * Move * Remove newlines * update README
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
# Query History *(preview)*
|
# 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?
|
### 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.
|
||||||
|
|
||||||

|

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

|

|
||||||
@@ -18,6 +20,12 @@ Each row consists of 4 parts :
|
|||||||
- Connection Info : The Server and Database the query was executed against
|
- Connection Info : The Server and Database the query was executed against
|
||||||
- Timestamp : The date and time the query was executed
|
- 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
|
### Query History row actions
|
||||||
|
|
||||||
Right clicking on a history row will bring up a menu with a number of actions available.
|
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.
|
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
|
### 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
|
## 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.
|
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.
|
||||||
|
|||||||
4
extensions/query-history/images/history.svg
Normal file
4
extensions/query-history/images/history.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
|
<path d="M15.4,4.9a8.3,8.3,0,0,1,0,6.2,9.009,9.009,0,0,1-1.7,2.6,9.009,9.009,0,0,1-2.6,1.7A8.112,8.112,0,0,1,8,16a7.509,7.509,0,0,1-2.6-.4,7.609,7.609,0,0,1-2.3-1.3,7.31,7.31,0,0,1-1.7-1.8L.7,11.4c-.1-.4-.3-.8-.4-1.3l1-.2a7.207,7.207,0,0,0,.9,2,8.716,8.716,0,0,0,1.6,1.7,6.9,6.9,0,0,0,1.9,1A6.184,6.184,0,0,0,8,15l1.9-.2,1.6-.8a4.9,4.9,0,0,0,1.4-1.1A4.9,4.9,0,0,0,14,11.5a7.976,7.976,0,0,0,.8-1.6A12.233,12.233,0,0,0,15,8a12.233,12.233,0,0,0-.2-1.9A7.976,7.976,0,0,0,14,4.5a4.9,4.9,0,0,0-1.1-1.4A4.9,4.9,0,0,0,11.5,2a4.61,4.61,0,0,0-1.6-.7A6.283,6.283,0,0,0,8,1a6.879,6.879,0,0,0-2,.3,5.292,5.292,0,0,0-1.7.8A4.708,4.708,0,0,0,2.8,3.4,4.6,4.6,0,0,0,1.7,5H4V6H0V2H1V4.1l.3-.4.3-.5A9.122,9.122,0,0,1,3.3,1.5,7.6,7.6,0,0,1,5.5.4,7.308,7.308,0,0,1,8,0a8.112,8.112,0,0,1,3.1.6,9.009,9.009,0,0,1,2.6,1.7A9.009,9.009,0,0,1,15.4,4.9Z" />
|
||||||
|
<polygon points="8 3 8 7.3 10.9 10.1 10.1 10.9 7 7.7 7 3 8 3" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1002 B |
@@ -2,15 +2,15 @@
|
|||||||
"name": "query-history",
|
"name": "query-history",
|
||||||
"displayName": "%queryHistory.displayName%",
|
"displayName": "%queryHistory.displayName%",
|
||||||
"description": "%queryHistory.description%",
|
"description": "%queryHistory.description%",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||||
"icon": "images/extension.png",
|
"icon": "images/extension.png",
|
||||||
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.30.1",
|
"vscode": "*",
|
||||||
"azdata": ">=1.12.0"
|
"azdata": ">=1.38.0"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"*"
|
"*"
|
||||||
@@ -26,33 +26,159 @@
|
|||||||
"supported": true
|
"supported": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extensionDependencies": [],
|
"extensionDependencies": [
|
||||||
|
"Microsoft.mssql"
|
||||||
|
],
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
"configuration": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"title": "%queryHistory.displayName%",
|
||||||
|
"properties": {
|
||||||
|
"queryHistory.captureEnabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "%queryHistory.captureEnabledDesc%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "queryHistory.clear",
|
"command": "queryHistory.open",
|
||||||
"title": "%queryHistory.clear%",
|
"title": "%queryHistory.open%",
|
||||||
"category": "%queryHistory.displayName%"
|
"category": "%queryHistory.displayName%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "queryHistory.toggleCapture",
|
"command": "queryHistory.run",
|
||||||
"title": "%queryHistory.toggleCapture%",
|
"title": "%queryHistory.run%",
|
||||||
"category": "%queryHistory.displayName%"
|
"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": {
|
"menus": {
|
||||||
"commandPalette": [
|
"commandPalette": [
|
||||||
{
|
{
|
||||||
"command": "queryHistory.clear"
|
"command": "queryHistory.open",
|
||||||
|
"when": "false"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "queryHistory.toggleCapture"
|
"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": {},
|
"dependencies": {},
|
||||||
"devDependencies": { },
|
"devDependencies": {
|
||||||
|
"@types/node": "^12.11.7"
|
||||||
|
},
|
||||||
"__metadata": {
|
"__metadata": {
|
||||||
"id": "55",
|
"id": "55",
|
||||||
"publisherDisplayName": "Microsoft",
|
"publisherDisplayName": "Microsoft",
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
{
|
{
|
||||||
"queryHistory.displayName": "Query History",
|
"queryHistory.displayName": "Query History",
|
||||||
"queryHistory.description": "View and run previously executed queries",
|
"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.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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,39 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* 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 * as vscode from 'vscode';
|
||||||
|
import { QueryHistoryNode } from './queryHistoryNode';
|
||||||
|
import { QueryHistoryProvider } from './queryHistoryProvider';
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||||
// Currently all the functionality for this is contained within the core ADS
|
const provider = new QueryHistoryProvider();
|
||||||
// code as the extensibility API doesn't currently support all the required
|
context.subscriptions.push(provider);
|
||||||
// functionality (such as contributing tab panels)
|
context.subscriptions.push(vscode.window.registerTreeDataProvider('queryHistory', provider));
|
||||||
void vscode.commands.executeCommand('queryHistory.enableQueryHistory');
|
context.subscriptions.push(vscode.commands.registerCommand('queryHistory.open', async (node: QueryHistoryNode) => {
|
||||||
}
|
return azdata.queryeditor.openQueryDocument(
|
||||||
|
{
|
||||||
export async function deactivate(): Promise<void> {
|
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);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
22
extensions/query-history/src/queryHistoryNode.ts
Normal file
22
extensions/query-history/src/queryHistoryNode.ts
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
80
extensions/query-history/src/queryHistoryProvider.ts
Normal file
80
extensions/query-history/src/queryHistoryProvider.ts
Normal file
@@ -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<QueryHistoryNode>, vscode.Disposable {
|
||||||
|
|
||||||
|
private _onDidChangeTreeData: vscode.EventEmitter<QueryHistoryNode | undefined> = new vscode.EventEmitter<QueryHistoryNode | undefined>();
|
||||||
|
readonly onDidChangeTreeData: vscode.Event<QueryHistoryNode | undefined> = 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<void> {
|
||||||
|
this._captureEnabled = enabled;
|
||||||
|
return vscode.workspace.getConfiguration(QUERY_HISTORY_CONFIG_SECTION).update(CAPTURE_ENABLED_CONFIG_SECTION, this._captureEnabled, vscode.ConfigurationTarget.Global);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,5 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
|
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
||||||
|
/// <reference path='../../../../src/sql/azdata.proposed.d.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 function removeNewLines(str: string): string {
|
||||||
export const QUERY_HISTORY_VIEW_ID = 'workbench.panel.queryHistory.view';
|
return str.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
|
||||||
|
}
|
||||||
@@ -2,3 +2,7 @@
|
|||||||
# yarn lockfile v1
|
# 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==
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#d02e00;}</style></defs><title>globalerror_red</title><path class="cls-1" d="M8,0a7.92,7.92,0,0,1,4,1.09A8.15,8.15,0,0,1,14.91,4a8,8,0,0,1,.81,1.91,8,8,0,0,1-.81,6.16A8.15,8.15,0,0,1,12,14.92a8,8,0,0,1-8.07,0,8.15,8.15,0,0,1-2.87-2.87A8,8,0,0,1,1.09,4,8.15,8.15,0,0,1,4,1.11,7.92,7.92,0,0,1,8,0ZM8,15a6.88,6.88,0,0,0,1.86-.25,7,7,0,0,0,4.89-4.89,7.07,7.07,0,0,0,0-3.73A7,7,0,0,0,9.86,1.27a7.07,7.07,0,0,0-3.73,0A7,7,0,0,0,1.25,6.15a7.07,7.07,0,0,0,0,3.73,7,7,0,0,0,4.89,4.89A6.88,6.88,0,0,0,8,15Zm3.46-9.76L8.71,8l2.75,2.76-.7.7L8,8.73,5.24,11.48l-.7-.7L7.29,8,4.54,5.26l.7-.7L8,7.31l2.76-2.75Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 721 B |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#3bb44a;}</style></defs><title>success_16x16</title><path class="cls-1" d="M16,3.16,5.48,13.69,0,8.2l.89-.89,4.6,4.59,9.63-9.62Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 255 B |
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<any> {
|
|
||||||
if (element instanceof QueryHistoryNode) {
|
|
||||||
return element.children;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the element's parent
|
|
||||||
*/
|
|
||||||
public async getParent(tree: ITree, element: any): Promise<any> {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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) { }
|
|
||||||
}
|
|
||||||
@@ -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<IDisposable>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 = <any>selection[0];
|
|
||||||
}
|
|
||||||
// convert to old VS Code tree interface with expandable methods
|
|
||||||
const expandableTree: IExpandableTree = <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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(QueryHistoryWorkbenchContribution, LifecyclePhase.Restored);
|
|
||||||
@@ -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<IConfigurationRegistry>(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<IWorkbenchActionRegistry>(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<IViewContainersRegistry>(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<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews([{
|
|
||||||
id: QUERY_HISTORY_VIEW_ID,
|
|
||||||
name: localize('queryHistory', "Query History"),
|
|
||||||
canToggleVisibility: false,
|
|
||||||
canMoveView: false,
|
|
||||||
ctorDescriptor: new SyncDescriptor(QueryHistoryView),
|
|
||||||
}], VIEW_CONTAINER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -287,7 +287,6 @@ export class QueryModelService implements IQueryModelService {
|
|||||||
});
|
});
|
||||||
queryRunner.onQueryEnd(totalMilliseconds => {
|
queryRunner.onQueryEnd(totalMilliseconds => {
|
||||||
this._onRunQueryComplete.fire(queryRunner.uri);
|
this._onRunQueryComplete.fire(queryRunner.uri);
|
||||||
|
|
||||||
// fire extensibility API event
|
// fire extensibility API event
|
||||||
let event: IQueryEvent = {
|
let event: IQueryEvent = {
|
||||||
type: 'queryStop',
|
type: 'queryStop',
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<IQueryHistoryService>(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<QueryHistoryInfo[]>;
|
|
||||||
/**
|
|
||||||
* Event fired whenever the Query History capture state has changed
|
|
||||||
*/
|
|
||||||
onQueryHistoryCaptureChanged: Event<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<void>;
|
|
||||||
/**
|
|
||||||
* Starts the Query History Service
|
|
||||||
*/
|
|
||||||
start(): void;
|
|
||||||
}
|
|
||||||
@@ -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<QueryHistoryInfo[]> = new Emitter<QueryHistoryInfo[]>();
|
|
||||||
private _onQueryHistoryCaptureChanged: Emitter<boolean> = new Emitter<boolean>();
|
|
||||||
private _captureEnabled: boolean;
|
|
||||||
private _started: boolean = false;
|
|
||||||
|
|
||||||
// EVENTS //////////////////////////////////////////////////////////////
|
|
||||||
public get onInfosUpdated(): Event<QueryHistoryInfo[]> { return this._onInfosUpdated.event; }
|
|
||||||
public get onQueryHistoryCaptureChanged(): Event<boolean> { 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<void> {
|
|
||||||
const captureEnabled = !!this._configurationService.getValue<boolean>('queryHistory.captureEnabled');
|
|
||||||
await this._configurationService.updateValue('queryHistory.captureEnabled', !captureEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateCaptureEnabled(): void {
|
|
||||||
const currentCaptureEnabled = this._captureEnabled;
|
|
||||||
this._captureEnabled = !!this._configurationService.getValue<boolean>('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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -82,8 +82,6 @@ import { ISqlOAuthService } from 'sql/platform/oAuth/common/sqlOAuthService';
|
|||||||
import { SqlOAuthService } from 'sql/platform/oAuth/electron-browser/sqlOAuthServiceImpl';
|
import { SqlOAuthService } from 'sql/platform/oAuth/electron-browser/sqlOAuthServiceImpl';
|
||||||
import { IClipboardService as sqlIClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
import { IClipboardService as sqlIClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||||
import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/electron-browser/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 { IAzureBlobService } from 'sql/platform/azureBlob/common/azureBlobService';
|
||||||
import { AzureBlobService } from 'sql/workbench/services/azureBlob/browser/azureBlobService';
|
import { AzureBlobService } from 'sql/workbench/services/azureBlob/browser/azureBlobService';
|
||||||
import { IAzureAccountService } from 'sql/platform/azureAccount/common/azureAccountService';
|
import { IAzureAccountService } from 'sql/platform/azureAccount/common/azureAccountService';
|
||||||
@@ -91,7 +89,6 @@ import { AzureAccountService } from 'sql/workbench/services/azureAccount/browser
|
|||||||
|
|
||||||
registerSingleton(ISqlOAuthService, SqlOAuthService);
|
registerSingleton(ISqlOAuthService, SqlOAuthService);
|
||||||
registerSingleton(sqlIClipboardService, sqlClipboardService);
|
registerSingleton(sqlIClipboardService, sqlClipboardService);
|
||||||
registerSingleton(IQueryHistoryService, QueryHistoryService);
|
|
||||||
registerSingleton(IAzureBlobService, AzureBlobService);
|
registerSingleton(IAzureBlobService, AzureBlobService);
|
||||||
registerSingleton(IAzureAccountService, AzureAccountService);
|
registerSingleton(IAzureAccountService, AzureAccountService);
|
||||||
// {{SQL CARBON EDIT}} - End
|
// {{SQL CARBON EDIT}} - End
|
||||||
@@ -146,9 +143,6 @@ import 'vs/workbench/contrib/extensions/electron-browser/extensions.contribution
|
|||||||
// getting started
|
// getting started
|
||||||
import 'sql/workbench/update/electron-browser/gettingStarted.contribution';
|
import 'sql/workbench/update/electron-browser/gettingStarted.contribution';
|
||||||
|
|
||||||
// query history
|
|
||||||
import 'sql/workbench/contrib/queryHistory/electron-browser/queryHistory.contribution';
|
|
||||||
|
|
||||||
// CLI
|
// CLI
|
||||||
import 'sql/workbench/contrib/commandLine/electron-browser/commandLine.contribution';
|
import 'sql/workbench/contrib/commandLine/electron-browser/commandLine.contribution';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user