Compare commits

...

22 Commits

Author SHA1 Message Date
Charles Gagnon
f0b6180e15 Fix (#7434)
* Fix query history icons

* Capitalize description text
2019-10-01 10:07:27 -07:00
Karl Burtram
cd4660abaa Bump Import extension to 0.11.0 for Oct 2019-09-30 12:43:12 -07:00
Karl Burtram
8320686170 Bump agent extension to 0.43.0 for Oct 2019-09-30 12:42:12 -07:00
Charles Gagnon
0a6779f96e Add toggle query history capture command/action (#7427)
* Add toggle query history capture command/action

* Add extension updates
2019-09-30 12:03:15 -07:00
Charles Gagnon
3e17618056 Add Clear All Query History command/action (#7408)
* Add clear all query history action/command

* Fix display issue when clearing

* Change localize ID and fix registration
2019-09-30 12:01:53 -07:00
Charles Gagnon
7d27d2f4f4 Fix endpoint text overflowing and add title tooltip (#7390)
* Fix endpoint text overflowing and add title tooltip

* Remove unneeded string interning
2019-09-27 12:43:53 -07:00
Chris LaFreniere
30ae023d73 Stop clearing out connecting and connected state when cancelling out of connection dialog (#7254)
* Stop clearing out connecting and connected state

* tweaks

* Handling cancel while connecting

* fix typo

* PR comments
2019-09-27 12:36:02 -07:00
Chris LaFreniere
c36596d3b3 Notebook Tokenization Fixes (#7375)
* Fix don't like; unclear if grammar necessssary too

* Cleanup and sanity check

* Cleanup and sanity check

* Add test

* Call onBeforeAttached for 3 types of editor models
2019-09-27 12:35:53 -07:00
Anthony Dresser
afbe004c4f add sql folding (#7270) 2019-09-27 12:35:39 -07:00
Chris LaFreniere
539f2b2b2e Add Default File Type when Saving Chart (#7235)
* add file filter

* Code cleanup
2019-09-27 12:35:28 -07:00
Charles Gagnon
43e360165f Add enable logs setting for Flat File Import (#7342)
* Add config for enabling Flat File Import logging

* Move logs to default log location for extensions

* Add localized strings
2019-09-27 12:35:20 -07:00
Amir Omidi
c2727264ad EoL chars (#7225) 2019-09-27 12:35:11 -07:00
Karl Burtram
3c17ac1333 Activate XML features when SQL loads (#7228) 2019-09-27 12:35:03 -07:00
Charles Gagnon
218dad1b17 Update whoIsActive extension to use azdata (#7287)
* Update whoIsActive extension to use azdata

* Change path

* Update package-lock
2019-09-27 12:34:54 -07:00
jamesrod817
2ff4b51fe6 Tempdb (#7022)
* Server changes by James

* tempdb
2019-09-27 12:34:34 -07:00
Amir Omidi
423ad40210 Better cell selection (#6914)
* Better cell selection

* Explicit return type and undefined assignment

* More complex copy/paste

* Get TS to be less mad at me

* Remove EoL

* Fail safe if statement

* strict null check
2019-09-27 12:34:15 -07:00
Charles Gagnon
788103b2d3 Update query history README (#7164)
* Update query history README

* Fix typos
2019-09-27 12:34:07 -07:00
Charles Gagnon
c4ce709dfb Query History feature (#6579)
* Initial commit

* Fix up QueryEventType

* Making query history visible in view and open query command (#6479)

* Add QueryInfo to query event events

* Pull actual query text/connection info for displaying

* cons and expand (#6489)

* Making query history visible in view and open query command

* expand and icons

* Failure icon enabled (#6491)

* Making query history visible in view and open query command

* expand and icons

* failure icon enabled

* Minor cleanup

* Open query with connection and add run query (#6496)

* Add initial query-history extension

* Fix issues caused by master merge, cleanup and add query-history extension (#6567)

* Open query with connection and add run query

* Fix issues caused by latest master merges, cleanup and add query-history extension

* Remove child nodes (#6568)

* Open query with connection and add run query

* Fix issues caused by latest master merges, cleanup and add query-history extension

* Remove child node expansion

* Layering movement and add delete action (#6574)

* Open query with connection and add run query

* Fix issues caused by latest master merges, cleanup and add query-history extension

* Remove child node expansion

* Some layering movement and add delete action

* Move query tracking into service (#6578)

* Open query with connection and add run query

* Fix issues caused by latest master merges, cleanup and add query-history extension

* Remove child node expansion

* Some layering movement and add delete action

* Move query history tracking into service

* Add comment

* Fix actions

* Remove unnecessary type

* cleanup

* Remove unused section of README

* Fix merge issues and address PR comments

* Fix compile and tslint errors

* Change startup function name
2019-09-27 12:33:57 -07:00
Karl Burtram
e875e4a271 Bump to SQL Tools 2.0.0.17 for Notebook Agent fixes 2019-09-27 12:32:18 -07:00
Aasim Khan
ba99be6ec1 Added opening latest notebook run to context menu from notebooks pane (#7066)
* added agent notebooks, notebook history view and view materialized notebook button

* Got a basic UI running for viewing notebook history

* made some changes to make UI look good

* Added new notebook dialog

* Added new notebook Dialog

* Added create notebook dialog

* Added edit and delete notebook job

* Added some notebook history features

* Added new notebook job icons, fixed a minor bug
in openmaterializednotebookAPI and added fixed the
schedule Picker API.

* Fixed Bugs in Notebook Grid expansion

* Fixed Notebook table highlighting and
grid generation is done using code.

* fixed some UI bugs

* Added changes to reflect sqltoolservice api

* Fixed some localize keys

* Made changes in the PR and added
ability to open Template Notebooks from
notebook history view.

* Added pin and renaming to notebook history

* made some library calls async

* fixed an import bug caused by merging from master

* Validation in NotebookJobDialog

* Added entry points for scheduling notebooks
on file explorer and notebook editor

* Handled no active connections and
a small bug in collapsing grid

* fix a bug in scheduling notebook from explorer
and toolbar

* setting up agent providers from connection now

* changed modals

* Reupload edited template

* Add dialog info, solved an edit bug and localized
UI strings.

* Bug fixes in UI, notebook renaming and
editing template on fly.

* fixed a bug that failed editing notebook jobs from notebook jobs table

* Fixed a cyclic dependency, made strings const and
some other changes in the PR

* Made some cyclic dependency and some fixes from PR

* made some changes mentioned in the PR

* Changed storage database health text

* Changed the sqltoolservice version to the point to the latest build.

* Added open Latest notebook notebook run to notebooks view context menu

* Fixed a small compilation error

* fixed a spelling mistake in function name

* made changes mentioned in the PR added open Notebook Functionality to charts

* Changed some context menues strings and order

* made some changes from the PR and fixed an API call

* made some changes mentioned in the PR

* Changed sqltoolsservice version to point to the latest build
2019-09-27 11:28:19 -07:00
Karl Burtram
146aa48c51 Update SQL Tools to 2.0.0-release.16 for IntelliSense fix 2019-09-25 12:56:43 -07:00
Karl Burtram
60f82785c2 Bump package.json for October release 2019-09-11 15:33:55 -07:00
117 changed files with 14974 additions and 7283 deletions

View File

@@ -65,6 +65,7 @@ const indentationFilter = [
// except multiple specific files
'!**/package.json',
'!**/package-lock.json', // {{SQL CARBON EDIT}}
'!**/yarn.lock',
'!**/yarn-error.log',
@@ -158,6 +159,7 @@ const copyrightFilter = [
'!extensions/notebook/src/prompts/**',
'!extensions/mssql/src/prompts/**',
'!extensions/notebook/resources/jupyter_config/**',
'!extensions/query-history/images/**',
'!**/*.gif',
'!**/*.xlf',
'!**/*.dacpac',

View File

@@ -199,7 +199,8 @@ const sqlBuiltInExtensions = [
'admin-pack',
'dacpac',
'schema-compare',
'cms'
'cms',
'query-history'
];
// make resource deployment and BDC extension only available in insiders
if (process.env['VSCODE_QUALITY'] === 'stable') {

View File

@@ -235,7 +235,8 @@ const sqlBuiltInExtensions = [
'admin-pack',
'dacpac',
'schema-compare',
'cms'
'cms',
'query-history'
];
// make resource deployment and BDC extension only available in insiders

View File

@@ -1,93 +1,93 @@
{
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.42.0",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
"icon": "images/sqlserver.png",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"engines": {
"vscode": "^1.25.0"
},
"activationEvents": [
"*"
],
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/azuredatastudio.git"
},
"extensionDependencies": [
"Microsoft.mssql"
],
"contributes": {
"outputChannels": [
"sqlagent"
],
"dashboard.tabs": [
{
"id": "data-management-agent",
"description": "Manage and troubleshoot SQL Agent jobs",
"provider": "MSSQL",
"title": "SQL Agent",
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
"container": {
"controlhost-container": {
"type": "agent"
}
}
}
],
"commands": [
{
"command": "agent.openNotebookDialog",
"title": "Schedule Notebook",
"icon": {
"dark": "resources/dark/open_notebook_inverse.svg",
"light": "resources/light/open_notebook.svg"
}
},
{
"command": "agent.reuploadTemplate",
"title": "Reupload Template",
"icon": {
"dark": "resources/dark/open_notebook_inverse.svg",
"light": "resources/light/open_notebook.svg"
}
}
],
"menus": {
"notebook/toolbar": [
{
"command": "agent.openNotebookDialog",
"when": "providerId == sql"
},
{
"command": "agent.reuploadTemplate",
"when": "agent:trackedTemplate"
}
],
"explorer/context": [
{
"command": "agent.openNotebookDialog",
"when": "resourceExtname == .ipynb"
}
]
}
},
"dependencies": {
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"@types/mocha": "^5.2.5",
"@types/node": "^8.10.25",
"mocha": "^5.2.0",
"should": "^13.2.1",
"typemoq": "^2.1.0",
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.43.0",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
"icon": "images/sqlserver.png",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"engines": {
"vscode": "^1.25.0"
},
"activationEvents": [
"*"
],
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/azuredatastudio.git"
},
"extensionDependencies": [
"Microsoft.mssql"
],
"contributes": {
"outputChannels": [
"sqlagent"
],
"dashboard.tabs": [
{
"id": "data-management-agent",
"description": "Manage and troubleshoot SQL Agent jobs",
"provider": "MSSQL",
"title": "SQL Agent",
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
"container": {
"controlhost-container": {
"type": "agent"
}
}
}
],
"commands": [
{
"command": "agent.openNotebookDialog",
"title": "Schedule Notebook",
"icon": {
"dark": "resources/dark/open_notebook_inverse.svg",
"light": "resources/light/open_notebook.svg"
}
},
{
"command": "agent.reuploadTemplate",
"title": "Update Template",
"icon": {
"dark": "resources/dark/open_notebook_inverse.svg",
"light": "resources/light/open_notebook.svg"
}
}
],
"menus": {
"notebook/toolbar": [
{
"command": "agent.openNotebookDialog",
"when": "providerId == sql && !agent:trackedTemplate"
},
{
"command": "agent.reuploadTemplate",
"when": "agent:trackedTemplate"
}
],
"explorer/context": [
{
"command": "agent.openNotebookDialog",
"when": "resourceExtname == .ipynb"
}
]
}
},
"dependencies": {
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"@types/mocha": "^5.2.5",
"@types/node": "^8.10.25",
"mocha": "^5.2.0",
"should": "^13.2.1",
"typemoq": "^2.1.0",
"vscode": "1.1.5"
}
}

View File

@@ -723,4 +723,4 @@ export class JobDialog extends AgentDialog<JobData> {
this.model.alerts = this.alerts;
this.model.categoryId = +this.model.jobCategoryIdsMap.find(cat => cat.name === this.model.category).id;
}
}
}

View File

@@ -66,8 +66,6 @@ export class NotebookDialog extends AgentDialog<NotebookData> {
private removeScheduleButton: azdata.ButtonComponent;
private descriptionTextBox: azdata.InputBoxComponent;
private isEdit: boolean = false;
// Job objects

View File

@@ -70,3 +70,8 @@ export class IconPathHelper {
};
}
}
export namespace cssStyles {
export const hyperlink = { 'user-select': 'text', 'color': '#0078d4', 'text-decoration': 'underline', 'cursor': 'pointer' };
export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' };
}

View File

@@ -9,7 +9,7 @@ import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { BdcDashboardModel } from './bdcDashboardModel';
import { IconPathHelper } from '../constants';
import { IconPathHelper, cssStyles } from '../constants';
import { getStateDisplayText, getHealthStatusDisplayText, getEndpointDisplayText, getHealthStatusIcon, getServiceNameDisplayText, Endpoint } from '../utils';
import { EndpointModel, ServiceStatusModel, BdcStatusModel } from '../controller/apiGenerated';
import { BdcDashboard } from './bdcDashboard';
@@ -250,13 +250,21 @@ function createServiceEndpointRow(modelBuilder: azdata.ModelBuilder, container:
endPointRow.addItem(nameCell, { CSSStyles: { 'width': serviceEndpointRowServiceNameCellWidth, 'min-width': serviceEndpointRowServiceNameCellWidth, 'user-select': 'text', 'text-align': 'center' } });
if (isHyperlink) {
const endpointCell = modelBuilder.hyperlink()
.withProperties({ label: endpoint.endpoint, url: endpoint.endpoint, CSSStyles: { 'height': '15px' } })
.withProperties<azdata.HyperlinkComponentProperties>({
label: endpoint.endpoint,
title: endpoint.endpoint,
url: endpoint.endpoint, CSSStyles: { 'height': '15px' }
})
.component();
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': serviceEndpointRowEndpointCellWidth, 'min-width': serviceEndpointRowEndpointCellWidth, 'color': '#0078d4', 'text-decoration': 'underline', 'overflow': 'hidden', 'padding-left': '10px' } });
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': serviceEndpointRowEndpointCellWidth, 'min-width': serviceEndpointRowEndpointCellWidth, 'overflow': 'hidden', 'text-overflow': 'ellipsis', ...cssStyles.hyperlink } });
}
else if (endpoint.name === Endpoint.sqlServerMaster) {
const endpointCell = modelBuilder.text()
.withProperties({ value: endpoint.endpoint, CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px', 'user-select': 'text', 'cursor': 'pointer', 'color': '#0078d4', 'text-decoration': 'underline' } })
.withProperties<azdata.TextComponentProperties>({
value: endpoint.endpoint,
title: endpoint.endpoint,
CSSStyles: { 'overflow': 'hidden', 'text-overflow': 'ellipsis', ...cssStyles.text, ...cssStyles.hyperlink }
})
.component();
endpointCell.onDidClick(async () => {
const connProfile = bdcModel.getSqlServerMasterConnectionProfile();
@@ -271,13 +279,17 @@ function createServiceEndpointRow(modelBuilder: azdata.ModelBuilder, container:
azdata.connection.openConnectionDialog(undefined, connProfile);
}
});
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': serviceEndpointRowEndpointCellWidth, 'min-width': serviceEndpointRowEndpointCellWidth, 'overflow': 'hidden', 'padding-left': '10px' } });
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': serviceEndpointRowEndpointCellWidth, 'min-width': serviceEndpointRowEndpointCellWidth } });
}
else {
const endpointCell = modelBuilder.text()
.withProperties({ value: endpoint.endpoint, CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px', 'user-select': 'text' } })
.withProperties<azdata.TextComponentProperties>({
value: endpoint.endpoint,
title: endpoint.endpoint,
CSSStyles: { 'overflow': 'hidden', 'text-overflow': 'ellipsis', ...cssStyles.text }
})
.component();
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': serviceEndpointRowEndpointCellWidth, 'min-width': serviceEndpointRowEndpointCellWidth, 'overflow': 'hidden', 'padding-left': '10px' } });
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': serviceEndpointRowEndpointCellWidth, 'min-width': serviceEndpointRowEndpointCellWidth } });
}
const copyValueCell = modelBuilder.button().component();
copyValueCell.iconPath = IconPathHelper.copy;

View File

@@ -2,7 +2,7 @@
"name": "import",
"displayName": "SQL Server Import",
"description": "SQL Server Import for Azure Data Studio supports importing CSV or JSON files into SQL Server.",
"version": "0.10.0",
"version": "0.11.0",
"publisher": "Microsoft",
"preview": true,
"engines": {
@@ -50,6 +50,17 @@
"group": "import"
}
]
},
"configuration":{
"type": "object",
"title": "%flatfileimport.configuration.title%",
"properties": {
"flatFileImport.logDebugInfo": {
"type": "boolean",
"default": false,
"description": "%flatfileimport.logDebugInfo%"
}
}
}
},
"dependencies": {

View File

@@ -0,0 +1,4 @@
{
"flatfileimport.configuration.title": "Flat File Import configuration",
"flatfileimport.logDebugInfo": "[Optional] Log debug output to the console (View -> Output) and then select appropriate output channel from the dropdown"
}

View File

@@ -44,7 +44,7 @@ export class ServiceClient {
return new Promise((resolve, reject) => {
serverdownloader.getOrDownloadServer().then(e => {
const installationComplete = Date.now();
let serverOptions = this.generateServerOptions(e);
let serverOptions = this.generateServerOptions(e, context);
client = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
const processStart = Date.now();
client.onReady().then(() => {
@@ -90,10 +90,10 @@ export class ServiceClient {
};
}
private generateServerOptions(executablePath: string): ServerOptions {
private generateServerOptions(executablePath: string, context: vscode.ExtensionContext): ServerOptions {
let launchArgs = [];
launchArgs.push('--log-dir');
let logFileLocation = path.join(serviceUtils.getDefaultLogLocation(), 'flatfileimport');
let logFileLocation = context.logPath;
launchArgs.push(logFileLocation);
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
if (config) {

View File

@@ -20,10 +20,6 @@ export function getAppDataPath(): string {
}
}
export function getDefaultLogLocation(): string {
return path.join(getAppDataPath(), 'azuredatastudio');
}
export function ensure(target: object, key: string): any {
if (target[key] === void 0) {
target[key] = {} as any;

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "2.0.0-release.10",
"version": "2.0.0-release.17",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.2.zip",
"Windows_64": "win-x64-netcoreapp2.2.zip",

View File

@@ -298,7 +298,7 @@ export namespace UpdateAgentNotebookRunNameRequest {
}
export namespace DeleteMaterializedNotebookRequest {
export const type = new RequestType<DeleteAgentMaterializedNotebookParams, azdata.ResultStatus, void, void>('agent/deletenotebookmaterialized');
export const type = new RequestType<DeleteAgentMaterializedNotebookParams, azdata.ResultStatus, void, void>('agent/deletematerializednotebook');
}
export namespace UpdateAgentNotebookRunPinRequest {

View File

@@ -60,16 +60,28 @@ export function registerServiceEndpoints(context: vscode.ExtensionContext): void
const container = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component();
endpointsArray.forEach(endpointInfo => {
const endPointRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
const nameCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: endpointInfo.description }).component();
endPointRow.addItem(nameCell, { CSSStyles: { 'width': '35%', 'font-weight': '600', 'user-select': 'text' } });
if (hyperlinkedEndpoints.findIndex(e => e === endpointInfo.serviceName) >= 0) {
const linkCell = view.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({ label: endpointInfo.endpoint, url: endpointInfo.endpoint }).component();
endPointRow.addItem(linkCell, { CSSStyles: { 'width': '62%', 'color': '#0078d4', 'text-decoration': 'underline', 'padding-top': '10px' } });
const linkCell = view.modelBuilder.hyperlink()
.withProperties<azdata.HyperlinkComponentProperties>({
label: endpointInfo.endpoint,
title: endpointInfo.endpoint,
url: endpointInfo.endpoint
}).component();
endPointRow.addItem(linkCell, { CSSStyles: { 'width': '62%', 'color': '#0078d4', 'text-decoration': 'underline', 'padding-top': '10px', 'overflow': 'hidden', 'text-overflow': 'ellipsis' } });
}
else {
const endpointCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: endpointInfo.endpoint }).component();
const endpointCell =
view.modelBuilder.text()
.withProperties<azdata.TextComponentProperties>(
{
value: endpointInfo.endpoint,
title: endpointInfo.endpoint,
CSSStyles: { 'overflow': 'hidden', 'text-overflow': 'ellipsis' }
})
.component();
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': '62%', 'user-select': 'text' } });
}
const copyValueCell = view.modelBuilder.button().component();

View File

@@ -153,6 +153,13 @@
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "notebook",
"scopeName": "source.notebook",
"path": "./syntaxes/notebook.tmLanguage.json"
}
],
"menus": {
"commandPalette": [
{

View File

@@ -0,0 +1,213 @@
{
"information_for_contributors": [
"This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage",
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70",
"name": "notebook",
"scopeName": "source.notebook",
"patterns": [
{
"include": "#value"
}
],
"repository": {
"array": {
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.definition.array.begin.notebook"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.definition.array.end.notebook"
}
},
"name": "meta.structure.array.notebook",
"patterns": [
{
"include": "#value"
},
{
"match": ",",
"name": "punctuation.separator.array.notebook"
},
{
"match": "[^\\s\\]]",
"name": "invalid.illegal.expected-array-separator.notebook"
}
]
},
"comments": {
"patterns": [
{
"begin": "/\\*\\*(?!/)",
"captures": {
"0": {
"name": "punctuation.definition.comment.notebook"
}
},
"end": "\\*/",
"name": "comment.block.documentation.notebook"
},
{
"begin": "/\\*",
"captures": {
"0": {
"name": "punctuation.definition.comment.notebook"
}
},
"end": "\\*/",
"name": "comment.block.notebook"
},
{
"captures": {
"1": {
"name": "punctuation.definition.comment.notebook"
}
},
"match": "(//).*$\\n?",
"name": "comment.line.double-slash.js"
}
]
},
"constant": {
"match": "\\b(?:true|false|null)\\b",
"name": "constant.language.notebook"
},
"number": {
"match": "(?x) # turn on extended mode\n -? # an optional minus\n (?:\n 0 # a zero\n | # ...or...\n [1-9] # a 1-9 character\n \\d* # followed by zero or more digits\n )\n (?:\n (?:\n \\. # a period\n \\d+ # followed by one or more digits\n )?\n (?:\n [eE] # an e character\n [+-]? # followed by an option +/-\n \\d+ # followed by one or more digits\n )? # make exponent optional\n )? # make decimal portion optional",
"name": "constant.numeric.notebook"
},
"object": {
"begin": "\\{",
"beginCaptures": {
"0": {
"name": "punctuation.definition.dictionary.begin.notebook"
}
},
"end": "\\}",
"endCaptures": {
"0": {
"name": "punctuation.definition.dictionary.end.notebook"
}
},
"name": "meta.structure.dictionary.notebook",
"patterns": [
{
"comment": "the notebook object key",
"include": "#objectkey"
},
{
"include": "#comments"
},
{
"begin": ":",
"beginCaptures": {
"0": {
"name": "punctuation.separator.dictionary.key-value.notebook"
}
},
"end": "(,)|(?=\\})",
"endCaptures": {
"1": {
"name": "punctuation.separator.dictionary.pair.notebook"
}
},
"name": "meta.structure.dictionary.value.notebook",
"patterns": [
{
"comment": "the notebook object value",
"include": "#value"
},
{
"match": "[^\\s,]",
"name": "invalid.illegal.expected-dictionary-separator.notebook"
}
]
},
{
"match": "[^\\s\\}]",
"name": "invalid.illegal.expected-dictionary-separator.notebook"
}
]
},
"string": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.notebook"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.notebook"
}
},
"name": "string.quoted.double.notebook",
"patterns": [
{
"include": "#stringcontent"
}
]
},
"objectkey": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.support.type.property-name.begin.notebook"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.support.type.property-name.end.notebook"
}
},
"name": "string.notebook support.type.property-name.notebook",
"patterns": [
{
"include": "#stringcontent"
}
]
},
"stringcontent": {
"patterns": [
{
"match": "(?x) # turn on extended mode\n \\\\ # a literal backslash\n (?: # ...followed by...\n [\"\\\\/bfnrt] # one of these characters\n | # ...or...\n u # a u\n [0-9a-fA-F]{4}) # and four hex digits",
"name": "constant.character.escape.notebook"
},
{
"match": "\\\\.",
"name": "invalid.illegal.unrecognized-string-escape.notebook"
}
]
},
"value": {
"patterns": [
{
"include": "#constant"
},
{
"include": "#number"
},
{
"include": "#string"
},
{
"include": "#array"
},
{
"include": "#object"
},
{
"include": "#comments"
}
]
}
}
}

1
extensions/query-history/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.vsix

View File

@@ -0,0 +1,5 @@
src/**
.gitignore
tsconfig.json
cgmanifest.json
.vscode

View File

@@ -0,0 +1,50 @@
# Query History *(preview)*
Adds a Query History panel for viewing and running past executed queries.
### How do I view the history?
Query History is displayed as a tab in the tab panel, which is toggled by the *View: Toggle Panel* command.
![Query History tab](https://github.com/microsoft/azuredatastudio/tree/master/extensions/query-history/images/QueryHistoryTab.png)
Initially this view will be empty but once you execute a query editor that will be captured in the window - with a separate row displayed for every query executed.
![Query History tab with queries](https://github.com/microsoft/azuredatastudio/tree/master/extensions/query-history/images/QueryHistoryTabWithQueries.png)
Each row consists of 3 parts :
1. Status icon : This will be a ✔️ if the query executed successfully. If any errors occurred a ❌is shown.
2. Query Text : This is the text of the query that was executed
3. Connection Info : The Server and Database the query was executed against
4. Timestamp : The date and time the query was executed
### Query History row actions
Right clicking on a history row will bring up a menu with a number of actions available.
![Query History action menu](https://github.com/microsoft/azuredatastudio/tree/master/extensions/query-history/images/QueryHistoryActionMenu.png)
#### Open Query
This will open a new query editor window populated with the query text from the query executed and using the connection of that query.
#### Run Query
This will do the same thing as Open Query but will additionally run the statement immediately.
#### Delete
This will permanently delete the row from the view.
## 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.
## Privacy Statement
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt).

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,54 @@
{
"name": "query-history",
"displayName": "%queryHistory.displayName%",
"description": "%queryHistory.description%",
"version": "0.1.0",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
"icon": "images/sqlserver.png",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"engines": {
"vscode": "^1.30.1",
"azdata": ">=1.12.0"
},
"activationEvents": [
"*"
],
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/azuredatastudio.git"
},
"extensionDependencies": [
],
"contributes": {
"commands": [
{
"command": "queryHistory.clear",
"title": "%queryHistory.clear%",
"category": "%queryHistory.displayName%"
},
{
"command": "queryHistory.toggleCapture",
"title": "%queryHistory.toggleCapture%",
"category": "%queryHistory.displayName%"
}
],
"menus": {
"commandPalette": [
{
"command": "queryHistory.clear"
},
{
"command": "queryHistory.toggleCapture"
}
]
}
},
"dependencies": {
},
"devDependencies": {
"vscode": "1.0.1"
}
}

View File

@@ -0,0 +1,6 @@
{
"queryHistory.displayName": "Query History",
"queryHistory.description": "View and run previously executed queries",
"queryHistory.clear": "Clear All History",
"queryHistory.toggleCapture": "Toggle Query History Capture"
}

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* 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';
export async function activate(context: vscode.ExtensionContext): Promise<void> {
// Currently all the functionality for this is contained within the core ADS
// code as the extensibility API doesn't currently support all the required
// functionality (such as contributing tab panels)
vscode.commands.executeCommand('queryHistory.enableQueryHistory');
}
export async function deactivate(): Promise<void> {
}

View File

@@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>

View File

@@ -0,0 +1,21 @@
{
"compileOnSave": true,
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "./out",
"lib": [
"es6", "es2015.promise"
],
"typeRoots": [
"./node_modules/@types"
],
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"moduleResolution": "node"
},
"exclude": [
"node_modules"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,13 @@
["\"", "\""],
["'", "'"],
["`", "`"]
]
],
"folding": {
"markers": {
"start": "^\\s*--\\s*#region\\s*.*$",
"end": "^\\s*--\\s*#endregion\\s*.*$"
}
}
// enhancedBrackets:[
// { openTrigger: 'n', open: /begin$/i, closeComplete: 'end', matchCase: true },
@@ -31,4 +37,4 @@
// { openTrigger: 'n', open: /when$/i, closeComplete: 'then', matchCase: true }
// ],
// noindentBrackets: '()',
}
}

View File

@@ -12,6 +12,7 @@
"Programming Languages"
],
"activationEvents": [
"onLanguage:sql",
"onLanguage:xml"
],
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "azuredatastudio",
"version": "1.11.0",
"version": "1.12.0",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"

File diff suppressed because it is too large Load Diff

View File

@@ -1,236 +1,404 @@
{
"name": "server-report",
"displayName": "Server Reports",
"description": "Server Reports",
"version": "0.1.5",
"publisher": "Microsoft",
"preview": true,
"engines": {
"vscode": "^1.26.0",
"azdata": "*"
},
"icon": "images/sqlserver.png",
"license": "SEE LICENSE IN LICENSE.txt",
"repository": "https://github.com/Microsoft/azuredatastudio",
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/src/extension",
"contributes": {
"configuration": [],
"commands": [],
"views": {},
"menus": {},
"dashboard.tabs": [
{
"id": "Server-Reports",
"title": "Server Reports",
"description": "This extension shows useful reports for a server.",
"container": {
"nav-section": [
{
"id": "server-reports-monitoring",
"title": "Monitor",
"icon": {
"light": "./out/src/media/monitor.svg",
"dark": "./out/src/media/monitor_inverse.svg"
},
"container": {
"server-reports-monitoring-container": {}
}
},
{
"id": "server-reports-performance",
"title": "Performance",
"icon": {
"light": "./out/src/media/performance.svg",
"dark": "./out/src/media/performance_inverse.svg"
},
"container": {
"server-reports-performance-container": {}
}
}
]
}
}
],
"dashboard.insights": [
{
"id": "extension-dbspace-usage",
"contrib": {
"type": {
"horizontalBar": {
"dataDirection": "vertical",
"dataType": "number",
"legendPosition": "top",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/all_db_space_used.sql"
}
},
{
"id": "extension-cpu-utilization",
"contrib": {
"type": {
"timeSeries": {
"dataDirection": "horizontal",
"dataType": "point",
"legendPosition": "top",
"labelFirstColumn": false,
"columnsAsLabels": false
}
},
"queryFile": "./out/src/sql/cpumetric.sql"
}
},
{
"id": "extension-backup-growth-trend",
"details": "Abbie wants it",
"contrib": {
"type": {
"timeSeries": {
"dataDirection": "horizontal",
"dataType": "point",
"legendPosition": "none",
"labelFirstColumn": false,
"columnsAsLabels": false
}
},
"queryFile": "./out/src/sql/backup_size_trend.sql"
}
},
{
"id": "extension-wait-counts-by-Paul-Randal",
"contrib": {
"type": {
"horizontalBar": {
"dataDirection": "vertical",
"dataType": "number",
"legendPosition": "none",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/waits_paul_randal.sql",
"details": {
"queryFile": "./out/src/sql/waits_detail_paul_randal.sql",
"label": {
"column": "WaitType",
"state": []
},
"value": "Percentage"
}
}
},
{
"id": "extension-dbbuffer-usage",
"contrib": {
"type": {
"horizontalBar": {
"dataDirection": "vertical",
"dataType": "number",
"legendPosition": "top",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/memorybydb.sql"
}
}
],
"dashboard.containers": [
{
"id": "server-reports-monitoring-container",
"container": {
"widgets-container": [
{
"name": "Top 10 DB Space Usage",
"gridItemConfig": {
"sizex": 2,
"sizey": 2
},
"widget": {
"extension-dbspace-usage": {}
}
},
{
"name": "Top 10 DB Buffer Usage",
"gridItemConfig": {
"sizex": 2,
"sizey": 2
},
"widget": {
"extension-dbbuffer-usage": {}
}
},
{
"name": "CPU Utilization",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"extension-cpu-utilization": {}
}
},
{
"name": "Backup Growth Trend",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"extension-backup-growth-trend": {}
}
}
]
}
},
{
"id": "server-reports-performance-container",
"container": {
"widgets-container": [
{
"name": "Wait Counts by Paul Randal",
"gridItemConfig": {
"sizex": 2,
"sizey": 2
},
"widget": {
"extension-wait-counts-by-Paul-Randal": {}
}
}
]
}
}
],
"snippets": []
},
"scripts": {
"build": "gulp build",
"compile": "gulp compile",
"watch": "gulp watch",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"dependencies": {},
"devDependencies": {
"child-process-promise": "^2.2.1",
"del": "^3.0.0",
"gulp": "^4.0.0",
"gulp-color": "0.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-tslint": "^6.0.2",
"gulp-typescript": "^3.2.4",
"should": "^13.2.1",
"tslint": "^3.14.0",
"typemoq": "^2.1.0",
"typescript": "^2.6.1",
"vsce": "1.36.2",
"vscode": "^1.1.6"
}
}
"name": "server-report",
"displayName": "Server Reports",
"description": "Server Reports",
"version": "0.1.5",
"publisher": "Microsoft",
"preview": true,
"engines": {
"vscode": "^1.26.0",
"azdata": "*"
},
"icon": "images/sqlserver.png",
"license": "SEE LICENSE IN LICENSE.txt",
"repository": "https://github.com/Microsoft/azuredatastudio",
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/src/extension",
"contributes": {
"commands": [
{
"command": "tempdb.startEvent",
"title": "Start",
"icon": {
"light": "./out/src/media/launch.svg",
"dark": "./out/src/media/launch_inverse.svg"
}
},
{
"command": "tempdb.stopEvent",
"title": "Stop",
"icon": {
"light": "./out/src/media/blocker.svg",
"dark": "./out/src/media/blocker_inverse.svg"
}
},
{
"command": "tempdb.contention",
"title": "Contention Help for Tempdb",
"icon": {
"light": "./out/src/media/documentation.svg",
"dark": "./out/src/media/documentation_inverse.svg"
}
},
{
"command": "tempdb.pauseEvent",
"title": "Pause",
"icon": {
"light": "./out/src/media/insights.svg",
"dark": "./out/src/media/insights_inverse.svg"
}
}
],
"configuration": [],
"views": {},
"menus": {},
"dashboard.tabs": [
{
"id": "Server-Reports",
"title": "Server Reports",
"description": "This extension shows useful reports for a server.",
"container": {
"nav-section": [
{
"id": "server-reports-monitoring",
"title": "Monitor",
"icon": {
"light": "./out/src/media/monitor.svg",
"dark": "./out/src/media/monitor_inverse.svg"
},
"container": {
"server-reports-monitoring-container": {}
}
},
{
"id": "server-reports-performance",
"title": "Performance",
"icon": {
"light": "./out/src/media/performance.svg",
"dark": "./out/src/media/performance_inverse.svg"
},
"container": {
"server-reports-performance-container": {}
}
},
{
"id": "server-reports-tempdb",
"title": "TempDB",
"container": {
"server-reports-tempdb-container": {}
},
"icon": {
"light": "./out/src/media/tempdb.svg",
"dark": "./out/src/media/tempdb_inverse.svg"
}
}
]
}
}
],
"dashboard.insights": [
{
"id": "extension-dbspace-usage",
"contrib": {
"type": {
"horizontalBar": {
"dataDirection": "vertical",
"dataType": "number",
"legendPosition": "top",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/all_db_space_used.sql"
}
},
{
"id": "extension-cpu-utilization",
"contrib": {
"type": {
"timeSeries": {
"dataDirection": "horizontal",
"dataType": "point",
"legendPosition": "top",
"labelFirstColumn": false,
"columnsAsLabels": false
}
},
"queryFile": "./out/src/sql/cpumetric.sql"
}
},
{
"id": "extension-backup-growth-trend",
"details": "Abbie wants it",
"contrib": {
"type": {
"timeSeries": {
"dataDirection": "horizontal",
"dataType": "point",
"legendPosition": "none",
"labelFirstColumn": false,
"columnsAsLabels": false
}
},
"queryFile": "./out/src/sql/backup_size_trend.sql"
}
},
{
"id": "extension-wait-counts-by-Paul-Randal",
"contrib": {
"type": {
"horizontalBar": {
"dataDirection": "vertical",
"dataType": "number",
"legendPosition": "none",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/waits_paul_randal.sql",
"details": {
"queryFile": "./out/src/sql/waits_detail_paul_randal.sql",
"label": {
"column": "WaitType",
"state": []
},
"value": "Percentage"
}
}
},
{
"id": "extension-wait-resource-statistics",
"contrib": {
"type": {
"bar": {
"dataDirection": "horizontal",
"dataType": "number",
"legendPosition": "none",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/wait_resources.sql",
"details": {
"queryFile": "./out/src/sql/wait_resources.sql",
"label": {
"column": "WaitType",
"state": []
},
"value": "Percentage"
}
}
},
{
"id": "extension-dbbuffer-usage",
"contrib": {
"type": {
"horizontalBar": {
"dataDirection": "vertical",
"dataType": "number",
"legendPosition": "top",
"labelFirstColumn": false,
"columnsAsLabels": true
}
},
"queryFile": "./out/src/sql/memorybydb.sql"
}
},
{
"id": "type-of-contention",
"contrib": {
"type": {
"bar": {
"dataDirection": "vertical",
"columnsAsLabels": true,
"labelFirstColumn": false,
"legendPosition": "none"
}
},
"queryFile": "./out/src/sql/typeofContentions.sql",
"autoRefreshInterval": 0.05
}
},
{
"id": "metadata-contention",
"contrib": {
"type": {
"bar": {
"dataDirection": "vertical",
"columnsAsLabels": true,
"labelFirstColumn": false,
"legendPosition": "none",
"xAxisLabel": "Object Ids for System Tables"
}
},
"queryFile": "./out/src/sql/metadataContention.sql",
"autoRefreshInterval": 0.05
}
},
{
"id": "allocation-contention",
"contrib": {
"type": {
"bar": {
"dataDirection": "vertical",
"columnsAsLabels": true,
"labelFirstColumn": false,
"legendPosition": "none",
"xAxisLabel": "Page Types"
}
},
"queryFile": "./out/src/sql/allocationContention.sql",
"autoRefreshInterval": 0.05
}
}
],
"dashboard.containers": [
{
"id": "server-reports-monitoring-container",
"container": {
"widgets-container": [
{
"name": "Top 10 DB Space Usage",
"gridItemConfig": {
"sizex": 2,
"sizey": 2
},
"widget": {
"extension-dbspace-usage": {}
}
},
{
"name": "Top 10 DB Buffer Usage",
"gridItemConfig": {
"sizex": 2,
"sizey": 2
},
"widget": {
"extension-dbbuffer-usage": {}
}
},
{
"name": "CPU Utilization",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"extension-cpu-utilization": {}
}
},
{
"name": "Backup Growth Trend",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"extension-backup-growth-trend": {}
}
}
]
}
},
{
"id": "server-reports-performance-container",
"container": {
"widgets-container": [
{
"name": "Wait Counts by Paul Randal",
"gridItemConfig": {
"sizex": 2,
"sizey": 2
},
"widget": {
"extension-wait-counts-by-Paul-Randal": {}
}
}
]
}
},
{
"id": "server-reports-tempdb-container",
"container": {
"widgets-container": [
{
"name": "Tasks",
"widget": {
"tasks-widget": [
"tempdb.startEvent",
"tempdb.contention",
"tempdb.pauseEvent",
"tempdb.stopEvent"
]
}
},
{
"name": "Overall Contention",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"type-of-contention": {}
}
},
{
"name": "Metadata Contention",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"metadata-contention": {}
}
},
{
"name": "Allocation Contention",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"widget": {
"allocation-contention": {}
}
}
]
}
}
],
"snippets": []
},
"scripts": {
"build": "gulp build",
"compile": "gulp compile",
"watch": "gulp watch",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"dependencies": {
"fs": "0.0.1-security",
"fs-extra": "^8.1.0",
"generator-sqlops": "^0.10.4",
"handlebars": "^4.1.2",
"openurl": "^1.1.1"
},
"devDependencies": {
"child-process-promise": "^2.2.1",
"del": "^3.0.0",
"gulp": "^4.0.0",
"gulp-color": "0.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-tslint": "^6.0.2",
"gulp-typescript": "^3.2.4",
"should": "^13.2.1",
"tslint": "^3.14.0",
"typemoq": "^2.1.0",
"typescript": "^2.9.2",
"vsce": "1.36.2",
"vscode": "^1.1.6"
}
}

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
export default abstract class ControllerBase implements vscode.Disposable {
protected _context: vscode.ExtensionContext;
public constructor(context: vscode.ExtensionContext) {
this._context = context;
}
abstract activate(): Promise<boolean>;
abstract deactivate(): void;
public dispose(): void {
this.deactivate();
}
}

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as azdata from 'azdata';
import * as Utils from '../utils';
import ControllerBase from './controllerBase';
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import * as openurl from 'openurl';
/**
* The main controller class that initializes the extension
*/
export default class MainController extends ControllerBase {
public apiWrapper;
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Deactivates the extension
*/
public deactivate(): void {
Utils.logDebug('Main controller deactivated');
}
public activate(): Promise<boolean> {
azdata.tasks.registerTask("tempdb.startEvent", e => this.onExecute(e, 'startEvent.sql'));
azdata.tasks.registerTask("tempdb.stopEvent", e => this.onExecute(e, 'stopEvent.sql'));
azdata.tasks.registerTask("tempdb.contention", () => this.openurl('https://aka.ms/tempdbblog'));
azdata.tasks.registerTask("tempdb.pauseEvent", e => this.stopAutoRefresh(e));
return Promise.resolve(true);
}
private openurl(link: string): void {
openurl.open(link);
}
private onExecute(connection: azdata.IConnectionProfile, fileName: string): void {
//Command to start/stop autorefresh and run the query
vscode.commands.executeCommand('azdata.widget.setAutoRefreshState', 'type-of-contention', connection.id, true);
vscode.commands.executeCommand('azdata.widget.setAutoRefreshState', 'metadata-contention', connection.id, true);
vscode.commands.executeCommand('azdata.widget.setAutoRefreshState', 'allocation-contention', connection.id, true);
let sqlContent = fs.readFileSync(path.join(__dirname, '..', 'sql', fileName)).toString();
vscode.workspace.openTextDocument({ language: 'sql', content: sqlContent }).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.Active, false).then(() => {
let filePath = doc.uri.toString();
azdata.queryeditor.connect(filePath, connection.id).then(() => azdata.queryeditor.runQuery(filePath, undefined, false));
});
});
}
private stopAutoRefresh(connection: azdata.IConnectionProfile) {
vscode.commands.executeCommand('azdata.widget.setAutoRefreshState', 'type-of-contention', connection.id, false);
vscode.commands.executeCommand('azdata.widget.setAutoRefreshState', 'metadata-contention', connection.id, false);
vscode.commands.executeCommand('azdata.widget.setAutoRefreshState', 'allocation-contention', connection.id, false);
}
}

View File

@@ -7,10 +7,33 @@
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext): void {
// No-op
import ControllerBase from './controllers/controllerBase';
import MainController from './controllers/mainController';
let controllers: ControllerBase[] = [];
export function activate(context: vscode.ExtensionContext): Promise<boolean> {
let activations: Promise<boolean>[] = [];
// Start the main controller
let mainController = new MainController(context);
controllers.push(mainController);
context.subscriptions.push(mainController);
activations.push(mainController.activate());
return Promise.all(activations)
.then((results: boolean[]) => {
for (let result of results) {
if (!result) {
return false;
}
}
return true;
});
}
export function deactivate(): void {
// No-op
for (let controller of controllers) {
controller.deactivate();
}
}

View File

@@ -4,4 +4,4 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
// TODO: localize!
export const msgErrorLoadingTab = 'An error occurred while loading the tab.';

View File

@@ -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:#212121;}</style></defs><title>blocker</title><polygon class="cls-1" points="0.99 3.99 -0.01 3.99 -0.01 0.03 3.98 0.03 3.98 1.03 0.99 1.03 0.99 3.99"/><polygon class="cls-1" points="16.01 3.99 15.01 3.99 15.01 1.03 12.02 1.03 12.02 0.03 16.01 0.03 16.01 3.99"/><polygon class="cls-1" points="16.01 15.97 12.02 15.97 12.02 14.97 15.01 14.97 15.01 12.01 16.01 12.01 16.01 15.97"/><polygon class="cls-1" points="4 15.97 0.01 15.97 0.01 12.01 1.01 12.01 1.01 14.97 4 14.97 4 15.97"/><path class="cls-1" d="M8.41,3.18A4.82,4.82,0,1,0,13.23,8,4.83,4.83,0,0,0,8.41,3.18Zm0,.74A4.08,4.08,0,0,1,12.49,8a4,4,0,0,1-.85,2.47L5.69,5A4,4,0,0,1,8.41,3.93Zm0,8.15A4.08,4.08,0,0,1,4.34,8a4,4,0,0,1,.85-2.47L11.14,11A4,4,0,0,1,8.41,12.07Z"/></svg>

After

Width:  |  Height:  |  Size: 847 B

View File

@@ -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:#fff;}</style></defs><title>blocker_inverse</title><polygon class="cls-1" points="0.99 3.99 -0.01 3.99 -0.01 0.03 3.98 0.03 3.98 1.03 0.99 1.03 0.99 3.99"/><polygon class="cls-1" points="16.01 3.99 15.01 3.99 15.01 1.03 12.02 1.03 12.02 0.03 16.01 0.03 16.01 3.99"/><polygon class="cls-1" points="16.01 15.97 12.02 15.97 12.02 14.97 15.01 14.97 15.01 12.01 16.01 12.01 16.01 15.97"/><polygon class="cls-1" points="4 15.97 0.01 15.97 0.01 12.01 1.01 12.01 1.01 14.97 4 14.97 4 15.97"/><path class="cls-1" d="M8.41,3.18A4.82,4.82,0,1,0,13.23,8,4.83,4.83,0,0,0,8.41,3.18Zm0,.74A4.08,4.08,0,0,1,12.49,8a4,4,0,0,1-.85,2.47L5.69,5A4,4,0,0,1,8.41,3.93Zm0,8.15A4.08,4.08,0,0,1,4.34,8a4,4,0,0,1,.85-2.47L11.14,11A4,4,0,0,1,8.41,12.07Z"/></svg>

After

Width:  |  Height:  |  Size: 852 B

View File

@@ -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:#212121;}</style></defs><title>documentation</title><path class="cls-1" d="M14,4.29V16H1V0H9.72ZM13,5H9V1H2V15H13ZM3,4H8V5H3ZM3,8H8V9H3Zm0,4H8v1H3ZM9,7h3v3H9Zm0,4h3v3H9Zm1-7H12.3L10,1.71Zm0,5h1V8H10Zm0,4h1V12H10Z"/></svg>

After

Width:  |  Height:  |  Size: 339 B

View File

@@ -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:#fff;}</style></defs><title>documentation_1</title><path class="cls-1" d="M14,4.29V16H1V0H9.72ZM13,5H9V1H2V15H13ZM3,4H8V5H3ZM3,8H8V9H3Zm0,4H8v1H3ZM9,7h3v3H9Zm0,4h3v3H9Zm1-7H12.3L10,1.71Zm0,5h1V8H10Zm0,4h1V12H10Z"/></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@@ -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:#212121;}</style></defs><title>insights</title><path class="cls-1" d="M15,4V8H14V5.71L9.49,10.2l-2-2L2,13.71V14H15v1H1V1H2V12.29L7.49,6.8l2,2L13.28,5H11V4Z"/></svg>

After

Width:  |  Height:  |  Size: 282 B

View File

@@ -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:#fff;}</style></defs><title>insights_inverse</title><path class="cls-1" d="M15,4V8H14V5.71L9.49,10.2l-2-2L2,13.71V14H15v1H1V1H2V12.29L7.49,6.8l2,2L13.28,5H11V4Z"/></svg>

After

Width:  |  Height:  |  Size: 287 B

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>launch</title><path d="M12.8,8.4l1.07-1.07v6.4H0V4.13H10.67L9.6,5.2H1.07v7.47H12.8Zm0-6.4H16V5.2H14.93V3.82l-3.8,3.8-.76-.76,3.8-3.8H12.8Z"/></svg>

After

Width:  |  Height:  |  Size: 247 B

View File

@@ -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:#fff;}</style></defs><title>launch_inverse</title><path class="cls-1" d="M12.8,8.4l1.07-1.07v6.4H0V4.13H10.67L9.6,5.2H1.07v7.47H12.8Zm0-6.4H16V5.2H14.93V3.82l-3.8,3.8-.76-.76,3.8-3.8H12.8Z"/></svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<title>TemporaryDatabase</title>
<g>
<path d="M3.086,3.758a1.565,1.565,0,0,0,.492.445,3.788,3.788,0,0,0,.719.336,6.428,6.428,0,0,0,.848.234q.449.095.878.153t.817.082c.258.015.477.023.66.023s.4-.008.66-.023.53-.043.817-.082.579-.09.878-.153a6.428,6.428,0,0,0,.848-.234,3.842,3.842,0,0,0,.715-.332,1.6,1.6,0,0,0,.5-.449.555.555,0,0,0,.063-.11A.3.3,0,0,0,12,3.531a.462.462,0,0,0-.133-.312,1.7,1.7,0,0,0-.32-.274,3.616,3.616,0,0,0-.383-.222c-.13-.065-.234-.114-.312-.145a6.1,6.1,0,0,0-.79-.254q-.421-.1-.859-.172t-.871-.093c-.289-.018-.566-.028-.832-.028s-.543.01-.832.028-.58.049-.871.093-.578.1-.859.172a6.047,6.047,0,0,0-.79.254c-.078.031-.182.08-.312.145a3.616,3.616,0,0,0-.383.222,1.673,1.673,0,0,0-.32.274A.462.462,0,0,0,3,3.531a.3.3,0,0,0,.023.117A.555.555,0,0,0,3.086,3.758Z" fill="none"/>
<path d="M7.435,14.03c-.282,0-.6-.017-.951-.046a10.214,10.214,0,0,1-1.148-.16,7.424,7.424,0,0,1-1.106-.3,3.184,3.184,0,0,1-.894-.481,1.519,1.519,0,0,1-.223-.219A.436.436,0,0,1,3,12.531V5.016A5.236,5.236,0,0,0,4.023,5.5a8.163,8.163,0,0,0,1.149.312,10.5,10.5,0,0,0,1.191.168c.4.035.778.051,1.137.051s.738-.016,1.137-.051a10.368,10.368,0,0,0,1.187-.168A8.39,8.39,0,0,0,10.973,5.5,5.218,5.218,0,0,0,12,5.016V7a5.092,5.092,0,0,1,1,.1V3.531a1.333,1.333,0,0,0-.152-.625,1.97,1.97,0,0,0-.4-.515,3.518,3.518,0,0,0-1.039-.664,7.269,7.269,0,0,0-1.3-.418,10.78,10.78,0,0,0-1.367-.215c-.456-.041-.871-.063-1.246-.063-.245,0-.511.01-.8.028s-.583.048-.891.089-.617.1-.929.164a7.689,7.689,0,0,0-.9.254,5.562,5.562,0,0,0-.8.356,2.788,2.788,0,0,0-.637.469,2.164,2.164,0,0,0-.395.511A1.3,1.3,0,0,0,2,3.531v9a1.316,1.316,0,0,0,.176.676,2.049,2.049,0,0,0,.465.543,3.278,3.278,0,0,0,.656.426,6.65,6.65,0,0,0,.75.32,7.384,7.384,0,0,0,.75.227c.245.06.463.1.656.136a11.923,11.923,0,0,0,2.047.172c.177,0,.354-.005.532-.013A5.006,5.006,0,0,1,7.435,14.03ZM3.133,3.219a1.673,1.673,0,0,1,.32-.274,3.616,3.616,0,0,1,.383-.222c.13-.065.234-.114.312-.145a6.047,6.047,0,0,1,.79-.254q.422-.1.859-.172c.291-.044.582-.075.871-.093s.566-.028.832-.028.543.01.832.028.579.049.871.093.578.1.859.172a6.1,6.1,0,0,1,.79.254c.078.031.182.08.312.145a3.616,3.616,0,0,1,.383.222,1.7,1.7,0,0,1,.32.274A.462.462,0,0,1,12,3.531a.3.3,0,0,1-.023.117.555.555,0,0,1-.063.11,1.6,1.6,0,0,1-.5.449,3.842,3.842,0,0,1-.715.332,6.428,6.428,0,0,1-.848.234q-.449.095-.878.153t-.817.082q-.387.022-.66.023c-.183,0-.4-.008-.66-.023s-.53-.043-.817-.082-.579-.09-.878-.153A6.428,6.428,0,0,1,4.3,4.539,3.788,3.788,0,0,1,3.578,4.2a1.565,1.565,0,0,1-.492-.445.555.555,0,0,1-.063-.11A.3.3,0,0,1,3,3.531.462.462,0,0,1,3.133,3.219Z"/>
<polygon points="12 10 11 10 11 13 13.5 13 13.5 12 12 12 12 10"/>
<path d="M15.688,10.441a3.99,3.99,0,0,0-2.129-2.129,4.051,4.051,0,0,0-3.118,0,3.99,3.99,0,0,0-2.129,2.129,4.051,4.051,0,0,0,0,3.118,3.99,3.99,0,0,0,2.129,2.129,4.051,4.051,0,0,0,3.118,0,3.99,3.99,0,0,0,2.129-2.129,4.051,4.051,0,0,0,0-3.118Zm-.922,2.727a3.029,3.029,0,0,1-1.6,1.6,3.022,3.022,0,0,1-2.332,0,3.04,3.04,0,0,1-1.6-1.6,3.022,3.022,0,0,1,0-2.332,3.029,3.029,0,0,1,1.6-1.6,3.022,3.022,0,0,1,2.332,0,3.019,3.019,0,0,1,1.6,1.6,3.022,3.022,0,0,1,0,2.332Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<title>TemporaryDatabase_Inverse</title>
<g>
<path d="M3.086,3.758a1.565,1.565,0,0,0,.492.445,3.788,3.788,0,0,0,.719.336,6.428,6.428,0,0,0,.848.234q.449.095.878.153t.817.082c.258.015.477.023.66.023s.4-.008.66-.023.53-.043.817-.082.579-.09.878-.153a6.428,6.428,0,0,0,.848-.234,3.842,3.842,0,0,0,.715-.332,1.6,1.6,0,0,0,.5-.449.555.555,0,0,0,.063-.11A.3.3,0,0,0,12,3.531a.462.462,0,0,0-.133-.312,1.7,1.7,0,0,0-.32-.274,3.616,3.616,0,0,0-.383-.222c-.13-.065-.234-.114-.312-.145a6.1,6.1,0,0,0-.79-.254q-.421-.1-.859-.172t-.871-.093c-.289-.018-.566-.028-.832-.028s-.543.01-.832.028-.58.049-.871.093-.578.1-.859.172a6.047,6.047,0,0,0-.79.254c-.078.031-.182.08-.312.145a3.616,3.616,0,0,0-.383.222,1.673,1.673,0,0,0-.32.274A.462.462,0,0,0,3,3.531a.3.3,0,0,0,.023.117A.555.555,0,0,0,3.086,3.758Z" fill="#fff"/>
<path d="M7.435,14.03c-.282,0-.6-.017-.951-.046a10.214,10.214,0,0,1-1.148-.16,7.424,7.424,0,0,1-1.106-.3,3.184,3.184,0,0,1-.894-.481,1.519,1.519,0,0,1-.223-.219A.436.436,0,0,1,3,12.531V5.016A5.236,5.236,0,0,0,4.023,5.5a8.163,8.163,0,0,0,1.149.312,10.5,10.5,0,0,0,1.191.168c.4.035.778.051,1.137.051s.738-.016,1.137-.051a10.368,10.368,0,0,0,1.187-.168A8.39,8.39,0,0,0,10.973,5.5,5.218,5.218,0,0,0,12,5.016V7a5.092,5.092,0,0,1,1,.1V3.531a1.333,1.333,0,0,0-.152-.625,1.97,1.97,0,0,0-.4-.515,3.518,3.518,0,0,0-1.039-.664,7.269,7.269,0,0,0-1.3-.418,10.78,10.78,0,0,0-1.367-.215c-.456-.041-.871-.063-1.246-.063-.245,0-.511.01-.8.028s-.583.048-.891.089-.617.1-.929.164a7.689,7.689,0,0,0-.9.254,5.562,5.562,0,0,0-.8.356,2.788,2.788,0,0,0-.637.469,2.164,2.164,0,0,0-.395.511A1.3,1.3,0,0,0,2,3.531v9a1.316,1.316,0,0,0,.176.676,2.049,2.049,0,0,0,.465.543,3.278,3.278,0,0,0,.656.426,6.65,6.65,0,0,0,.75.32,7.384,7.384,0,0,0,.75.227c.245.06.463.1.656.136a11.923,11.923,0,0,0,2.047.172c.177,0,.354-.005.532-.013A5.006,5.006,0,0,1,7.435,14.03ZM3.133,3.219a1.673,1.673,0,0,1,.32-.274,3.616,3.616,0,0,1,.383-.222c.13-.065.234-.114.312-.145a6.047,6.047,0,0,1,.79-.254q.422-.1.859-.172c.291-.044.582-.075.871-.093s.566-.028.832-.028.543.01.832.028.579.049.871.093.578.1.859.172a6.1,6.1,0,0,1,.79.254c.078.031.182.08.312.145a3.616,3.616,0,0,1,.383.222,1.7,1.7,0,0,1,.32.274A.462.462,0,0,1,12,3.531a.3.3,0,0,1-.023.117.555.555,0,0,1-.063.11,1.6,1.6,0,0,1-.5.449,3.842,3.842,0,0,1-.715.332,6.428,6.428,0,0,1-.848.234q-.449.095-.878.153t-.817.082q-.387.022-.66.023c-.183,0-.4-.008-.66-.023s-.53-.043-.817-.082-.579-.09-.878-.153A6.428,6.428,0,0,1,4.3,4.539,3.788,3.788,0,0,1,3.578,4.2a1.565,1.565,0,0,1-.492-.445.555.555,0,0,1-.063-.11A.3.3,0,0,1,3,3.531.462.462,0,0,1,3.133,3.219Z" fill="#fff"/>
<polygon points="12 10 11 10 11 13 13.5 13 13.5 12 12 12 12 10" fill="#fff"/>
<path d="M15.688,10.441a3.99,3.99,0,0,0-2.129-2.129,4.051,4.051,0,0,0-3.118,0,3.99,3.99,0,0,0-2.129,2.129,4.051,4.051,0,0,0,0,3.118,3.99,3.99,0,0,0,2.129,2.129,4.051,4.051,0,0,0,3.118,0,3.99,3.99,0,0,0,2.129-2.129,4.051,4.051,0,0,0,0-3.118Zm-.922,2.727a3.029,3.029,0,0,1-1.6,1.6,3.022,3.022,0,0,1-2.332,0,3.04,3.04,0,0,1-1.6-1.6,3.022,3.022,0,0,1,0-2.332,3.029,3.029,0,0,1,1.6-1.6,3.022,3.022,0,0,1,2.332,0,3.019,3.019,0,0,1,1.6,1.6,3.022,3.022,0,0,1,0,2.332Z" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script type="text/javascript">
window.onload = function () {
document.getElementById('frame').src = "{{{url}}}";
};
</script>
<style>
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#frame {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
</style>
</head>
<body>
<iframe id="frame" width="100%" height="100%" frameborder="0"></iframe>
</body>
</html>

View File

@@ -0,0 +1,19 @@
--SQL script to grab allocation contention from histogram
Use tempdb
DECLARE @target_data XML;
SELECT @target_data = CAST(t.target_data AS XML)
FROM sys.dm_xe_sessions AS s
JOIN sys.dm_xe_session_targets AS t
ON t.event_session_address = s.address
WHERE s.name = N'PageContention' and t.target_name = N'histogram';
with wait_stats as
(
SELECT
n.value('(value)[1]','bigint') AS id,
n.value('(@count)[1]', 'bigint') AS [Count]
FROM @target_data.nodes('//HistogramTarget/Slot') AS q(n)
)
SELECT [dbo].[mapPageType](wait_stats.id), wait_stats.Count
FROM wait_stats

View File

@@ -0,0 +1,22 @@
--SQL script to grab metadata contention from histogram
Use tempdb
DECLARE @target_data XML;
SELECT @target_data = CAST(t.target_data AS XML)
FROM sys.dm_xe_sessions AS s
JOIN sys.dm_xe_session_targets AS t
ON t.event_session_address = s.address
WHERE s.name = N'ObjectContention' and t.target_name = N'histogram';
with wait_stats as
(
SELECT
n.value('(value)[1]','bigint') AS alloc_unit_id,
n.value('(@count)[1]', 'bigint') AS [Count]
FROM @target_data.nodes('//HistogramTarget/Slot') AS q(n)
)
SELECT objects.id, SUM(objects.count) as [Count] FROM
(SELECT [dbo].[isSystemTable](wait_stats.alloc_unit_id) AS id, wait_stats.Count AS [count]
FROM wait_stats
WHERE [dbo].[isSystemTable](wait_stats.alloc_unit_id) not in (0, 99)) AS objects
GROUP BY objects.id

View File

@@ -0,0 +1,80 @@
--Starts the XEvents sessions and creates the functions needed to find object id and give name to the page types
use tempdb
BEGIN TRY
IF NOT EXISTS (SELECT * FROM sys.dm_xe_sessions WHERE name = 'PageContention')
BEGIN
CREATE EVENT SESSION [PageContention] ON SERVER
ADD EVENT latch_suspend_end(
WHERE class = 28
AND (page_type_id = 8
OR page_type_id = 9
OR page_type_id = 11))
ADD TARGET package0.histogram(SET slots=16, filtering_event_name=N'latch_suspend_end', source=N'page_type_id', source_type=(0))
ALTER EVENT SESSION [PageContention] ON SERVER
STATE = START
END
IF NOT EXISTS (SELECT * FROM sys.dm_xe_sessions WHERE name = 'ObjectContention')
BEGIN
CREATE EVENT SESSION [ObjectContention] ON SERVER
ADD EVENT latch_suspend_end(
WHERE class = 28
AND database_id = 2)
ADD TARGET package0.histogram(SET slots=256, filtering_event_name=N'latch_suspend_end', source=N'page_alloc_unit_id', source_type=(0))
ALTER EVENT SESSION [ObjectContention] ON SERVER
STATE = START
END
END TRY
BEGIN CATCH
PRINT 'XEvent fields not supported'
END CATCH
GO
IF OBJECT_ID(N'[dbo].[isSystemTable]', N'FN') IS NOT NULL
DROP FUNCTION [dbo].[isSystemTable]
GO
CREATE FUNCTION [dbo].[isSystemTable] (@alloc bigint)
RETURNS bigint
AS BEGIN
DECLARE @index BIGINT;
DECLARE @objId BIGINT;
SELECT @index =
CONVERT (BIGINT,
CONVERT (FLOAT, @alloc)
* (1 / POWER (2.0, 48))
);
SELECT @objId =
CONVERT (BIGINT,
CONVERT (FLOAT, @alloc - (@index * CONVERT (BIGINT, POWER (2.0, 48))))
* (1 / POWER (2.0, 16))
);
IF (@objId > 0 AND @objId <= 100 AND @index <= 255)
return @objId
return 0
END
GO
IF OBJECT_ID(N'[dbo].[mapPageType]', N'FN') IS NOT NULL
DROP FUNCTION [dbo].[mapPageType]
GO
CREATE FUNCTION [dbo].[mapPageType] (@pageTypeId bigint)
RETURNS varchar(20)
AS BEGIN
IF @pageTypeId = 8
return 'GAM_PAGE'
ELSE IF @pageTypeId = 9
return 'SGAM_PAGE'
ELSE IF @pageTypeId = 11
return 'PFS_PAGE'
return ''
END
GO

View File

@@ -0,0 +1,3 @@
--Stops the XEvent Sessions
DROP EVENT SESSION [PageContention] ON SERVER
DROP EVENT SESSION [ObjectContention] ON SERVER

View File

@@ -0,0 +1,33 @@
--SQL script to grab all contention
Use tempdb
DECLARE @pc XML;
DECLARE @obj XML;
SELECT @pc = CAST(t.target_data AS XML)
FROM sys.dm_xe_sessions AS s
JOIN sys.dm_xe_session_targets AS t
ON t.event_session_address = s.address
WHERE s.name = N'PageContention' and t.target_name = N'histogram';
SELECT @obj = CAST(t.target_data AS XML)
FROM sys.dm_xe_sessions AS s
JOIN sys.dm_xe_session_targets AS t
ON t.event_session_address = s.address
WHERE s.name = N'ObjectContention' and t.target_name = N'histogram';
SELECT 'Metadata Contention' AS wait_type, SUM(obj.count) AS [Count]
FROM (
SELECT
n.value('(value)[1]','bigint') AS alloc_unit_id,
n.value('(@count)[1]', 'bigint') AS [count]
FROM @obj.nodes('//HistogramTarget/Slot') AS q(n)
) obj
WHERE [dbo].[isSystemTable](obj.alloc_unit_id) not in (0, 99)
UNION
SELECT 'Allocation Contention' AS wait_type, SUM(pc.count) AS [Count]
FROM
(
SELECT
n.value('(@count)[1]', 'bigint') AS [count]
FROM @pc.nodes('//HistogramTarget/Slot') AS q(n)
) pc

View File

@@ -0,0 +1,95 @@
WITH [Waits] AS
(
SELECT wait_type, resource_description, count(resource_description) AS RESOURCE_USE, COUNT(*) AS TOTAL FROM sys.dm_os_waiting_tasks
WHERE [wait_type] NOT IN (
-- These wait types are almost 100% never a problem and so they are
-- filtered out to avoid them skewing the results. Click on the URL
-- for more information.
N'BROKER_EVENTHANDLER', -- https://www.sqlskills.com/help/waits/BROKER_EVENTHANDLER
N'BROKER_RECEIVE_WAITFOR', -- https://www.sqlskills.com/help/waits/BROKER_RECEIVE_WAITFOR
N'BROKER_TASK_STOP', -- https://www.sqlskills.com/help/waits/BROKER_TASK_STOP
N'BROKER_TO_FLUSH', -- https://www.sqlskills.com/help/waits/BROKER_TO_FLUSH
N'BROKER_TRANSMITTER', -- https://www.sqlskills.com/help/waits/BROKER_TRANSMITTER
N'CHECKPOINT_QUEUE', -- https://www.sqlskills.com/help/waits/CHECKPOINT_QUEUE
N'CHKPT', -- https://www.sqlskills.com/help/waits/CHKPT
N'CLR_AUTO_EVENT', -- https://www.sqlskills.com/help/waits/CLR_AUTO_EVENT
N'CLR_MANUAL_EVENT', -- https://www.sqlskills.com/help/waits/CLR_MANUAL_EVENT
N'CLR_SEMAPHORE', -- https://www.sqlskills.com/help/waits/CLR_SEMAPHORE
N'CXCONSUMER', -- https://www.sqlskills.com/help/waits/CXCONSUMER
-- Maybe comment these four out if you have mirroring issues
N'DBMIRROR_DBM_EVENT', -- https://www.sqlskills.com/help/waits/DBMIRROR_DBM_EVENT
N'DBMIRROR_EVENTS_QUEUE', -- https://www.sqlskills.com/help/waits/DBMIRROR_EVENTS_QUEUE
N'DBMIRROR_WORKER_QUEUE', -- https://www.sqlskills.com/help/waits/DBMIRROR_WORKER_QUEUE
N'DBMIRRORING_CMD', -- https://www.sqlskills.com/help/waits/DBMIRRORING_CMD
N'DIRTY_PAGE_POLL', -- https://www.sqlskills.com/help/waits/DIRTY_PAGE_POLL
N'DISPATCHER_QUEUE_SEMAPHORE', -- https://www.sqlskills.com/help/waits/DISPATCHER_QUEUE_SEMAPHORE
N'EXECSYNC', -- https://www.sqlskills.com/help/waits/EXECSYNC
N'FSAGENT', -- https://www.sqlskills.com/help/waits/FSAGENT
N'FT_IFTS_SCHEDULER_IDLE_WAIT', -- https://www.sqlskills.com/help/waits/FT_IFTS_SCHEDULER_IDLE_WAIT
N'FT_IFTSHC_MUTEX', -- https://www.sqlskills.com/help/waits/FT_IFTSHC_MUTEX
-- Maybe comment these six out if you have AG issues
N'HADR_CLUSAPI_CALL', -- https://www.sqlskills.com/help/waits/HADR_CLUSAPI_CALL
N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', -- https://www.sqlskills.com/help/waits/HADR_FILESTREAM_IOMGR_IOCOMPLETION
N'HADR_LOGCAPTURE_WAIT', -- https://www.sqlskills.com/help/waits/HADR_LOGCAPTURE_WAIT
N'HADR_NOTIFICATION_DEQUEUE', -- https://www.sqlskills.com/help/waits/HADR_NOTIFICATION_DEQUEUE
N'HADR_TIMER_TASK', -- https://www.sqlskills.com/help/waits/HADR_TIMER_TASK
N'HADR_WORK_QUEUE', -- https://www.sqlskills.com/help/waits/HADR_WORK_QUEUE
N'KSOURCE_WAKEUP', -- https://www.sqlskills.com/help/waits/KSOURCE_WAKEUP
N'LAZYWRITER_SLEEP', -- https://www.sqlskills.com/help/waits/LAZYWRITER_SLEEP
N'LOGMGR_QUEUE', -- https://www.sqlskills.com/help/waits/LOGMGR_QUEUE
N'MEMORY_ALLOCATION_EXT', -- https://www.sqlskills.com/help/waits/MEMORY_ALLOCATION_EXT
N'ONDEMAND_TASK_QUEUE', -- https://www.sqlskills.com/help/waits/ONDEMAND_TASK_QUEUE
N'PARALLEL_REDO_DRAIN_WORKER', -- https://www.sqlskills.com/help/waits/PARALLEL_REDO_DRAIN_WORKER
N'PARALLEL_REDO_LOG_CACHE', -- https://www.sqlskills.com/help/waits/PARALLEL_REDO_LOG_CACHE
N'PARALLEL_REDO_TRAN_LIST', -- https://www.sqlskills.com/help/waits/PARALLEL_REDO_TRAN_LIST
N'PARALLEL_REDO_WORKER_SYNC', -- https://www.sqlskills.com/help/waits/PARALLEL_REDO_WORKER_SYNC
N'PARALLEL_REDO_WORKER_WAIT_WORK', -- https://www.sqlskills.com/help/waits/PARALLEL_REDO_WORKER_WAIT_WORK
N'PREEMPTIVE_OS_FLUSHFILEBUFFERS', -- https://www.sqlskills.com/help/waits/PREEMPTIVE_OS_FLUSHFILEBUFFERS
N'PREEMPTIVE_XE_GETTARGETSTATE', -- https://www.sqlskills.com/help/waits/PREEMPTIVE_XE_GETTARGETSTATE
N'PWAIT_ALL_COMPONENTS_INITIALIZED', -- https://www.sqlskills.com/help/waits/PWAIT_ALL_COMPONENTS_INITIALIZED
N'PWAIT_DIRECTLOGCONSUMER_GETNEXT', -- https://www.sqlskills.com/help/waits/PWAIT_DIRECTLOGCONSUMER_GETNEXT
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', -- https://www.sqlskills.com/help/waits/QDS_PERSIST_TASK_MAIN_LOOP_SLEEP
N'QDS_ASYNC_QUEUE', -- https://www.sqlskills.com/help/waits/QDS_ASYNC_QUEUE
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
-- https://www.sqlskills.com/help/waits/QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP
N'QDS_SHUTDOWN_QUEUE', -- https://www.sqlskills.com/help/waits/QDS_SHUTDOWN_QUEUE
N'REDO_THREAD_PENDING_WORK', -- https://www.sqlskills.com/help/waits/REDO_THREAD_PENDING_WORK
N'REQUEST_FOR_DEADLOCK_SEARCH', -- https://www.sqlskills.com/help/waits/REQUEST_FOR_DEADLOCK_SEARCH
N'RESOURCE_QUEUE', -- https://www.sqlskills.com/help/waits/RESOURCE_QUEUE
N'SERVER_IDLE_CHECK', -- https://www.sqlskills.com/help/waits/SERVER_IDLE_CHECK
N'SLEEP_BPOOL_FLUSH', -- https://www.sqlskills.com/help/waits/SLEEP_BPOOL_FLUSH
N'SLEEP_DBSTARTUP', -- https://www.sqlskills.com/help/waits/SLEEP_DBSTARTUP
N'SLEEP_DCOMSTARTUP', -- https://www.sqlskills.com/help/waits/SLEEP_DCOMSTARTUP
N'SLEEP_MASTERDBREADY', -- https://www.sqlskills.com/help/waits/SLEEP_MASTERDBREADY
N'SLEEP_MASTERMDREADY', -- https://www.sqlskills.com/help/waits/SLEEP_MASTERMDREADY
N'SLEEP_MASTERUPGRADED', -- https://www.sqlskills.com/help/waits/SLEEP_MASTERUPGRADED
N'SLEEP_MSDBSTARTUP', -- https://www.sqlskills.com/help/waits/SLEEP_MSDBSTARTUP
N'SLEEP_SYSTEMTASK', -- https://www.sqlskills.com/help/waits/SLEEP_SYSTEMTASK
N'SLEEP_TASK', -- https://www.sqlskills.com/help/waits/SLEEP_TASK
N'SLEEP_TEMPDBSTARTUP', -- https://www.sqlskills.com/help/waits/SLEEP_TEMPDBSTARTUP
N'SNI_HTTP_ACCEPT', -- https://www.sqlskills.com/help/waits/SNI_HTTP_ACCEPT
N'SOS_WORK_DISPATCHER', -- https://www.sqlskills.com/help/waits/SOS_WORK_DISPATCHER
N'SP_SERVER_DIAGNOSTICS_SLEEP', -- https://www.sqlskills.com/help/waits/SP_SERVER_DIAGNOSTICS_SLEEP
N'SQLTRACE_BUFFER_FLUSH', -- https://www.sqlskills.com/help/waits/SQLTRACE_BUFFER_FLUSH
N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', -- https://www.sqlskills.com/help/waits/SQLTRACE_INCREMENTAL_FLUSH_SLEEP
N'SQLTRACE_WAIT_ENTRIES', -- https://www.sqlskills.com/help/waits/SQLTRACE_WAIT_ENTRIES
N'VDI_CLIENT_OTHER', -- https://www.sqlskills.com/help/waits/VDI_CLIENT_OTHER
N'WAIT_FOR_RESULTS', -- https://www.sqlskills.com/help/waits/WAIT_FOR_RESULTS
N'WAITFOR', -- https://www.sqlskills.com/help/waits/WAITFOR
N'WAITFOR_TASKSHUTDOWN', -- https://www.sqlskills.com/help/waits/WAITFOR_TASKSHUTDOWN
N'WAIT_XTP_RECOVERY', -- https://www.sqlskills.com/help/waits/WAIT_XTP_RECOVERY
N'WAIT_XTP_HOST_WAIT', -- https://www.sqlskills.com/help/waits/WAIT_XTP_HOST_WAIT
N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', -- https://www.sqlskills.com/help/waits/WAIT_XTP_OFFLINE_CKPT_NEW_LOG
N'WAIT_XTP_CKPT_CLOSE', -- https://www.sqlskills.com/help/waits/WAIT_XTP_CKPT_CLOSE
N'XE_DISPATCHER_JOIN', -- https://www.sqlskills.com/help/waits/XE_DISPATCHER_JOIN
N'XE_DISPATCHER_WAIT', -- https://www.sqlskills.com/help/waits/XE_DISPATCHER_WAIT
N'XE_TIMER_EVENT' -- https://www.sqlskills.com/help/waits/XE_TIMER_EVENT
)
GROUP BY sys.dm_os_waiting_tasks.resource_description, wait_type
)
SELECT [TASKS].[wait_type] AS [WaitType], [TASKS].[resource_description] as [Resource], 100.0 * [TASKS].[TOTAL] / SUM ([TASKS].[TOTAL]) OVER () AS [Percentage] FROM Waits AS TASKS GROUP BY [TASKS].[wait_type], [TASKS].[resource_description], [TASKS].[TOTAL]

View File

@@ -5,21 +5,42 @@
'use strict';
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs-extra';
import * as handlebars from 'handlebars';
import * as Constants from './constants';
import * as LocalizedConstants from './localizedConstants';
/**
* Helper to log messages to the developer console if enabled
* @param msg Message to log to the console
*/
export function logDebug(msg: any): void {
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
let logDebugInfo = config[Constants.configLogDebugInfo];
if (logDebugInfo === true) {
let currentTime = new Date().toLocaleTimeString();
let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : '';
console.log(outputMsg);
}
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
let logDebugInfo = config[Constants.configLogDebugInfo];
if (logDebugInfo === true) {
let currentTime = new Date().toLocaleTimeString();
let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : '';
console.log(outputMsg);
}
}
export function renderTemplateHtml(extensionPath: string, templateName: string, templateValues: object): Thenable<string> {
let templatePath = path.join(extensionPath, 'resources', templateName);
// 1) Read the template from the disk
// 2) Compile it as a handlebars template and render the HTML
// 3) On failure, return a simple string as an error
return fs.readFile(templatePath, 'utf-8')
.then(templateText => {
let template = handlebars.compile(templateText);
return template(templateValues);
})
.then(
undefined,
error => {
logDebug(error);
return LocalizedConstants.msgErrorLoadingTab;
}
);
}

View File

@@ -18,67 +18,67 @@ let tsProject = ts.createProject('tsconfig.json');
// GULP TASKS //////////////////////////////////////////////////////////////
gulp.task('clean', function(done) {
return del('out', done);
gulp.task('clean', function (done) {
return del('out', done);
});
gulp.task('lint', () => {
return gulp.src([
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/test/**/*.ts'
])
.pipe((tslint({
formatter: "verbose"
})))
.pipe(tslint.report());
return gulp.src([
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/test/**/*.ts'
])
.pipe((tslint({
formatter: "verbose"
})))
.pipe(tslint.report());
});
gulp.task('compile:src', function(done) {
gulp.src([
config.paths.project.root + '/src/**/*.sql',
config.paths.project.root + '/src/**/*.svg',
config.paths.project.root + '/src/**/*.html'
]).pipe(gulp.dest('out/src/'));
gulp.task('compile:src', function (done) {
gulp.src([
config.paths.project.root + '/src/**/*.sql',
config.paths.project.root + '/src/**/*.svg',
config.paths.project.root + '/src/**/*.html'
]).pipe(gulp.dest('out/src/'));
let srcFiles = [
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/src/**/*.js',
config.paths.project.root + '/typings/**/*.ts'
];
let srcFiles = [
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/src/**/*.js',
config.paths.project.root + '/typings/**/*.ts'
];
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile extension source, see above.');
process.exit(1);
}
})
// TODO: Reinstate localization code
// .pipe(nls.rewriteLocalizeCalls())
// .pipe(nls.createAdditionalLanguageFiles(nls.coreLanguages, config.paths.project.root + '/localization/i18n', undefined, false))
.pipe(srcmap.write('.', { sourceRoot: function(file) { return file.cwd + '/src'; }}))
.pipe(gulp.dest('out/src/'));
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function () {
if (process.env.BUILDMACHINE) {
done('Failed to compile extension source, see above.');
process.exit(1);
}
})
// TODO: Reinstate localization code
// .pipe(nls.rewriteLocalizeCalls())
// .pipe(nls.createAdditionalLanguageFiles(nls.coreLanguages, config.paths.project.root + '/localization/i18n', undefined, false))
.pipe(srcmap.write('.', { sourceRoot: function (file) { return file.cwd + '/src'; } }))
.pipe(gulp.dest('out/src/'));
});
gulp.task('compile:test', function(done) {
let srcFiles = [
config.paths.project.root + '/test/**/*.ts',
config.paths.project.root + '/typings/**/*.ts'
];
gulp.task('compile:test', function (done) {
let srcFiles = [
config.paths.project.root + '/test/**/*.ts',
config.paths.project.root + '/typings/**/*.ts'
];
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile test source, see above.');
process.exit(1);
}
})
.pipe(srcmap.write('.', {sourceRoot: function(file) { return file.cwd + '/test'; }}))
.pipe(gulp.dest('out/test/'));
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function () {
if (process.env.BUILDMACHINE) {
done('Failed to compile test source, see above.');
process.exit(1);
}
})
.pipe(srcmap.write('.', { sourceRoot: function (file) { return file.cwd + '/test'; } }))
.pipe(gulp.dest('out/test/'));
});
// COMPOSED GULP TASKS /////////////////////////////////////////////////////
@@ -86,33 +86,38 @@ gulp.task("compile", gulp.series("compile:src", "compile:test"));
gulp.task("build", gulp.series("clean", "lint", "compile"));
gulp.task("watch", function() {
gulp.watch([config.paths.project.root + '/src/**/*',
config.paths.project.root + '/test/**/*.ts'],
gulp.series('build'))
gulp.task("watch", function () {
gulp.watch([config.paths.project.root + '/src/**/*',
config.paths.project.root + '/test/**/*.ts'],
gulp.series('build'));
});
gulp.task('test', (done) => {
let workspace = process.env['WORKSPACE'];
if (!workspace) {
workspace = process.cwd();
}
process.env.JUNIT_REPORT_PATH = workspace + '/test-reports/ext_xunit.xml';
let workspace = process.env['WORKSPACE'];
if (!workspace) {
workspace = process.cwd();
}
process.env.JUNIT_REPORT_PATH = workspace + '/test-reports/ext_xunit.xml';
let azuredatastudioPath = 'azuredatastudio';
if (process.env['SQLOPS_DEV']) {
let suffix = os.platform === 'win32' ? 'bat' : 'sh';
azuredatastudioPath = `${process.env['SQLOPS_DEV']}/scripts/sql-cli.${suffix}`;
}
console.log(`Using SQLOPS Path of ${azuredatastudioPath}`);
let azuredatastudioPath = 'azuredatastudio';
if (process.env['SQLOPS_DEV']) {
let suffix = os.platform === 'win32' ? 'bat' : 'sh';
azuredatastudioPath = `${process.env['SQLOPS_DEV']}/scripts/sql-cli.${suffix}`;
}
console.log(`Using SQLOPS Path of ${azuredatastudioPath}`);
cproc.exec(`${azuredatastudioPath} --extensionDevelopmentPath="${workspace}" --extensionTestsPath="${workspace}/out/test" --verbose`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
process.exit(1);
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
done();
});
cproc.exec(`${azuredatastudioPath} --extensionDevelopmentPath="${workspace}" --extensionTestsPath="${workspace}/out/test" --verbose`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
process.exit(1);
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
done();
});
});
gulp.task('copytypings', function () {
return gulp.src(config.paths.project.root + '/../../src/sql/sqlops.proposed.d.ts')
.pipe(gulp.dest('typings/'));
});

View File

@@ -6,4 +6,4 @@ node_modules
.DS_Store
.idea
test-reports/**
typings/sqlops.proposed.d.ts
typings/azdata.d.ts

View File

@@ -9,7 +9,7 @@
// - open Azure Data Studio
// - run the command "Install 'azuredatastudio' command in PATH"
{
"version": "0.2.0",
"version": "0.2.0",
"configurations": [
{
@@ -21,37 +21,37 @@
"--extensionDevelopmentPath=${workspaceFolder}"
]
},
{
"type": "node",
"request": "attach",
"name": "Attach to Ops Studio",
"protocol": "inspector",
"port": 5870,
"restart": true,
{
"type": "node",
"request": "attach",
"name": "Attach to Azure Data Studio",
"protocol": "inspector",
"port": 5870,
"restart": true,
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/out/**/*.js"
],
"outFiles": [
"${workspaceRoot}/out/**/*.js"
],
"preLaunchTask": "",
"timeout": 25000
},
},
{
"name": "Debug in enlistment",
"type": "sqlopsExtensionHost",
"type": "azuredatastudioExtensionHost",
"request": "launch",
"windows": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.bat"
},
"osx": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
},
"linux": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
},
"windows": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.bat"
},
"osx": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
},
"linux": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
},
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"timeout": 20000
}
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@
"name": "whoisactive",
"displayName": "whoisactive",
"description": "sp_whoisactive for Azure Data Studio",
"version": "0.1.1",
"version": "0.1.2",
"publisher": "Microsoft",
"preview": true,
"engines": {
"vscode": "^1.26.0",
"azdata": "*"
"azdata": "^1.11.0"
},
"icon": "images/sqlserver.png",
"license": "SEE LICENSE IN LICENSE.txt",
@@ -241,17 +241,17 @@
"compile": "gulp compile",
"watch": "gulp watch",
"typings": "gulp copytypings",
"postinstall": "node ./node_modules/vscode/bin/install && node ./node_modules/sqlops/bin/install && gulp copytypings"
"postinstall": "node ./node_modules/vscode/bin/install && node ./node_modules/azdata/bin/install && gulp copytypings"
},
"dependencies": {
"fs-extra": "^5.0.0",
"handlebars": "^4.0.11",
"openurl": "^1.1.1",
"sqlops": "github:anthonydresser/sqlops-extension-sqlops"
"openurl": "^1.1.1"
},
"devDependencies": {
"@types/mocha": "^2.2.42",
"@types/node": "^7.0.43",
"azdata": "github:microsoft/azdata-extension-azdata",
"child-process-promise": "^2.2.1",
"del": "^3.0.0",
"gulp": "^4.0.0",

View File

@@ -1,4 +1,3 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
@@ -9,18 +8,18 @@
import * as vscode from 'vscode';
export default abstract class ControllerBase implements vscode.Disposable {
protected _context: vscode.ExtensionContext;
protected _context: vscode.ExtensionContext;
public constructor(context: vscode.ExtensionContext) {
this._context = context;
}
public constructor(context: vscode.ExtensionContext) {
this._context = context;
}
abstract activate(): Promise<boolean>;
abstract activate(): Promise<boolean>;
abstract deactivate(): void;
abstract deactivate(): void;
public dispose(): void {
this.deactivate();
}
public dispose(): void {
this.deactivate();
}
}

View File

@@ -5,7 +5,7 @@
'use strict';
import * as sqlops from 'sqlops';
import * as azdata from 'azdata';
import * as Utils from '../utils';
import ControllerBase from './controllerBase';
import * as fs from 'fs';
@@ -19,35 +19,35 @@ import * as openurl from 'openurl';
*/
export default class MainController extends ControllerBase {
public apiWrapper;
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Deactivates the extension
*/
public deactivate(): void {
Utils.logDebug('Main controller deactivated');
}
public apiWrapper;
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Deactivates the extension
*/
public deactivate(): void {
Utils.logDebug('Main controller deactivated');
}
public activate(): Promise<boolean> {
sqlops.tasks.registerTask('sp_whoisactive.install', e => this.openurl('http://whoisactive.com/downloads/'));
sqlops.tasks.registerTask('sp_whoisactive.documentation', e => this.openurl('http://whoisactive.com/docs/'));
sqlops.tasks.registerTask('sp_whoisactive.findBlockLeaders', e => this.onExecute(e, 'findBlockLeaders.sql'));
sqlops.tasks.registerTask('sp_whoisactive.getPlans', e => this.onExecute(e, 'getPlans.sql'));
public activate(): Promise<boolean> {
azdata.tasks.registerTask('sp_whoisactive.install', e => this.openurl('http://whoisactive.com/downloads/'));
azdata.tasks.registerTask('sp_whoisactive.documentation', e => this.openurl('http://whoisactive.com/docs/'));
azdata.tasks.registerTask('sp_whoisactive.findBlockLeaders', e => this.onExecute(e, 'findBlockLeaders.sql'));
azdata.tasks.registerTask('sp_whoisactive.getPlans', e => this.onExecute(e, 'getPlans.sql'));
return Promise.resolve(true);
}
return Promise.resolve(true);
}
private openurl(link: string): void {
openurl.open(link);
}
private openurl(link: string): void {
openurl.open(link);
}
private onExecute(connection: sqlops.IConnectionProfile, fileName: string): void {
let sqlContent = fs.readFileSync(path.join(__dirname, '..', 'sql', fileName)).toString();
vscode.workspace.openTextDocument({language: 'sql', content: sqlContent}).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.Active, false).then(() => {
let filePath = doc.uri.toString();
sqlops.queryeditor.connect(filePath, connection.id).then(() => sqlops.queryeditor.runQuery(filePath));
});
});
}
private onExecute(connection: azdata.IConnectionProfile, fileName: string): void {
let sqlContent = fs.readFileSync(path.join(__dirname, '..', 'sql', fileName)).toString();
vscode.workspace.openTextDocument({language: 'sql', content: sqlContent}).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.Active, false).then(() => {
let filePath = doc.uri.toString();
azdata.queryeditor.connect(filePath, connection.id).then(() => azdata.queryeditor.runQuery(filePath));
});
});
}
}

View File

@@ -12,6 +12,7 @@ let tslint = require('gulp-tslint');
let ts = require('gulp-typescript');
let cproc = require('child_process');
let os = require('os');
let path = require('path');
let config = require('./config');
let tsProject = ts.createProject('tsconfig.json');
@@ -19,66 +20,66 @@ let tsProject = ts.createProject('tsconfig.json');
// GULP TASKS //////////////////////////////////////////////////////////////
gulp.task('clean', function(done) {
return del('out', done);
return del('out', done);
});
gulp.task('lint', () => {
return gulp.src([
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/test/**/*.ts'
])
.pipe((tslint({
formatter: "verbose"
})))
.pipe(tslint.report());
return gulp.src([
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/test/**/*.ts'
])
.pipe((tslint({
formatter: "verbose"
})))
.pipe(tslint.report());
});
gulp.task('compile:src', function(done) {
gulp.src([
config.paths.project.root + '/src/**/*.sql',
config.paths.project.root + '/src/**/*.svg',
config.paths.project.root + '/src/**/*.html'
]).pipe(gulp.dest('out/src/'));
gulp.src([
config.paths.project.root + '/src/**/*.sql',
config.paths.project.root + '/src/**/*.svg',
config.paths.project.root + '/src/**/*.html'
]).pipe(gulp.dest('out/src/'));
let srcFiles = [
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/src/**/*.js',
config.paths.project.root + '/typings/**/*.ts'
];
let srcFiles = [
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/src/**/*.js',
config.paths.project.root + '/typings/**/*.ts'
];
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile extension source, see above.');
process.exit(1);
}
})
// TODO: Reinstate localization code
// .pipe(nls.rewriteLocalizeCalls())
// .pipe(nls.createAdditionalLanguageFiles(nls.coreLanguages, config.paths.project.root + '/localization/i18n', undefined, false))
.pipe(srcmap.write('.', { sourceRoot: function(file) { return file.cwd + '/src'; }}))
.pipe(gulp.dest('out/src/'));
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile extension source, see above.');
process.exit(1);
}
})
// TODO: Reinstate localization code
// .pipe(nls.rewriteLocalizeCalls())
// .pipe(nls.createAdditionalLanguageFiles(nls.coreLanguages, config.paths.project.root + '/localization/i18n', undefined, false))
.pipe(srcmap.write('.', { sourceRoot: function(file) { return file.cwd + '/src'; }}))
.pipe(gulp.dest('out/src/'));
});
gulp.task('compile:test', function(done) {
let srcFiles = [
config.paths.project.root + '/test/**/*.ts',
config.paths.project.root + '/typings/**/*.ts'
];
let srcFiles = [
config.paths.project.root + '/test/**/*.ts',
config.paths.project.root + '/typings/**/*.ts'
];
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile test source, see above.');
process.exit(1);
}
})
.pipe(srcmap.write('.', {sourceRoot: function(file) { return file.cwd + '/test'; }}))
.pipe(gulp.dest('out/test/'));
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile test source, see above.');
process.exit(1);
}
})
.pipe(srcmap.write('.', {sourceRoot: function(file) { return file.cwd + '/test'; }}))
.pipe(gulp.dest('out/test/'));
});
// COMPOSED GULP TASKS /////////////////////////////////////////////////////
@@ -87,37 +88,37 @@ gulp.task("compile", gulp.series("compile:src", "compile:test"));
gulp.task("build", gulp.series("clean", "lint", "compile"));
gulp.task("watch", function() {
gulp.watch([config.paths.project.root + '/src/**/*',
config.paths.project.root + '/test/**/*.ts'],
gulp.series('build'))
gulp.watch([config.paths.project.root + '/src/**/*',
config.paths.project.root + '/test/**/*.ts'],
gulp.series('build'));
});
gulp.task('test', (done) => {
let workspace = process.env['WORKSPACE'];
if (!workspace) {
workspace = process.cwd();
}
process.env.JUNIT_REPORT_PATH = workspace + '/test-reports/ext_xunit.xml';
let workspace = process.env['WORKSPACE'];
if (!workspace) {
workspace = process.cwd();
}
process.env.JUNIT_REPORT_PATH = workspace + '/test-reports/ext_xunit.xml';
let azuredatastudioPath = 'azuredatastudio';
if (process.env['SQLOPS_DEV']) {
let suffix = os.platform === 'win32' ? 'bat' : 'sh';
azuredatastudioPath = `${process.env['SQLOPS_DEV']}/scripts/sql-cli.${suffix}`;
}
console.log(`Using SQLOPS Path of ${azuredatastudioPath}`);
let azuredatastudioPath = 'azuredatastudio';
if (process.env['VSCODE_DEV']) {
let suffix = os.platform === 'win32' ? 'bat' : 'sh';
azuredatastudioPath = `${process.env['VSCODE_DEV']}/scripts/sql-cli.${suffix}`;
}
console.log(`Using ADS Path of ${azuredatastudioPath}`);
cproc.exec(`${azuredatastudioPath} --extensionDevelopmentPath="${workspace}" --extensionTestsPath="${workspace}/out/test" --verbose`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
process.exit(1);
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
done();
});
cproc.exec(`${azuredatastudioPath} --extensionDevelopmentPath="${workspace}" --extensionTestsPath="${workspace}/out/test" --verbose`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
process.exit(1);
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
done();
});
});
gulp.task('copytypings', function() {
return gulp.src(config.paths.project.root + '/../../src/sql/sqlops.proposed.d.ts')
.pipe(gulp.dest('typings/'));
return gulp.src(config.paths.project.root + '/../../src/sql/azdata.d.ts')
.pipe(gulp.dest('typings/'));
});

View File

@@ -1,123 +1,123 @@
{
"rules": {
"align": [
true,
"parameters",
"statements"
],
"ban": false,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"eofline": true,
"forin": true,
"indent": [
true,
"spaces"
],
"interface-name": true,
"jsdoc-format": true,
"label-position": true,
"label-undefined": true,
"max-line-length": [
true,
160
],
"member-access": false,
"member-ordering": false,
"no-any": false,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-constructor-vars": false,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": true,
"no-eval": true,
"no-inferrable-types": false,
"no-internal-module": true,
"no-null-keyword": true,
"no-require-imports": false,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-switch-case-fall-through": false,
"no-trailing-whitespace": true,
"no-unreachable": true,
"no-unused-expression": false,
"no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-var-requires": false,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-finally",
"check-whitespace"
],
"quotemark": [
true,
"single",
"avoid-escape"
],
"radix": true,
"semicolon": true,
"switch-default": true,
"trailing-comma": [
true,
{
"multiline": "never",
"singleline": "never"
}
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef": [
true,
"call-signature",
"property-declaration"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"use-strict": false,
"variable-name": [
true,
"allow-leading-underscore",
"ban-keywords"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}
"rules": {
"align": [
true,
"parameters",
"statements"
],
"ban": false,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"eofline": true,
"forin": true,
"indent": [
true,
"tabs"
],
"interface-name": true,
"jsdoc-format": true,
"label-position": true,
"label-undefined": true,
"max-line-length": [
true,
160
],
"member-access": false,
"member-ordering": false,
"no-any": false,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-constructor-vars": false,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": true,
"no-eval": true,
"no-inferrable-types": false,
"no-internal-module": true,
"no-null-keyword": true,
"no-require-imports": false,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-switch-case-fall-through": false,
"no-trailing-whitespace": true,
"no-unreachable": true,
"no-unused-expression": false,
"no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-var-requires": false,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-finally",
"check-whitespace"
],
"quotemark": [
true,
"single",
"avoid-escape"
],
"radix": true,
"semicolon": true,
"switch-default": true,
"trailing-comma": [
true,
{
"multiline": "never",
"singleline": "never"
}
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef": [
true,
"call-signature",
"property-declaration"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"use-strict": false,
"variable-name": [
true,
"allow-leading-underscore",
"ban-keywords"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}

21
src/sql/azdata.d.ts vendored
View File

@@ -3051,10 +3051,9 @@ declare module 'azdata' {
focused?: boolean;
}
export interface TextComponentProperties {
export interface TextComponentProperties extends ComponentProperties, TitledComponentProperties {
value?: string;
links?: LinkArea[];
CSSStyles?: { [key: string]: string };
}
export interface LinkArea {
@@ -3062,7 +3061,7 @@ declare module 'azdata' {
url: string;
}
export interface HyperlinkComponentProperties extends ComponentProperties {
export interface HyperlinkComponentProperties extends ComponentProperties, TitledComponentProperties {
label: string;
url: string;
}
@@ -3165,6 +3164,13 @@ declare module 'azdata' {
clickable?: boolean;
}
export interface TitledComponentProperties {
/**
* The title for the component. This title will show when hovered over
*/
title?: string;
}
export interface CardComponent extends Component, CardProperties {
onDidActionClick: vscode.Event<ActionDescriptor>;
onCardSelectedChanged: vscode.Event<any>;
@@ -3174,8 +3180,7 @@ declare module 'azdata' {
}
export interface TextComponent extends Component, ComponentProperties {
value: string;
export interface TextComponent extends Component, TextComponentProperties {
/**
* An event called when the text is clicked
*/
@@ -3183,8 +3188,6 @@ declare module 'azdata' {
}
export interface HyperlinkComponent extends Component, HyperlinkComponentProperties {
label: string;
url: string;
}
export interface InputBoxComponent extends Component, InputBoxProperties {
@@ -3785,7 +3788,7 @@ declare module 'azdata' {
* Namespace for interacting with query editor
*/
export namespace queryeditor {
export type QueryEvent =
export type QueryEventType =
| 'queryStart'
| 'queryUpdate'
| 'queryStop'
@@ -3800,7 +3803,7 @@ declare module 'azdata' {
* visualize: ResultSetSummary
*/
export interface QueryEventListener {
onQueryEvent(type: QueryEvent, document: queryeditor.QueryDocument, args: ResultSetSummary | string | undefined): void;
onQueryEvent(type: QueryEventType, document: queryeditor.QueryDocument, args: ResultSetSummary | string | undefined): void;
}
// new extensibility interfaces

View File

@@ -28,6 +28,7 @@ export interface ICellRangeSelectorOptions {
export interface ICellRangeSelector<T> extends Slick.Plugin<T> {
onCellRangeSelected: Slick.Event<Slick.Range>;
onBeforeCellRangeSelected: Slick.Event<Slick.Cell>;
onAppendCellRangeSelected: Slick.Event<Slick.Range>;
}
export interface ICellRangeDecorator {
@@ -45,6 +46,7 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
public onBeforeCellRangeSelected = new Slick.Event<Slick.Cell>();
public onCellRangeSelected = new Slick.Event<Slick.Range>();
public onAppendCellRangeSelected = new Slick.Event<Slick.Range>();
constructor(private options: ICellRangeSelectorOptions) {
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
@@ -138,11 +140,18 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
if (!dd || !dd.range || !dd.range.start || !dd.range.end) {
return;
}
this.onCellRangeSelected.notify(new Slick.Range(
let newRange = new Slick.Range(
dd.range.start.row,
dd.range.start.cell,
dd.range.end.row,
dd.range.end.cell
));
);
if (e.ctrlKey) {
this.onAppendCellRangeSelected.notify(newRange);
} else {
this.onCellRangeSelected.notify(newRange);
}
}
}

View File

@@ -36,11 +36,14 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this._handler.subscribe(this.grid.onActiveCellChanged, (e: Event, args: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, args));
this._handler.subscribe(this.grid.onClick, (e: MouseEvent, args: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, args));
this._handler.subscribe(this.grid.onKeyDown, (e: KeyboardEvent) => this.handleKeyDown(e));
this._handler.subscribe(this.grid.onClick, (e: MouseEvent, args: Slick.OnClickEventArgs<T>) => this.handleIndividualCellSelection(e, args));
this._handler.subscribe(this.grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
this.grid.registerPlugin(this.selector);
this._handler.subscribe(this.selector.onCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range));
this._handler.subscribe(this.selector.onCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range, false));
this._handler.subscribe(this.selector.onAppendCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range, true));
this._handler.subscribe(this.selector.onBeforeCellRangeSelected, (e: Event, cell: Slick.Cell) => this.handleBeforeCellRangeSelected(e, cell));
}
@@ -87,13 +90,18 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
return true;
}
private handleCellRangeSelected(e: Event, range: Slick.Range) {
private handleCellRangeSelected(e: Event, range: Slick.Range, append: boolean) {
this.grid.setActiveCell(range.fromRow, range.fromCell, false, false, true);
this.setSelectedRanges([range]);
if (append) {
this.setSelectedRanges(this.insertIntoSelections(this.getSelectedRanges(), range));
} else {
this.setSelectedRanges([range]);
}
}
private handleActiveCellChange(e: Event, args: Slick.OnActiveCellChangedEventArgs<T>) {
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell)) {
private handleActiveCellChange(e: MouseEvent, args: Slick.OnActiveCellChangedEventArgs<T>) {
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell) && !e.ctrlKey) {
this.setSelectedRanges([new Slick.Range(args.row, args.cell)]);
} else if (!this.options.selectActiveCell) {
// clear the previous selection once the cell changes
@@ -118,6 +126,119 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
}
}
/**
* DO NOT CALL THIS DIRECTLY - GO THROUGH INSERT INTO SELECTIONS
*
*/
private mergeSelections(ranges: Array<Slick.Range>, range: Slick.Range) {
// New ranges selection
let newRanges: Array<Slick.Range> = [];
// Have we handled this value
let handled = false;
for (let current of ranges) {
// We've already processed everything. Add everything left back to the list.
if (handled) {
newRanges.push(current);
continue;
}
let newRange: Slick.Range | undefined = undefined;
// if the ranges are the same.
if (current.fromRow === range.fromRow &&
current.fromCell === range.fromCell &&
current.toRow === range.toRow &&
current.toCell === range.toCell) {
// If we're actually not going to handle it during this loop
// this region will be added with the handled boolean check
continue;
}
// Rows are the same - horizontal merging of the selection area
if (current.fromRow === range.fromRow && current.toRow === range.toRow) {
// Check if the new region is adjacent to the old selection group
if (range.toCell + 1 === current.fromCell || range.fromCell - 1 === current.toCell) {
handled = true;
let fromCell = Math.min(range.fromCell, current.fromCell, range.toCell, current.toCell);
let toCell = Math.max(range.fromCell, current.fromCell, range.toCell, current.toCell);
newRange = new Slick.Range(range.fromRow, fromCell, range.toRow, toCell);
}
// Cells are the same - vertical merging of the selection area
} else if (current.fromCell === range.fromCell && current.toCell === range.toCell) {
// Check if the new region is adjacent to the old selection group
if (range.toRow + 1 === current.fromRow || range.fromRow - 1 === current.toRow) {
handled = true;
let fromRow = Math.min(range.fromRow, current.fromRow, range.fromRow, current.fromRow);
let toRow = Math.max(range.toRow, current.toRow, range.toRow, current.toRow);
newRange = new Slick.Range(fromRow, range.fromCell, toRow, range.toCell);
}
}
if (newRange) {
newRanges.push(newRange);
} else {
newRanges.push(current);
}
}
if (!handled) {
newRanges.push(range);
}
return {
newRanges,
handled
};
}
private insertIntoSelections(ranges: Array<Slick.Range>, range: Slick.Range): Array<Slick.Range> {
let result = this.mergeSelections(ranges, range);
let newRanges = result.newRanges;
// Keep merging the rows until we stop having changes
let i = 0;
while (true) {
if (i++ > 10000) {
console.error('InsertIntoSelection infinite loop: Report this error on github');
break;
}
let shouldContinue = false;
for (let current of newRanges) {
result = this.mergeSelections(newRanges, current);
if (result.handled) {
shouldContinue = true;
newRanges = result.newRanges;
break;
}
}
if (shouldContinue) {
continue;
}
break;
}
return newRanges;
}
private handleIndividualCellSelection(e: MouseEvent, args: Slick.OnClickEventArgs<T>) {
if (!e.ctrlKey) {
return;
}
let ranges: Array<Slick.Range>;
ranges = this.getSelectedRanges();
ranges = this.insertIntoSelections(ranges, new Slick.Range(args.row, args.cell));
this.grid.setActiveCell(args.row, args.cell);
this.setSelectedRanges(ranges);
e.preventDefault();
e.stopImmediatePropagation();
}
private handleKeyDown(e: KeyboardEvent) {
/***
* Кey codes

View File

@@ -589,7 +589,7 @@ export class NewNotebookJobAction extends Action {
export class EditNotebookJobAction extends Action {
public static ID = 'notebookaction.editNotebook';
public static LABEL = nls.localize('notebookaction.editJob', "Edit Notebook Job");
public static LABEL = nls.localize('notebookaction.editJob', "Edit");
constructor(
@ICommandService private _commandService: ICommandService
@@ -624,7 +624,7 @@ export class OpenTemplateNotebookAction extends Action {
export class DeleteNotebookAction extends Action {
public static ID = 'notebookaction.deleteNotebook';
public static LABEL = nls.localize('notebookaction.deleteNotebook', "Delete Notebook");
public static LABEL = nls.localize('notebookaction.deleteNotebook', "Delete");
constructor(
@INotificationService private _notificationService: INotificationService,
@@ -732,3 +732,19 @@ export class RenameNotebookMaterializedAction extends Action {
return Promise.resolve(true);
}
}
export class OpenLatestRunMaterializedNotebook extends Action {
public static ID = 'notebookaction.openLatestRun';
public static LABEL = nls.localize('notebookaction.openLatestRun', "Open Latest Run");
constructor(
@ICommandService private _commandService: ICommandService,
) {
super(OpenLatestRunMaterializedNotebook.ID, OpenLatestRunMaterializedNotebook.LABEL);
}
public run(actionInfo: IJobActionInfo): Promise<boolean> {
actionInfo.component.openLastNRun(actionInfo.targetObject.job, 0, 1);
return Promise.resolve(true);
}
}

View File

@@ -43,22 +43,27 @@ export interface IGridDataProvider {
}
export async function getResultsString(provider: IGridDataProvider, selection: Slick.Range[], includeHeaders?: boolean): Promise<string> {
let headers: Map<Number, string> = new Map();
let rows: Map<Number, Map<Number, string>> = new Map();
let copyTable: string[][] = [];
const eol = provider.getEolString();
// create a mapping of the ranges to get promises
let tasks = selection.map((range, i) => {
return async () => {
let selectionsCopy = selection;
let startCol = range.fromCell;
let startRow = range.fromRow;
const result = await provider.getRowData(range.fromRow, range.toRow - range.fromRow + 1);
// If there was a previous selection separate it with a line break. Currently
// when there are multiple selections they are never on the same line
if (provider.shouldIncludeHeaders(includeHeaders)) {
let columnHeaders = provider.getColumnHeaders(range);
if (columnHeaders !== undefined) {
if (copyTable[0] === undefined) {
copyTable[0] = [];
}
copyTable[0].push(...columnHeaders);
let columnHeaders = provider.getColumnHeaders(range);
if (columnHeaders !== undefined) {
let idx = 0;
for (let header of columnHeaders) {
headers.set(startCol + idx, header);
idx++;
}
}
// Iterate over the rows to paste into the copy string
@@ -70,11 +75,17 @@ export async function getResultsString(provider: IGridDataProvider, selection: S
? cellObjects.map(x => removeNewLines(x.displayValue))
: cellObjects.map(x => x.displayValue);
let idx = rowIndex + 1;
if (copyTable[idx] === undefined) {
copyTable[idx] = [];
let idx = 0;
for (let cell of cells) {
let map = rows.get(rowIndex + startRow);
if (!map) {
map = new Map();
rows.set(rowIndex + startRow, map);
}
map.set(startCol + idx, cell);
idx++;
}
copyTable[idx].push(...cells);
}
};
});
@@ -88,12 +99,25 @@ export async function getResultsString(provider: IGridDataProvider, selection: S
}
let copyString = '';
copyTable.forEach((row) => {
if (row === undefined) {
return;
if (includeHeaders) {
copyString = [...headers.values()].join('\t').concat(eol);
}
const rowKeys = [...headers.keys()];
for (let rowEntry of rows) {
let rowMap = rowEntry[1];
for (let rowIdx of rowKeys) {
let value = rowMap.get(rowIdx);
if (value) {
copyString = copyString.concat(value);
}
copyString = copyString.concat('\t');
}
copyString = copyString.concat(row.join('\t').concat(eol));
});
copyString = copyString.concat(eol);
}
// Removes EoL from the end of the string
copyString = copyString.slice(0, -1 * eol.length);
return copyString;

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import QueryRunner from 'sql/platform/query/common/queryRunner';
import QueryRunner, { IQueryMessage } from 'sql/platform/query/common/queryRunner';
import { DataService } from 'sql/workbench/parts/grid/common/dataService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
@@ -31,9 +31,15 @@ export interface IQueryPlanInfo {
planXml: string;
}
export interface IQueryInfo {
selection: ISelectionData[];
messages: IQueryMessage[];
}
export interface IQueryEvent {
type: queryeditor.QueryEvent;
type: queryeditor.QueryEventType;
uri: string;
queryInfo: IQueryInfo;
params?: any;
}

View File

@@ -283,7 +283,12 @@ export class QueryModelService implements IQueryModelService {
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStop',
uri: uri
uri: uri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
}
};
this._onQueryEvent.fire(event);
@@ -296,7 +301,12 @@ export class QueryModelService implements IQueryModelService {
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStart',
uri: uri
uri: uri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
}
};
this._onQueryEvent.fire(event);
@@ -307,7 +317,12 @@ export class QueryModelService implements IQueryModelService {
let event: IQueryEvent = {
type: 'queryUpdate',
uri: uri
uri: uri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
}
};
this._onQueryEvent.fire(event);
@@ -319,6 +334,11 @@ export class QueryModelService implements IQueryModelService {
let event: IQueryEvent = {
type: 'executionPlan',
uri: planInfo.fileUri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
},
params: planInfo
};
this._onQueryEvent.fire(event);
@@ -328,6 +348,11 @@ export class QueryModelService implements IQueryModelService {
let event: IQueryEvent = {
type: 'visualize',
uri: uri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
},
params: resultSetInfo
};
this._onQueryEvent.fire(event);
@@ -443,7 +468,12 @@ export class QueryModelService implements IQueryModelService {
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStop',
uri: ownerUri
uri: ownerUri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
},
};
this._onQueryEvent.fire(event);
@@ -455,7 +485,12 @@ export class QueryModelService implements IQueryModelService {
// fire extensibility API event
let event: IQueryEvent = {
type: 'queryStart',
uri: ownerUri
uri: ownerUri,
queryInfo:
{
selection: info.selection,
messages: info.queryRunner.messages
},
};
this._onQueryEvent.fire(event);

View File

@@ -44,6 +44,7 @@ export interface IQueryMessage extends azdata.IResultMessage {
export default class QueryRunner extends Disposable {
// MEMBER VARIABLES ////////////////////////////////////////////////////
private _resultLineOffset: number;
private _resultColumnOffset: number;
private _totalElapsedMilliseconds: number = 0;
private _isExecuting: boolean = false;
private _hasCompleted: boolean = false;
@@ -179,6 +180,7 @@ export default class QueryRunner extends Disposable {
if (types.isObject(input) || types.isUndefinedOrNull(input)) {
// Update internal state to show that we're executing the query
this._resultLineOffset = input ? input.startLine : 0;
this._resultColumnOffset = input ? input.startColumn : 0;
this._isExecuting = true;
this._totalElapsedMilliseconds = 0;
// TODO issue #228 add statusview callbacks here
@@ -243,8 +245,10 @@ export default class QueryRunner extends Disposable {
this._batchSets.map(batch => {
if (batch.selection) {
batch.selection.startLine = batch.selection.startLine + this._resultLineOffset;
batch.selection.endLine = batch.selection.endLine + this._resultLineOffset;
batch.selection.startLine += this._resultLineOffset;
batch.selection.startColumn += this._resultColumnOffset;
batch.selection.endLine += this._resultLineOffset;
batch.selection.endColumn += this._resultColumnOffset;
}
});
@@ -271,7 +275,9 @@ export default class QueryRunner extends Disposable {
// Recalculate the start and end lines, relative to the result line offset
if (batch.selection) {
batch.selection.startLine += this._resultLineOffset;
batch.selection.startColumn += this._resultColumnOffset;
batch.selection.endLine += this._resultLineOffset;
batch.selection.endColumn += this._resultColumnOffset;
}
// Set the result sets as an empty array so that as result sets complete we can add to the list

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* 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 ? connectionProfile.databaseName : '';
this.status = status;
}
}

View File

@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* 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/platform/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;
}

View File

@@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------------------------
* 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/platform/queryHistory/common/queryHistoryService.ts';
import { IQueryModelService, IQueryEvent } from 'sql/platform/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/platform/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';
import { IConfigurationChangedEvent } from 'sql/workbench/parts/profiler/browser/profilerFindWidget';
/**
* 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;
// 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._captureEnabled = !!this._configurationService.getValue<boolean>('queryHistory.captureEnabled');
this._register(this._configurationService.onDidChangeConfiguration((e: IConfigurationChangeEvent) => {
if (e.affectedKeys.includes('queryHistory.captureEnabled')) {
this.updateCaptureEnabled();
}
}));
this._register(_queryModelService.onQueryEvent((e: IQueryEvent) => {
if (this._captureEnabled && e.type === 'queryStop') {
const uri: URI = URI.parse(e.uri);
// 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 = _modelService.getModel(uri).getValueInRange(new Range(
e.queryInfo.selection[0].startLine + 1,
e.queryInfo.selection[0].startColumn + 1,
e.queryInfo.selection[0].endLine,
e.queryInfo.selection[0].endColumn + 1));
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');
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 {
}
}

View File

@@ -545,7 +545,7 @@ declare module 'sqlops' {
checked?: boolean;
}
export interface TextComponentProperties {
export interface TextComponentProperties extends ComponentProperties, TitledComponentProperties {
value?: string;
links?: LinkArea[];
}
@@ -555,7 +555,7 @@ declare module 'sqlops' {
url: string;
}
export interface HyperlinkComponentProperties extends ComponentProperties {
export interface HyperlinkComponentProperties extends ComponentProperties, TitledComponentProperties {
label: string;
url: string;
}
@@ -651,6 +651,13 @@ declare module 'sqlops' {
yOffsetChange?: number;
}
export interface TitledComponentProperties {
/**
* The title for the component. This title will show when hovered over
*/
title?: string;
}
export interface CardComponent extends Component, CardProperties {
onDidActionClick: vscode.Event<ActionDescriptor>;
onCardSelectedChanged: vscode.Event<any>;
@@ -660,13 +667,14 @@ declare module 'sqlops' {
}
export interface TextComponent extends Component, ComponentProperties {
value: string;
export interface TextComponent extends Component, TextComponentProperties {
/**
* An event called when the text is clicked
*/
onDidClick: vscode.Event<any>;
}
export interface HyperlinkComponent extends Component, HyperlinkComponentProperties {
label: string;
url: string;
}
export interface InputBoxComponent extends Component, InputBoxProperties {

View File

@@ -1112,6 +1112,13 @@ class TextComponentWrapper extends ComponentWrapper implements azdata.TextCompon
this.setProperty('value', v);
}
public get title(): string {
return this.properties['title'];
}
public set title(title: string) {
this.setProperty('title', title);
}
public get onDidClick(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
return emitter && emitter.event;

View File

@@ -326,8 +326,8 @@ export enum DataProviderType {
AgentServicesProvider = 'AgentServicesProvider',
CapabilitiesProvider = 'CapabilitiesProvider',
ObjectExplorerNodeProvider = 'ObjectExplorerNodeProvider',
IconProvider = 'IconProvider',
SerializationProvider = 'SerializationProvider'
SerializationProvider = 'SerializationProvider',
IconProvider = 'IconProvider'
}
export enum DeclarativeDataType {
@@ -579,10 +579,96 @@ export class ConnectionProfile {
options: { [name: string]: any };
static createFrom(options: any[]): ConnectionProfile {
throw new Error('Method not implemented');
// create from options
return undefined;
}
}
export enum SchemaUpdateAction {
Delete = 0,
Change = 1,
Add = 2
}
export enum SchemaDifferenceType {
Object = 0,
Property = 1
}
export enum SchemaCompareEndpointType {
Database = 0,
Dacpac = 1
}
export enum SchemaObjectType {
Aggregates = 0,
ApplicationRoles = 1,
Assemblies = 2,
AssemblyFiles = 3,
AsymmetricKeys = 4,
BrokerPriorities = 5,
Certificates = 6,
ColumnEncryptionKeys = 7,
ColumnMasterKeys = 8,
Contracts = 9,
DatabaseOptions = 10,
DatabaseRoles = 11,
DatabaseTriggers = 12,
Defaults = 13,
ExtendedProperties = 14,
ExternalDataSources = 15,
ExternalFileFormats = 16,
ExternalTables = 17,
Filegroups = 18,
FileTables = 19,
FullTextCatalogs = 20,
FullTextStoplists = 21,
MessageTypes = 22,
PartitionFunctions = 23,
PartitionSchemes = 24,
Permissions = 25,
Queues = 26,
RemoteServiceBindings = 27,
RoleMembership = 28,
Rules = 29,
ScalarValuedFunctions = 30,
SearchPropertyLists = 31,
SecurityPolicies = 32,
Sequences = 33,
Services = 34,
Signatures = 35,
StoredProcedures = 36,
SymmetricKeys = 37,
Synonyms = 38,
Tables = 39,
TableValuedFunctions = 40,
UserDefinedDataTypes = 41,
UserDefinedTableTypes = 42,
ClrUserDefinedTypes = 43,
Users = 44,
Views = 45,
XmlSchemaCollections = 46,
Audits = 47,
Credentials = 48,
CryptographicProviders = 49,
DatabaseAuditSpecifications = 50,
DatabaseEncryptionKeys = 51,
DatabaseScopedCredentials = 52,
Endpoints = 53,
ErrorMessages = 54,
EventNotifications = 55,
EventSessions = 56,
LinkedServerLogins = 57,
LinkedServers = 58,
Logins = 59,
MasterKeys = 60,
Routes = 61,
ServerAuditSpecifications = 62,
ServerRoleMembership = 63,
ServerRoles = 64,
ServerTriggers = 65
}
export enum ColumnType {
text = 0,
checkBox = 1,
@@ -600,3 +686,9 @@ export enum NotebookChangeKind {
Save = 2,
CellExecuted = 3
}
export type QueryEventType =
| 'queryStart'
| 'queryStop'
| 'executionPlan'
| 'visualize';

View File

@@ -10,14 +10,14 @@ import {
import * as azdata from 'azdata';
import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/workbench/browser/modelComponents/interfaces';
import { TitledComponent } from 'sql/workbench/browser/modelComponents/titledComponent';
@Component({
selector: 'modelview-hyperlink',
template: `<a [href]="getUrl()" target="blank">{{getLabel()}}</a>`
template: `<a [href]="getUrl()" [title]="title" target="blank">{{getLabel()}}</a>`
})
export default class HyperlinkComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
export default class HyperlinkComponent extends TitledComponent implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;

View File

@@ -101,3 +101,7 @@ export interface IModelStore {
*/
validate(component: IComponent): Thenable<boolean>;
}
export interface ITitledComponent {
title?: string;
}

View File

@@ -11,16 +11,16 @@ import {
import * as azdata from 'azdata';
import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/workbench/browser/modelComponents/interfaces';
import { SafeHtml, DomSanitizer } from '@angular/platform-browser';
import { TitledComponent } from 'sql/workbench/browser/modelComponents/titledComponent';
@Component({
selector: 'modelview-text',
template: `
<p [style.width]="getWidth()" [innerHTML]="getValue()" [ngStyle]="this.CSSStyles" (click)="onClick()"></p>`
<p [style.width]="getWidth()" [innerHTML]="getValue()" [title]="title" [ngStyle]="this.CSSStyles" (click)="onClick()"></p>`
})
export default class TextComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
export default class TextComponent extends TitledComponent implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;

View File

@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
ChangeDetectorRef, ElementRef
} from '@angular/core';
import { ITitledComponent } from 'sql/workbench/browser/modelComponents/interfaces';
import * as azdata from 'azdata';
import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase';
export abstract class TitledComponent extends ComponentBase implements ITitledComponent {
constructor(
protected _changeRef: ChangeDetectorRef,
protected _el: ElementRef) {
super(_changeRef, _el);
}
public get title(): string {
return this.getPropertyOrDefault<azdata.HyperlinkComponentProperties, string>((props) => props.title, '');
}
public set title(newTitle: string) {
this.setPropertyFromUI<azdata.HyperlinkComponentProperties, string>((properties, title) => { properties.title = title; }, newTitle);
}
}

View File

@@ -10,13 +10,11 @@ import {
RunQueryOnConnectionMode, IConnectionResult
} from 'sql/platform/connection/common/connectionManagement';
import { EditDataInput } from 'sql/workbench/parts/editData/browser/editDataInput';
import { IRestoreDialogController } from 'sql/platform/restore/common/restoreService';
import { IInsightsDialogService } from 'sql/workbench/services/insights/browser/insightsDialogService';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { QueryInput } from 'sql/workbench/parts/query/common/queryInput';
import { DashboardInput } from 'sql/workbench/parts/dashboard/browser/dashboardInput';
import { ProfilerInput } from 'sql/workbench/parts/profiler/browser/profilerInput';
import { IBackupUiService } from 'sql/workbench/services/backup/common/backupUiService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IInsightsConfig } from 'sql/platform/dashboard/browser/insightRegistry';

View File

@@ -9,7 +9,7 @@ import { IClipboardService } from 'sql/platform/clipboard/common/clipboardServic
import { localize } from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowsService, FileFilter } from 'vs/platform/windows/common/windows';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { URI } from 'vs/base/common/uri';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
@@ -152,7 +152,9 @@ export class SaveImageAction extends Action {
public async run(context: IChartActionContext): Promise<boolean> {
if (context.insight instanceof Graph) {
const filePath = await this.fileDialogService.pickFileToSave({});
let fileFilters = new Array<FileFilter>({ extensions: ['png'], name: localize('resultsSerializer.saveAsFileExtensionPNGTitle', "PNG") });
const filePath = await this.fileDialogService.pickFileToSave({ filters: fileFilters });
const data = (<Graph>context.insight).getCanvasData();
if (!data) {
this.notificationService.error(localize('chartNotFound', "Could not find chart to save"));

View File

@@ -124,6 +124,9 @@ export abstract class JobManagementView extends TabChild implements AfterContent
public refreshJobs() {
this._agentViewComponent.refresh = true;
}
public openLastNRun(notebook: azdata.AgentNotebookInfo, n: number, maxVisibleElements: number) {
}
}
export interface JobActionContext {

View File

@@ -441,6 +441,7 @@ notebookhistory-component
height: 40px;
background-size: 40px 40px;
background-repeat: no-repeat;
cursor: pointer;
}
.vs-dark .notebook-grid-item > .img-error,

View File

@@ -5,18 +5,17 @@
agentview-component {
height: 100%;
width : 100%;
width: 100%;
display: block;
}
jobsview-component,
notebooksview-component {
height: 100%;
width : 100%;
width: 100%;
display: block;
}
.job-heading-container {
height: 50px;
border-bottom: 3px solid #f4f4f4;
@@ -33,13 +32,13 @@ notebooksview-component {
.jobview-grid {
height: calc(100% - 75px);
width : 100%;
width: 100%;
display: block;
}
.jobnotebooksview-grid {
height: calc(100% - 75px);
width : 100%;
width: 100%;
display: block;
}
@@ -53,13 +52,35 @@ notebooksview-component {
font-weight: bold;
}
.hc-black #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell,
.hc-black #notebooksDiv notebooks-component .jobnotebooksview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
.hc-black
#jobsDiv
jobsview-component
.jobview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell,
.hc-black
#notebooksDiv
notebooks-component
.jobnotebooksview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell {
border: 1px solid #2b56f2;
}
#jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell,
#notebooksDiv notebooksview-component .jobnotebooksview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
#jobsDiv
jobsview-component
.jobview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell,
#notebooksDiv
notebooksview-component
.jobnotebooksview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell {
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
@@ -103,8 +124,14 @@ notebooksview-component {
}
#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext,
#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext,
#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1.notebook-error-row .jobview-jobnametext {
#notebooksDiv
.jobnotebooksview-grid
.slick-cell.l1.r1.error-row
.jobview-jobnametext,
#notebooksDiv
.jobnotebooksview-grid
.slick-cell.l1.r1.notebook-error-row
.jobview-jobnametext {
width: 100%;
}
@@ -117,7 +144,10 @@ notebooksview-component {
display: inline-block;
}
#operatorsDiv .joboperatorsview-grid .slick-cell.l1.r1 .operatorview-operatornametext,
#operatorsDiv
.joboperatorsview-grid
.slick-cell.l1.r1
.operatorview-operatornametext,
#alertsDiv .jobalertsview-grid .slick-cell.l1.r1 .alertview-alertnametext,
#proxiesDiv .jobproxiesview-grid .slick-cell.l1.r1 .proxyview-proxynametext {
text-overflow: ellipsis;
@@ -132,8 +162,18 @@ notebooksview-component {
border-bottom: none;
}
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row,
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row {
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
.slick-cell.l1.r1.error-row,
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
.slick-cell.l1.r1.error-row {
width: 100%;
opacity: 1;
font-weight: 700;
@@ -141,7 +181,12 @@ notebooksview-component {
color: orangered;
}
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.notebook-error-row {
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
.slick-cell.l1.r1.notebook-error-row {
width: 100%;
opacity: 1;
font-weight: 700;
@@ -150,9 +195,24 @@ notebooksview-component {
color: orange;
}
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row,
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row,
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.notebook-error-row {
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
.slick-cell._detail_selector.error-row,
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
.slick-cell._detail_selector.error-row,
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
.slick-cell._detail_selector.notebook-error-row {
opacity: 1;
}
@@ -192,7 +252,7 @@ notebooksview-component {
#jobsDiv .detail,
#notebooksDiv .detail {
padding: 5px
padding: 5px;
}
#jobsDiv .preload,
@@ -202,69 +262,83 @@ notebooksview-component {
#jobsDiv .dynamic-cell-detail > :first-child,
#notebooksDiv .dynamic-cell-detail > :first-child {
vertical-align: middle;
line-height: 13px;
padding: 10px;
margin-left: 20px;
vertical-align: middle;
line-height: 13px;
padding: 10px;
margin-left: 20px;
}
.jobsview-icon {
background-image: url('./job.svg');
background-image: url("./job.svg");
}
.vs-dark .jobsview-icon,
.hc-black .jobsview-icon {
background-image: url('./job_inverse.svg');
background-image: url("./job_inverse.svg");
}
.notebooksview-icon {
background-image: url('./notebook.svg');
background-image: url("./notebook.svg");
}
.vs-dark .notebooksview-icon,
.hc-black .notebooksview-icon {
background-image: url('./notebook_inverse.svg');
background-image: url("./notebook_inverse.svg");
}
.alertsview-icon {
background-image: url('./alert.svg');
background-image: url("./alert.svg");
}
.vs-dark .alertsview-icon,
.hc-black .alertsview-icon {
background-image: url('./alert_inverse.svg');
background-image: url("./alert_inverse.svg");
}
.proxiesview-icon {
background-image: url('./proxy.svg');
background-image: url("./proxy.svg");
}
.vs-dark .proxiesview-icon,
.hc-black .proxiesview-icon {
background-image: url('./proxy_inverse.svg');
background-image: url("./proxy_inverse.svg");
}
.operatorsview-icon {
background-image: url('./operator.svg');
background-image: url("./operator.svg");
}
.vs-dark .operatorsview-icon,
.hc-black .operatorsview-icon {
background-image: url('./operator_inverse.svg');
background-image: url("./operator_inverse.svg");
}
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.even > .slick-cell,
agentview-component .jobnotebooksview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
agentview-component
.jobview-grid
.grid-canvas
> .ui-widget-content.slick-row.even
> .slick-cell,
agentview-component
.jobnotebooksview-grid
.grid-canvas
> .ui-widget-content.slick-row.odd
> .slick-cell {
cursor: pointer;
}
.vs-dark .jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.vs-dark
.jobview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted white;
}
.hc-black .jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.hc-black
.jobview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted #2b56f2;
}
@@ -279,47 +353,188 @@ agentview-component .jobnotebooksview-grid .grid-canvas > .ui-widget-content.sli
padding-left: 15px;
}
#jobsDiv jobsview-component .jobview-grid .slick-cell.l1.r1.error-row td.jobview-jobnameindicatorfailure,
#notebooksDiv notebooksview-component .jobnotebooksview-grid .slick-cell.l1.r1.error-row td.jobview-jobnameindicatorfailure,
#notebooksDiv notebooksview-component .jobnotebooksview-grid .slick-cell.l1.r1.notebook-error-row td.jobview-jobnameindicatorfailure {
#jobsDiv
jobsview-component
.jobview-grid
.slick-cell.l1.r1.error-row
td.jobview-jobnameindicatorfailure,
#notebooksDiv
notebooksview-component
.jobnotebooksview-grid
.slick-cell.l1.r1.error-row
td.jobview-jobnameindicatorfailure,
#notebooksDiv
notebooksview-component
.jobnotebooksview-grid
.slick-cell.l1.r1.notebook-error-row
td.jobview-jobnameindicatorfailure {
width: 0;
background: none;
}
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered,
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
.vs-dark
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.vs-dark
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.vs-dark
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
.vs-dark
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.vs-dark
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.vs-dark
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell {
background: #444444 !important;
}
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
.hc-black #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.hc-black #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.hc-black #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
.hc-black
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.hc-black
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.hc-black
#jobsDiv
.jobview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
.hc-black
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.hc-black
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.hc-black
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell {
background: none !important;
}
table.jobprevruns div.bar0, table.jobprevruns div.bar1, table.jobprevruns div.bar2,
table.jobprevruns div.bar3, table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
table.jobprevruns div.bar,
table.jobprevruns div.bar0,
table.jobprevruns div.bar1,
table.jobprevruns div.bar2,
table.jobprevruns div.bar3,
table.jobprevruns div.bar4,
table.jobprevruns div.bar5 {
padding-top: 3px;
padding-left: 5px;
width: 10px;
}
table.jobprevruns div.bar {
cursor: pointer;
}
.jobview-grid .slick-cell.l10.r10,
.jobnotebooksview-grid .slick-cell.l10.r10 {
text-align: center;
@@ -336,51 +551,51 @@ table.jobprevruns > tbody {
#notebooksDiv .jobnotebooksview-grid {
height: calc(100% - 75px);
width : 100%;
width: 100%;
display: block;
}
#alertsDiv .jobalertsview-grid {
height: calc(100% - 75px);
width : 100%;
width: 100%;
display: block;
}
#operatorsDiv .joboperatorsview-grid {
height: calc(100% - 75px);
width : 100%;
width: 100%;
display: block;
overflow: scroll;
}
#proxiesDiv .jobproxiesview-grid {
height: calc(100% - 75px);
width : 100%;
width: 100%;
display: block;
}
.vs .action-label.icon.refreshIcon {
background-image: url('refresh.svg');
background-image: url("refresh.svg");
}
.vs-dark .action-label.icon.refreshIcon,
.hc-black .action-label.icon.refreshIcon {
background-image: url('refresh_inverse.svg');
.hc-black .action-label.icon.refreshIcon {
background-image: url("refresh_inverse.svg");
}
.vs .action-label.icon.openNotebook {
background-image: url('open_notebook.svg');
background-image: url("open_notebook.svg");
}
.vs-dark .action-label.icon.openNotebook,
.hc-black .action-label.icon.openNotebook {
background-image: url('open_notebook_inverse.svg');
.hc-black .action-label.icon.openNotebook {
background-image: url("open_notebook_inverse.svg");
}
.agent-actionbar-container .monaco-action-bar > ul.actions-container > li.action-item {
.agent-actionbar-container
.monaco-action-bar
> ul.actions-container
> li.action-item {
padding-left: 20px;
}
@@ -390,107 +605,311 @@ notebooksview-component .jobnotebooksview-grid .slick-cell.notebook-error-row {
opacity: 0;
}
#notebooksDiv notebooksview-component .jobnotebooksview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
#notebooksDiv
notebooksview-component
.jobnotebooksview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell {
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
.vs-dark
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.vs-dark
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.vs-dark
#notebooksDiv
.jobnotebooksview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell {
background: #444444 !important;
}
.vs-dark .jobnotebooksview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.vs-dark
.jobnotebooksview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted white;
}
.jobnotebooksview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.jobnotebooksview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted #444444;
}
#alertsDiv jobalertsview-component .jobalertsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
#alertsDiv
jobalertsview-component
.jobalertsview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell {
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
#alertsDiv
.jobalertsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
#alertsDiv
.jobalertsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
#alertsDiv
.jobalertsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
.vs-dark
#alertsDiv
.jobalertsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.vs-dark
#alertsDiv
.jobalertsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.vs-dark
#alertsDiv
.jobalertsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell {
background: #444444 !important;
}
.vs-dark .jobalertsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.vs-dark
.jobalertsview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted white;
}
.jobalertsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.jobalertsview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted #444444;
}
#operatorsDiv joboperatorsview-component .joboperatorsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
#operatorsDiv
joboperatorsview-component
.joboperatorsview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell {
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
#operatorsDiv
.joboperatorsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
#operatorsDiv
.joboperatorsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
#operatorsDiv
.joboperatorsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
.vs-dark
#operatorsDiv
.joboperatorsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.vs-dark
#operatorsDiv
.joboperatorsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.vs-dark
#operatorsDiv
.joboperatorsview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell {
background: #444444 !important;
}
.vs-dark .joboperatorsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.vs-dark
.joboperatorsview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted white;
}
.joboperatorsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.joboperatorsview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted #444444;
}
#proxiesDiv jobproxiesview-component .jobproxiesview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
#proxiesDiv
jobproxiesview-component
.jobproxiesview-grid
.grid-canvas
.ui-widget-content.slick-row
.slick-cell {
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
#proxiesDiv
.jobproxiesview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
#proxiesDiv
.jobproxiesview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell,
#proxiesDiv
.jobproxiesview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
.vs-dark
#proxiesDiv
.jobproxiesview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row:hover
> .slick-cell,
.vs-dark
#proxiesDiv
.jobproxiesview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row
> .slick-cell.hovered,
.vs-dark
#proxiesDiv
.jobproxiesview-grid
> .monaco-table
.slick-viewport
> .grid-canvas
> .ui-widget-content.slick-row.hovered
> .slick-cell {
background: #444444 !important;
}
.vs-dark .jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.vs-dark
.jobproxiesview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted white;
}
.jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
.jobproxiesview-grid
> .monaco-table
.slick-header-columns
.slick-resizable-handle {
border-left: 1px dotted #444444;
}

View File

@@ -5,7 +5,7 @@
*--------------------------------------------------------------------------------------------*/
-->
<div class="jobhistory-heading-container">
<h1 class="job-heading">Notebook | {{ this._agentNotebookInfo?.name }}</h1>
<h1 class="job-heading">Notebook Jobs| {{ this._agentNotebookInfo?.name }}</h1>
<div class="icon in-progress" *ngIf="showProgressWheel()"></div>
</div>
@@ -29,9 +29,9 @@
<!-- Overview -->
<div class="overview-container">
<div class="overview-tab" (click)="toggleCollapse()" tabindex="0">
<div class="overview-tab" (click)="toggleCollapse()">
<input id="accordion" type="checkbox" />
<label for="accordion">
<label for="accordion" tabindex="0">
<div
class="resultsViewCollapsible collapsed"
(click)="toggleCollapse()"
@@ -48,10 +48,10 @@
{{ this._agentNotebookInfo?.targetDatabase }}
</td>
<td id="col3">
Enabled:
Has Schedule:
</td>
<td id="col4">
{{ this._agentNotebookInfo?.enabled }}
{{ this._agentNotebookInfo?.hasSchedule }}
</td>
</tr>
<tr>
@@ -81,11 +81,10 @@
<div
class="overview-tab"
(click)="toggleGridCollapse(i)"
tabindex="{{i}}"
*ngIf="grid.histories?.length"
>
<input id="accordion{{ i }}" type="checkbox" class="grid-arrow" />
<label for="accordion{{ i }}">
<input id="accordion{{ i }}" type="checkbox" class="grid-arrow"/>
<label for="accordion{{ i }}" tabindex="0">
<div id= "history-grid-icon{{i}}" (click)="toggleGridCollapse(i)"
class="resultsViewCollapsible"></div>
{{ grid.title }}
@@ -94,11 +93,13 @@
<div
*ngFor="let history of grid.histories"
class="notebook-grid-item"
tabindex="0"
(dblclick)="openNotebook(history)"
title="{{ createdTooltip(history) }}"
(contextmenu)="
openHistoryContextMenu($event, history, grid.contextMenuType)
"
(keydown.enter)="openNotebook(history)"
>
<div
*ngIf="history.materializedNotebookErrorInfo"

View File

@@ -14,7 +14,7 @@ import { Table } from 'sql/base/browser/ui/table/table';
import { AgentViewComponent } from 'sql/workbench/parts/jobManagement/browser/agentView.component';
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
import { NotebookCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
import { EditJobAction, NewNotebookJobAction, RunJobAction, EditNotebookJobAction, JobsRefreshAction, IJobActionInfo, DeleteNotebookAction } from 'sql/platform/jobManagement/browser/jobActions';
import { EditJobAction, NewNotebookJobAction, RunJobAction, EditNotebookJobAction, JobsRefreshAction, IJobActionInfo, DeleteNotebookAction, OpenLatestRunMaterializedNotebook } from 'sql/platform/jobManagement/browser/jobActions';
import { JobManagementUtilities } from 'sql/platform/jobManagement/browser/jobManagementUtilities';
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
@@ -395,6 +395,22 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit,
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
});
jQuery('.bar').click((e) => {
let clickEventTarget = e.target;
let barId = Number(clickEventTarget.id.replace('bar', ''));
let jobId = clickEventTarget.parentElement.offsetParent.id.replace('notebook', '');
let notebooks = this._notebookCacheObject.notebooks;
let targetNotebook: azdata.AgentNotebookInfo;
for (let i = 0; i < notebooks.length; i++) {
if (jobId === notebooks[i].jobId) {
targetNotebook = notebooks[i];
break;
}
}
this.openLastNRun(targetNotebook, barId, 5);
e.stopPropagation();
});
// cache the dataview for future use
this._notebookCacheObject.dataView = this.dataView;
this.filterValueMap['start'] = [[], this.dataView.getItems()];
@@ -540,21 +556,21 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit,
if (runChart && runChart.length > 0) {
return `<table class="jobprevruns" id="${dataContext.id}">
<tr>
<td>${runChart[0] ? runChart[0] : '<div class="bar0"></div>'}</td>
<td>${runChart[1] ? runChart[1] : '<div class="bar1"></div>'}</td>
<td>${runChart[2] ? runChart[2] : '<div class="bar2"></div>'}</td>
<td>${runChart[3] ? runChart[3] : '<div class="bar3"></div>'}</td>
<td>${runChart[4] ? runChart[4] : '<div class="bar4"></div>'}</td>
<td>${runChart[0] ? runChart[0] : '<div class="bar" id="bar0"></div>'}</td>
<td>${runChart[1] ? runChart[1] : '<div class="bar" id="bar1"></div>'}</td>
<td>${runChart[2] ? runChart[2] : '<div class="bar" id="bar2"></div>'}</td>
<td>${runChart[3] ? runChart[3] : '<div class="bar" id="bar3"></div>'}</td>
<td>${runChart[4] ? runChart[4] : '<div class="bar" id="bar4"></div>'}</td>
</tr>
</table>`;
} else {
return `<table class="jobprevruns" id="${dataContext.id}">
<tr>
<td><div class="bar0"></div></td>
<td><div class="bar1"></div></td>
<td><div class="bar2"></div></td>
<td><div class="bar3"></div></td>
<td><div class="bar4"></div></td>
<td><div class="bar" id="bar0"></div></td>
<td><div class="bar" id="bar1"></div></td>
<td><div class="bar" id="bar2"></div></td>
<td><div class="bar" id="bar3"></div></td>
<td><div class="bar" id="bar4"></div></td>
</tr>
</table>`;
}
@@ -667,7 +683,7 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit,
if (jobHistories[i].materializedNotebookErrorInfo !== null && jobHistories[i].materializedNotebookErrorInfo.length > 0) {
bgColor = 'orange';
}
let runGraph = jQuery(`table.jobprevruns#${jobId} > tbody > tr > td > div.bar${i}`);
let runGraph = jQuery(`table.jobprevruns#${jobId} > tbody > tr > td > #bar${i}`);
if (runGraph.length > 0) {
runGraph.css('height', chartHeights[i]);
@@ -884,10 +900,11 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit,
const editAction = this._instantiationService.createInstance(EditJobAction);
const editNotebookAction = this._instantiationService.createInstance(EditNotebookJobAction);
const runJobAction = this._instantiationService.createInstance(RunJobAction);
const openLatestRunAction = this._instantiationService.createInstance(OpenLatestRunMaterializedNotebook);
return [
runJobAction,
openLatestRunAction,
editNotebookAction,
editAction,
this._instantiationService.createInstance(DeleteNotebookAction)
];
}
@@ -981,4 +998,31 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit,
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
await this._commandService.executeCommand('agent.openNotebookDialog', ownerUri);
}
public async openLastNRun(notebook: azdata.AgentNotebookInfo, n: number, maxVisibleElements: number) {
let notebookHistories = this._notebookCacheObject.getNotebookHistory(notebook.jobId);
if (notebookHistories && n < notebookHistories.length) {
notebookHistories = notebookHistories.sort((h1, h2) => {
return new Date(h2.runDate).getTime() - new Date(h1.runDate).getTime();
});
if (notebookHistories.length > maxVisibleElements) {
n = notebookHistories.length - (maxVisibleElements - n);
}
n = notebookHistories.length - 1 - n;
let history: azdata.AgentNotebookHistoryInfo = notebookHistories[n];
// Did Job Fail? if yes, then notebook to return
if (history.runStatus === 0) {
return;
}
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
let targetDatabase = notebook.targetDatabase;
const result = await this._jobManagementService.getMaterialziedNotebook(ownerUri, targetDatabase, history.materializedNotebookId);
if (result) {
let regex = /:|-/gi;
let readableDataTimeString = history.runDate.replace(regex, '').replace(' ', '');
let tempNotebookFileName = notebook.name + '_' + readableDataTimeString;
await this._commandService.executeCommand('agent.openNotebookEditorFromJsonString', tempNotebookFileName, result.notebookMaterialized);
}
}
}
}

View File

@@ -341,10 +341,17 @@ export class NotebookInput extends EditorInput {
} else {
let textOrUntitledEditorModel: UntitledEditorModel | IEditorModel;
if (this.resource.scheme === Schemas.untitled) {
textOrUntitledEditorModel = this._untitledEditorModel ? this._untitledEditorModel : await this._textInput.resolve();
}
else {
if (this._untitledEditorModel) {
this._untitledEditorModel.textEditorModel.onBeforeAttached();
textOrUntitledEditorModel = this._untitledEditorModel;
} else {
let resolvedInput = await this._textInput.resolve();
resolvedInput.textEditorModel.onBeforeAttached();
textOrUntitledEditorModel = resolvedInput;
}
} else {
const textEditorModelReference = await this.textModelService.createModelReference(this.resource);
textEditorModelReference.object.textEditorModel.onBeforeAttached();
textOrUntitledEditorModel = await textEditorModelReference.object.load();
}
this._model = this.instantiationService.createInstance(NotebookEditorModel, this.resource, textOrUntitledEditorModel);
@@ -385,6 +392,9 @@ export class NotebookInput extends EditorInput {
}
public dispose(): void {
if (this._model) {
this._model.editorModel.textEditorModel.onBeforeDetached();
}
this._disposeContainer();
super.dispose();
}

View File

@@ -69,17 +69,23 @@ export class NotebookTextFileModel {
} else {
newOutput = '\n'.concat(newOutput).concat('\n');
}
let range = this.getEndOfOutputs(textEditorModel, contentChange.cells[0].cellGuid);
if (range) {
// Execution count will always be after the end of the outputs in JSON. This is a sanity mechanism.
let executionCountMatch = this.getExecutionCountRange(textEditorModel, contentChange.cells[0].cellGuid);
if (!executionCountMatch || !executionCountMatch.range) {
return false;
}
let endOutputsRange = this.getEndOfOutputs(textEditorModel, contentChange.cells[0].cellGuid);
if (endOutputsRange && endOutputsRange.startLineNumber < executionCountMatch.range.startLineNumber) {
textEditorModel.textEditorModel.applyEdits([{
range: new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn),
range: new Range(endOutputsRange.startLineNumber, endOutputsRange.startColumn, endOutputsRange.startLineNumber, endOutputsRange.startColumn),
text: newOutput
}]);
return true;
}
} else {
return false;
}
return true;
return false;
}
public transformAndApplyEditForCellUpdated(contentChange: NotebookContentChange, textEditorModel: TextFileEditorModel | UntitledEditorModel): boolean {

View File

@@ -589,6 +589,66 @@ suite('Notebook Editor Model', function (): void {
should(notebookEditorModel.lastEditFullReplacement).equal(false);
});
test('should not insert update at incorrect location', async function (): Promise<void> {
await createNewNotebookModel();
let notebookEditorModel = await createTextEditorModel(this);
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
let newCell = notebookModel.addCell(CellTypes.Code);
let contentChange: NotebookContentChange = {
changeType: NotebookChangeType.CellsModified,
cells: [newCell],
cellIndex: 0
};
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
should(notebookEditorModel.lastEditFullReplacement).equal(true);
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(14)).equal(' "outputs": [');
// First update the model with unmatched brackets
let newUnmatchedBracketOutput: nb.IStreamResult = { output_type: 'stream', name: 'stdout', text: '[0em' };
newCell[<any>'_outputs'] = newCell.outputs.concat(newUnmatchedBracketOutput);
contentChange = {
changeType: NotebookChangeType.CellOutputUpdated,
cells: [newCell]
};
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(8)).equal(' "source": [');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(12)).equal(' "azdata_cell_guid": "' + newCell.cellGuid + '"');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(14)).equal(' "outputs": [');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(26)).equal(' "text": "[0em"');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(27)).equal('}');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(28)).equal(' ],');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(29)).equal(' "execution_count": 0');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(30)).equal(' }');
should(notebookEditorModel.lastEditFullReplacement).equal(false);
// Now test updating the model after an unmatched bracket was previously output
let newBracketlessOutput: nb.IStreamResult = { output_type: 'stream', name: 'stdout', text: 'test test test' };
newCell[<any>'_outputs'] = newCell[<any>'_outputs'].concat(newBracketlessOutput);
contentChange = {
changeType: NotebookChangeType.CellOutputUpdated,
cells: [newCell]
};
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(32)).equal(' "text": "test test test"');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(33)).equal(' }');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(34)).equal(' ],');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(35)).equal(' "execution_count": 0');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(36)).equal(' }');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(37)).equal(' ]');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(38)).equal('}');
should(notebookEditorModel.lastEditFullReplacement).equal(true);
});
test('should not replace entire text model for output changes (1st update)', async function (): Promise<void> {
await createNewNotebookModel();

View File

@@ -302,8 +302,12 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
}
public onConnectCanceled(): void {
// If we're currently connecting and then cancel, set connected state to false
// Otherwise, keep connected state as it was
if (this.state.connecting) {
this.state.connected = false;
}
this.state.connecting = false;
this.state.connected = false;
}
public onConnectSuccess(params?: INewConnectionParams): void {

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#C5C5C5"/>
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#C5C5C5"/>
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#C5C5C5"/>
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#C5C5C5"/>
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="white"/>
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="white"/>
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="white"/>
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="white"/>
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#424242"/>
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#424242"/>
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#424242"/>
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#424242"/>
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -0,0 +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-blue{fill:#00539c;}</style></defs><title>pause</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="M13,2V14H8.5V2ZM3,14H7.5V2H3Z"/></g><g id="iconBg"><path class="icon-vs-action-blue" d="M4,3H6.5V13H4ZM9.5,3V13H12V3Z"/></g></svg>

After

Width:  |  Height:  |  Size: 505 B

View File

@@ -0,0 +1 @@
<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-blue{fill:#75beff;}</style></defs><title>pause</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="M13,2V14H8.5V2ZM3,14H7.5V2H3Z"/></g><g id="iconBg"><path class="icon-vs-action-blue" d="M4,3H6.5V13H4ZM9.5,3V13H12V3Z"/></g></svg>

After

Width:  |  Height:  |  Size: 505 B

Some files were not shown because too many files have changed in this diff Show More