diff --git a/extensions/query-history/package.json b/extensions/query-history/package.json index 90fe10f245..7472e2c99d 100644 --- a/extensions/query-history/package.json +++ b/extensions/query-history/package.json @@ -39,6 +39,19 @@ "type": "boolean", "default": true, "description": "%queryHistory.captureEnabledDesc%" + }, + "queryHistory.doubleClickAction": { + "type": "string", + "description": "%queryHistory.doubleClickAction%", + "default": "open", + "enum": [ + "open", + "run" + ], + "enumDescriptions": [ + "%queryHistory.doubleClickAction.open%", + "%queryHistory.doubleClickAction.run%" + ] } } } diff --git a/extensions/query-history/package.nls.json b/extensions/query-history/package.nls.json index c5addc4cd8..e5a4caeb1a 100644 --- a/extensions/query-history/package.nls.json +++ b/extensions/query-history/package.nls.json @@ -2,6 +2,9 @@ "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.doubleClickAction": "The action taken when a history item is double clicked", + "queryHistory.doubleClickAction.open": "Open a new disconnected editor with the query from the selected history item", + "queryHistory.doubleClickAction.run": "Open a new connected editor with the query and connection from the selected history item and automatically run the query", "queryHistory.open": "Open Query", "queryHistory.run": "Run Query", "queryHistory.delete": "Delete", diff --git a/extensions/query-history/src/constants.ts b/extensions/query-history/src/constants.ts new file mode 100644 index 0000000000..60e376647e --- /dev/null +++ b/extensions/query-history/src/constants.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const QUERY_HISTORY_CONFIG_SECTION = 'queryHistory'; +export const CAPTURE_ENABLED_CONFIG_SECTION = 'captureEnabled'; +export const DOUBLE_CLICK_ACTION_CONFIG_SECTION = 'doubleClickAction'; + +export const ITEM_SELECTED_COMMAND_ID = 'queryHistory.itemSelected'; diff --git a/extensions/query-history/src/main.ts b/extensions/query-history/src/main.ts index 2b95dfeb71..15b3a1afb4 100644 --- a/extensions/query-history/src/main.ts +++ b/extensions/query-history/src/main.ts @@ -5,37 +5,89 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; +import { DOUBLE_CLICK_ACTION_CONFIG_SECTION, ITEM_SELECTED_COMMAND_ID, QUERY_HISTORY_CONFIG_SECTION } from './constants'; import { QueryHistoryItem } from './queryHistoryItem'; import { QueryHistoryProvider } from './queryHistoryProvider'; +let lastSelectedItem: { item: QueryHistoryItem | undefined, time: number | undefined } = { + item: undefined, + time: undefined +}; +/** + * The time in ms between clicks to count as a double click on our tree view items + */ +const DOUBLE_CLICK_TIMEOUT_MS = 500; + export async function activate(context: vscode.ExtensionContext): Promise { - const provider = new QueryHistoryProvider(); - context.subscriptions.push(provider); - context.subscriptions.push(vscode.window.registerTreeDataProvider('queryHistory', provider)); + const treeDataProvider = new QueryHistoryProvider(); + context.subscriptions.push(treeDataProvider); + const treeView = vscode.window.createTreeView('queryHistory', { + treeDataProvider, + canSelectMany: false + }); + context.subscriptions.push(treeView); + // This is an internal-only command so not adding to package.json + context.subscriptions.push(vscode.commands.registerCommand(ITEM_SELECTED_COMMAND_ID, async (selectedItem: QueryHistoryItem) => { + // VS Code doesn't provide a native way to detect a double-click so we track it ourselves by keeping track of the last item clicked and + // when it was clicked to compare, then if a click happens on the same element quickly enough we trigger the configured action + const clickTime = new Date().getTime(); + if (lastSelectedItem.item === selectedItem && lastSelectedItem.time && (clickTime - lastSelectedItem.time) < DOUBLE_CLICK_TIMEOUT_MS) { + const doubleClickAction = vscode.workspace.getConfiguration(QUERY_HISTORY_CONFIG_SECTION).get(DOUBLE_CLICK_ACTION_CONFIG_SECTION); + switch (doubleClickAction) { + case 'run': + await runQuery(selectedItem); + break; + case 'open': + default: + await openQuery(selectedItem); + break; + } + // Clear out the last selected item so we don't run the command again on a 3rd click + lastSelectedItem = { + item: undefined, + time: undefined + }; + } else { + // Update the last selected item since we didn't run a command + lastSelectedItem = { + item: selectedItem, + time: clickTime + }; + } + + })); context.subscriptions.push(vscode.commands.registerCommand('queryHistory.open', async (item: QueryHistoryItem) => { - return azdata.queryeditor.openQueryDocument( - { - content: item.queryText - }, item.connectionProfile?.providerId); + return openQuery(item); })); context.subscriptions.push(vscode.commands.registerCommand('queryHistory.run', async (item: QueryHistoryItem) => { - const doc = await azdata.queryeditor.openQueryDocument( - { - content: item.queryText - }, item.connectionProfile?.providerId); - await azdata.queryeditor.connect(doc.uri, item.connectionProfile?.connectionId || ''); - azdata.queryeditor.runQuery(doc.uri); + return runQuery(item); })); context.subscriptions.push(vscode.commands.registerCommand('queryHistory.delete', (item: QueryHistoryItem) => { - provider.deleteItem(item); + treeDataProvider.deleteItem(item); })); context.subscriptions.push(vscode.commands.registerCommand('queryHistory.clear', () => { - provider.clearAll(); + treeDataProvider.clearAll(); })); context.subscriptions.push(vscode.commands.registerCommand('queryHistory.disableCapture', async () => { - return provider.setCaptureEnabled(false); + return treeDataProvider.setCaptureEnabled(false); })); context.subscriptions.push(vscode.commands.registerCommand('queryHistory.enableCapture', async () => { - return provider.setCaptureEnabled(true); + return treeDataProvider.setCaptureEnabled(true); })); } + +async function openQuery(item: QueryHistoryItem): Promise { + await azdata.queryeditor.openQueryDocument( + { + content: item.queryText + }, item.connectionProfile?.providerId); +} + +async function runQuery(item: QueryHistoryItem): Promise { + const doc = await azdata.queryeditor.openQueryDocument( + { + content: item.queryText + }, item.connectionProfile?.providerId); + await azdata.queryeditor.connect(doc.uri, item.connectionProfile?.connectionId || ''); + azdata.queryeditor.runQuery(doc.uri); +} diff --git a/extensions/query-history/src/queryHistoryProvider.ts b/extensions/query-history/src/queryHistoryProvider.ts index c737e07f74..7cd254a1a1 100644 --- a/extensions/query-history/src/queryHistoryProvider.ts +++ b/extensions/query-history/src/queryHistoryProvider.ts @@ -8,9 +8,8 @@ import * as azdata from 'azdata'; import { EOL } from 'os'; import { QueryHistoryItem } from './queryHistoryItem'; import { removeNewLines } from './utils'; +import { CAPTURE_ENABLED_CONFIG_SECTION, ITEM_SELECTED_COMMAND_ID, QUERY_HISTORY_CONFIG_SECTION } from './constants'; -const QUERY_HISTORY_CONFIG_SECTION = 'queryHistory'; -const CAPTURE_ENABLED_CONFIG_SECTION = 'captureEnabled'; const DEFAULT_CAPTURE_ENABLED = true; const successIcon = new vscode.ThemeIcon('check', new vscode.ThemeColor('testing.iconPassed')); const failedIcon = new vscode.ThemeIcon('error', new vscode.ThemeColor('testing.iconFailed')); @@ -70,6 +69,7 @@ export class QueryHistoryProvider implements vscode.TreeDataProvider