Compare commits

..

45 Commits

Author SHA1 Message Date
Matt Irvine
d2b6f6844d Add connection API method to get URI (#2021) 2018-07-30 10:52:24 -07:00
Matt Irvine
e9ef95ef1f Save query result selection/scroll when switching tabs (#2052) 2018-07-27 14:01:34 -07:00
Aditya Bist
10eeb5374f enabled button to import queries from sql files (#2042) 2018-07-27 13:44:20 -07:00
Karl Burtram
332951bc8e Pick-up newer version proxy node module (#2074) 2018-07-27 15:20:25 -04:00
Amir Ali Omidi
4daf3280ff Bring in all the extensibility updates we added during the hackathon (#2056)
Extensibility updates
2018-07-26 16:33:32 -07:00
Aditya Bist
87bb2c74d9 added .sql to associated files to sql ops (#2060) 2018-07-26 16:33:08 -07:00
Abbie Petchtes
ba011853a0 Add text editor component for model view (#2058)
* add text editor component for model view

* add comments
2018-07-26 15:52:33 -07:00
Aditya Bist
461a158ac3 bumped version for insider build (#2053) 2018-07-26 13:41:21 -07:00
Aditya Bist
85f59f1103 removed the export from defaultSort function (#2048) 2018-07-26 11:10:14 -07:00
Aditya Bist
335b9f445f Feature: Parse Query Syntax (#1997)
* feature/parseSyntax

* added error when no connection

* removed elapsed time logic

* code review comments

* changed error message to match SSMS
2018-07-25 16:11:58 -07:00
ranasaria
7cda45c904 fix for issue #1604 (#2029)
* Adding min height to .connection-dialog .tabbedPanel so that connection dialog add a vertical scroll bar appropriately when the ops studio window is vertically resized.
2018-07-25 12:31:17 -07:00
Anthony Dresser
4159fdc1a3 add off by one handler for selection (#2009) 2018-07-25 12:30:38 -07:00
Matt Irvine
190da30979 Update some Agent license headers (#2008) 2018-07-24 18:14:57 -07:00
Aditya Bist
ed9c74b900 localized the unlocalized strings (#2018) 2018-07-24 16:49:06 -07:00
Leila Lali
6783766c33 declarative table layout and option (#2007)
* added scroll bar to table content
2018-07-24 15:04:21 -07:00
Karl Burtram
b27018b379 Update SQL Tools to 1.5.0-alpha.14 2018-07-24 12:22:21 -07:00
Karl Burtram
021d07e04a Add VS Code version to product metadata (#1998) 2018-07-24 13:54:29 -04:00
Ian Y. Choi
bf0baec392 Fixes a typo: Mimunum -> Minimum (#1994)
(trivial)
2018-07-24 12:35:46 -04:00
Karl Burtram
923cbac400 Add // {{SQL CARBON EDIT}} for previous commit 2018-07-23 19:09:56 -07:00
Karl Burtram
ab938f2536 Remove @ from word separators (#1990) 2018-07-23 21:10:01 -04:00
Amir Ali Omidi
a55b1804e9 Tackles issue #1723 (#1988)
* Tackles issue #1723
2018-07-23 16:55:53 -07:00
Matt Irvine
4bfa6b3a5d Save editor cursor/scroll position when switching sql files (#1978)
* Save editor layouts when switching sql files

* Move import

* Restore the view state after laying out the editor
2018-07-20 10:48:12 -04:00
Anthony Dresser
d20f24be18 Bump slickgrid to fix html content issue (#1980)
* add ' to escape strings for html

* update slickgrid to fix injection
2018-07-20 10:47:16 -04:00
Matt Irvine
8a17bae7a6 Loading spinner while validating next/done (#1975) 2018-07-19 17:31:30 -07:00
Anthony Dresser
d14c73fad5 vbump service-downloader (#1965) 2018-07-19 14:06:41 -07:00
Anthony Dresser
ce878e1def Rework slickgrid keyboard navigation (#1930)
* rewrite keybind nav to handle ctrl + home and end

* testing different options

* working on removed slickgrid changes we don't need

* formatting

* handle click handler to rowNumber

* fixing various bugs

* formatting

* readd click column to select

* add shift key to column select

* added logic for additional keybindings on grid

* add down and up arrow into keyboard navigation

* update styling and update slickgrid

* formatting

* update angular-slickgrid version

* remove index.js changes
2018-07-19 14:05:54 -07:00
Karl Burtram
a64a0d1db6 Bump SQL Ops to 0.32.1 for August iteration 2018-07-19 11:43:32 -07:00
Anthony Dresser
feab43f16d add ' to escape strings for html (#1974) 2018-07-19 14:42:29 -04:00
Karl Burtram
0d60fe775f Update Readme and Changelog (#1968) 2018-07-19 01:21:06 -04:00
Leila Lali
6680be6a73 adding task integration with wizard and dialog framework (#1929)
* adding task integration with wizard and dialog framework
2018-07-18 16:28:36 -07:00
Karl Burtram
e026ab85a7 Escape the aria string for Edit Data grid (#1958) 2018-07-17 18:45:10 -07:00
Karl Burtram
e53c903205 Update tools service to 1.5.0-alpha.12 2018-07-17 18:44:45 -07:00
Matt Irvine
03dbe8565f Set element text instead of HTML where possible (#1956) 2018-07-17 16:48:38 -07:00
Karl Burtram
708793cb23 Update tools service to 1.5.0-alpha.11 2018-07-17 16:47:31 -07:00
Aditya Bist
43ae4fb0aa Fixed 2 bugs in Agent Steps page. (#1953)
* fixed bug where steps werent being shown

* fixed customer reported issue
2018-07-17 15:49:38 -07:00
Aditya Bist
6b1d552277 Agent/step finishes (#1948)
* misc fixes in dialogs

* removed unused import

* disabled more advanced options
2018-07-17 10:51:31 -07:00
Madeline MacDonald
f24f576b72 Profiler display fixes (#1949)
* Fixing details tab, window resizing, and having profiler options in object explorer

* Fixing displaying connection names

* spacing

* Removing unnecessary code
2018-07-16 17:20:13 -07:00
Matt Irvine
c23328564f Hide correct element when hiding buttons (#1945) 2018-07-16 17:09:44 -07:00
Karl Burtram
cd6dd3dafa Bump Tools Service to 1.5.0-alpha.10 (#1947) 2018-07-16 15:40:41 -07:00
Aditya Bist
4081e15bef misc fixes in dialogs (#1942)
* misc fixes in dialogs

* removed unused import
2018-07-16 15:25:11 -07:00
Karl Burtram
b05e3813d1 Bump product version to 0.31.4 from July Public Preview (#1944) 2018-07-16 15:13:10 -07:00
Karl Burtram
3048311f40 Bump agent extension version to 0.31.4 (#1943) 2018-07-16 15:12:54 -07:00
Madeline MacDonald
1045392d91 Escaping profiler text (#1940) 2018-07-16 13:20:56 -07:00
Madeline MacDonald
7b23ca8ee7 Improving profiler controls and toolbar (#1931)
* Profiler toolbar improvements

* Fixing formatting issues
2018-07-16 11:44:14 -07:00
Karl Burtram
4d67eca8bb Dashboard agent tab style updates (#1934) 2018-07-15 10:51:20 -07:00
120 changed files with 2274 additions and 735 deletions

View File

@@ -1,5 +1,26 @@
# Change Log
## Version 0.31.4
* Release date: July 19, 2018
* Release status: Public Preview
## What's new in this version
* SQL Server Agent for SQL Operations Studio extension improvements
* Added view of Alerts, Operators, and Proxies and icons on left pane
* Added dialogs for New Job, New Job Step, New Alert, and New Operator
* Added Delete Job, Delete Alert, and Delete Operator (right-click)
* Added Previous Runs visualization
* Added Filters for each column name
* SQL Server Profiler for SQL Operations Studio extension improvements
* Added Hotkeys to quickly launch and start/stop Profiler
* Added 5 Default Templates to view Extended Events
* Added Server/Database connection name
* Added support for Azure SQL Database instances
* Added suggestion to exit Profiler when tab is closed when Profiler is still running
* Release of Combine Scripts Extension
* Wizard and Dialog Extensibility
* Fix GitHub Issues
## Version 0.30.6
* Release date: June 20, 2018
* Release status: Public Preview

View File

@@ -8,12 +8,12 @@ SQL Operations Studio is a data management tool that enables you to work with SQ
Platform | Link
-- | --
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=2005949
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2005950
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2005959
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2005960
Linux RPM | https://go.microsoft.com/fwlink/?linkid=2006083
Linux DEB | https://go.microsoft.com/fwlink/?linkid=2006084
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
@@ -85,7 +85,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Chinese (Traditional): Bruce Chen, Chiayi Yen, Kevin Yang, Winnie Lin, 保哥 Will, 謝政廷
* Korean: Do-Kyun Kim, Evelyn Kim, Helen Jung, Hong Jmee, jeongwoo choi, Jun Hyoung Lee, Jungsun Kim정선, Justin Yoo, Kavrith mucha, Kiwoong Youm, MinGyu Ju, MVP_JUNO BEA, Sejun Kim, SOONMAN KWON, sung man ko, Yeongrak Choi, younggun kim, Youngjae Kim, 소영 이
* Russian: Andrey Veselov, Anton Fontanov, Anton Savin, Elena Ostrovskaia, Igor Babichev, Maxim Zelensky, Rodion Fedechkin, Tasha T, Vladimir Zyryanov
* Portuguese Brazil: Daniel de Sousa, Diogo Duarte, Douglas Correa, Douglas Eccker, José Emanuel Mendes, Marcelo Fernandes, Marcondes Alexandre, Roberto Fonseca, Rodrigo Crespi
* Portuguese Brazil: Daniel de Sousa, Diogo Duarte, Douglas Correa, Douglas Eccker, José Emanuel Mendes, Marcelo Fernandes, Marcondes Alexandre, Roberto Fonseca, Rodrigo Crespi
And of course we'd like to thank the authors of all upstream dependencies. Please see a full list in the [ThirdPartyNotices.txt](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/ThirdPartyNotices.txt)

View File

@@ -61,6 +61,7 @@ Type: filesandordirs; Name: "{app}\_"
[Tasks]
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent
@@ -82,6 +83,13 @@ Root: HKCR; Subkey: "{#RegValueName}SourceFile\DefaultIcon"; ValueType: string;
Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin'))
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.sql"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
[Code]
// Don't allow installing conflicting architectures
function InitializeSetup(): Boolean;

View File

@@ -2,7 +2,7 @@
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.31.1",
"version": "0.31.4",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",

View File

@@ -117,7 +117,7 @@ export class JobData implements IAgentDialogData {
if (!result || !result.success) {
vscode.window.showErrorMessage(
localize('alertData.saveErrorMessage', "Alert update failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
localize('jobData.saveErrorMessage', "Job update failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
}
}

View File

@@ -35,7 +35,6 @@ export class ProxyData implements IAgentDialogData {
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.createProxy(this.ownerUri, this.toAgentProxyInfo());
console.log(result);
if (!result || !result.success) {
// TODO handle error here
}

View File

@@ -63,8 +63,9 @@ export class AlertDialog extends AgentDialog<AlertData> {
private static readonly AlertTypes: string[] = [
AlertData.AlertTypeSqlServerEventString,
AlertData.AlertTypePerformanceConditionString,
AlertData.AlertTypeWmiEventString
// Disabled until next release
// AlertData.AlertTypePerformanceConditionString,
// AlertData.AlertTypeWmiEventString
];
private static readonly AlertSeverities: string[] = [

View File

@@ -204,7 +204,7 @@ export class JobDialog extends AgentDialog<JobData> {
this.StepsTable_FailureColumnString
],
data: [],
height: 300
height: 430
}).component();
this.moveStepUpButton = view.modelBuilder.button()
@@ -219,6 +219,9 @@ export class JobDialog extends AgentDialog<JobData> {
width: 80
}).component();
this.moveStepUpButton.enabled = false;
this.moveStepDownButton.enabled = false;
this.newStepButton = view.modelBuilder.button().withProperties({
label: this.NewStepButtonString,
width: 80
@@ -261,7 +264,7 @@ export class JobDialog extends AgentDialog<JobData> {
this.AlertNameLabelString
],
data: [],
height: 300,
height: 430,
width: 400
}).component();
@@ -296,7 +299,7 @@ export class JobDialog extends AgentDialog<JobData> {
this.ScheduleNameLabelString
],
data: [],
height: 300,
height: 430,
width: 420
}).component();

View File

@@ -30,6 +30,7 @@ export class JobStepDialog {
private readonly PreviousButtonText: string = localize('jobStepDialog.previous','Previous');
private readonly SuccessfulParseText: string = localize('jobStepDialog.successParse', 'The command was successfully parsed.');
private readonly FailureParseText: string = localize('jobStepDialog.failParse', 'The command failed.');
private readonly BlankStepNameErrorText: string = localize('jobStepDialog.blankStepName', 'The step name cannot be left blank');
// General Control Titles
private readonly StepNameLabelString: string = localize('jobStepDialog.stepNameLabel', 'Step Name');
@@ -139,13 +140,19 @@ export class JobStepDialog {
this.openButton = view.modelBuilder.button()
.withProperties({
label: this.OpenCommandText,
width: '80px'
width: '80px',
isFile: true
}).component();
this.parseButton = view.modelBuilder.button()
.withProperties({
label: this.ParseCommandText,
width: '80px'
width: '80px',
isFile: false
}).component();
this.openButton.onDidClick(e => {
let queryContent = e;
this.commandTextBox.value = queryContent;
});
this.parseButton.onDidClick(e => {
if (this.commandTextBox.value) {
queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => {
@@ -185,6 +192,12 @@ export class JobStepDialog {
.withProperties({
}).component();
this.nameTextBox.required = true;
this.nameTextBox.onTextChanged(() => {
if (this.nameTextBox.value.length > 0) {
this.dialog.message = null;
}
});
this.typeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: this.TSQLScript,
@@ -465,7 +478,12 @@ export class JobStepDialog {
return outputFileForm;
}
private async execute() {
protected execute() {
this.model.stepName = this.nameTextBox.value;
if (!this.model.stepName || this.model.stepName.length === 0) {
this.dialog.message = this.dialog.message = { text: this.BlankStepNameErrorText };
return;
}
this.model.jobName = this.jobName;
this.model.id = this.stepId;
this.model.server = this.server;
@@ -479,7 +497,6 @@ export class JobStepDialog {
this.model.failureAction = this.failureActionDropdown.value as string;
this.model.outputFileName = this.outputFileNameBox.value;
this.model.appendToLogFile = this.appendToExistingFileCheckbox.checked;
await this.model.save();
}
public async openNewStepDialog() {

View File

@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { AlertDialog } from './dialogs/alertDialog';
@@ -12,6 +14,8 @@ import { ProxyDialog } from './dialogs/proxyDialog';
import { JobStepDialog } from './dialogs/jobStepDialog';
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
const localize = nls.loadMessageBundle();
/**
* The main controller class that initializes the extension
*/
@@ -23,6 +27,11 @@ export class MainController {
this._context = context;
}
public static showNotYetImplemented(): void {
vscode.window.showInformationMessage(
localize('mainController.notImplemented', "This feature is under development. Check-out the latest insiders build if you'd like to try out the most recent changes!"));
}
/**
* Activates the extension
*/
@@ -48,8 +57,10 @@ export class MainController {
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo, credentials: sqlops.CredentialInfo[]) => {
let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials);
dialog.openDialog();
//@TODO: reenable create proxy after snapping July release (7/14/18)
// let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials);
// dialog.openDialog();
MainController.showNotYetImplemented();
});
}

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

View File

@@ -658,9 +658,9 @@
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.0",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.5.0-alpha.9",
"version": "1.5.0-alpha.14",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",

View File

@@ -49,9 +49,9 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.9":
version "0.1.9"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.0":
version "0.2.0"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/c7a691c7fc5ad54a147090784a7f11efda301f4b"
dependencies:
vscode-languageclient "3.5.0"
@@ -296,9 +296,9 @@ semver@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2":
version "0.1.2"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798"
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"

View File

@@ -12,8 +12,9 @@
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "N'", "close": "'", "notIn": ["string", "comment"] },
{ "open": "'", "close": "'", "notIn": ["string", "comment"] }
],
"surroundingPairs": [
["{", "}"],

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.31.3",
"version": "0.32.2",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -34,7 +34,7 @@
"@angular/router": "~4.1.3",
"@angular/upgrade": "~4.1.3",
"angular2-grid": "2.0.6",
"angular2-slickgrid": "git://github.com/Microsoft/angular2-slickgrid.git#1.3.11",
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.3.12",
"applicationinsights": "0.18.0",
"chart.js": "^2.6.0",
"fast-plist": "0.1.2",
@@ -43,8 +43,8 @@
"getmac": "1.0.7",
"graceful-fs": "4.1.11",
"html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.4",
"http-proxy-agent": "0.2.7",
"https-proxy-agent": "0.3.6",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.1",
"iconv-lite": "0.4.19",
"jquery": "3.1.0",
"jschardet": "1.6.0",
@@ -60,7 +60,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "5.4.0",
"semver": "4.3.6",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.23",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.25",
"spdlog": "0.6.0",
"sudo-prompt": "^8.0.0",
"svg.js": "^2.2.5",

View File

@@ -30,6 +30,7 @@
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
"vscodeVersion": "1.23.1",
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
"date": "2017-12-15T12:00:00.000Z",
"recommendedExtensions": [
@@ -42,4 +43,4 @@
"extensionsGallery": {
"serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json"
}
}
}

View File

@@ -69,10 +69,10 @@ export default class MainController implements vscode.Disposable {
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
let inputBox = view.modelBuilder.inputBox()
.withProperties({
multiline: true,
height: 100
}).component();
.withProperties({
multiline: true,
height: 100
}).component();
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
inputBoxWrapper.loading = false;
customButton1.onClick(() => {
@@ -145,8 +145,8 @@ export default class MainController implements vscode.Disposable {
component: inputBox4,
title: 'inputBox4'
}], {
horizontal: true
}).component();
horizontal: true
}).component();
let groupModel1 = view.modelBuilder.groupContainer()
.withLayout({
}).withItems([
@@ -178,8 +178,8 @@ export default class MainController implements vscode.Disposable {
}).component();
let declarativeTable = view.modelBuilder.declarativeTable()
.withProperties({
columns: [{
.withProperties({
columns: [{
displayName: 'Column 1',
valueType: sqlops.DeclarativeDataType.string,
width: '20px',
@@ -204,12 +204,12 @@ export default class MainController implements vscode.Disposable {
{ name: 'options2', displayName: 'option 2' }
]
}
],
data: [
['Data00', 'Data01', false, 'options2'],
['Data10', 'Data11', true, 'options1']
]
}).component();
],
data: [
['Data00', 'Data01', false, 'options2'],
['Data10', 'Data11', true, 'options1']
]
}).component();
declarativeTable.onDataChanged(e => {
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
@@ -223,7 +223,7 @@ export default class MainController implements vscode.Disposable {
height: 150
}).withItems([
radioButton, groupModel1, radioButton2]
, { flex: '1 1 50%' }).component();
, { flex: '1 1 50%' }).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: inputBoxWrapper,
@@ -254,9 +254,9 @@ export default class MainController implements vscode.Disposable {
component: listBox,
title: 'List Box'
}], {
horizontal: false,
componentWidth: componentWidth
}).component();
horizontal: false,
componentWidth: componentWidth
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
customButton2.onClick(() => {
@@ -301,6 +301,18 @@ export default class MainController implements vscode.Disposable {
page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800);
});
/*
wizard.registerOperation({
displayName: 'test task',
description: 'task description',
isCancelable: true
}, op => {
op.updateStatus(sqlops.TaskStatus.InProgress);
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000);
});*/
wizard.pages = [page1, page2];
wizard.open();
}
@@ -343,15 +355,32 @@ export default class MainController implements vscode.Disposable {
webview2.message = count;
});
let flexModel = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'column',
alignItems: 'flex-start',
height: 500
}).withItems([
webview1, webview2
], { flex: '1 1 50%' })
let editor1 = view.modelBuilder.editor()
.withProperties({
content: 'select * from sys.tables'
})
.component();
let editor2 = view.modelBuilder.editor()
.withProperties({
content: 'print("Hello World !")',
languageMode: 'python'
})
.component();
let flexModel = view.modelBuilder.flexContainer().component();
flexModel.addItem(editor1, { flex: '1' });
flexModel.addItem(editor2, { flex: '1' });
flexModel.setLayout({
flexFlow: 'column',
alignItems: 'stretch',
height: '100%'
});
view.onClosed((params) => {
vscode.window.showInformationMessage('editor1: language: ' + editor1.languageMode + ' Content1: ' + editor1.content);
vscode.window.showInformationMessage('editor2: language: ' + editor2.languageMode + ' Content2: ' + editor2.content);
});
await view.initializeModel(flexModel);
});
editor.openEditor();
@@ -378,13 +407,14 @@ export default class MainController implements vscode.Disposable {
let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
let monitorIcon = {
light: monitorLightPath,
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg') };
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg')
};
let monitorButton = view.modelBuilder.button()
.withProperties({
label: 'Monitor',
iconPath: monitorIcon
}).component();
.withProperties({
label: 'Monitor',
iconPath: monitorIcon
}).component();
let toolbarModel = view.modelBuilder.toolbarContainer()
.withToolbarItems([{
component: inputBox,

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* 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 {
Component, Inject, forwardRef, ElementRef, OnInit, Input,
Output, OnChanges, SimpleChanges, EventEmitter
} from '@angular/core';
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@Component({
selector: 'editable-select-box',
template: ''
})
export class EditableDropDown extends AngularDisposable implements OnInit, OnChanges {
private _selectbox: Dropdown;
@Input() options: string[];
@Input() selectedOption: string;
@Input() onlyEmitOnChange = false;
@Output() onDidSelect = new EventEmitter<string>();
private _previousVal: string;
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IThemeService) private themeService: IThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super();
}
ngOnInit(): void {
let dropdownOptions: IDropdownOptions = {
values: [],
strictSelection: false,
placeholder: '',
maxHeight: 125,
ariaLabel: '',
actionLabel: ''
};
this._selectbox = new Dropdown(this._el.nativeElement, this.contextViewService, this.themeService, dropdownOptions);
this._selectbox.values = this.options;
this._selectbox.value = this.selectedOption;
this._selectbox.onValueChange(e => {
if (this.onlyEmitOnChange) {
if (this._previousVal !== e) {
this.onDidSelect.emit(e);
this._previousVal = e;
}
} else {
this.onDidSelect.emit(e);
}
});
this._register(attachEditableDropdownStyler(this._selectbox, this.themeService));
}
ngOnChanges(changes: SimpleChanges): void {
}
public get value(): string {
return this._selectbox.value;
}
}

View File

@@ -33,7 +33,7 @@ export function appendRowLink(container: Builder, label: string, labelClass: str
container.element('tr', {}, (rowContainer) => {
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
labelCellContainer.div({}, (labelContainer) => {
labelContainer.innerHtml(label);
labelContainer.text(label);
});
});
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {

View File

@@ -150,8 +150,8 @@ export class OptionsDialog extends Modal {
private onOptionLinkClicked(optionName: string): void {
var option = this._optionElements[optionName].option;
this._optionTitle.innerHtml(option.displayName);
this._optionDescription.innerHtml(option.description);
this._optionTitle.text(option.displayName);
this._optionDescription.text(option.description);
}
private fillInOptions(container: Builder, options: sqlops.ServiceOption[]): void {

View File

@@ -62,7 +62,7 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
tabLabelcontainer.classList.add(this.tab.iconClass);
} else {
tabLabelcontainer.className = 'tabLabel';
tabLabelcontainer.innerHTML = this.tab.title;
tabLabelcontainer.textContent = this.tab.title;
}
tabLabelcontainer.title = this.tab.title;
}

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 { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
/**
* Implements the various additional navigation keybindings we want out of slickgrid
*/
export class AdditionalKeyBindings<T> implements Slick.Plugin<T> {
private grid: Slick.Grid<T>;
private handler = new Slick.EventHandler();
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler.subscribe(this.grid.onKeyDown, (e, args) => this.handleKeyDown(e, args));
}
public destroy() {
this.handler.unsubscribeAll();
}
private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
let event = new StandardKeyboardEvent(e);
let handled = true;
if (event.equals(KeyCode.RightArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(args.row, this.grid.getColumns().length - 1);
} else if (event.equals(KeyCode.LeftArrow | KeyMod.CtrlCmd)) {
// account for row column
if (this.grid.canCellBeActive(args.row, 0)) {
this.grid.setActiveCell(args.row, 0);
} else {
this.grid.setActiveCell(args.row, 1);
}
} else if (event.equals(KeyCode.UpArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(0, args.cell);
} else if (event.equals(KeyCode.DownArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, args.cell);
} else if (event.equals(KeyCode.Home | KeyMod.CtrlCmd)) {
// account for row column
if (this.grid.canCellBeActive(0, 0)) {
this.grid.setActiveCell(0, 0);
} else {
this.grid.setActiveCell(0, 1);
}
} else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1);
} else {
handled = false;
}
if (handled) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
}
}

View File

@@ -16,7 +16,7 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
private _context: CanvasRenderingContext2D;
private _options: IAutoColumnSizeOptions;
constructor(options: IAutoColumnSizeOptions) {
constructor(options: IAutoColumnSizeOptions = defaultOptions) {
this._options = mixin(options, defaultOptions, false);
}

View File

@@ -0,0 +1,192 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { mixin } from 'vs/base/common/objects';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IThemeService } from 'vs/platform/theme/common/themeService';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangeselector');
export interface ICellRangeSelector<T> extends Slick.Plugin<T> {
onCellRangeSelected: Slick.Event<{ range: Slick.Range }>;
onBeforeCellRangeSelected: Slick.Event<Slick.Cell>;
}
export interface ICellSelectionModelOptions {
cellRangeSelector?: any;
selectActiveCell?: boolean;
}
const defaults: ICellSelectionModelOptions = {
selectActiveCell: true
};
export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private grid: Slick.Grid<T>;
private selector: ICellRangeSelector<T>;
private ranges: Array<Slick.Range> = [];
public onSelectedRangesChanged = new Slick.Event<Array<Slick.Range>>();
constructor(private options: ICellSelectionModelOptions = defaults) {
this.options = mixin(this.options, defaults, false);
if (this.options.cellRangeSelector) {
this.selector = this.options.cellRangeSelector;
} else {
// this is added by the noderequires above
this.selector = new (<any>Slick).CellRangeSelector({ selectionCss: { 'border': '2px dashed grey' } });
}
}
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.grid.onActiveCellChanged.subscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.subscribe(e => this.handleKeyDown(e));
this.grid.onHeaderClick.subscribe((e: MouseEvent, args) => this.handleHeaderClick(e, args));
this.grid.registerPlugin(this.selector);
this.selector.onCellRangeSelected.subscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.subscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
}
public destroy() {
this.grid.onActiveCellChanged.unsubscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.unsubscribe(e => this.handleKeyDown(e));
this.selector.onCellRangeSelected.unsubscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.unsubscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
this.grid.unregisterPlugin(this.selector);
}
private removeInvalidRanges(ranges: Array<Slick.Range>): Array<Slick.Range> {
let result: Array<Slick.Range> = [];
for (let i = 0; i < ranges.length; i++) {
let r = ranges[i];
if (this.grid.canCellBeSelected(r.fromRow, r.fromCell) && this.grid.canCellBeSelected(r.toRow, r.toCell)) {
result.push(r);
} else if (this.grid.canCellBeSelected(r.fromRow, r.fromCell + 1) && this.grid.canCellBeSelected(r.toRow, r.toCell)) {
// account for number row
result.push(new Slick.Range(r.fromRow, r.fromCell + 1, r.toRow, r.toCell));
}
}
return result;
}
public setSelectedRanges(ranges: Array<Slick.Range>): void {
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
if ((!this.ranges || this.ranges.length === 0) && (!ranges || ranges.length === 0)) {
return;
}
this.ranges = this.removeInvalidRanges(ranges);
this.onSelectedRangesChanged.notify(this.ranges);
}
public getSelectedRanges() {
return this.ranges;
}
private handleBeforeCellRangeSelected(e, args: Slick.Cell) {
if (this.grid.getEditorLock().isActive()) {
e.stopPropagation();
return false;
}
return true;
}
private handleCellRangeSelected(e, args: { range: Slick.Range }) {
this.grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);
this.setSelectedRanges([args.range]);
}
private handleActiveCellChange(e, args) {
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell)) {
this.setSelectedRanges([new Slick.Range(args.row, args.cell)]);
} else if (!this.options.selectActiveCell) {
// clear the previous selection once the cell changes
this.setSelectedRanges([]);
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
if (!isUndefinedOrNull(args.column)) {
let columnIndex = this.grid.getColumnIndex(args.column.id);
if (this.grid.canCellBeSelected(0, columnIndex)) {
let ranges: Array<Slick.Range>;
if (e.shiftKey) {
ranges = this.getSelectedRanges();
ranges.push(new Slick.Range(0, columnIndex, this.grid.getDataLength() - 1, columnIndex));
} else {
ranges = [new Slick.Range(0, columnIndex, this.grid.getDataLength() - 1, columnIndex)];
}
this.grid.setActiveCell(0, columnIndex);
this.setSelectedRanges(ranges);
}
}
}
private handleKeyDown(e) {
/***
* Кey codes
* 37 left
* 38 up
* 39 right
* 40 down
*/
let ranges, last;
let active = this.grid.getActiveCell();
let metaKey = e.ctrlKey || e.metaKey;
if (active && e.shiftKey && !metaKey && !e.altKey &&
(e.which === 37 || e.which === 39 || e.which === 38 || e.which === 40)) {
ranges = this.getSelectedRanges();
if (!ranges.length) {
ranges.push(new Slick.Range(active.row, active.cell));
}
// keyboard can work with last range only
last = ranges.pop();
// can't handle selection out of active cell
if (!last.contains(active.row, active.cell)) {
last = new Slick.Range(active.row, active.cell);
}
let dRow = last.toRow - last.fromRow,
dCell = last.toCell - last.fromCell,
// walking direction
dirRow = active.row === last.fromRow ? 1 : -1,
dirCell = active.cell === last.fromCell ? 1 : -1;
if (e.which === 37) {
dCell -= dirCell;
} else if (e.which === 39) {
dCell += dirCell;
} else if (e.which === 38) {
dRow -= dirRow;
} else if (e.which === 40) {
dRow += dirRow;
}
// define new selection range
let new_last = new Slick.Range(active.row, active.cell, active.row + dirRow * dRow, active.cell + dirCell * dCell);
if (this.removeInvalidRanges([new_last]).length) {
ranges.push(new_last);
let viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;
let viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;
this.grid.scrollRowIntoView(viewRow, false);
this.grid.scrollCellIntoView(viewRow, viewCell, false);
} else {
ranges.push(last);
}
this.setSelectedRanges(ranges);
e.preventDefault();
e.stopPropagation();
}
}
}

View File

@@ -1,364 +0,0 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { clone } from 'sql/base/common/objects';
export class DragCellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private readonly keyColResizeIncr = 5;
private _grid: Slick.Grid<T>;
private _ranges: Array<Slick.Range> = [];
private _dragging = false;
private _handler = new Slick.EventHandler();
public onSelectedRangesChanged = new Slick.Event<Slick.Range[]>();
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler.subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, data));
this._handler.subscribe(this._grid.onKeyDown, (e: JQueryInputEventObject) => this.handleKeyDown(e));
this._handler.subscribe(this._grid.onClick, (e: MouseEvent) => this.handleClick(e));
this._handler.subscribe(this._grid.onDrag, (e: MouseEvent) => this.handleDrag(e));
this._handler.subscribe(this._grid.onDragInit, (e: MouseEvent) => this.handleDragInit(e));
this._handler.subscribe(this._grid.onDragStart, (e: MouseEvent) => this.handleDragStart(e));
this._handler.subscribe(this._grid.onDragEnd, (e: MouseEvent) => this.handleDragEnd(e));
this._handler.subscribe(this._grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
}
public destroy(): void {
this._handler.unsubscribeAll();
}
private rangesToRows(ranges: Array<Slick.Range>): Array<number> {
let rows = [];
for (let i = 0; i < ranges.length; i++) {
for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
rows.push(j);
}
}
return rows;
}
private rowsToRanges(rows: Array<number>): Array<Slick.Range> {
let ranges = [];
let lastCell = this._grid.getColumns().length - 1;
for (let i = 0; i < rows.length; i++) {
ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
}
return ranges;
}
public getSelectedRows(): Array<number> {
return this.rangesToRows(this._ranges);
}
public setSelectedRows(rows: Array<number>) {
this.setSelectedRanges(this.rowsToRanges(rows));
}
public setSelectedRanges(ranges: Array<Slick.Range>) {
this._ranges = ranges;
this.onSelectedRangesChanged.notify(this._ranges);
}
public getSelectedRanges(): Array<Slick.Range> {
return this._ranges;
}
private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) { }
private isNavigationKey(e: BaseJQueryEventObject) {
// Nave keys (home, end, arrows) are all in sequential order so use a
switch (e.which) {
case $.ui.keyCode.HOME:
case $.ui.keyCode.END:
case $.ui.keyCode.LEFT:
case $.ui.keyCode.UP:
case $.ui.keyCode.RIGHT:
case $.ui.keyCode.DOWN:
return true;
default:
return false;
}
}
private navigateLeft(e: JQueryInputEventObject, activeCell: Slick.Cell) {
if (activeCell.cell > 1) {
let isHome = e.which === $.ui.keyCode.HOME;
let newActiveCellColumn = isHome ? 1 : activeCell.cell - 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the rightmost edge of the range and we navigate left,
// we want to deselect the rightmost cell
if (last.fromCell <= newRangeColumn) { last.toCell -= 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private navigateRight(e: JQueryInputEventObject, activeCell: Slick.Cell) {
let columnLength = this._grid.getColumns().length;
if (activeCell.cell < columnLength) {
let isEnd = e.which === $.ui.keyCode.END;
let newActiveCellColumn = isEnd ? columnLength : activeCell.cell + 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the leftmost edge of the range and we navigate right,
// we want to deselect the leftmost cell
if (newRangeColumn <= last.toCell) { last.fromCell += 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private handleKeyDown(e: JQueryInputEventObject) {
let activeCell = this._grid.getActiveCell();
if (activeCell) {
// navigation keys
if (this.isNavigationKey(e)) {
e.stopImmediatePropagation();
if (e.ctrlKey || e.metaKey) {
let event = new CustomEvent('gridnav', {
detail: {
which: e.which,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
altKey: e.altKey
}
});
window.dispatchEvent(event);
return;
}
// end key
if (e.which === $.ui.keyCode.END) {
this.navigateRight(e, activeCell);
}
// home key
if (e.which === $.ui.keyCode.HOME) {
this.navigateLeft(e, activeCell);
}
// left arrow
if (e.which === $.ui.keyCode.LEFT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width - this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateLeft(e, activeCell);
}
// up arrow
} else if (e.which === $.ui.keyCode.UP && activeCell.row > 0) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the bottommost edge of the range and we navigate up,
// we want to deselect the bottommost row
let newRangeRow = activeCell.row - 1;
if (last.fromRow <= newRangeRow) { last.toRow -= 1; }
let fromRow = Math.min(activeCell.row - 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(newRangeRow, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row - 1, activeCell.cell - 1, activeCell.row - 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row - 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
// right arrow
} else if (e.which === $.ui.keyCode.RIGHT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width + this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateRight(e, activeCell);
}
// down arrow
} else if (e.which === $.ui.keyCode.DOWN && activeCell.row < this._grid.getDataLength() - 1) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the topmost edge of the range and we navigate down,
// we want to deselect the topmost row
let newRangeRow = activeCell.row + 1;
if (newRangeRow <= last.toRow) { last.fromRow += 1; }
let fromRow = Math.min(activeCell.row + 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(activeCell.row + 1, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row + 1, activeCell.cell - 1, activeCell.row + 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row + 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
}
}
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
let columnIndex = this._grid.getColumnIndex(args.column.id);
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex));
this._grid.setActiveCell(0, columnIndex + 1);
} else if (e.shiftKey && this._ranges.length) {
let last = this._ranges.pop().fromCell;
let from = Math.min(columnIndex, last);
let to = Math.max(columnIndex, last);
this._ranges = [];
for (let i = from; i <= to; i++) {
if (i !== last) {
this._ranges.push(new Slick.Range(0, i, this._grid.getDataLength() - 1, i));
}
}
this._ranges.push(new Slick.Range(0, last, this._grid.getDataLength() - 1, last));
} else {
this._ranges = [new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex)];
this._grid.resetActiveCell();
}
this.setSelectedRanges(this._ranges);
e.stopImmediatePropagation();
return true;
}
private handleClick(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
if (!e.ctrlKey && !e.shiftKey && !e.metaKey) {
if (cell.cell !== 0) {
this._ranges = [new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, cell.cell);
return true;
} else {
this._ranges = [new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, 1);
return true;
}
}
else if (this._grid.getOptions().multiSelect) {
if (e.ctrlKey || e.metaKey) {
if (cell.cell === 0) {
this._ranges.push(new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1));
this._grid.setActiveCell(cell.row, 1);
} else {
this._ranges.push(new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1));
this._grid.setActiveCell(cell.row, cell.cell);
}
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
if (cell.cell === 0) {
let fromRow = Math.min(cell.row, last.fromRow);
let toRow = Math.max(cell.row, last.fromRow);
this._ranges = [new Slick.Range(fromRow, 0, toRow, this._grid.getColumns().length - 1)];
} else {
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
}
}
}
this.setSelectedRanges(this._ranges);
return true;
}
private handleDragInit(e: MouseEvent) {
e.stopImmediatePropagation();
}
private handleDragStart(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
e.stopImmediatePropagation();
this._dragging = true;
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(cell.row, cell.cell));
this._grid.setActiveCell(cell.row, cell.cell);
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(cell.row, cell.cell)];
this._grid.setActiveCell(cell.row, cell.cell);
}
this.setSelectedRanges(this._ranges);
}
private handleDrag(e: MouseEvent) {
if (this._dragging) {
let cell = this._grid.getCellFromEvent(e);
let activeCell = this._grid.getActiveCell();
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
this._ranges.pop();
if (activeCell.cell === 0) {
let lastCell = this._grid.getColumns().length - 1;
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
this._ranges.push(new Slick.Range(firstRow, 0, lastRow, lastCell));
} else {
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
let firstColumn = Math.min(cell.cell - 1, activeCell.cell - 1);
let lastColumn = Math.max(cell.cell - 1, activeCell.cell - 1);
this._ranges.push(new Slick.Range(firstRow, firstColumn, lastRow, lastColumn));
}
this.setSelectedRanges(this._ranges);
return true;
}
return false;
}
private handleDragEnd(e: MouseEvent) {
this._dragging = false;
}
}

View File

@@ -6,6 +6,7 @@ import { mixin } from 'vs/base/common/objects';
import { SlickGrid } from 'angular2-slickgrid';
import { Button } from '../../button/button';
import { attachButtonStyler } from 'sql/common/theme/styler';
import { escape } from 'sql/base/common/strings';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export class HeaderFilter {
@@ -174,7 +175,7 @@ export class HeaderFilter {
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
filterOptions += '<label><input type="checkbox" value="' + i + '"'
+ (filtered ? ' checked="checked"' : '')
+ '/>' + filterItems[i] + '</label>';
+ '/>' + escape(filterItems[i]) + '</label>';
}
}
let $filter = $('<div class="filter">')

View File

@@ -1,5 +1,6 @@
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
// heavily modified
import { escape } from 'sql/base/common/strings';
import { mixin } from 'vs/base/common/objects';
import * as nls from 'vs/nls';
@@ -354,7 +355,7 @@ export class RowDetailView {
html.push("style='height:", dataContext._height, "px;"); //set total height of padding
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
html.push("<div id='detailViewContainer_", dataContext.id, "' class='detail-container' style='max-height:" + (dataContext._height - rowHeight + bottomMargin) + "px'>"); //sub ctr for custom styling
html.push("<div id='innerDetailView_", dataContext.id, "'>", dataContext._detailContent, "</div></div>");
html.push("<div id='innerDetailView_", dataContext.id, "'>", escape(dataContext._detailContent), "</div></div>");
//&omit a final closing detail container </div> that would come next
return html.join('');

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* 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 { range } from 'vs/base/common/arrays';
export interface IRowNumberColumnOptions {
numberOfRows: number;
cssClass?: string;
}
const sizePerDigit = 15;
export class RowNumberColumn<T> implements Slick.Plugin<T> {
private handler = new Slick.EventHandler();
private grid: Slick.Grid<T>;
constructor(private options: IRowNumberColumnOptions) {
}
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler
.subscribe(this.grid.onClick, (e, args) => this.handleClick(e, args))
.subscribe(this.grid.onHeaderClick, (e, args) => this.handleHeaderClick(e, args));
}
public destroy() {
this.handler.unsubscribeAll();
}
private handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void {
if (this.grid.getColumns()[args.cell].id === 'rowNumber') {
this.grid.setActiveCell(args.row, 1);
this.grid.setSelectedRows([args.row]);
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>): void {
if (args.column.id === 'rowNumber') {
this.grid.setActiveCell(0, 1);
this.grid.setSelectedRows(range(this.grid.getDataLength()));
}
}
public getColumnDefinition(): Slick.Column<T> {
return {
id: 'rowNumber',
name: '',
field: 'rowNumber',
width: this.options.numberOfRows.toString().length * sizePerDigit,
resizable: false,
cssClass: this.options.cssClass,
focusable: false,
selectable: false,
formatter: (r, c, v, cd, dc) => this.formatter(r, c, v, cd, dc)
};
}
private formatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
if (dataContext) {
return `<span>${row}</span>`;
}
return null;
}
}

View File

@@ -14,7 +14,7 @@ export interface IFindPosition {
row: number;
}
export function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
let field = args.sortCol.field;
let sign = args.sortAsc ? 1 : -1;
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);

View File

@@ -82,7 +82,7 @@ export class Taskbar {
public static createTaskbarText(inputText: string): HTMLElement {
let element = document.createElement('div');
element.className = 'taskbarTextSeparator';
element.innerHTML = inputText;
element.textContent = inputText;
return element;
}

View File

@@ -15,6 +15,7 @@ export function escape(html: string): string {
case '>': return '&gt;';
case '&': return '&amp;';
case '"': return '&quot;';
case '\'': return '&#39';
default: return match;
}
});

View File

@@ -100,7 +100,7 @@ export class AutoOAuthDialog extends Modal {
let inputBox: InputBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label);
labelContainer.text(label);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {

View File

@@ -145,7 +145,7 @@ export class FirewallRuleDialog extends Modal {
subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement();
subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.FROM);
labelContainer.text(LocalizedStrings.FROM);
});
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
@@ -155,7 +155,7 @@ export class FirewallRuleDialog extends Modal {
});
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.TO);
labelContainer.text(LocalizedStrings.TO);
});
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
@@ -234,7 +234,7 @@ export class FirewallRuleDialog extends Modal {
className += ' header';
}
container.div({ 'class': className }, (labelContainer) => {
labelContainer.innerHtml(content);
labelContainer.text(content);
});
}

View File

@@ -152,10 +152,12 @@ export interface IConnectionManagementService {
getAdvancedProperties(): sqlops.ConnectionOption[];
getConnectionId(connectionProfile: IConnectionProfile): string;
getConnectionUri(connectionProfile: IConnectionProfile): string;
getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string;
getConnectionUriFromId(connectionId: string): string;
isConnected(fileUri: string): boolean;
/**
@@ -320,12 +322,13 @@ export enum MetadataType {
}
export enum TaskStatus {
notStarted = 0,
inProgress = 1,
succeeded = 2,
succeededWithWarning = 3,
failed = 4,
canceled = 5
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export interface IConnectionParams {

View File

@@ -648,6 +648,15 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return this._connectionStatusManager.getActiveConnectionProfiles();
}
public getConnectionUriFromId(connectionId: string): string {
let connection = this.getActiveConnections().find(connection => connection.id === connectionId);
if (connection) {
return this.getConnectionUri(connection);
} else {
return undefined;
}
}
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.AddServerGroup);
return new Promise<string>((resolve, reject) => {
@@ -704,7 +713,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return false;
}
public getConnectionId(connectionProfile: IConnectionProfile): string {
public getConnectionUri(connectionProfile: IConnectionProfile): string {
return this._connectionStatusManager.getOriginalOwnerUri(Utils.generateUri(connectionProfile));
}
@@ -716,7 +725,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
*/
public getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string {
if (this._connectionStatusManager.isDefaultTypeUri(uri)) {
return this.getConnectionId(connectionProfile);
return this.getConnectionUri(connectionProfile);
} else {
return uri;
}

View File

@@ -63,7 +63,7 @@ export class ConnectionController implements IConnectionComponentController {
tempProfile.password = password;
tempProfile.groupFullName = '';
tempProfile.saveProfile = false;
let uri = this._connectionManagementService.getConnectionId(tempProfile);
let uri = this._connectionManagementService.getConnectionUri(tempProfile);
return new Promise<string[]>((resolve, reject) => {
if (this._databaseCache.has(uri)) {
let cachedDatabases: string[] = this._databaseCache.get(uri);

View File

@@ -262,7 +262,7 @@ export class ConnectionDialogWidget extends Modal {
let recentHistoryLabel = localize('recentHistory', 'Recent history');
recentConnectionContainer.div({ class: 'recent-titles-container' }, (container) => {
container.div({ class: 'connection-history-label' }, (recentTitle) => {
recentTitle.innerHtml(recentHistoryLabel);
recentTitle.text(recentHistoryLabel);
});
container.div({ class: 'connection-history-actions' }, (actionsContainer) => {
this._actionbar = this._register(new ActionBar(actionsContainer.getHTMLElement(), { animated: false }));
@@ -303,7 +303,7 @@ export class ConnectionDialogWidget extends Modal {
this._noRecentConnectionBuilder.div({ class: 'connection-recent-content' }, (noRecentConnectionContainer) => {
let noRecentHistoryLabel = localize('noRecentConnections', 'No recent connection');
noRecentConnectionContainer.div({ class: 'no-recent-connections' }, (noRecentTitle) => {
noRecentTitle.innerHtml(noRecentHistoryLabel);
noRecentTitle.text(noRecentHistoryLabel);
});
});
}
@@ -335,7 +335,7 @@ export class ConnectionDialogWidget extends Modal {
this._noSavedConnectionBuilder.div({ class: 'connection-saved-content' }, (noSavedConnectionContainer) => {
let noSavedConnectionLabel = localize('noSavedConnections', 'No saved connection');
noSavedConnectionContainer.div({ class: 'no-saved-connections' }, (titleContainer) => {
titleContainer.innerHtml(noSavedConnectionLabel);
titleContainer.text(noSavedConnectionLabel);
});
});
}

View File

@@ -22,6 +22,7 @@
border-top-color: transparent;
height: calc(100% - 350px);
display: block;
min-height: 120px;
}
.connection-recent, .connection-saved {

View File

@@ -57,13 +57,14 @@ import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsV
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,];
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, EditableDropDown, SelectBox, InputBox,];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';

View File

@@ -163,7 +163,7 @@ export class NewDashboardTabDialog extends Modal {
this._noExtensionViewContainer = DOM.$('.no-extension-view');
let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label'));
let noExtensionLabel = localize('newdashboardTabDialog.noExtensionLabel', 'No dashboard extensions are installed at this time. Go to Extension Manager to explore recommended extensions.');
noExtensionTitle.innerHTML = noExtensionLabel;
noExtensionTitle.textContent = noExtensionLabel;
DOM.append(container, this._noExtensionViewContainer);
}

View File

@@ -16,7 +16,7 @@ const properties: IJSONSchema = {
properties: {
yAxisMin: {
type: 'number',
description: nls.localize('yAxisMin', "Minumum value of the y axis")
description: nls.localize('yAxisMin', "Minimum value of the y axis")
},
yAxisMax: {
type: 'number',
@@ -28,7 +28,7 @@ const properties: IJSONSchema = {
},
xAxisMin: {
type: 'number',
description: nls.localize('xAxisMin', "Minumum value of the x axis")
description: nls.localize('xAxisMin', "Minimum value of the x axis")
},
xAxisMax: {
type: 'number',

View File

@@ -11,8 +11,8 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { attachTableStyler} from 'sql/common/theme/styler';
import { attachTableStyler } from 'sql/common/theme/styler';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
@Component({
template: ''
@@ -63,7 +63,7 @@ export default class TableInsight extends Disposable implements IInsightsView, O
private createTable() {
if (!this.table) {
this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true });
this.table.setSelectionModel(new DragCellSelectionModel());
this.table.setSelectionModel(new CellSelectionModel());
this._register(attachTableStyler(this.table, this.themeService));
}
}

View File

@@ -146,7 +146,7 @@ export class BackupUiService implements IBackupUiService {
let backupOptions = this.getOptions(this._currentProvider);
return new TPromise<void>(() => {
let uri = this._connectionManagementService.getConnectionId(connection)
let uri = this._connectionManagementService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator
+ ConnectionUtils.ConnectionUriBackupIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator

View File

@@ -179,8 +179,8 @@ export class RestoreDialogController implements IRestoreDialogController {
private isSuccessfulRestore(response: TaskNode): boolean {
return (response.taskName === this._restoreTaskName &&
response.message === this._restoreCompleted &&
(response.status === TaskStatus.succeeded ||
response.status === TaskStatus.succeededWithWarning) &&
(response.status === TaskStatus.Succeeded ||
response.status === TaskStatus.SucceededWithWarning) &&
(response.taskExecutionMode === TaskExecutionMode.execute ||
response.taskExecutionMode === TaskExecutionMode.executeAndScript));
}
@@ -293,7 +293,7 @@ export class RestoreDialogController implements IRestoreDialogController {
return new TPromise<void>((resolve, reject) => {
let result: void;
this._ownerUri = this._connectionService.getConnectionId(connection)
this._ownerUri = this._connectionService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator
+ Utils.ConnectionUriRestoreIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator

View File

@@ -226,7 +226,7 @@ export class RestoreDialog extends Modal {
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.TARGETDATABASE);
labelContainer.text(LocalizedStrings.TARGETDATABASE);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
@@ -471,7 +471,7 @@ export class RestoreDialog extends Modal {
className += ' header';
}
container.div({ class: className }, (labelContainer) => {
labelContainer.innerHtml(content);
labelContainer.text(content);
});
}
@@ -535,7 +535,7 @@ export class RestoreDialog extends Modal {
let selectBox: SelectBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label);
labelContainer.text(label);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {

View File

@@ -4,7 +4,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IColumnDefinition, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
import { ISlickColumn, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
export interface ISlickRange {
fromCell: number;
@@ -42,7 +42,7 @@ export interface IGridIcon {
export interface IGridDataSet {
dataRows: IObservableCollection<IGridDataRow>;
columnDefinitions: IColumnDefinition[];
columnDefinitions: ISlickColumn<any>[];
resized: any; // EventEmitter<any>;
totalRows: number;
batchId: number;

View File

@@ -17,14 +17,13 @@
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
[plugins]="slickgridPlugins"
[plugins]="plugins[i]"
(activeCellChanged)="onActiveCellChanged($event)"
(cellEditBegin)="onCellEditBegin($event)"
(cellEditExit)="onCellEditEnd($event)"
(rowEditBegin)="onRowEditBegin($event)"
(rowEditExit)="onRowEditEnd($event)"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
[isColumnEditable]="onIsColumnEditable"
[isCellEditValid]="onIsCellEditValid"
[overrideCellFn]="overrideCellFn"
enableEditing="true"

View File

@@ -24,6 +24,9 @@ import { error } from 'sql/base/common/log';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { escape } from 'sql/base/common/strings';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -36,7 +39,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export const EDITDATA_SELECTOR: string = 'editdata-component';
@Component({
@@ -66,6 +68,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
private newRowVisible: boolean;
private removingNewRow: boolean;
private rowIdMappings: { [gridRowId: number]: number } = {};
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// Edit Data functions
public onActiveCellChanged: (event: { row: number, column: number }) => void;
@@ -167,17 +170,6 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onRowEditEnd = (event: { row: number }): void => { };
this.onIsColumnEditable = (column: number): boolean => {
let result = false;
// Check that our variables exist
if (column !== undefined && !!this.dataSet && !!this.dataSet.columnDefinitions[column]) {
result = this.dataSet.columnDefinitions[column].isEditable;
}
// If no column definition exists then the row is not editable
return result;
};
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
let returnVal = '';
if (Services.DBCellValue.isDBCellValue(value)) {
@@ -197,9 +189,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.idMapping[rowIndex] = row.id;
rowIndex++;
return {
values: row.cells.map(c => {
return mixin({ ariaLabel: c.displayValue }, c);
}), row: row.id
values: [{}].concat(row.cells.map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})), row: row.id
};
});
@@ -351,6 +343,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
let maxHeight = this.getMaxHeight(resultSet.rowCount);
let minHeight = this.getMinHeight(resultSet.rowCount);
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
@@ -365,7 +359,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: resultSet.columnInfo.map((c, i) => {
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
@@ -374,13 +368,14 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
type: self.stringToFieldType('string'),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,
isEditable: c.isUpdatable
};
})
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSet = dataSet;
// Create a dataSet to render without rows to reduce DOM size

View File

@@ -26,6 +26,7 @@ import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext, QueryEditorVisibleContext } from 'sql/parts/query/common/queryContext';
import { error } from 'sql/base/common/log';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { IAction } from 'vs/base/common/actions';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
@@ -33,8 +34,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -42,14 +41,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export abstract class GridParentComponent {
// CONSTANTS
// tslint:disable:no-unused-variable
protected get selectionModel(): DragCellSelectionModel<any> {
return new DragCellSelectionModel<any>();
}
protected get slickgridPlugins(): Array<any> {
return [
new AutoColumnSize<any>({})
];
}
protected get selectionModel() { return new CellSelectionModel(); }
protected _rowHeight = 29;
protected _defaultNumShowingRows = 8;
protected Constants = Constants;
@@ -94,7 +87,6 @@ export abstract class GridParentComponent {
public onRowEditBegin: (event: { row: number }) => void;
public onRowEditEnd: (event: { row: number }) => void;
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public onIsColumnEditable: (column: number) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
@@ -244,21 +236,25 @@ export abstract class GridParentComponent {
this.messagesFocussedContextKey.set(false);
}
protected getSelection(index?: number): ISlickRange[] {
let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges();
selection = selection.map(c => { return <ISlickRange>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
}
private copySelection(): void {
let messageText = this.getMessageText();
if (messageText.length > 0) {
this.clipboardService.writeText(messageText);
} else {
let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
this.dataService.copyResults(this.getSelection(activeGrid), this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
}
}
private copyWithHeaders(): void {
let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId,
this.dataService.copyResults(this.getSelection(activeGrid), this.renderedDataSets[activeGrid].batchId,
this.renderedDataSets[activeGrid].resultId, true);
}
@@ -372,8 +368,7 @@ export abstract class GridParentComponent {
let activeGrid = this.activeGrid;
let batchId = this.renderedDataSets[activeGrid].batchId;
let resultId = this.renderedDataSets[activeGrid].resultId;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: selection });
this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: this.getSelection(activeGrid) });
}
protected _keybindingFor(action: IAction): ResolvedKeybinding {
@@ -385,7 +380,7 @@ export abstract class GridParentComponent {
let slick: any = this.slickgrids.toArray()[index];
let grid = slick._grid;
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection && selection.length === 0) {
let cell = (grid as Slick.Grid<any>).getCellFromEvent(event);
@@ -423,7 +418,9 @@ export abstract class GridParentComponent {
let self = this;
return (gridIndex: number) => {
self.activeGrid = gridIndex;
self.slickgrids.toArray()[this.activeGrid].selection = true;
let grid = self.slickgrids.toArray()[self.activeGrid];
grid.setActive();
grid.selection = true;
};
}
@@ -433,22 +430,6 @@ export abstract class GridParentComponent {
}
}
/**
* Used to convert the string to a enum compatible with SlickGrid
*/
protected stringToFieldType(input: string): FieldType {
let fieldtype: FieldType;
switch (input) {
case 'string':
fieldtype = FieldType.String;
break;
default:
fieldtype = FieldType.String;
break;
}
return fieldtype;
}
/**
* Makes a resultset take up the full result height if this is not already true
* Otherwise rerenders the result sets from default

View File

@@ -10,9 +10,9 @@
<span> {{LocalizedConstants.resultPaneLabel}} </span>
<span class="queryResultsShortCut"> {{resultShortcut}} </span>
</div>
<div id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable"
(onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
(focusin)="onGridFocus()" (focusout)="onGridFocusout()">
<div #resultsScrollBox id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable"
(onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
(focusin)="onGridFocus()" (focusout)="onGridFocusout()">
<div class="boxRow content horzBox slickgrid" *ngFor="let dataSet of renderedDataSets; let i = index"
[style.max-height]="dataSet.maxHeight" [style.min-height]="dataSet.minHeight">
<slick-grid #slickgrid id="slickgrid_{{i}}" [columnDefinitions]="dataSet.columnDefinitions"
@@ -20,12 +20,11 @@
[dataRows]="dataSet.dataRows"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
enableAsyncPostRender="true"
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
(mousedown)="navigateToGrid(i)"
[selectionModel]="selectionModel"
[plugins]="slickgridPlugins"
[plugins]="plugins[i]"
class="boxCol content vertBox slickgrid"
[rowHeight]="rowHeight">
</slick-grid>

View File

@@ -15,7 +15,7 @@ import {
ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject,
ViewChildren, forwardRef, EventEmitter, Input, ViewChild
} from '@angular/core';
import { IGridDataRow, SlickGrid, VirtualizedCollection } from 'angular2-slickgrid';
import { IGridDataRow, SlickGrid, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -28,6 +28,9 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { escape } from 'sql/base/common/strings';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { format } from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom';
@@ -61,7 +64,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
// create a function alias to use inside query.component
// tslint:disable-next-line:no-unused-variable
private stringsFormat: any = format;
protected stringsFormat: any = format;
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// tslint:disable-next-line:no-unused-variable
private dataIcons: IGridIcon[] = [
@@ -86,7 +91,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveCsv'; },
hoverText: () => { return LocalizedConstants.saveCSVLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
@@ -99,7 +104,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveJson'; },
hoverText: () => { return LocalizedConstants.saveJSONLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
@@ -112,7 +117,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveExcel'; },
hoverText: () => { return LocalizedConstants.saveExcelLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
@@ -156,6 +161,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>();
public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>();
private savedViewState: {
gridSelections: ISlickRange[][];
resultsScroll: number;
messagePaneScroll: number;
slickGridScrolls: { vertical: number; horizontal: number }[];
};
@Input() public queryParameters: IQueryComponentParams;
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
@@ -163,6 +175,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
@ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef;
@ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef;
@ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef;
@ViewChild('resultsScrollBox', { read: ElementRef }) private _resultsScrollBox: ElementRef;
@ViewChildren('slickgrid', { read: ElementRef }) private _slickgridElements: QueryList<ElementRef>;
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@@ -220,6 +234,10 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
self._cd.detectChanges();
});
this.queryParameters.onSaveViewState(() => this.saveViewState());
this.queryParameters.onRestoreViewState(() => this.restoreViewState());
this.dataService.onAngularLoaded();
}
@@ -302,9 +320,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
for (let row = 0; row < rows.rows.length; row++) {
// Push row values onto end of gridData for slickgrid
gridData.push({
values: rows.rows[row].map(c => {
values: [{}].concat(rows.rows[row].map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})
}))
});
}
@@ -331,6 +349,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
minHeight = minHeightNumber.toString() + 'px';
}
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
@@ -345,7 +365,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: resultSet.columnInfo.map((c, i) => {
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
@@ -354,12 +374,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
type: self.stringToFieldType('string'),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
};
})
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSets.push(dataSet);
// check if the resultset is for a query plan
@@ -643,6 +664,43 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
}
private saveViewState(): void {
let gridSelections = this.slickgrids.map(grid => grid.getSelectedRanges());
let resultsScrollElement = (this._resultsScrollBox.nativeElement as HTMLElement);
let resultsScroll = resultsScrollElement.scrollTop;
let messagePaneScroll = (this._messagesContainer.nativeElement as HTMLElement).scrollTop;
let slickGridScrolls = this._slickgridElements.map(element => {
// Get the slick grid's viewport element and save its scroll position
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
return {
vertical: scrollElement.scrollTop,
horizontal: scrollElement.scrollLeft
};
});
this.savedViewState = {
gridSelections,
messagePaneScroll,
resultsScroll,
slickGridScrolls
};
}
private restoreViewState(): void {
if (this.savedViewState) {
this.slickgrids.forEach((grid, index) => grid.selection = this.savedViewState.gridSelections[index]);
(this._resultsScrollBox.nativeElement as HTMLElement).scrollTop = this.savedViewState.resultsScroll;
(this._messagesContainer.nativeElement as HTMLElement).scrollTop = this.savedViewState.messagePaneScroll;
this._slickgridElements.forEach((element, index) => {
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
let savedScroll = this.savedViewState.slickGridScrolls[index];
scrollElement.scrollTop = savedScroll.vertical;
scrollElement.scrollLeft = savedScroll.horizontal;
});
this.savedViewState = undefined;
}
}
layout() {
this.resizeGrids();
}

View File

@@ -352,6 +352,14 @@ jobsview-component .jobview-grid .slick-cell.error-row {
background: #444444 !important;
}
.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 {
border-left: 1px dotted #444444;
}
.vs-dark #operatorsDiv joboperatorsview-component .joboperatorsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
@@ -375,6 +383,14 @@ jobsview-component .jobview-grid .slick-cell.error-row {
background: #444444 !important;
}
.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 {
border-left: 1px dotted #444444;
}
.vs-dark #proxiesDiv jobproxiesview-component .jobproxiesview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
@@ -397,3 +413,11 @@ jobsview-component .jobview-grid .slick-cell.error-row {
.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 {
border-left: 1px dotted white;
}
.jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #444444;
}

View File

@@ -154,7 +154,7 @@
</td>
</tr>
</table>
<div #jobsteps>
<div #jobsteps style="height: 100%">
<jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component>
</div>
<h3 *ngIf="showSteps === false">No Steps Available</h3>

View File

@@ -174,10 +174,12 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
if (self.agentJobHistoryInfo) {
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
if (self.agentJobHistoryInfo.steps) {
let jobStepStatus = this.didJobFail(self.agentJobHistoryInfo);
self._stepRows = self.agentJobHistoryInfo.steps.map(step => {
let stepViewRow = new JobStepsViewRow();
stepViewRow.message = step.message;
stepViewRow.runStatus = JobManagementUtilities.convertToStatusString(step.runStatus);
stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
JobManagementUtilities.convertToStatusString(step.runStatus);
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
stepViewRow.stepName = step.stepName;
stepViewRow.stepID = step.stepId.toString();
@@ -191,6 +193,15 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
}
}
private didJobFail(job: sqlops.AgentJobHistoryInfo): boolean {
for (let i = 0; i < job.steps.length; i++) {
if (job.steps[i].runStatus === 0) {
return true;
}
}
return false;
}
private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) {
self._treeController.jobHistories = jobHistories;
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories);

View File

@@ -104,7 +104,6 @@ export interface IListTemplate {
}
export class JobHistoryRenderer implements tree.IRenderer {
private _statusIcon: HTMLElement;
public getHeight(tree: tree.ITree, element: JobHistoryRow): number {
return 30;
@@ -121,11 +120,10 @@ export class JobHistoryRenderer implements tree.IRenderer {
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
let row = DOM.$('.list-row');
let label = DOM.$('.label');
this._statusIcon = this.createStatusIcon();
row.appendChild(this._statusIcon);
let statusIcon = this.createStatusIcon();
row.appendChild(statusIcon);
row.appendChild(label);
container.appendChild(row);
let statusIcon = this._statusIcon;
return { statusIcon, label };
}
@@ -133,13 +131,13 @@ export class JobHistoryRenderer implements tree.IRenderer {
templateData.label.innerHTML = element.runDate + '&nbsp;&nbsp;' + element.runStatus;
let statusClass: string;
if (element.runStatus === 'Succeeded') {
statusClass = ' job-passed';
statusClass = 'status-icon job-passed';
} else if (element.runStatus === 'Failed') {
statusClass = ' job-failed';
statusClass = 'status-icon job-failed';
} else {
statusClass = ' job-unknown';
statusClass = 'status-icon job-unknown';
}
this._statusIcon.className += statusClass;
templateData.statusIcon.className = statusClass;
}
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void {
@@ -148,7 +146,6 @@ export class JobHistoryRenderer implements tree.IRenderer {
private createStatusIcon(): HTMLElement {
let statusIcon: HTMLElement = DOM.$('div');
statusIcon.className += ' status-icon';
return statusIcon;
}
}

View File

@@ -59,6 +59,7 @@ export abstract class JobManagementView extends TabChild implements AfterContent
this._showProgressWheel = true;
this.isRefreshing = true;
this.onFirstVisible();
this.layout();
this._parentComponent.refresh = false;
} else if (this.isVisible === true && this._visibilityElement.nativeElement.offsetParent === null) {
this.isVisible = false;

View File

@@ -88,15 +88,6 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
}
public layout() {
let jobsViewToolbar = $('jobhistory-component .actionbar-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (jobsViewToolbar && statusBar) {
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._tableContainer.nativeElement),
statusTop - toolbarBottom));
}
}
}

View File

@@ -35,6 +35,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { escape } from 'sql/base/common/strings';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45;
@@ -485,7 +486,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
return '<table class="jobview-jobnametable"><tr class="jobview-jobnamerow">' +
'<td nowrap class=' + resultIndicatorClass + '></td>' +
'<td nowrap class="jobview-jobnametext">' + dataContext.name + '</td>' +
'<td nowrap class="jobview-jobnametext">' + escape(dataContext.name) + '</td>' +
'</tr></table>';
}

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./button';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
Component, Input, Inject, ChangeDetectorRef, forwardRef,
ViewChild, ElementRef, OnDestroy, AfterViewInit
} from '@angular/core';
import * as sqlops from 'sqlops';
@@ -24,7 +24,13 @@ import { Color } from 'vs/base/common/color';
@Component({
selector: 'modelview-button',
template: `
<div #input style="width: 100%"></div>
<div>
<label for={{this.label}}>
<div #input style="width: 100%">
<input #fileInput *ngIf="this.isFile === true" id={{this.label}} type="file" accept=".sql" style="display: none">
</div>
</label>
</div>
`
})
export default class ButtonComponent extends ComponentWithIconBase implements IComponent, OnDestroy, AfterViewInit {
@@ -33,17 +39,16 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
private _button: Button;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
@ViewChild('fileInput', { read: ElementRef }) private _fileInputContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
@@ -55,10 +60,27 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND, buttonForeground: SIDE_BAR_TITLE_FOREGROUND
}));
this._register(this._button.onDidClick(e => {
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: e
});
if (this._fileInputContainer) {
const self = this;
this._fileInputContainer.nativeElement.onchange = () => {
let file = self._fileInputContainer.nativeElement.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let text = (<FileReader>e.target).result;
self.fileContent = text;
self._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: self.fileContent
});
};
reader.readAsText(file);
};
} else {
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: e
});
}
}));
}
}
@@ -119,9 +141,31 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
}
private get isFile(): boolean {
return this.getPropertyOrDefault<sqlops.ButtonProperties, boolean>((props) => props.isFile, false);
}
private set isFile(newValue: boolean) {
this.setPropertyFromUI<sqlops.ButtonProperties, boolean>(this.setFileProperties, newValue);
}
private get fileContent(): string {
return this.getPropertyOrDefault<sqlops.ButtonProperties, string>((props) => props.fileContent, '');
}
private set fileContent(newValue: string) {
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setFileContentProperties, newValue);
}
private setFileContentProperties(properties: sqlops.ButtonProperties, fileContent: string) : void {
properties.fileContent = fileContent;
}
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
properties.label = label;
}
private setFileProperties(properties: sqlops.ButtonProperties, isFile: boolean): void {
properties.isFile = isFile;
}
}

View File

@@ -20,6 +20,7 @@ import TableComponent from './table.component';
import TextComponent from './text.component';
import LoadingComponent from './loadingComponent.component';
import FileBrowserTreeComponent from './fileBrowserTree.component';
import EditorComponent from './editor.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
@@ -74,3 +75,6 @@ registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, L
export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component';
registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent);
export const EDITOR_COMPONENT = 'editor-component';
registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorComponent);

View File

@@ -17,6 +17,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { Event, Emitter } from 'vs/base/common/event';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import * as nls from 'vs/nls';
@@ -24,29 +25,38 @@ import * as nls from 'vs/nls';
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
boolean = 'boolean',
editableCategory = 'editableCategory'
}
@Component({
selector: 'modelview-declarativeTable',
template: `
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
<thead>
<tr style="display:block;">
<ng-container *ngFor="let column of columns;let h = index">
<th class="declarative-table-header" tabindex="-1" role="button" aria-sort="none">{{column.displayName}}</th>
<td class="declarative-table-header" tabindex="-1" [style.width]="getColumnWidth(h)" role="button" aria-sort="none">{{column.displayName}}</td>
</ng-container>
</tr>
</thead>
<ng-container *ngIf="data">
<tbody style="display: block; width:100%; overflow-y: scroll" [style.height]="getHeight()">
<ng-container *ngFor="let row of data;let r = index">
<tr class="declarative-table-row">
<ng-container *ngFor="let cellData of row;let c = index">
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)">
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
<select-box *ngIf="isSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></select-box>
<editable-select-box *ngIf="isEditableSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></editable-select-box>
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
</td>
</ng-container>
</tr>
</ng-container>
</tbody>
</ng-container>
</table>
`
@@ -109,10 +119,20 @@ export default class DeclarativeTableComponent extends ComponentBase implements
this.onCellDataChanged(e, row, cell);
}
private onSelectBoxChanged(e: ISelectData, row: number, cell: number): void {
private onSelectBoxChanged(e: ISelectData | string, row: number, cell: number): void {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
if (column.categoryValues) {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
if (typeof e === 'string') {
let category = column.categoryValues.find(c => c.displayName === e);
if (category) {
this.onCellDataChanged(category.name, row, cell);
} else {
this.onCellDataChanged(e, row, cell);
}
} else {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
}
}
}
@@ -135,6 +155,11 @@ export default class DeclarativeTableComponent extends ComponentBase implements
return column.valueType === DeclarativeDataType.category;
}
private isEditableSelectBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.editableCategory;
}
private isInputBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.string && !column.isReadOnly;
@@ -142,7 +167,7 @@ export default class DeclarativeTableComponent extends ComponentBase implements
private getColumnWidth(cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return this.convertSize(column.width);
return this.convertSize(column.width, '30px');
}
private GetOptions(cell: number): string[] {
@@ -155,7 +180,13 @@ export default class DeclarativeTableComponent extends ComponentBase implements
let cellData = this.data[row][cell];
if (cellData && column.categoryValues) {
let category = column.categoryValues.find(v => v.name === cellData);
return category.displayName;
if (category) {
return category.displayName;
} else if (this.isEditableSelectBox(cell)) {
return cellData;
} else {
return undefined;
}
} else {
return '';
}

View File

@@ -2,7 +2,6 @@
.declarative-table {
padding: 5px 30px 0px 30px;
box-sizing: border-box;
width:100%;
border-collapse: collapse;
}
@@ -13,6 +12,7 @@
padding: 5px;
border: 1px solid gray;
background-color: #F5F5F5;
vertical-align: top;
}
.vs-dark .declarative-table-header {

View File

@@ -25,7 +25,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
template: `
<div [style.width]="getWidth()">
<div [style.display]="getEditableDisplay()" #editableDropDown style="width: 100%;"></div>
<div [style.display]="getEditableDisplay()" #editableDropDown style="width: 100%;"></div>
<div [style.display]="getNotEditableDisplay()" #dropDown style="width: 100%;"></div>
</div>
`
@@ -79,7 +79,6 @@ export default class DropDownComponent extends ComponentBase implements ICompone
this._selectBox = new SelectBox(this.getValues(), this.getSelectedValue(), this.contextViewService, this._dropDownContainer.nativeElement);
this._selectBox.render(this._dropDownContainer.nativeElement);
this._register(this._selectBox);
this._register(attachSelectBoxStyler(this._selectBox, this.themeService));
this._register(this._selectBox.onDidSelect(e => {
if (!this.editable) {

View File

@@ -0,0 +1,135 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./editor';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList
} from '@angular/core';
import * as sqlops from 'sqlops';
import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import URI from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor';
@Component({
template: '',
selector: 'modelview-editor-component'
})
export default class EditorComponent extends ComponentBase implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _editor: QueryTextEditor;
private _editorInput: UntitledEditorInput;
private _editorModel: ITextModel;
private _renderedContent: string;
private _langaugeMode: string;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
this._createEditor();
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
this.layout();
}));
}
private _createEditor(): void {
this._editor = this._instantiationService.createInstance(QueryTextEditor);
this._editor.create(this._el.nativeElement);
this._editor.setVisible(true);
this._editorInput = this._instantiationService.createInstance(UntitledEditorInput, URI.from({ scheme: Schemas.untitled, path: `${this.descriptor.type}-${this.descriptor.id}` }), false, 'sql', '', '');
this._editor.setInput(this._editorInput, undefined);
this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel);
this._register(this._editor);
this._register(this._editorInput);
this._register(this._editorModel.onDidChangeContent(e => {
this.content = this._editorModel.getValue();
}));
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public layout(): void {
let width: number = this.convertSizeToNumber(this.width);
let height: number = this.convertSizeToNumber(this.height);
this._editor.layout(new DOM.Dimension(
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
height && height > 0 ? height : DOM.getContentHeight(this._el.nativeElement)));
}
/// Editor Functions
private updateModel() {
if (this._editorModel) {
this._renderedContent = this.content;
this._modelService.updateModel(this._editorModel, this._renderedContent);
}
}
private updateLanguageMode() {
if (this._editorModel && this._editor) {
this._langaugeMode = this.languageMode;
this._modeService.getOrCreateMode(this._langaugeMode).then((modeValue) => {
this._modelService.setMode(this._editorModel, modeValue);
});
}
}
/// IComponent implementation
public setLayout(layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (this.content !== this._renderedContent) {
this.updateModel();
}
if (this.languageMode !== this._langaugeMode) {
this.updateLanguageMode();
}
}
// CSS-bound properties
public get content(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.content, undefined);
}
public set content(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, content) => { properties.content = content; }, newValue);
}
public get languageMode(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.languageMode, undefined);
}
public set languageMode(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue);
}
}

View File

@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
modelview-editor-component {
height: 100%;
width : 100%;
display: block;
}

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
Component, Input, Inject, ChangeDetectorRef, forwardRef,
ViewChild, ElementRef, OnDestroy, AfterViewInit
} from '@angular/core';
import * as sqlops from 'sqlops';

View File

@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import * as nls from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { TPromise } from 'vs/base/common/winjs.base';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { EditorOptions } from 'vs/workbench/common/editor';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
class QueryCodeEditor extends CodeEditor {
protected _getContributions(): IEditorContributionCtor[] {
let contributions = super._getContributions();
let skipContributions = [FoldingController.prototype];
contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
return contributions;
}
}
/**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
*/
export class QueryTextEditor extends BaseTextEditor {
public static ID = 'modelview.editors.textEditor';
private _dimension: DOM.Dimension;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IModeService modeService: IModeService,
@ITextFileService textFileService: ITextFileService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
super(QueryTextEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
}
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(QueryCodeEditor, parent, configuration);
}
protected getConfigurationOverrides(): IEditorOptions {
const options = super.getConfigurationOverrides();
if (this.input) {
options.inDiffEditor = true;
options.scrollBeyondLastLine = false;
options.folding = false;
options.renderWhitespace = 'none';
options.wordWrap = 'on';
options.renderIndentGuides = false;
options.rulers = [];
options.glyphMargin = true;
options.minimap = {
enabled: false
};
}
return options;
}
setInput(input: UntitledEditorInput, options: EditorOptions): TPromise<void> {
return super.setInput(input, options)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
protected getAriaLabel(): string {
return nls.localize('queryTextEditorAriaLabel', 'modelview code editor for view model.');
}
public layout(dimension?: DOM.Dimension){
if (dimension) {
this._dimension = dimension;
}
this.getControl().layout(dimension);
}
public setWidth(width: number) {
if (this._dimension) {
this._dimension.width = width;
this.layout();
}
}
public setHeight(height: number) {
if (this._dimension) {
this._dimension.height = height;
this.layout();
}
}
}

View File

@@ -16,14 +16,13 @@ import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { attachTableStyler } from 'sql/common/theme/styler';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom';
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
@Component({
selector: 'modelview-table',
template: `
<div #table style="width: 100%;height:100%"></div>
<div #table style="width: 100%;height:100%" [style.font-size]="fontSize"></div>
`
})
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@@ -99,7 +98,7 @@ export default class TableComponent extends ComponentBase implements IComponent,
syncColumnCellResize: true,
enableColumnReorder: false,
enableCellNavigation: true,
forceFitColumns: true,
forceFitColumns: true
};
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
@@ -179,6 +178,10 @@ export default class TableComponent extends ComponentBase implements IComponent,
return this.getPropertyOrDefault<sqlops.TableComponentProperties, string[]>((props) => props.columns, []);
}
public get fontSize(): number | string {
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number | string>((props) => props.fontSize, '');
}
public set columns(newValue: string[]) {
this.setPropertyFromUI<sqlops.TableComponentProperties, string[]>((props, value) => props.columns = value, newValue);
}

View File

@@ -79,7 +79,7 @@ export class ServerGroupDialog extends Modal {
// Connection Group Name
let serverGroupNameLabel = localize('connectionGroupName', 'Server group name');
this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(serverGroupNameLabel);
labelContainer.text(serverGroupNameLabel);
});
this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => {
let errorMessage = localize('MissingGroupNameError', 'Group name is required.');
@@ -94,7 +94,7 @@ export class ServerGroupDialog extends Modal {
// Connection Group Description
let groupDescriptionLabel = localize('groupDescription', 'Group description');
this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(groupDescriptionLabel);
labelContainer.text(groupDescriptionLabel);
});
this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => {
this._groupDescriptionInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, {
@@ -105,7 +105,7 @@ export class ServerGroupDialog extends Modal {
// Connection Group Color
this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => {
let groupColorLabel = localize('groupColor', 'Group color');
labelContainer.innerHtml(groupColorLabel);
labelContainer.text(groupColorLabel);
});
this._bodyBuilder.div({ class: 'group-color-options' }, (groupColorContainer) => {

View File

@@ -185,7 +185,7 @@ export class OEScriptSelectAction extends ScriptSelectAction {
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode);
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
var metadata = this._objectExplorerTreeNode.metadata;
@@ -255,7 +255,7 @@ export class OEScriptCreateAction extends ScriptCreateAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -291,7 +291,7 @@ export class OEScriptExecuteAction extends ScriptExecuteAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -327,7 +327,7 @@ export class OEScriptAlterAction extends ScriptAlterAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -363,7 +363,7 @@ export class OEScriptDeleteAction extends ScriptDeleteAction {
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {

View File

@@ -77,7 +77,6 @@ export class ProfilerStart extends Action {
}
public run(input: ProfilerInput): TPromise<boolean> {
this.enabled = false;
input.data.clear();
return TPromise.wrap(this._profilerService.startSession(input.id));
}
@@ -127,7 +126,6 @@ export class ProfilerStop extends Action {
}
public run(input: ProfilerInput): TPromise<boolean> {
this.enabled = false;
return TPromise.wrap(this._profilerService.stopSession(input.id));
}
}
@@ -137,7 +135,7 @@ export class ProfilerClear extends Action {
public static LABEL = nls.localize('profiler.clear', "Clear Data");
constructor(id: string, label: string) {
super(id, label, 'stop');
super(id, label);
}
run(input: ProfilerInput): TPromise<void> {
@@ -148,14 +146,15 @@ export class ProfilerClear extends Action {
export class ProfilerAutoScroll extends Action {
public static ID = 'profiler.autoscroll';
public static LABEL = nls.localize('profiler.toggleAutoscroll', "Toggle Auto Scroll");
public static LABEL = nls.localize('profiler.autoscrollOn', "Auto Scroll: On");
constructor(id: string, label: string) {
super(id, label, 'stop');
super(id, label);
}
run(input: ProfilerInput): TPromise<boolean> {
this.checked = !this.checked;
this._setLabel(this.checked ? nls.localize('profilerAction.autoscrollOn', "Auto Scroll: On") : nls.localize('profilerAction.autoscrollOff', "Auto Scroll: Off"));
input.state.change({ autoscroll: this.checked });
return TPromise.as(true);
}

View File

@@ -166,6 +166,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll
public layout(dimension: Dimension): void {
this._currentDimensions = dimension;
this._profilerTable.layout(dimension);
this._profilerTable.autosizeColumns();
this._onDidChangeConfiguration.fire({ layoutInfo: true });
}

View File

@@ -50,6 +50,7 @@ class BasicView extends View {
private _previousSize: number;
private _collapsed: boolean;
public headerSize: number;
constructor(
initialSize: number,
private _element: HTMLElement,
@@ -58,6 +59,7 @@ class BasicView extends View {
opts: IViewOptions
) {
super(initialSize, opts);
this._previousSize = initialSize;
}
render(container: HTMLElement, orientation: Orientation): void {
@@ -116,6 +118,7 @@ export class ProfilerEditor extends BaseEditor {
private _viewTemplateSelector: SelectBox;
private _viewTemplates: Array<IProfilerViewTemplate>;
private _connectionInfoText: HTMLElement;
// Actions
private _connectAction: Actions.ProfilerConnect;
@@ -199,8 +202,16 @@ export class ProfilerEditor extends BaseEditor {
}));
let dropdownContainer = document.createElement('div');
dropdownContainer.style.width = '150px';
dropdownContainer.style.paddingRight = '5px';
this._viewTemplateSelector.render(dropdownContainer);
this._connectionInfoText = document.createElement('div');
this._connectionInfoText.style.paddingRight = '5px';
this._connectionInfoText.innerText = '';
this._connectionInfoText.style.textAlign = 'center';
this._connectionInfoText.style.display = 'flex';
this._connectionInfoText.style.alignItems = 'center';
this._register(attachSelectBoxStyler(this._viewTemplateSelector, this.themeService));
this._actionBar.setContent([
@@ -211,6 +222,8 @@ export class ProfilerEditor extends BaseEditor {
{ action: this._autoscrollAction },
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) },
{ element: dropdownContainer },
{ element: Taskbar.createTaskbarSeparator() },
{ element: this._connectionInfoText }
]);
}
@@ -361,6 +374,7 @@ export class ProfilerEditor extends BaseEditor {
autoscroll: true,
isPanelCollapsed: true
});
this._connectionInfoText.innerText = input.connectionName;
this._profilerTableEditor.updateState();
this._splitView.layout();
this._profilerTableEditor.focus();

View File

@@ -9,6 +9,7 @@ import { ProfilerState } from './profilerState';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import * as sqlops from 'sqlops';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
@@ -17,8 +18,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Event, Emitter } from 'vs/base/common/event';
import { generateUuid } from 'vs/base/common/uuid';
import * as nls from 'vs/nls';
import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs';
import { escape } from 'sql/base/common/strings';
export class ProfilerInput extends EditorInput implements IProfilerSession {
@@ -40,7 +41,8 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
private _connection: IConnectionProfile,
@IInstantiationService private _instantiationService: IInstantiationService,
@IProfilerService private _profilerService: IProfilerService,
@INotificationService private _notificationService: INotificationService
@INotificationService private _notificationService: INotificationService,
@IDialogService private _dialogService: IDialogService
) {
super();
this._state = new ProfilerState();
@@ -64,6 +66,23 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
return ret;
};
this._data = new TableDataView<Slick.SlickData>(undefined, searchFn);
this.onDispose(() => {
if (this._state.isRunning || this.state.isPaused) {
let confirm: IConfirmation = {
message: nls.localize('confirmStopProfilerSession', "Would you like to stop the running XEvent session?"),
primaryButton: nls.localize('profilerClosingActions.yes', 'Yes'),
secondaryButton: nls.localize('profilerClosingActions.no', 'No'),
type: 'question'
};
this._dialogService.confirm(confirm).then(result => {
if (result.confirmed) {
this._profilerService.stopSession(this.id);
}
});
}
});
}
public set viewTemplate(template: IProfilerViewTemplate) {
@@ -130,6 +149,19 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
this._onColumnsChanged.fire(this.columns);
}
public get connectionName(): string {
if (this._connection !== null) {
if (this._connection.databaseName) {
return `${ this._connection.serverName } ${ this._connection.databaseName }`;
} else {
return `${ this._connection.serverName }`;
}
}
else {
return nls.localize('profilerInput.notConnected', "Not connected");
}
}
public get id(): ProfilerSessionID {
return this._id;
}
@@ -171,7 +203,7 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
let columnName = this._columnMapping[key];
if (columnName) {
let value = e.values[key];
data[columnName] = value;
data[columnName] = escape(value);
}
}
this._data.push(data);

View File

@@ -98,7 +98,6 @@ export interface IProfilerSettings {
export interface IColumnViewTemplate {
name: string;
width: string;
eventsMapped: Array<string>;
}

View File

@@ -17,6 +17,7 @@ import * as sqlops from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
class TwoWayMap<T, K> {
private forwardMap: Map<T, K>;
@@ -52,7 +53,8 @@ export class ProfilerService implements IProfilerService {
constructor(
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@IConfigurationService public _configurationService: IConfigurationService,
@IInstantiationService private _instantiationService: IInstantiationService
@IInstantiationService private _instantiationService: IInstantiationService,
@INotificationService private _notificationService: INotificationService
) { }
public registerProvider(providerId: string, provider: sqlops.ProfilerProvider): void {
@@ -99,6 +101,8 @@ export class ProfilerService implements IProfilerService {
return this._runAction(id, provider => provider.startSession(this._idMap.get(id))).then(() => {
this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isRunning: true, isStopped: false, isPaused: false });
return true;
}, (reason) => {
this._notificationService.error(reason.message);
});
}
@@ -110,6 +114,8 @@ export class ProfilerService implements IProfilerService {
return this._runAction(id, provider => provider.stopSession(this._idMap.get(id))).then(() => {
this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isStopped: true, isPaused: false, isRunning: false });
return true;
}, (reason) => {
this._notificationService.error(reason.message);
});
}

View File

@@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor';
import { IEditorControl, Position, IEditor, IEditorInput } from 'vs/platform/editor/common/editor';
import { VerticalFlexibleSash, HorizontalFlexibleSash, IFlexibleSash } from 'sql/parts/query/views/flexibleSash';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
@@ -31,6 +31,8 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IRange } from 'vs/editor/common/core/range';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { Emitter } from 'vs/base/common/event';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { QueryInput } from 'sql/parts/query/common/queryInput';
@@ -40,7 +42,7 @@ import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import {
RunQueryAction, CancelQueryAction, ListDatabasesAction, ListDatabasesActionItem,
ConnectDatabaseAction, ToggleConnectDatabaseAction, EstimatedQueryPlanAction,
ActualQueryPlanAction
ActualQueryPlanAction, ParseSyntaxAction
} from 'sql/parts/query/execution/queryActions';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
@@ -86,6 +88,10 @@ export class QueryEditor extends BaseEditor {
private _listDatabasesAction: ListDatabasesAction;
private _estimatedQueryPlanAction: EstimatedQueryPlanAction;
private _actualQueryPlanAction: ActualQueryPlanAction;
private _parseSyntaxAction: ParseSyntaxAction;
private _savedViewStates = new Map<IEditorInput, IEditorViewState>();
private _resultViewStateChangeEmitters = new Map<QueryResultsInput, { onSaveViewState: Emitter<void>; onRestoreViewState: Emitter<void> }>();
constructor(
@ITelemetryService _telemetryService: ITelemetryService,
@@ -365,6 +371,22 @@ export class QueryEditor extends BaseEditor {
return true;
}
public getAllText(): string {
if (this._sqlEditor && this._sqlEditor.getControl()) {
let control = this._sqlEditor.getControl();
let codeEditor: CodeEditor = <CodeEditor>control;
if (codeEditor) {
let value = codeEditor.getValue();
if (value !== undefined && value.length > 0) {
return value;
} else {
return '';
}
}
}
return undefined;
}
public getSelectionText(): string {
if (this._sqlEditor && this._sqlEditor.getControl()) {
let control = this._sqlEditor.getControl();
@@ -434,6 +456,7 @@ export class QueryEditor extends BaseEditor {
this._listDatabasesAction = this._instantiationService.createInstance(ListDatabasesAction, this);
this._estimatedQueryPlanAction = this._instantiationService.createInstance(EstimatedQueryPlanAction, this);
this._actualQueryPlanAction = this._instantiationService.createInstance(ActualQueryPlanAction, this);
this._parseSyntaxAction = this._instantiationService.createInstance(ParseSyntaxAction, this);
// Create HTML Elements for the taskbar
let separator = Taskbar.createTaskbarSeparator();
@@ -448,6 +471,7 @@ export class QueryEditor extends BaseEditor {
{ action: this._listDatabasesAction },
{ element: separator },
{ action: this._estimatedQueryPlanAction },
{ action: this._parseSyntaxAction }
];
this._taskbar.setContent(content);
}
@@ -481,10 +505,17 @@ export class QueryEditor extends BaseEditor {
private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): TPromise<void> {
if (this._sqlEditor) {
let sqlEditorViewState = this._sqlEditor.getControl().saveViewState();
this._savedViewStates.set(this._sqlEditor.input, sqlEditorViewState);
this._sqlEditor.clearInput();
}
if (oldInput) {
let resultViewStateChangeEmitters = this._resultViewStateChangeEmitters.get(oldInput.results);
if (resultViewStateChangeEmitters) {
resultViewStateChangeEmitters.onSaveViewState.fire();
}
this._disposeEditors();
}
@@ -557,7 +588,15 @@ export class QueryEditor extends BaseEditor {
// Run all three steps synchronously
return createEditors()
.then(onEditorsCreated)
.then(doLayout);
.then(doLayout)
.then(() => {
if (this._resultViewStateChangeEmitters.has(newInput.results)) {
this._resultViewStateChangeEmitters.get(newInput.results).onRestoreViewState.fire();
}
if (this._savedViewStates.has(newInput.sql)) {
this._sqlEditor.getControl().restoreViewState(this._savedViewStates.get(newInput.sql));
}
});
}
/**
@@ -588,6 +627,14 @@ export class QueryEditor extends BaseEditor {
*/
private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise<void> {
this._resultsEditor = resultsEditor;
if (!this._resultViewStateChangeEmitters.has(resultsInput)) {
this._resultViewStateChangeEmitters.set(resultsInput, {
onRestoreViewState: new Emitter<void>(),
onSaveViewState: new Emitter<void>()
});
}
let emitters = this._resultViewStateChangeEmitters.get(resultsInput);
this._resultsEditor.setViewStateChangeEvents(emitters.onRestoreViewState.event, emitters.onSaveViewState.event);
return this._resultsEditor.setInput(resultsInput, options);
}

View File

@@ -26,6 +26,7 @@ import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { QueryOutputModule } from 'sql/parts/query/views/queryOutput.module';
import { QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
export const RESULTS_GRID_DEFAULTS = {
cellPadding: [6, 10, 5],
@@ -95,6 +96,8 @@ export class QueryResultsEditor extends BaseEditor {
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
protected _rawOptions: BareResultsGridInfo;
protected _input: QueryResultsInput;
private _restoreViewStateEvent: Event<void>;
private _saveViewStateEvent: Event<void>;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -149,6 +152,11 @@ export class QueryResultsEditor extends BaseEditor {
return TPromise.wrap<void>(null);
}
public setViewStateChangeEvents(onRestoreViewStateEvent: Event<void>, onSaveViewStateEvent: Event<void>) {
this._restoreViewStateEvent = onRestoreViewStateEvent;
this._saveViewStateEvent = onSaveViewStateEvent;
}
/**
* Load the angular components and record for this input that we have done so
*/
@@ -169,7 +177,11 @@ export class QueryResultsEditor extends BaseEditor {
// Note: pass in input so on disposal this is cleaned up.
// Otherwise many components will be left around and be subscribed
// to events from the backing data service
let params: IQueryComponentParams = { dataService: dataService };
let params: IQueryComponentParams = {
dataService: dataService,
onSaveViewState: this._saveViewStateEvent,
onRestoreViewState: this._restoreViewStateEvent
};
bootstrapAngular(this._instantiationService,
QueryOutputModule,
this.getContainer(),

View File

@@ -25,6 +25,7 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
/**
* Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -417,6 +418,55 @@ export class ListDatabasesAction extends QueryTaskbarAction {
}
}
/**
* Action class that parses the query string in the current SQL text document.
*/
export class ParseSyntaxAction extends QueryTaskbarAction {
public static EnabledClass = '';
public static ID = 'parseQueryAction';
constructor(
editor: QueryEditor,
@IConnectionManagementService connectionManagementService: IConnectionManagementService,
@IQueryManagementService private _queryManagementService: IQueryManagementService,
@INotificationService private _notificationService: INotificationService,
) {
super(connectionManagementService, editor, ParseSyntaxAction.ID, ParseSyntaxAction.EnabledClass);
this.enabled = true;
this.label = nls.localize('parseSyntaxLabel', 'Parse Query');
}
public run(): TPromise<void> {
if (!this.editor.isSelectionEmpty()) {
if (this.isConnected(this.editor)) {
let text = this.editor.getSelectionText();
if (text === '') {
text = this.editor.getAllText();
}
this._queryManagementService.parseSyntax(this.editor.connectedUri, text).then(result => {
if (result && result.parseable) {
this._notificationService.notify({
severity: Severity.Info,
message: nls.localize('queryActions.parseSyntaxSuccess', 'Commands completed successfully')
});
} else if (result && result.errors.length > 0) {
let errorMessage = nls.localize('queryActions.parseSyntaxFailure', 'Command failed: ');
this._notificationService.error(`${errorMessage}${result.errors[0]}`);
}
});
} else {
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('queryActions.notConnected', 'Please connect to a server')
});
}
}
return TPromise.as(null);
}
}
/*
* Action item that handles the dropdown (combobox) that lists the available databases.
* Based off StartDebugActionItem.

View File

@@ -17,7 +17,6 @@ import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { QueryComponent } from 'sql/parts/grid/views/query/query.component';
import { QueryPlanComponent } from 'sql/parts/queryPlan/queryPlan.component';
import { TopOperationsComponent } from 'sql/parts/queryPlan/topOperations.component';
import { ChartViewerComponent } from 'sql/parts/grid/views/query/chartViewer.component';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { PanelComponent, IPanelOptions } from 'sql/base/browser/ui/panel/panel.component';

View File

@@ -7,13 +7,13 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import { generateUuid } from 'vs/base/common/uuid';
export enum TaskStatus {
notStarted = 0,
inProgress = 1,
succeeded = 2,
succeededWithWarning = 3,
failed = 4,
canceled = 5,
canceling = 6
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export enum TaskExecutionMode {
@@ -107,7 +107,7 @@ export class TaskNode {
this.databaseName = databaseName;
this.timer = StopWatch.create();
this.startTime = new Date().toLocaleTimeString();
this.status = TaskStatus.inProgress;
this.status = TaskStatus.InProgress;
this.hasChildren = false;
this.taskExecutionMode = taskExecutionMode;
this.isCancelable = isCancelable;

View File

@@ -14,6 +14,7 @@ import { localize } from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
export const SERVICE_ID = 'taskHistoryService';
export const ITaskService = createDecorator<ITaskService>(SERVICE_ID);
@@ -27,6 +28,8 @@ export interface ITaskService {
getAllTasks(): TaskNode;
getNumberOfInProgressTasks(): number;
onNewTaskCreated(handle: number, taskInfo: sqlops.TaskInfo);
createNewTask(taskInfo: sqlops.TaskInfo);
updateTask(taskProgressInfo: sqlops.TaskProgressInfo);
onTaskStatusChanged(handle: number, taskProgressInfo: sqlops.TaskProgressInfo);
cancelTask(providerId: string, taskId: string): Thenable<boolean>;
/**
@@ -52,7 +55,8 @@ export class TaskService implements ITaskService {
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@IDialogService private dialogService: IDialogService,
@IQueryEditorService private queryEditorService: IQueryEditorService
@IQueryEditorService private queryEditorService: IQueryEditorService,
@IConnectionManagementService private connectionManagementService: IConnectionManagementService
) {
this._taskQueue = new TaskNode('Root', undefined, undefined);
this._onTaskComplete = new Emitter<TaskNode>();
@@ -70,12 +74,27 @@ export class TaskService implements ITaskService {
}
public onNewTaskCreated(handle: number, taskInfo: sqlops.TaskInfo) {
let node: TaskNode = new TaskNode(taskInfo.name, taskInfo.serverName, taskInfo.databaseName, taskInfo.taskId, taskInfo.taskExecutionMode, taskInfo.isCancelable);
this.createNewTask(taskInfo);
}
public createNewTask(taskInfo: sqlops.TaskInfo) {
let databaseName: string = taskInfo.databaseName;
let serverName: string = taskInfo.serverName;
if (taskInfo && taskInfo.connection) {
let connectionProfile = this.connectionManagementService.getConnectionProfile(taskInfo.connection.connectionId);
if (connectionProfile && !!databaseName) {
databaseName = connectionProfile.databaseName;
}
if (connectionProfile && !!serverName) {
serverName = connectionProfile.serverName;
}
}
let node: TaskNode = new TaskNode(taskInfo.name, serverName, databaseName, taskInfo.taskId, taskInfo.taskExecutionMode, taskInfo.isCancelable);
node.providerName = taskInfo.providerName;
this.handleNewTask(node);
}
public onTaskStatusChanged(handle: number, taskProgressInfo: sqlops.TaskProgressInfo) {
public updateTask(taskProgressInfo: sqlops.TaskProgressInfo) {
this.handleTaskComplete({
taskId: taskProgressInfo.taskId,
status: taskProgressInfo.status,
@@ -84,15 +103,23 @@ export class TaskService implements ITaskService {
});
}
public onTaskStatusChanged(handle: number, taskProgressInfo: sqlops.TaskProgressInfo) {
this.updateTask(taskProgressInfo);
}
public cancelTask(providerId: string, taskId: string): Thenable<boolean> {
let task = this.getTaskInQueue(taskId);
task.status = TaskStatus.canceling;
task.status = TaskStatus.Canceling;
this._onTaskComplete.fire(task);
let provider = this._providers[providerId];
if (provider) {
return provider.cancelTask({
taskId: taskId
});
if (providerId) {
let provider = this._providers[providerId];
if (provider && provider.cancelTask) {
return provider.cancelTask({
taskId: taskId
});
}
} else {
return Promise.resolve(true);
}
return Promise.resolve(undefined);
}
@@ -100,7 +127,7 @@ export class TaskService implements ITaskService {
private cancelAllTasks(): Thenable<void> {
return new TPromise<void>((resolve, reject) => {
let promises = this._taskQueue.children.map(task => {
if (task.status === TaskStatus.inProgress || task.status === TaskStatus.notStarted) {
if (task.status === TaskStatus.InProgress || task.status === TaskStatus.NotStarted) {
return this.cancelTask(task.providerName, task.id);
}
return Promise.resolve(true);
@@ -173,10 +200,10 @@ export class TaskService implements ITaskService {
task.message = eventArgs.message;
}
switch (task.status) {
case TaskStatus.canceled:
case TaskStatus.succeeded:
case TaskStatus.succeededWithWarning:
case TaskStatus.failed:
case TaskStatus.Canceled:
case TaskStatus.Succeeded:
case TaskStatus.SucceededWithWarning:
case TaskStatus.Failed:
task.endTime = new Date().toLocaleTimeString();
task.timer.stop();
this._onTaskComplete.fire(task);
@@ -185,7 +212,7 @@ export class TaskService implements ITaskService {
break;
}
if ((task.status === TaskStatus.succeeded || task.status === TaskStatus.succeededWithWarning)
if ((task.status === TaskStatus.Succeeded || task.status === TaskStatus.SucceededWithWarning)
&& eventArgs.script && eventArgs.script !== '') {
if (task.taskExecutionMode === TaskExecutionMode.script) {
this.queryEditorService.newSqlEditor(eventArgs.script);
@@ -214,7 +241,7 @@ export class TaskService implements ITaskService {
public getNumberOfInProgressTasks(): number {
if (this._taskQueue.hasChildren) {
var inProgressTasks = this._taskQueue.children.filter(x => x.status === TaskStatus.inProgress);
var inProgressTasks = this._taskQueue.children.filter(x => x.status === TaskStatus.InProgress);
return inProgressTasks ? inProgressTasks.length : 0;
}
return 0;

View File

@@ -52,12 +52,12 @@ export class TaskHistoryActionProvider extends ContributableActionProvider {
var actions = [];
// get actions for tasks in progress
if (element.status === TaskStatus.inProgress && element.isCancelable) {
if (element.status === TaskStatus.InProgress && element.isCancelable) {
actions.push(this._instantiationService.createInstance(CancelAction, CancelAction.ID, CancelAction.LABEL));
}
// get actions for tasks succeeded
if (element.status === TaskStatus.succeeded || element.status === TaskStatus.succeededWithWarning) {
if (element.status === TaskStatus.Succeeded || element.status === TaskStatus.SucceededWithWarning) {
if (element.taskExecutionMode === TaskExecutionMode.executeAndScript) {
actions.push(this._instantiationService.createInstance(ScriptAction, ScriptAction.ID, ScriptAction.LABEL));
}

View File

@@ -67,27 +67,27 @@ export class TaskHistoryRenderer implements IRenderer {
if (taskNode) {
templateData.icon.className = TaskHistoryRenderer.ICON_CLASS;
switch (taskNode.status) {
case TaskStatus.succeeded:
case TaskStatus.Succeeded:
templateData.icon.classList.add(TaskHistoryRenderer.SUCCESS_CLASS);
taskStatus = localize('succeeded', "succeeded");
break;
case TaskStatus.failed:
case TaskStatus.Failed:
templateData.icon.classList.add(TaskHistoryRenderer.FAIL_CLASS);
taskStatus = localize('failed', "failed");
break;
case TaskStatus.inProgress:
case TaskStatus.InProgress:
templateData.icon.classList.add(TaskHistoryRenderer.INPROGRESS_CLASS);
taskStatus = localize('inProgress', "in progress");
break;
case TaskStatus.notStarted:
case TaskStatus.NotStarted:
templateData.icon.classList.add(TaskHistoryRenderer.NOTSTARTED_CLASS);
taskStatus = localize('notStarted', "not started");
break;
case TaskStatus.canceled:
case TaskStatus.Canceled:
templateData.icon.classList.add(TaskHistoryRenderer.CANCELED_CLASS);
taskStatus = localize('canceled', "canceled");
break;
case TaskStatus.canceling:
case TaskStatus.Canceling:
templateData.icon.classList.add(TaskHistoryRenderer.INPROGRESS_CLASS);
taskStatus = localize('canceling', "canceling");
break;
@@ -117,7 +117,7 @@ export class TaskHistoryRenderer implements IRenderer {
public timer(taskNode: TaskNode, templateData: ITaskHistoryTemplateData) {
let timeLabel = '';
if (taskNode.status === TaskStatus.failed) {
if (taskNode.status === TaskStatus.Failed) {
timeLabel += taskNode.startTime + ' Error: ' + taskNode.message;
} else {
if (taskNode.startTime) {

View File

@@ -141,9 +141,9 @@ export class TaskHistoryView {
let isMouseOrigin = event.payload && (event.payload.origin === 'mouse');
let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2;
if (isDoubleClick) {
if (task.status === TaskStatus.failed) {
if (task.status === TaskStatus.Failed) {
var err = task.taskName + ': ' + task.message;
this._errorMessageService.showDialog(Severity.Error, nls.localize('taskError','Task error'), err);
this._errorMessageService.showDialog(Severity.Error, nls.localize('taskError', 'Task error'), err);
}
}
}

View File

@@ -22,6 +22,7 @@ import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/boot
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -36,6 +37,7 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
declarations: [
Checkbox,
SelectBox,
EditableDropDown,
InputBox,
DialogContainer,
WizardNavigation,

View File

@@ -105,7 +105,7 @@ export class DialogModal extends Modal {
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton, requireDialogValid: boolean = false) {
buttonElement.label = dialogButton.label;
buttonElement.enabled = requireDialogValid ? dialogButton.enabled && this._dialog.valid : dialogButton.enabled;
dialogButton.hidden ? buttonElement.element.classList.add('dialogModal-hidden') : buttonElement.element.classList.remove('dialogModal-hidden');
dialogButton.hidden ? buttonElement.element.parentElement.classList.add('dialogModal-hidden') : buttonElement.element.parentElement.classList.remove('dialogModal-hidden');
}
protected renderBody(container: HTMLElement): void {
@@ -125,11 +125,19 @@ export class DialogModal extends Modal {
public async done(): Promise<void> {
if (this._doneButton.enabled) {
let buttonSpinnerHandler = setTimeout(() => {
this._doneButton.enabled = false;
this._doneButton.element.innerHTML = '&nbsp';
this._doneButton.element.classList.add('validating');
}, 100);
if (await this._dialog.validateClose()) {
this._onDone.fire();
this.dispose();
this.hide();
}
clearTimeout(buttonSpinnerHandler);
this._doneButton.element.classList.remove('validating');
this.updateButtonElement(this._doneButton, this._dialog.okButton, true);
}
}

View File

@@ -31,6 +31,21 @@
margin: 0;
}
.footer-button .validating {
background-size: 15px;
background-repeat: no-repeat;
background-position: center;
}
.vs .footer-button .validating {
background-image: url("loading.svg");
}
.vs-dark .footer-button .validating,
.hc-black .footer-button .validating {
background-image: url("loading_inverse.svg");
}
.dialogModal-wizardHeader {
padding: 10px 30px;
}

View File

@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 0.6s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.075s; }
circle:nth-child(3) { animation-delay: 0.15s; }
circle:nth-child(4) { animation-delay: 0.225s; }
circle:nth-child(5) { animation-delay: 0.3s; }
circle:nth-child(6) { animation-delay: 0.375s; }
circle:nth-child(7) { animation-delay: 0.45s; }
circle:nth-child(8) { animation-delay: 0.525s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g>
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 0.6s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.075s; }
circle:nth-child(3) { animation-delay: 0.15s; }
circle:nth-child(4) { animation-delay: 0.225s; }
circle:nth-child(5) { animation-delay: 0.3s; }
circle:nth-child(6) { animation-delay: 0.375s; }
circle:nth-child(7) { animation-delay: 0.45s; }
circle:nth-child(8) { animation-delay: 0.525s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g style="fill:white;">
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -119,7 +119,8 @@ export class WizardModal extends Modal {
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton, requirePageValid: boolean = false) {
buttonElement.label = dialogButton.label;
buttonElement.enabled = requirePageValid ? dialogButton.enabled && this._wizard.pages[this._wizard.currentPage].valid : dialogButton.enabled;
dialogButton.hidden ? buttonElement.element.classList.add('dialogModal-hidden') : buttonElement.element.classList.remove('dialogModal-hidden');
dialogButton.hidden ? buttonElement.element.parentElement.classList.add('dialogModal-hidden') : buttonElement.element.parentElement.classList.remove('dialogModal-hidden');
this.setButtonsForPage(this._wizard.currentPage);
}
protected renderBody(container: HTMLElement): void {
@@ -167,7 +168,7 @@ export class WizardModal extends Modal {
this.done(validate);
return;
}
if (validate && !await this._wizard.validateNavigation(index)) {
if (validate && !await this.validateNavigation(index)) {
return;
}
this._dialogPanes.forEach((dialogPane, page) => {
@@ -185,20 +186,24 @@ export class WizardModal extends Modal {
}
private setButtonsForPage(index: number) {
if (this._wizard.pages[index - 1]) {
this._previousButton.element.parentElement.classList.remove('dialogModal-hidden');
this._previousButton.enabled = this._wizard.pages[index - 1].enabled;
} else {
this._previousButton.element.parentElement.classList.add('dialogModal-hidden');
if (this._previousButton) {
if (this._wizard.pages[index - 1]) {
this._previousButton.element.parentElement.classList.remove('dialogModal-hidden');
this._previousButton.enabled = this._wizard.pages[index - 1].enabled;
} else {
this._previousButton.element.parentElement.classList.add('dialogModal-hidden');
}
}
if (this._wizard.pages[index + 1]) {
this._nextButton.element.parentElement.classList.remove('dialogModal-hidden');
this._nextButton.enabled = this._wizard.pages[index + 1].enabled;
this._doneButton.element.parentElement.classList.add('dialogModal-hidden');
} else {
this._nextButton.element.parentElement.classList.add('dialogModal-hidden');
this._doneButton.element.parentElement.classList.remove('dialogModal-hidden');
if (this._nextButton && this._doneButton) {
if (this._wizard.pages[index + 1]) {
this._nextButton.element.parentElement.classList.remove('dialogModal-hidden');
this._nextButton.enabled = this._wizard.pages[index + 1].enabled;
this._doneButton.element.parentElement.classList.add('dialogModal-hidden');
} else {
this._nextButton.element.parentElement.classList.add('dialogModal-hidden');
this._doneButton.element.parentElement.classList.remove('dialogModal-hidden');
}
}
}
@@ -225,7 +230,7 @@ export class WizardModal extends Modal {
public async done(validate: boolean = true): Promise<void> {
if (this._doneButton.enabled) {
if (validate && !await this._wizard.validateNavigation(undefined)) {
if (validate && !await this.validateNavigation(undefined)) {
return;
}
this._onDone.fire();
@@ -240,6 +245,20 @@ export class WizardModal extends Modal {
this.hide();
}
private async validateNavigation(newPage: number): Promise<boolean> {
let button = newPage === undefined ? this._doneButton : this._nextButton;
let buttonSpinnerHandler = setTimeout(() => {
button.enabled = false;
button.element.innerHTML = '&nbsp';
button.element.classList.add('validating');
}, 100);
let navigationValid = await this._wizard.validateNavigation(newPage);
clearTimeout(buttonSpinnerHandler);
button.element.classList.remove('validating');
this.updateButtonElement(button, newPage === undefined ? this._wizard.doneButton : this._wizard.nextButton, true);
return navigationValid;
}
protected hide(): void {
super.hide();
}

View File

@@ -8,9 +8,12 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IBootstrapParams } from './bootstrapService';
import { Event } from 'vs/base/common/event';
export interface IQueryComponentParams extends IBootstrapParams {
dataService: DataService;
onSaveViewState: Event<void>;
onRestoreViewState: Event<void>;
}
export interface IEditDataComponentParams extends IBootstrapParams {

19
src/sql/sqlops.d.ts vendored
View File

@@ -777,7 +777,7 @@ declare module 'sqlops' {
export interface SyntaxParseResult {
parseable: boolean;
errorMessages: string[];
errors: string[];
}
// Query Batch Notification -----------------------------------------------------------------------
@@ -1531,12 +1531,13 @@ declare module 'sqlops' {
// Task service interfaces ----------------------------------------------------------------------------
export enum TaskStatus {
notStarted = 0,
inProgress = 1,
succeeded = 2,
succeededWithWarning = 3,
failed = 4,
canceled = 5
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export enum TaskExecutionMode {
@@ -1550,6 +1551,7 @@ declare module 'sqlops' {
}
export interface TaskInfo {
connection?: connection.Connection;
taskId: string;
status: TaskStatus;
taskExecutionMode: TaskExecutionMode;
@@ -1573,8 +1575,7 @@ declare module 'sqlops' {
taskId: string;
status: TaskStatus;
message: string;
script: string;
duration: number;
script?: string;
}
export interface TaskServicesProvider extends DataProvider {

View File

@@ -23,6 +23,7 @@ declare module 'sqlops' {
checkBox(): ComponentBuilder<CheckBoxComponent>;
radioButton(): ComponentBuilder<RadioButtonComponent>;
webView(): ComponentBuilder<WebViewComponent>;
editor(): ComponentBuilder<EditorComponent>;
text(): ComponentBuilder<TextComponent>;
button(): ComponentBuilder<ButtonComponent>;
dropDown(): ComponentBuilder<DropDownComponent>;
@@ -375,6 +376,7 @@ declare module 'sqlops' {
export interface TableComponentProperties extends ComponentProperties {
data: any[][];
columns: string[] | TableColumn[];
fontSize?: number | string;
selectedRows?: number[];
}
@@ -390,7 +392,8 @@ declare module 'sqlops' {
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
boolean = 'boolean',
editableCategory = 'editableCategory'
}
export interface RadioButtonProperties {
@@ -434,8 +437,24 @@ declare module 'sqlops' {
html?: string;
}
/**
* Editor properties for the editor component
*/
export interface EditorProperties {
/**
* The content inside the text editor
*/
content?: string;
/**
* The languge mode for this text editor. The language mode is SQL by default.
*/
languageMode?: string
}
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
label?: string;
isFile?: boolean;
fileContent?: string;
}
export interface LoadingComponentProperties {
@@ -501,7 +520,21 @@ declare module 'sqlops' {
onMessage: vscode.Event<any>;
}
export interface ButtonComponent extends Component {
/**
* Editor component for displaying the text code editor
*/
export interface EditorComponent extends Component {
/**
* The content inside the text editor
*/
content: string;
/**
* The languge mode for this text editor. The language mode is SQL by default.
*/
languageMode: string;
}
export interface ButtonComponent extends Component, ButtonProperties {
label: string;
iconPath: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
onDidClick: vscode.Event<any>;
@@ -711,6 +744,12 @@ declare module 'sqlops' {
* done. Return true to allow the dialog to close or false to block it from closing
*/
registerCloseValidator(validator: () => boolean | Thenable<boolean>): void;
/**
* Register an operation to run in the background when the dialog is done
* @param operationInfo Operation Information
*/
registerOperation(operationInfo: BackgroundOperationInfo): void;
}
export interface DialogTab extends ModelViewPanel {
@@ -894,6 +933,12 @@ declare module 'sqlops' {
* undefined or the text is empty or undefined. The default level is error.
*/
message: DialogMessage
/**
* Register an operation to run in the background when the wizard is done
* @param operationInfo Operation Information
*/
registerOperation(operationInfo: BackgroundOperationInfo): void;
}
}
}
@@ -1000,4 +1045,86 @@ declare module 'sqlops' {
nodeInfo: NodeInfo;
}
/**
* Background Operation
*/
export interface BackgroundOperation {
/**
* Updates the operation status or adds progress message
* @param status Operation Status
* @param message Progress message
*/
updateStatus(status: TaskStatus, message?: string): void;
/**
* Operation Id
*/
id: string;
/**
* Event raised when operation is canceled in UI
*/
onCanceled: vscode.Event<void>;
}
/**
* Operation Information
*/
export interface BackgroundOperationInfo {
/**
* The operation id. A unique id will be assigned to it If not specified a
*/
operationId?: string;
/**
* Connection information
*/
connection: connection.Connection;
/**
* Operation Display Name
*/
displayName: string;
/**
* Operation Description
*/
description: string;
/**
* True if the operation is cancelable
*/
isCancelable: boolean;
/**
* The actual operation to execute
*/
operation: (operation: BackgroundOperation) => void
}
namespace tasks {
/**
* Starts an operation to run in the background
* @param operationInfo Operation Information
*/
export function startBackgroundOperation(operationInfo: BackgroundOperationInfo): void;
}
export namespace connection {
/**
* List the databases that can be accessed from the given connection
* @param {string} connectionId The ID of the connection
* @returns {string[]} An list of names of databases
*/
export function listDatabases(connectionId: string): Thenable<string[]>;
/**
* Get a URI corresponding to the given connection so that it can be used with data
* providers and other APIs that require a connection API.
* Note: If the given connection corresponds to multiple URIs this may return any of them
* @param connectionId The ID of the connection
*/
export function getUriForConnection(connectionId: string): Thenable<string>;
}
}

View File

@@ -39,12 +39,13 @@ export enum EditRowState {
}
export enum TaskStatus {
notStarted = 0,
inProgress = 1,
succeeded = 2,
succeededWithWarning = 3,
failed = 4,
canceled = 5
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export enum TaskExecutionMode {
@@ -99,7 +100,7 @@ export enum AlertType {
}
export enum FrequencyTypes {
Unknown ,
Unknown,
OneTime = 1 << 1,
Daily = 1 << 2,
Weekly = 1 << 3,
@@ -145,7 +146,8 @@ export enum ModelComponentTypes {
Group,
Toolbar,
LoadingComponent,
FileBrowserTree
FileBrowserTree,
Editor
}
export interface IComponentShape {
@@ -271,11 +273,12 @@ export enum DataProviderType {
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
boolean = 'boolean',
editableCategory = 'editableCategory'
}
export enum CardType {
VerticalButton = 'VerticalButton',
VerticalButton = 'VerticalButton',
Details = 'Details'
}
export class SqlThemeIcon {

View File

@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* 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 { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostBackgroundTaskManagementShape, SqlMainContext, MainThreadBackgroundTaskManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { Emitter } from 'vs/base/common/event';
import { generateUuid } from 'vs/base/common/uuid';
export enum TaskStatus {
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export class ExtBackgroundOperation implements sqlops.BackgroundOperation {
private readonly _proxy: MainThreadBackgroundTaskManagementShape;
private _onCanceled = new Emitter<void>();
constructor(
private _id: string,
mainContext: IMainContext
) {
this._proxy = mainContext.getProxy(SqlMainContext.MainThreadBackgroundTaskManagement);
}
public updateStatus(status: TaskStatus, message?: string): void {
this._proxy.$updateTask({
message: message,
status: status,
taskId: this.id
});
}
public get onCanceled(): vscode.Event<void> {
return this._onCanceled.event;
}
public cancel(): void {
this._onCanceled.fire();
}
public get id(): string {
return this._id;
}
}
export class ExtHostBackgroundTaskManagement implements ExtHostBackgroundTaskManagementShape {
private readonly _proxy: MainThreadBackgroundTaskManagementShape;
private readonly _handlers = new Map<string, sqlops.BackgroundOperationInfo>();
private readonly _operations = new Map<string, ExtBackgroundOperation>();
private readonly _mainContext: IMainContext;
constructor(
mainContext: IMainContext
) {
this._proxy = mainContext.getProxy(SqlMainContext.MainThreadBackgroundTaskManagement);
this._mainContext = mainContext;
}
$onTaskRegistered(operationId: string): void {
let extOperationInfo = new ExtBackgroundOperation(operationId, this._mainContext);
this._operations.set(operationId, extOperationInfo);
let operationInfo = this._handlers.get(operationId);
if (operationInfo) {
operationInfo.operation(extOperationInfo);
}
}
$onTaskCanceled(operationId: string): void {
let operation = this._operations.get(operationId);
if (operation) {
operation.cancel();
}
}
$registerTask(operationInfo: sqlops.BackgroundOperationInfo): void {
let operationId = operationInfo.operationId || `OperationId${generateUuid()}`;
if (this._handlers.has(operationId)) {
throw new Error(`operation '${operationId}' already exists`);
}
this._handlers.set(operationId, operationInfo);
let taskInfo: sqlops.TaskInfo = {
databaseName: undefined,
serverName: undefined,
description: operationInfo.description,
isCancelable: operationInfo.isCancelable,
name: operationInfo.displayName,
providerName: undefined, //setting provider name will cause the task to be processed by the provider. But this task is created in the extension and needs to be handled
//by the extension
taskExecutionMode: 0,
taskId: operationId,
status: TaskStatus.NotStarted,
connection: operationInfo.connection
};
this._proxy.$registerTask(taskInfo);
}
$removeTask(operationId: string) {
if (this._handlers.has(operationId)) {
this._handlers.delete(operationId);
}
}
}

View File

@@ -8,7 +8,7 @@ import { ExtHostConnectionManagementShape, SqlMainContext, MainThreadConnectionM
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import * as sqlops from 'sqlops';
export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape {
export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape {
private _proxy: MainThreadConnectionManagementShape;
@@ -27,7 +27,15 @@ export class ExtHostConnectionManagement extends ExtHostConnectionManagementShap
return this._proxy.$getCurrentConnection();
}
public $getCredentials(connectionId: string): Thenable<{ [name: string]: string}> {
public $getCredentials(connectionId: string): Thenable<{ [name: string]: string }> {
return this._proxy.$getCredentials(connectionId);
}
public $listDatabases(connectionId: string): Thenable<string[]> {
return this._proxy.$listDatabases(connectionId);
}
public $getUriForConnection(connectionId: string): Thenable<string> {
return this._proxy.$getUriForConnection(connectionId);
}
}

View File

@@ -101,6 +101,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
editor(): sqlops.ComponentBuilder<sqlops.EditorComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.EditorComponent> = this.getComponentBuilder(new EditorWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
button(): sqlops.ComponentBuilder<sqlops.ButtonComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.ButtonComponent> = this.getComponentBuilder(new ButtonWrapper(this._proxy, this._handle, id), id);
@@ -740,6 +747,28 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
}
}
class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.Editor, id);
this.properties = {};
}
public get content(): string {
return this.properties['content'];
}
public set content(v: string) {
this.setProperty('content', v);
}
public get languageMode(): string {
return this.properties['languageMode'];
}
public set languageMode(v: string) {
this.setProperty('languageMode', v);
}
}
class RadioButtonWrapper extends ComponentWrapper implements sqlops.RadioButtonComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
@@ -818,6 +847,14 @@ class TableComponentWrapper extends ComponentWrapper implements sqlops.TableComp
this.setProperty('columns', v);
}
public get fontSize(): number | string {
return this.properties['fontSize'];
}
public set fontSize(size: number | string) {
this.setProperty('fontSize', size);
}
public get selectedRows(): number[] {
return this.properties['selectedRows'];
}

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