Feature: Query History (#1561)
* initial query history working * added context menu actions and empty node * added clear, empty node and sorting of history * change default limit to 20 * added delete method * implemented open query * fixed tests * implemented run query * added setting to capture query history * added button to start/pause query history capture * fixed clauses for command palette * added query history UI in command palette * finished command palette UI * review comments * remove the node itself * put clear all in view action * fix tooltip order * changed icons, added user setting for feature * fix tooltip and text for nodes * fixed labels for node * explicit check for no selection * more cleanup * code review comments * change command string
12
gulpfile.js
|
@ -36,13 +36,21 @@ gulp.task('ext:lint', () => {
|
|||
});
|
||||
|
||||
// Copy icons for OE
|
||||
gulp.task('ext:copy-assets', (done) => {
|
||||
gulp.task('ext:copy-OE-assets', (done) => {
|
||||
return gulp.src([
|
||||
config.paths.project.root + '/src/objectExplorer/objectTypes/*'
|
||||
])
|
||||
.pipe(gulp.dest('out/src/objectExplorer/objectTypes'));
|
||||
});
|
||||
|
||||
// Copy icons for Query History
|
||||
gulp.task('ext:copy-queryHistory-assets', (done) => {
|
||||
return gulp.src([
|
||||
config.paths.project.root + '/src/queryHistory/icons/*'
|
||||
])
|
||||
.pipe(gulp.dest('out/src/queryHistory/icons'));
|
||||
});
|
||||
|
||||
// Compile source
|
||||
gulp.task('ext:compile-src', (done) => {
|
||||
return gulp.src([
|
||||
|
@ -208,7 +216,7 @@ gulp.task('ext:compile-tests', (done) => {
|
|||
|
||||
});
|
||||
|
||||
gulp.task('ext:compile', gulp.series('ext:compile-src', 'ext:compile-tests', 'ext:copy-assets'));
|
||||
gulp.task('ext:compile', gulp.series('ext:compile-src', 'ext:compile-tests', 'ext:copy-OE-assets', 'ext:copy-queryHistory-assets'));
|
||||
|
||||
gulp.task('ext:copy-tests', () => {
|
||||
return gulp.src(config.paths.project.root + '/test/resources/**/*')
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-action-green{fill:#388a34;}</style></defs><title>continue</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.334,8,3.667,16H3V0h.667Z"/></g><g id="iconBg"><path class="icon-vs-action-green" d="M4,1.5v13L12.667,8,4,1.5Z"/></g></svg>
|
||||
<svg width="14" height="14" 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>run</title><path class="cls-1" d="M3.24,0,14.61,8,3.24,16Zm2,12.07L11.13,8,5.24,3.88Z"/><path class="cls-1" d="M3.74,1l10,7-10,7Zm1,1.92V13.07L12,8Z"/></svg>
|
До Ширина: | Высота: | Размер: 506 B После Ширина: | Высота: | Размер: 329 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-action-green{fill:#89d185;}</style></defs><title>continue</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.334,8,3.667,16H3V0h.667Z"/></g><g id="iconBg"><path class="icon-vs-action-green" d="M4,1.5v13L12.667,8,4,1.5Z"/></g></svg>
|
До Ширина: | Высота: | Размер: 506 B |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-action-red{fill:#a1260d;}</style></defs><title>stop</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,2V14H2V2Z"/></g><g id="iconBg"><path class="icon-vs-action-red" d="M13,3V13H3V3Z"/></g></svg>
|
||||
<svg width="14" height="14" 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>stop</title><path class="cls-1" d="M.5,15.3V.3h15v15Zm13-2V2.3H2.5v11Z"/><path class="cls-1" d="M1,.8H15v14H1Zm13,13V1.8H2v12Z"/></svg>
|
До Ширина: | Высота: | Размер: 470 B После Ширина: | Высота: | Размер: 307 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-action-red{fill:#f48771;}</style></defs><title>stop</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,2V14H2V2Z"/></g><g id="iconBg"><path class="icon-vs-action-red" d="M13,3V13H3V3Z"/></g></svg>
|
До Ширина: | Высота: | Размер: 470 B |
|
@ -251,9 +251,24 @@
|
|||
<trans-unit id="msgPromptFirewallRuleCreated">
|
||||
<source xml:lang="en">Firewall rule successfully created.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgChooseQueryHistory">
|
||||
<source xml:lang="en">Choose Query History</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgChooseQueryHistoryAction">
|
||||
<source xml:lang="en">Choose An Action</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgOpenQueryHistory">
|
||||
<source xml:lang="en">Open Query History</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgRunQueryHistory">
|
||||
<source xml:lang="en">Run Query History</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgInvalidIpAddress">
|
||||
<source xml:lang="en">Invalid IP Address </source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgNoQueriesAvailable">
|
||||
<source xml:lang="en">No Queries Available</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="retryLabel">
|
||||
<source xml:lang="en">Retry</source>
|
||||
</trans-unit>
|
||||
|
|
111
package.json
|
@ -151,6 +151,11 @@
|
|||
{
|
||||
"id": "objectExplorer",
|
||||
"name": "%extension.connections%"
|
||||
},
|
||||
{
|
||||
"id": "queryHistory",
|
||||
"name": "%extension.queryHistory%",
|
||||
"when": "config.mssql.enableQueryHistoryFeature"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -204,6 +209,24 @@
|
|||
"when": "view == objectExplorer",
|
||||
"title": "%mssql.addObjectExplorer%",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "mssql.startQueryHistoryCapture",
|
||||
"when": "view == queryHistory && config.mssql.enableQueryHistoryFeature && !config.mssql.enableQueryHistoryCapture",
|
||||
"title": "%mssql.startQueryHistoryCapture%",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "mssql.pauseQueryHistoryCapture",
|
||||
"when": "view == queryHistory && config.mssql.enableQueryHistoryFeature && config.mssql.enableQueryHistoryCapture",
|
||||
"title": "%mssql.pauseQueryHistoryCapture%",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "mssql.clearAllQueryHistory",
|
||||
"when": "view == queryHistory",
|
||||
"title": "%mssql.clearAllQueryHistory%",
|
||||
"group": "secondary"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
|
@ -251,6 +274,21 @@
|
|||
"command": "mssql.scriptAlter",
|
||||
"when": "view == objectExplorer && viewItem =~ /^(AggregateFunction|PartitionFunction|ScalarValuedFunction|StoredProcedure|TableValuedFunction|View)$/",
|
||||
"group": "MS_SQL@4"
|
||||
},
|
||||
{
|
||||
"command": "mssql.openQueryHistory",
|
||||
"when": "view == queryHistory && viewItem == queryHistoryNode",
|
||||
"group": "MS_SQL@1"
|
||||
},
|
||||
{
|
||||
"command": "mssql.runQueryHistory",
|
||||
"when": "view == queryHistory && viewItem == queryHistoryNode",
|
||||
"group": "MS_SQL@2"
|
||||
},
|
||||
{
|
||||
"command": "mssql.deleteQueryHistory",
|
||||
"when": "view == queryHistory && viewItem == queryHistoryNode",
|
||||
"group": "MS_SQL@3"
|
||||
}
|
||||
],
|
||||
"commandPalette": [
|
||||
|
@ -293,6 +331,14 @@
|
|||
{
|
||||
"command": "mssql.toggleSqlCmd",
|
||||
"when": "editorFocus && editorLangId == 'sql'"
|
||||
},
|
||||
{
|
||||
"command": "mssql.startQueryHistoryCapture",
|
||||
"when": "config.mssql.enableQueryHistoryFeature && !config.mssql.enableQueryHistoryCapture"
|
||||
},
|
||||
{
|
||||
"command": "mssql.pauseQueryHistoryCapture",
|
||||
"when": "config.mssql.enableQueryHistoryFeature && config.mssql.enableQueryHistoryCapture"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -303,7 +349,7 @@
|
|||
"category": "MS SQL",
|
||||
"icon": {
|
||||
"light": "images/start.svg",
|
||||
"dark": "images/start_inverse.svg"
|
||||
"dark": "images/start.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -317,7 +363,7 @@
|
|||
"category": "MS SQL",
|
||||
"icon": {
|
||||
"light": "images/stop.svg",
|
||||
"dark": "images/stop_inverse.svg"
|
||||
"dark": "images/stop.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -418,6 +464,49 @@
|
|||
"command": "mssql.scriptAlter",
|
||||
"title": "%mssql.scriptAlter%",
|
||||
"group": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "mssql.openQueryHistory",
|
||||
"title": "%mssql.openQueryHistory%",
|
||||
"group": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "mssql.runQueryHistory",
|
||||
"title": "%mssql.runQueryHistory%",
|
||||
"group": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "mssql.deleteQueryHistory",
|
||||
"title": "%mssql.deleteQueryHistory%",
|
||||
"group": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "mssql.clearAllQueryHistory",
|
||||
"title": "%mssql.clearAllQueryHistory%",
|
||||
"group": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "mssql.startQueryHistoryCapture",
|
||||
"title": "%mssql.startQueryHistoryCapture%",
|
||||
"group": "MS SQL",
|
||||
"icon": {
|
||||
"light": "images/start.svg",
|
||||
"dark": "images/start.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "mssql.pauseQueryHistoryCapture",
|
||||
"title": "%mssql.pauseQueryHistoryCapture%",
|
||||
"group": "MS SQL",
|
||||
"icon": {
|
||||
"light": "images/stop.svg",
|
||||
"dark": "images/stop.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "mssql.commandPaletteQueryHistory",
|
||||
"title": "%mssql.commandPaletteQueryHistory%",
|
||||
"group": "MS SQL"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
|
@ -826,6 +915,24 @@
|
|||
"default": false,
|
||||
"description": "%mssql.persistQueryResultTabs%",
|
||||
"scope": "window"
|
||||
},
|
||||
"mssql.queryHistoryLimit": {
|
||||
"type": "number",
|
||||
"default": 20,
|
||||
"description": "%mssql.queryHistoryLimit%",
|
||||
"scope": "window"
|
||||
},
|
||||
"mssql.enableQueryHistoryCapture": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%mssql.enableQueryHistoryCapture%",
|
||||
"scope": "window"
|
||||
},
|
||||
"mssql.enableQueryHistoryFeature": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description" : "%mssql.enableQueryHistoryFeature%",
|
||||
"scope": "window"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,18 @@
|
|||
"mssql.scriptDelete":"Script as Drop",
|
||||
"mssql.scriptExecute":"Script as Execute",
|
||||
"mssql.scriptAlter":"Script as Alter",
|
||||
"mssql.openQueryHistory":"Open Query",
|
||||
"mssql.runQueryHistory":"Run Query",
|
||||
"mssql.deleteQueryHistory":"Delete",
|
||||
"mssql.clearAllQueryHistory":"Clear All Query History",
|
||||
"mssql.enableQueryHistoryCapture":"Enable Query History Capture",
|
||||
"mssql.startQueryHistoryCapture":"Start Query History Capture",
|
||||
"mssql.pauseQueryHistoryCapture":"Pause Query History Capture",
|
||||
"mssql.commandPaletteQueryHistory":"Open Query History in Command Palette",
|
||||
"mssql.removeObjectExplorerNode":"Remove",
|
||||
"mssql.refreshObjectExplorerNode":"Refresh",
|
||||
"extension.connections":"Connections",
|
||||
"extension.queryHistory":"Query History",
|
||||
"mssql.connect":"Connect",
|
||||
"mssql.disconnect":"Disconnect",
|
||||
"mssql.manageProfiles":"Manage Connection Profiles",
|
||||
|
@ -79,6 +88,8 @@
|
|||
"mssql.intelliSense.enableErrorChecking":"Should IntelliSense error checking be enabled",
|
||||
"mssql.intelliSense.enableSuggestions":"Should IntelliSense suggestions be enabled",
|
||||
"mssql.intelliSense.enableQuickInfo":"Should IntelliSense quick info be enabled",
|
||||
"mssql.enableQueryHistoryFeature":"Should Query History feature be enabled",
|
||||
"mssql.intelliSense.lowerCaseSuggestions":"Should IntelliSense suggestions be lowercase",
|
||||
"mssql.persistQueryResultTabs":"Should query result selections and scroll positions be saved when switching tabs (may impact performance)"
|
||||
"mssql.persistQueryResultTabs":"Should query result selections and scroll positions be saved when switching tabs (may impact performance)",
|
||||
"mssql.queryHistoryLimit":"Number of query history entries to show in the Query History view"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ export const extensionName = 'mssql';
|
|||
export const extensionConfigSectionName = 'mssql';
|
||||
export const mssqlProviderName = 'MSSQL';
|
||||
export const noneProviderName = 'None';
|
||||
export const objectExplorerId = 'objectExplorer';
|
||||
export const queryHistory = 'queryHistory';
|
||||
export const connectionApplicationName = 'vscode-mssql';
|
||||
export const outputChannelName = 'MSSQL';
|
||||
export const connectionConfigFilename = 'settings.json';
|
||||
|
@ -24,6 +26,14 @@ export const cmdChooseDatabase = 'mssql.chooseDatabase';
|
|||
export const cmdChooseLanguageFlavor = 'mssql.chooseLanguageFlavor';
|
||||
export const cmdShowReleaseNotes = 'mssql.showReleaseNotes';
|
||||
export const cmdShowGettingStarted = 'mssql.showGettingStarted';
|
||||
export const cmdRefreshQueryHistory = 'mssql.refreshQueryHistory';
|
||||
export const cmdClearAllQueryHistory = 'mssql.clearAllQueryHistory';
|
||||
export const cmdDeleteQueryHistory = 'mssql.deleteQueryHistory';
|
||||
export const cmdOpenQueryHistory = 'mssql.openQueryHistory';
|
||||
export const cmdRunQueryHistory = 'mssql.runQueryHistory';
|
||||
export const cmdStartQueryHistory = 'mssql.startQueryHistoryCapture';
|
||||
export const cmdPauseQueryHistory = 'mssql.pauseQueryHistoryCapture';
|
||||
export const cmdCommandPaletteQueryHistory = 'mssql.commandPaletteQueryHistory';
|
||||
export const cmdNewQuery = 'mssql.newQuery';
|
||||
export const cmdManageConnectionProfiles = 'mssql.manageProfiles';
|
||||
export const cmdRebuildIntelliSenseCache = 'mssql.rebuildIntelliSenseCache';
|
||||
|
@ -107,6 +117,9 @@ export const sqlToolsServiceDownloadUrlConfigKey = 'downloadUrl';
|
|||
export const extConfigResultFontFamily = 'resultsFontFamily';
|
||||
export const configApplyLocalization = 'applyLocalization';
|
||||
export const configPersistQueryResultTabs = 'persistQueryResultTabs';
|
||||
export const configQueryHistoryLimit = 'queryHistoryLimit';
|
||||
export const configEnableQueryHistoryCapture = 'enableQueryHistoryCapture';
|
||||
export const configEnableQueryHistoryFeature = 'enableQueryHistoryFeature';
|
||||
|
||||
// ToolsService Constants
|
||||
export const serviceInstallingTo = 'Installing SQL tools service to';
|
||||
|
|
|
@ -30,6 +30,8 @@ import { Deferred } from '../protocol';
|
|||
import { ConnectTreeNode } from '../objectExplorer/connectTreeNode';
|
||||
import { ObjectExplorerUtils } from '../objectExplorer/objectExplorerUtils';
|
||||
import { ScriptOperation } from '../models/contracts/scripting/scriptingRequest';
|
||||
import { QueryHistoryProvider } from '../queryHistory/queryHistoryProvider';
|
||||
import { QueryHistoryNode } from '../queryHistory/queryHistoryNode';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
|
@ -49,7 +51,9 @@ export default class MainController implements vscode.Disposable {
|
|||
private _lastOpenedTimer: Utils.Timer;
|
||||
private _untitledSqlDocumentService: UntitledSqlDocumentService;
|
||||
private _objectExplorerProvider: ObjectExplorerProvider;
|
||||
private _queryHistoryProvider: QueryHistoryProvider;
|
||||
private _scriptingService: ScriptingService;
|
||||
private _queryHistoryRegistered: boolean = false;
|
||||
|
||||
/**
|
||||
* The main controller constructor
|
||||
|
@ -63,7 +67,6 @@ export default class MainController implements vscode.Disposable {
|
|||
this._connectionMgr = connectionManager;
|
||||
}
|
||||
this._vscodeWrapper = vscodeWrapper || new VscodeWrapper();
|
||||
|
||||
this._untitledSqlDocumentService = new UntitledSqlDocumentService(this._vscodeWrapper);
|
||||
}
|
||||
|
||||
|
@ -139,116 +142,9 @@ export default class MainController implements vscode.Disposable {
|
|||
this.registerCommand(Constants.cmdToggleSqlCmd);
|
||||
this._event.on(Constants.cmdToggleSqlCmd, async () => { await self.onToggleSqlCmd(); });
|
||||
|
||||
// Register the object explorer tree provider
|
||||
this._objectExplorerProvider = new ObjectExplorerProvider(this._connectionMgr);
|
||||
this._context.subscriptions.push(
|
||||
vscode.window.registerTreeDataProvider('objectExplorer', this._objectExplorerProvider)
|
||||
);
|
||||
this.initializeObjectExplorer();
|
||||
|
||||
// Add Object Explorer Node
|
||||
this.registerCommand(Constants.cmdAddObjectExplorer);
|
||||
this._event.on(Constants.cmdAddObjectExplorer, async () => {
|
||||
if (!self._objectExplorerProvider.objectExplorerExists) {
|
||||
self._objectExplorerProvider.objectExplorerExists = true;
|
||||
}
|
||||
await self.createObjectExplorerSession();
|
||||
});
|
||||
|
||||
// Object Explorer New Query
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdObjectExplorerNewQuery, async (treeNodeInfo: TreeNodeInfo) => {
|
||||
const connectionCredentials = Object.assign({}, treeNodeInfo.connectionCredentials);
|
||||
const databaseName = ObjectExplorerUtils.getDatabaseName(treeNodeInfo);
|
||||
if (databaseName !== connectionCredentials.database &&
|
||||
databaseName !== LocalizedConstants.defaultDatabaseLabel) {
|
||||
connectionCredentials.database = databaseName;
|
||||
} else if (databaseName === LocalizedConstants.defaultDatabaseLabel) {
|
||||
connectionCredentials.database = '';
|
||||
}
|
||||
treeNodeInfo.connectionCredentials = connectionCredentials;
|
||||
await self.onNewQuery(treeNodeInfo);
|
||||
}));
|
||||
|
||||
// Remove Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdRemoveObjectExplorerNode, async (treeNodeInfo: TreeNodeInfo) => {
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(treeNodeInfo);
|
||||
let profile = <IConnectionProfile>treeNodeInfo.connectionCredentials;
|
||||
await this._connectionMgr.connectionStore.removeProfile(profile, false);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
}));
|
||||
|
||||
// Refresh Object Explorer Node
|
||||
this.registerCommand(Constants.cmdRefreshObjectExplorerNode);
|
||||
this._event.on(Constants.cmdRefreshObjectExplorerNode, () => {
|
||||
return this._objectExplorerProvider.refreshNode(this._objectExplorerProvider.currentNode);
|
||||
});
|
||||
|
||||
// Sign In into Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdObjectExplorerNodeSignIn, async (node: AccountSignInTreeNode) => {
|
||||
let profile = <IConnectionProfile>node.parentNode.connectionCredentials;
|
||||
profile = await self.connectionManager.connectionUI.promptForRetryCreateProfile(profile);
|
||||
if (profile) {
|
||||
node.parentNode.connectionCredentials = <IConnectionCredentials>profile;
|
||||
self._objectExplorerProvider.updateNode(node.parentNode);
|
||||
self._objectExplorerProvider.signInNodeServer(node.parentNode);
|
||||
return self._objectExplorerProvider.refresh(undefined);
|
||||
}
|
||||
}));
|
||||
|
||||
// Connect to Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdConnectObjectExplorerNode, async (node: ConnectTreeNode) => {
|
||||
self._objectExplorerProvider.currentNode = node.parentNode;
|
||||
await self.createObjectExplorerSession(node.parentNode.connectionCredentials);
|
||||
}));
|
||||
|
||||
// Disconnect Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdDisconnectObjectExplorerNode, async (node: TreeNodeInfo) => {
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(node, true);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
}));
|
||||
|
||||
// Initiate the scripting service
|
||||
this._scriptingService = new ScriptingService(this._connectionMgr);
|
||||
|
||||
// Script as Select
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptSelect, async (node: TreeNodeInfo) => {
|
||||
await this.scriptNode(node, ScriptOperation.Select, true);
|
||||
}));
|
||||
|
||||
// Script as Create
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptCreate, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Create)));
|
||||
|
||||
// Script as Drop
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptDelete, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Delete)));
|
||||
|
||||
// Script as Execute
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptExecute, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Execute)));
|
||||
|
||||
// Script as Alter
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptAlter, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Alter)));
|
||||
this.initializeQueryHistory();
|
||||
|
||||
// Add handlers for VS Code generated commands
|
||||
this._vscodeWrapper.onDidCloseTextDocument(async (params) => await this.onDidCloseTextDocument(params));
|
||||
|
@ -374,6 +270,204 @@ export default class MainController implements vscode.Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Object Explorer commands
|
||||
*/
|
||||
private initializeObjectExplorer(): void {
|
||||
const self = this;
|
||||
// Register the object explorer tree provider
|
||||
this._objectExplorerProvider = new ObjectExplorerProvider(this._connectionMgr);
|
||||
this._context.subscriptions.push(
|
||||
vscode.window.registerTreeDataProvider('objectExplorer', this._objectExplorerProvider)
|
||||
);
|
||||
|
||||
// Add Object Explorer Node
|
||||
this.registerCommand(Constants.cmdAddObjectExplorer);
|
||||
this._event.on(Constants.cmdAddObjectExplorer, async () => {
|
||||
if (!self._objectExplorerProvider.objectExplorerExists) {
|
||||
self._objectExplorerProvider.objectExplorerExists = true;
|
||||
}
|
||||
await self.createObjectExplorerSession();
|
||||
});
|
||||
|
||||
// Object Explorer New Query
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdObjectExplorerNewQuery, async (treeNodeInfo: TreeNodeInfo) => {
|
||||
const connectionCredentials = Object.assign({}, treeNodeInfo.connectionCredentials);
|
||||
const databaseName = ObjectExplorerUtils.getDatabaseName(treeNodeInfo);
|
||||
if (databaseName !== connectionCredentials.database &&
|
||||
databaseName !== LocalizedConstants.defaultDatabaseLabel) {
|
||||
connectionCredentials.database = databaseName;
|
||||
} else if (databaseName === LocalizedConstants.defaultDatabaseLabel) {
|
||||
connectionCredentials.database = '';
|
||||
}
|
||||
treeNodeInfo.connectionCredentials = connectionCredentials;
|
||||
await self.onNewQuery(treeNodeInfo);
|
||||
}));
|
||||
|
||||
// Remove Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdRemoveObjectExplorerNode, async (treeNodeInfo: TreeNodeInfo) => {
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(treeNodeInfo);
|
||||
let profile = <IConnectionProfile>treeNodeInfo.connectionCredentials;
|
||||
await this._connectionMgr.connectionStore.removeProfile(profile, false);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
}));
|
||||
|
||||
// Refresh Object Explorer Node
|
||||
this.registerCommand(Constants.cmdRefreshObjectExplorerNode);
|
||||
this._event.on(Constants.cmdRefreshObjectExplorerNode, () => {
|
||||
return this._objectExplorerProvider.refreshNode(this._objectExplorerProvider.currentNode);
|
||||
});
|
||||
|
||||
// Sign In into Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdObjectExplorerNodeSignIn, async (node: AccountSignInTreeNode) => {
|
||||
let profile = <IConnectionProfile>node.parentNode.connectionCredentials;
|
||||
profile = await self.connectionManager.connectionUI.promptForRetryCreateProfile(profile);
|
||||
if (profile) {
|
||||
node.parentNode.connectionCredentials = <IConnectionCredentials>profile;
|
||||
self._objectExplorerProvider.updateNode(node.parentNode);
|
||||
self._objectExplorerProvider.signInNodeServer(node.parentNode);
|
||||
return self._objectExplorerProvider.refresh(undefined);
|
||||
}
|
||||
}));
|
||||
|
||||
// Connect to Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdConnectObjectExplorerNode, async (node: ConnectTreeNode) => {
|
||||
self._objectExplorerProvider.currentNode = node.parentNode;
|
||||
await self.createObjectExplorerSession(node.parentNode.connectionCredentials);
|
||||
}));
|
||||
|
||||
// Disconnect Object Explorer Node
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdDisconnectObjectExplorerNode, async (node: TreeNodeInfo) => {
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(node, true);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
}));
|
||||
|
||||
// Initiate the scripting service
|
||||
this._scriptingService = new ScriptingService(this._connectionMgr);
|
||||
|
||||
// Script as Select
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptSelect, async (node: TreeNodeInfo) => {
|
||||
await this.scriptNode(node, ScriptOperation.Select, true);
|
||||
}));
|
||||
|
||||
// Script as Create
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptCreate, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Create)));
|
||||
|
||||
// Script as Drop
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptDelete, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Delete)));
|
||||
|
||||
// Script as Execute
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptExecute, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Execute)));
|
||||
|
||||
// Script as Alter
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptAlter, async (node: TreeNodeInfo) =>
|
||||
await this.scriptNode(node, ScriptOperation.Alter)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Query History commands
|
||||
*/
|
||||
private initializeQueryHistory(): void {
|
||||
|
||||
let config = this._vscodeWrapper.getConfiguration(Constants.extensionConfigSectionName);
|
||||
let queryHistoryFeature = config.get(Constants.configEnableQueryHistoryFeature);
|
||||
// If the query history feature is enabled
|
||||
if (queryHistoryFeature && !this._queryHistoryRegistered) {
|
||||
// Register the query history tree provider
|
||||
this._queryHistoryProvider = new QueryHistoryProvider(this._connectionMgr, this._outputContentProvider,
|
||||
this._vscodeWrapper, this._untitledSqlDocumentService, this._statusview, this._prompter);
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.window.registerTreeDataProvider('queryHistory', this._queryHistoryProvider)
|
||||
);
|
||||
|
||||
// Command to refresh Query History
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdRefreshQueryHistory, (ownerUri: string, hasError: boolean) => {
|
||||
config = this._vscodeWrapper.getConfiguration(Constants.extensionConfigSectionName);
|
||||
let queryHistoryFeatureEnabled = config.get(Constants.configEnableQueryHistoryFeature);
|
||||
let queryHistoryCaptureEnabled = config.get(Constants.configEnableQueryHistoryCapture);
|
||||
if (queryHistoryFeatureEnabled && queryHistoryCaptureEnabled) {
|
||||
const timeStamp = new Date();
|
||||
this._queryHistoryProvider.refresh(ownerUri, timeStamp, hasError);
|
||||
}
|
||||
}));
|
||||
|
||||
// Command to enable clear all entries in Query History
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdClearAllQueryHistory, () => {
|
||||
this._queryHistoryProvider.clearAll();
|
||||
}));
|
||||
|
||||
// Command to enable delete an entry in Query History
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdDeleteQueryHistory, (node: QueryHistoryNode) => {
|
||||
this._queryHistoryProvider.deleteQueryHistoryEntry(node);
|
||||
}));
|
||||
|
||||
// Command to enable open a query in Query History
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdOpenQueryHistory, async (node: QueryHistoryNode) => {
|
||||
await this._queryHistoryProvider.openQueryHistoryEntry(node);
|
||||
}));
|
||||
|
||||
// Command to enable run a query in Query History
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdRunQueryHistory, async (node: QueryHistoryNode) => {
|
||||
await this._queryHistoryProvider.openQueryHistoryEntry(node, true);
|
||||
}));
|
||||
|
||||
// Command to start the query history capture
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdStartQueryHistory, async (node: QueryHistoryNode) => {
|
||||
await this._queryHistoryProvider.startQueryHistoryCapture();
|
||||
}));
|
||||
|
||||
// Command to pause the query history capture
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdPauseQueryHistory, async (node: QueryHistoryNode) => {
|
||||
await this._queryHistoryProvider.pauseQueryHistoryCapture();
|
||||
}));
|
||||
|
||||
// Command to open the query history experience in the command palette
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdCommandPaletteQueryHistory, async () => {
|
||||
await this._queryHistoryProvider.showQueryHistoryCommandPalette();
|
||||
}));
|
||||
this._queryHistoryRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the command to enable SQLCMD mode
|
||||
|
@ -892,12 +986,24 @@ export default class MainController implements vscode.Disposable {
|
|||
this._lastSavedUri = savedDocumentUri;
|
||||
}
|
||||
|
||||
private async onChangeQueryHistoryConfig(): Promise<void> {
|
||||
let queryHistoryFeatureEnabled = this._vscodeWrapper.getConfiguration(Constants.extensionConfigSectionName)
|
||||
.get(Constants.configEnableQueryHistoryFeature);
|
||||
if (queryHistoryFeatureEnabled) {
|
||||
this.initializeQueryHistory();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by VS Code when user settings are changed
|
||||
* @param ConfigurationChangeEvent event that is fired when config is changed
|
||||
*/
|
||||
public async onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent): Promise<void> {
|
||||
if (e.affectsConfiguration(Constants.extensionName)) {
|
||||
// Query History settings change
|
||||
await this.onChangeQueryHistoryConfig();
|
||||
|
||||
// Connections change
|
||||
let needsRefresh = false;
|
||||
// user connections is a super set of object explorer connections
|
||||
let userConnections: any[] = this._vscodeWrapper.getConfiguration(Constants.extensionName).get(Constants.connectionsArrayName);
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface IResultSet {
|
|||
columns: string[];
|
||||
totalNumberOfRows: number;
|
||||
}
|
||||
|
||||
/*
|
||||
* Query Runner class which handles running a query, reports the results to the content manager,
|
||||
* and handles getting more rows from the service layer and disposing when the content is closed.
|
||||
|
@ -49,6 +50,7 @@ export default class QueryRunner {
|
|||
private _isSqlCmd: boolean = false;
|
||||
public eventEmitter: EventEmitter = new EventEmitter();
|
||||
private _uriToQueryPromiseMap = new Map<string, Deferred<boolean>>();
|
||||
private _uriToQueryStringMap = new Map<string, string>();
|
||||
|
||||
// CONSTRUCTOR /////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -160,6 +162,20 @@ export default class QueryRunner {
|
|||
querySelection: selection
|
||||
};
|
||||
|
||||
const doc = await this._vscodeWrapper.openTextDocument(this._vscodeWrapper.parseUri(this._ownerUri));
|
||||
let queryString: string;
|
||||
if (selection) {
|
||||
let range = this._vscodeWrapper.range(
|
||||
this._vscodeWrapper.position(selection.startLine, selection.startColumn),
|
||||
this._vscodeWrapper.position(selection.endLine, selection.endColumn));
|
||||
queryString = doc.getText(range);
|
||||
} else {
|
||||
queryString = doc.getText();
|
||||
}
|
||||
|
||||
// Set the query string for the uri
|
||||
this._uriToQueryStringMap.set(this._ownerUri, queryString);
|
||||
|
||||
// Send the request to execute the query
|
||||
if (promise) {
|
||||
this._uriToQueryPromiseMap.set(this._ownerUri, promise);
|
||||
|
@ -216,7 +232,8 @@ export default class QueryRunner {
|
|||
this._uriToQueryPromiseMap.delete(result.ownerUri);
|
||||
}
|
||||
this._statusView.executedQuery(result.ownerUri);
|
||||
this.eventEmitter.emit('complete', Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
|
||||
let hasError = this._batchSets.some(batch => batch.hasError === true);
|
||||
this.eventEmitter.emit('complete', Utils.parseNumAsTimeString(this._totalElapsedMilliseconds), hasError);
|
||||
}
|
||||
|
||||
public handleBatchStart(result: QueryExecuteBatchNotificationParams): void {
|
||||
|
@ -576,6 +593,13 @@ export default class QueryRunner {
|
|||
}
|
||||
}
|
||||
|
||||
public getQueryString(uri: string): string {
|
||||
if (this._uriToQueryStringMap.has(uri)) {
|
||||
return this._uriToQueryStringMap.get(uri);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public resetHasCompleted(): void {
|
||||
this._hasCompleted = false;
|
||||
}
|
||||
|
|
|
@ -345,6 +345,13 @@ export default class VscodeWrapper {
|
|||
return this.getConfiguration(extensionName).update(resource, value, vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a context for contributing command actions
|
||||
*/
|
||||
public async setContext(contextSection: string, value: any): Promise<void> {
|
||||
await this.executeCommand('setContext', contextSection, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when there's a change in the extensions
|
||||
*/
|
||||
|
|
|
@ -215,7 +215,8 @@ export class SqlOutputContentProvider {
|
|||
queryRunner.eventEmitter.on('message', (message) => {
|
||||
this._panels.get(uri).proxy.sendEvent('message', message);
|
||||
});
|
||||
queryRunner.eventEmitter.on('complete', (totalMilliseconds) => {
|
||||
queryRunner.eventEmitter.on('complete', (totalMilliseconds, hasError) => {
|
||||
this._vscodeWrapper.executeCommand(Constants.cmdRefreshQueryHistory, uri, hasError);
|
||||
this._panels.get(uri).proxy.sendEvent('complete', totalMilliseconds);
|
||||
});
|
||||
this._queryResultsMap.set(uri, new QueryRunnerState(queryRunner));
|
||||
|
|
|
@ -12,7 +12,7 @@ import * as findRemoveSync from 'find-remove';
|
|||
import vscode = require('vscode');
|
||||
import Constants = require('../constants/constants');
|
||||
import { AzureSignInQuickPickItem, IConnectionCredentials, IConnectionProfile, AuthenticationTypes } from './interfaces';
|
||||
import {ExtensionContext} from 'vscode';
|
||||
import { ExtensionContext } from 'vscode';
|
||||
import LocalizedConstants = require('../constants/localizedConstants');
|
||||
import fs = require('fs');
|
||||
|
||||
|
@ -59,14 +59,14 @@ export function generateGuid(): string {
|
|||
/* tslint:disable:no-bitwise */
|
||||
for (let a: number = 0; a < 4; a++) {
|
||||
tmp = (4294967296 * Math.random()) | 0;
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
}
|
||||
|
||||
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
||||
|
@ -148,17 +148,17 @@ export function logDebug(msg: any): void {
|
|||
|
||||
// Helper to show an info message
|
||||
export function showInfoMsg(msg: string): void {
|
||||
vscode.window.showInformationMessage(Constants.extensionName + ': ' + msg );
|
||||
vscode.window.showInformationMessage(Constants.extensionName + ': ' + msg);
|
||||
}
|
||||
|
||||
// Helper to show an warn message
|
||||
export function showWarnMsg(msg: string): void {
|
||||
vscode.window.showWarningMessage(Constants.extensionName + ': ' + msg );
|
||||
vscode.window.showWarningMessage(Constants.extensionName + ': ' + msg);
|
||||
}
|
||||
|
||||
// Helper to show an error message
|
||||
export function showErrorMsg(msg: string): void {
|
||||
vscode.window.showErrorMessage(Constants.extensionName + ': ' + msg );
|
||||
vscode.window.showErrorMessage(Constants.extensionName + ': ' + msg);
|
||||
}
|
||||
|
||||
export function isEmpty(str: any): boolean {
|
||||
|
@ -264,8 +264,8 @@ export function isSameConnection(conn: IConnectionCredentials, expectedConn: ICo
|
|||
&& isSameDatabase(expectedConn.database, conn.database)
|
||||
&& isSameAuthenticationType(expectedConn.authenticationType, conn.authenticationType)
|
||||
&& (conn.authenticationType === Constants.sqlAuthentication ?
|
||||
conn.user === expectedConn.user :
|
||||
isEmpty(conn.user) === isEmpty(expectedConn.user))
|
||||
conn.user === expectedConn.user :
|
||||
isEmpty(conn.user) === isEmpty(expectedConn.user))
|
||||
&& (<IConnectionProfile>conn).savePassword ===
|
||||
(<IConnectionProfile>expectedConn).savePassword;
|
||||
}
|
||||
|
@ -274,13 +274,13 @@ export function isSameConnection(conn: IConnectionCredentials, expectedConn: ICo
|
|||
* Check if a file exists on disk
|
||||
*/
|
||||
export function isFileExisting(filePath: string): boolean {
|
||||
try {
|
||||
fs.statSync(filePath);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
fs.statSync(filePath);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// One-time use timer for performance testing
|
||||
|
@ -298,7 +298,7 @@ export class Timer {
|
|||
return -1;
|
||||
} else if (!this._endTime) {
|
||||
let endTime = process.hrtime(<any>this._startTime);
|
||||
return endTime[0] * 1000 + endTime[1] / 1000000;
|
||||
return endTime[0] * 1000 + endTime[1] / 1000000;
|
||||
} else {
|
||||
return this._endTime[0] * 1000 + this._endTime[1] / 1000000;
|
||||
}
|
||||
|
@ -385,53 +385,51 @@ function getConfiguration(): vscode.WorkspaceConfiguration {
|
|||
}
|
||||
|
||||
export function getConfigTracingLevel(): string {
|
||||
let config = getConfiguration();
|
||||
if (config) {
|
||||
return config.get(configTracingLevel);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
let config = getConfiguration();
|
||||
if (config) {
|
||||
return config.get(configTracingLevel);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfigLogFilesRemovalLimit(): number {
|
||||
let config = getConfiguration();
|
||||
if (config) {
|
||||
return Number((config.get(configLogFilesRemovalLimit, 0).toFixed(0)));
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
let config = getConfiguration();
|
||||
if (config) {
|
||||
return Number((config.get(configLogFilesRemovalLimit, 0).toFixed(0)));
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfigLogRetentionSeconds(): number {
|
||||
let config = getConfiguration();
|
||||
if (config) {
|
||||
return Number((config.get(configLogRetentionMinutes, 0) * 60).toFixed(0));
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
let config = getConfiguration();
|
||||
if (config) {
|
||||
return Number((config.get(configLogRetentionMinutes, 0) * 60).toFixed(0));
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function removeOldLogFiles(logPath: string, prefix: string): JSON {
|
||||
return findRemoveSync(logPath, { age: { seconds: getConfigLogRetentionSeconds() }, limit: getConfigLogFilesRemovalLimit() });
|
||||
return findRemoveSync(logPath, { age: { seconds: getConfigLogRetentionSeconds() }, limit: getConfigLogFilesRemovalLimit() });
|
||||
}
|
||||
|
||||
export function getCommonLaunchArgsAndCleanupOldLogFiles(logPath: string, fileName: string, executablePath: string): string[] {
|
||||
let launchArgs = [];
|
||||
launchArgs.push('--log-file');
|
||||
let logFile = path.join(logPath, fileName);
|
||||
launchArgs.push(logFile);
|
||||
let launchArgs = [];
|
||||
launchArgs.push('--log-file');
|
||||
let logFile = path.join(logPath, fileName);
|
||||
launchArgs.push(logFile);
|
||||
|
||||
console.log(`logFile for ${path.basename(executablePath)} is ${logFile}`);
|
||||
console.log(`This process (ui Extenstion Host) is pid: ${process.pid}`);
|
||||
// Delete old log files
|
||||
let deletedLogFiles = removeOldLogFiles(logPath, fileName);
|
||||
console.log(`Old log files deletion report: ${JSON.stringify(deletedLogFiles)}`);
|
||||
launchArgs.push('--tracing-level');
|
||||
launchArgs.push(getConfigTracingLevel());
|
||||
return launchArgs;
|
||||
console.log(`logFile for ${path.basename(executablePath)} is ${logFile}`);
|
||||
console.log(`This process (ui Extenstion Host) is pid: ${process.pid}`);
|
||||
// Delete old log files
|
||||
let deletedLogFiles = removeOldLogFiles(logPath, fileName);
|
||||
console.log(`Old log files deletion report: ${JSON.stringify(deletedLogFiles)}`);
|
||||
launchArgs.push('--tracing-level');
|
||||
launchArgs.push(getConfigTracingLevel());
|
||||
return launchArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -454,4 +452,20 @@ export function getSignInQuickPickItems(): AzureSignInQuickPickItem[] {
|
|||
command: Constants.cmdAzureSignInToCloud
|
||||
};
|
||||
return [signInItem, signInWithDeviceCode, signInAzureCloud];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the size of a string with ellipses in the middle
|
||||
*/
|
||||
export function limitStringSize(input: string, forCommandPalette: boolean = false): string {
|
||||
if (!forCommandPalette) {
|
||||
if (input.length > 45) {
|
||||
return `${input.substr(0, 20)}...${input.substr(input.length - 20, input.length)}`;
|
||||
}
|
||||
} else {
|
||||
if (input.length > 100) {
|
||||
return `${input.substr(0, 45)}...${input.substr(input.length - 45, input.length)}`;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<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>
|
После Ширина: | Высота: | Размер: 721 B |
|
@ -0,0 +1 @@
|
|||
<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>
|
После Ширина: | Высота: | Размер: 255 B |
|
@ -0,0 +1,79 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as LocalizedConstants from '../constants/localizedConstants';
|
||||
import { queryHistory } from '../constants/constants';
|
||||
|
||||
/**
|
||||
* Empty Node shown when no queries are available
|
||||
*/
|
||||
export class EmptyHistoryNode extends vscode.TreeItem {
|
||||
|
||||
private static readonly contextValue = 'emptyHistoryNode';
|
||||
|
||||
constructor() {
|
||||
super(LocalizedConstants.msgNoQueriesAvailable, vscode.TreeItemCollapsibleState.None);
|
||||
this.contextValue = EmptyHistoryNode.contextValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query history node
|
||||
*/
|
||||
export class QueryHistoryNode extends vscode.TreeItem {
|
||||
|
||||
private static readonly contextValue = 'queryHistoryNode';
|
||||
private readonly iconsPath: string = path.join(__dirname, 'icons');
|
||||
private readonly successIcon: string = path.join(this.iconsPath, 'status_success.svg');
|
||||
private readonly failureIcon: string = path.join(this.iconsPath, 'status_error.svg');
|
||||
private _ownerUri: string;
|
||||
private _timeStamp: Date;
|
||||
private _isSuccess: boolean;
|
||||
private _queryString: string;
|
||||
private _connectionLabel: string;
|
||||
|
||||
constructor(
|
||||
label: string,
|
||||
tooltip: string,
|
||||
queryString: string,
|
||||
ownerUri: string,
|
||||
timeStamp: Date,
|
||||
connectionLabel: string,
|
||||
isSuccess: boolean
|
||||
) {
|
||||
super(label, vscode.TreeItemCollapsibleState.None);
|
||||
this._queryString = queryString;
|
||||
this._ownerUri = ownerUri;
|
||||
this._timeStamp = timeStamp;
|
||||
this._isSuccess = isSuccess;
|
||||
this._connectionLabel = connectionLabel;
|
||||
this.iconPath = this._isSuccess ? this.successIcon : this.failureIcon;
|
||||
this.tooltip = tooltip;
|
||||
this.contextValue = QueryHistoryNode.contextValue;
|
||||
}
|
||||
|
||||
/** Getters */
|
||||
public get historyNodeLabel(): string {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public get ownerUri(): string {
|
||||
return this._ownerUri;
|
||||
}
|
||||
|
||||
public get timeStamp(): Date {
|
||||
return this._timeStamp;
|
||||
}
|
||||
|
||||
public get queryString(): string {
|
||||
return this._queryString;
|
||||
}
|
||||
|
||||
public get connectionLabel(): string {
|
||||
return this._connectionLabel;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as Utils from '../models/utils';
|
||||
import ConnectionManager from '../controllers/connectionManager';
|
||||
import { SqlOutputContentProvider } from '../models/sqlOutputContentProvider';
|
||||
import { QueryHistoryNode, EmptyHistoryNode } from './queryHistoryNode';
|
||||
import VscodeWrapper from '../controllers/vscodeWrapper';
|
||||
import Constants = require('../constants/constants');
|
||||
import UntitledSqlDocumentService from '../controllers/untitledSqlDocumentService';
|
||||
import { Deferred } from '../protocol';
|
||||
import StatusView from '../views/statusView';
|
||||
import { IConnectionProfile } from '../models/interfaces';
|
||||
import { IPrompter } from '../prompts/question';
|
||||
import { QueryHistoryUI, QueryHistoryAction } from '../views/queryHistoryUI';
|
||||
|
||||
export class QueryHistoryProvider implements vscode.TreeDataProvider<any> {
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<any | undefined> = new vscode.EventEmitter<any | undefined>();
|
||||
readonly onDidChangeTreeData: vscode.Event<any | undefined> = this._onDidChangeTreeData.event;
|
||||
|
||||
private _queryHistoryNodes: vscode.TreeItem[] = [new EmptyHistoryNode()]
|
||||
private _queryHistoryLimit: number;
|
||||
private _queryHistoryUI: QueryHistoryUI;
|
||||
|
||||
constructor(
|
||||
private _connectionManager: ConnectionManager,
|
||||
private _outputContentProvider: SqlOutputContentProvider,
|
||||
private _vscodeWrapper: VscodeWrapper,
|
||||
private _untitledSqlDocumentService: UntitledSqlDocumentService,
|
||||
private _statusView: StatusView,
|
||||
private _prompter: IPrompter
|
||||
) {
|
||||
const config = this._vscodeWrapper.getConfiguration(Constants.extensionConfigSectionName);
|
||||
this._queryHistoryLimit = config.get(Constants.configQueryHistoryLimit);
|
||||
this._queryHistoryUI = new QueryHistoryUI(this._prompter, this._vscodeWrapper);
|
||||
}
|
||||
|
||||
clearAll(): void {
|
||||
this._queryHistoryNodes = [new EmptyHistoryNode()];
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
refresh(ownerUri: string, timeStamp: Date, hasError): void {
|
||||
const timeStampString = timeStamp.toLocaleString();
|
||||
const historyNodeLabel = this.createHistoryNodeLabel(ownerUri);
|
||||
const tooltip = this.createHistoryNodeTooltip(ownerUri, timeStampString);
|
||||
const queryString = this.getQueryString(ownerUri);
|
||||
const connectionLabel = this.getConnectionLabel(ownerUri);
|
||||
const node = new QueryHistoryNode(historyNodeLabel, tooltip, queryString,
|
||||
ownerUri, timeStamp, connectionLabel, !hasError);
|
||||
if (this._queryHistoryNodes.length === 1) {
|
||||
if (this._queryHistoryNodes[0] instanceof EmptyHistoryNode) {
|
||||
this._queryHistoryNodes = [];
|
||||
}
|
||||
}
|
||||
this._queryHistoryNodes.push(node);
|
||||
// sort the query history sorted by timestamp
|
||||
this._queryHistoryNodes.sort((a, b) => {
|
||||
return (b as QueryHistoryNode).timeStamp.getTime() -
|
||||
(a as QueryHistoryNode).timeStamp.getTime();
|
||||
});
|
||||
// Push out the first listing if it crosses limit to maintain
|
||||
// an LRU order
|
||||
if (this._queryHistoryNodes.length > this._queryHistoryLimit) {
|
||||
this._queryHistoryNodes.shift();
|
||||
}
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
getTreeItem(node: QueryHistoryNode): QueryHistoryNode {
|
||||
return node;
|
||||
}
|
||||
|
||||
getChildren(element?: any): vscode.TreeItem[] {
|
||||
if (this._queryHistoryNodes.length === 0) {
|
||||
this._queryHistoryNodes.push(new EmptyHistoryNode());
|
||||
}
|
||||
return this._queryHistoryNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the Query History List on the command palette
|
||||
*/
|
||||
public async showQueryHistoryCommandPalette(): Promise<void | undefined> {
|
||||
const options = this._queryHistoryNodes.map(node => this._queryHistoryUI.convertToQuickPickItem(node));
|
||||
let queryHistoryQuickPickItem = await this._queryHistoryUI.showQueryHistoryCommandPalette(options);
|
||||
if (queryHistoryQuickPickItem) {
|
||||
await this.openQueryHistoryEntry(queryHistoryQuickPickItem.node, queryHistoryQuickPickItem.action ===
|
||||
QueryHistoryAction.RunQueryHistoryAction);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the history capture by changing the setting
|
||||
* and changes context for menu actions
|
||||
*/
|
||||
public async startQueryHistoryCapture(): Promise<void> {
|
||||
await this._vscodeWrapper.setConfiguration(Constants.extensionConfigSectionName,
|
||||
Constants.configEnableQueryHistoryCapture, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the history capture by changing the setting
|
||||
* and changes context for menu actions
|
||||
*/
|
||||
public async pauseQueryHistoryCapture(): Promise<void> {
|
||||
await this._vscodeWrapper.setConfiguration(Constants.extensionConfigSectionName,
|
||||
Constants.configEnableQueryHistoryCapture, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a query history listing in a new query window
|
||||
*/
|
||||
public async openQueryHistoryEntry(node: QueryHistoryNode, isExecute: boolean = false): Promise<void> {
|
||||
const editor = await this._untitledSqlDocumentService.newQuery(node.queryString);
|
||||
let uri = editor.document.uri.toString(true);
|
||||
let title = path.basename(editor.document.fileName);
|
||||
const queryUriPromise = new Deferred<boolean>();
|
||||
let credentials = this._connectionManager.getConnectionInfo(node.ownerUri).credentials;
|
||||
await this._connectionManager.connect(uri, credentials, queryUriPromise);
|
||||
await queryUriPromise;
|
||||
this._statusView.languageFlavorChanged(uri, Constants.mssqlProviderName);
|
||||
this._statusView.sqlCmdModeChanged(uri, false);
|
||||
if (isExecute) {
|
||||
const queryPromise = new Deferred<boolean>();
|
||||
await this._outputContentProvider.runQuery(this._statusView, uri, undefined, title, queryPromise);
|
||||
await queryPromise;
|
||||
await this._connectionManager.connectionStore.removeRecentlyUsed(<IConnectionProfile>credentials);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a query history entry for a URI
|
||||
*/
|
||||
public deleteQueryHistoryEntry(node: QueryHistoryNode): void {
|
||||
let index = this._queryHistoryNodes.findIndex(n => {
|
||||
let historyNode = n as QueryHistoryNode;
|
||||
return historyNode === node;
|
||||
});
|
||||
this._queryHistoryNodes.splice(index, 1);
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
public get queryHistoryNodes(): vscode.TreeItem[] {
|
||||
return this._queryHistoryNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the node label for a query history node
|
||||
*/
|
||||
private createHistoryNodeLabel(ownerUri: string): string {
|
||||
const queryString = Utils.limitStringSize(this.getQueryString(ownerUri)).trim();
|
||||
const connectionLabel = Utils.limitStringSize(this.getConnectionLabel(ownerUri)).trim();
|
||||
return `${queryString} : ${connectionLabel}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selected text for the corresponding query history listing
|
||||
*/
|
||||
private getQueryString(ownerUri: string): string {
|
||||
const queryRunner = this._outputContentProvider.getQueryRunner(ownerUri);
|
||||
return queryRunner.getQueryString(ownerUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a connection label based on credentials
|
||||
*/
|
||||
private getConnectionLabel(ownerUri: string): string {
|
||||
const connInfo = this._connectionManager.getConnectionInfo(ownerUri);
|
||||
const credentials = connInfo.credentials;
|
||||
let connString = `(${credentials.server}|${credentials.database})`;
|
||||
if (credentials.authenticationType === Constants.sqlAuthentication) {
|
||||
connString = `${connString} : ${credentials.user}`;
|
||||
}
|
||||
return connString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a detailed tool tip when a node is hovered
|
||||
*/
|
||||
private createHistoryNodeTooltip(ownerUri: string, timeStamp: string): string {
|
||||
const queryString = this.getQueryString(ownerUri);
|
||||
const connectionLabel = this.getConnectionLabel(ownerUri);
|
||||
return `${connectionLabel}${os.EOL}${os.EOL}${timeStamp}${os.EOL}${os.EOL}${queryString}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as Utils from '../models/utils';
|
||||
import VscodeWrapper from '../controllers/vscodeWrapper';
|
||||
import { IPrompter, IQuestion, QuestionTypes } from '../prompts/question';
|
||||
import { QueryHistoryNode } from '../queryHistory/queryHistoryNode';
|
||||
import * as LocalizedConstants from '../constants/localizedConstants';
|
||||
|
||||
|
||||
export enum QueryHistoryAction {
|
||||
OpenQueryHistoryAction = 1,
|
||||
RunQueryHistoryAction = 2
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: interface-name
|
||||
export interface QueryHistoryQuickPickItem extends vscode.QuickPickItem {
|
||||
node: QueryHistoryNode;
|
||||
action: any;
|
||||
}
|
||||
|
||||
export class QueryHistoryUI {
|
||||
|
||||
constructor(
|
||||
private _prompter: IPrompter,
|
||||
private _vscodeWrapper: VscodeWrapper
|
||||
) {}
|
||||
|
||||
public convertToQuickPickItem(node: vscode.TreeItem): QueryHistoryQuickPickItem {
|
||||
let historyNode = node as QueryHistoryNode;
|
||||
let quickPickItem: QueryHistoryQuickPickItem = {
|
||||
label: Utils.limitStringSize(historyNode.queryString, true).trim(),
|
||||
detail: `${historyNode.connectionLabel}, ${historyNode.timeStamp.toLocaleString()}`,
|
||||
node: historyNode,
|
||||
action: undefined,
|
||||
picked: false
|
||||
};
|
||||
return quickPickItem;
|
||||
}
|
||||
|
||||
private showQueryHistoryActions(node: QueryHistoryNode): Promise<string | undefined> {
|
||||
let options = [{ label: LocalizedConstants.msgOpenQueryHistory },
|
||||
{ label: LocalizedConstants.msgRunQueryHistory }];
|
||||
let question: IQuestion = {
|
||||
type: QuestionTypes.expand,
|
||||
name: 'question',
|
||||
message: LocalizedConstants.msgChooseQueryHistoryAction,
|
||||
choices: options
|
||||
};
|
||||
return this._prompter.promptSingle(question).then((answer: vscode.QuickPickItem) => {
|
||||
if (answer) {
|
||||
return answer.label;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the Query History List on the command palette
|
||||
*/
|
||||
public showQueryHistoryCommandPalette(options: vscode.QuickPickItem[]): Promise<QueryHistoryQuickPickItem | undefined> {
|
||||
let question: IQuestion = {
|
||||
type: QuestionTypes.expand,
|
||||
name: 'question',
|
||||
message: LocalizedConstants.msgChooseQueryHistory,
|
||||
choices: options
|
||||
};
|
||||
return this._prompter.promptSingle(question).then((answer: QueryHistoryQuickPickItem) => {
|
||||
if (answer) {
|
||||
return this.showQueryHistoryActions(answer.node).then((actionAnswer: string) => {
|
||||
if (actionAnswer === LocalizedConstants.msgOpenQueryHistory) {
|
||||
answer.action = QueryHistoryAction.OpenQueryHistoryAction;
|
||||
} else if ( actionAnswer === LocalizedConstants.msgRunQueryHistory) {
|
||||
answer.action = QueryHistoryAction.RunQueryHistoryAction;
|
||||
}
|
||||
return answer;
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -72,6 +72,10 @@ suite('Query Runner tests', () => {
|
|||
// ... Mock up the view and VSCode wrapper to handle requests to update view
|
||||
testStatusView.setup(x => x.executingQuery(TypeMoq.It.isAnyString()));
|
||||
testVscodeWrapper.setup( x => x.logToOutputChannel(TypeMoq.It.isAnyString()));
|
||||
let testDoc: vscode.TextDocument = {
|
||||
getText() {}
|
||||
} as any;
|
||||
testVscodeWrapper.setup( x => x.openTextDocument(TypeMoq.It.isAny())).returns(() => Promise.resolve(testDoc));
|
||||
|
||||
// ... Mock up a event emitter to accept a start event (only)
|
||||
let mockEventEmitter = TypeMoq.Mock.ofType(EventEmitter, TypeMoq.MockBehavior.Loose);
|
||||
|
@ -122,6 +126,10 @@ suite('Query Runner tests', () => {
|
|||
// ... Setup the vs code wrapper to handle output logging and error messages
|
||||
testVscodeWrapper.setup(x => x.logToOutputChannel(TypeMoq.It.isAnyString()));
|
||||
testVscodeWrapper.setup(x => x.showErrorMessage(TypeMoq.It.isAnyString()));
|
||||
let testDoc: vscode.TextDocument = {
|
||||
getText() {}
|
||||
} as any;
|
||||
testVscodeWrapper.setup( x => x.openTextDocument(TypeMoq.It.isAny())).returns(() => Promise.resolve(testDoc));
|
||||
|
||||
// ... Setup the event emitter to handle nothing
|
||||
let testEventEmitter = TypeMoq.Mock.ofType(EventEmitter, TypeMoq.MockBehavior.Strict);
|
||||
|
@ -386,7 +394,7 @@ suite('Query Runner tests', () => {
|
|||
// Setup:
|
||||
// ... Create a mock for an event emitter that handles complete notifications
|
||||
let mockEventEmitter = TypeMoq.Mock.ofType(EventEmitter, TypeMoq.MockBehavior.Strict);
|
||||
mockEventEmitter.setup(x => x.emit('complete', TypeMoq.It.isAnyString()));
|
||||
mockEventEmitter.setup(x => x.emit('complete', TypeMoq.It.isAnyString(), TypeMoq.It.isAny()));
|
||||
|
||||
// ... Setup the VS Code view handlers
|
||||
testStatusView.setup(x => x.executedQuery(TypeMoq.It.isAny()));
|
||||
|
@ -426,7 +434,7 @@ suite('Query Runner tests', () => {
|
|||
testStatusView.verify(x => x.executedQuery(standardUri), TypeMoq.Times.once());
|
||||
|
||||
// ... The event emitter should have gotten a complete event
|
||||
mockEventEmitter.verify(x => x.emit('complete', TypeMoq.It.isAnyString()), TypeMoq.Times.once());
|
||||
mockEventEmitter.verify(x => x.emit('complete', TypeMoq.It.isAnyString(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
|
||||
// ... The state of the query runner has been updated
|
||||
assert.equal(queryRunner.batchSets.length, 1);
|
||||
|
|