Compare commits

..

70 Commits

Author SHA1 Message Date
Matt Irvine
216b6eecc0 Fix chart error that caused query editor to close (#2652) 2018-09-20 14:29:05 -07:00
Alan Ren
c0917e9276 reload the chart types when we actually need it (#2651) 2018-09-20 12:38:36 -07:00
Abbie Petchtes
0bbcbf0d2d fix css issue (#2650) 2018-09-20 12:16:20 -07:00
Aditya Bist
9b998e3fca fixed right click context menu bug in jobs view (#2632) 2018-09-19 20:19:48 -07:00
Matt Irvine
d7d65cdf21 Fix broken 'Clear Token Cache' command (#2643) 2018-09-19 18:31:23 -07:00
Matt Irvine
27925289d4 Scroll query editor when clicking batch links (#2644) 2018-09-19 17:27:10 -07:00
Anthony Dresser
03ea265bab Hide tabs on reexecute (#2624)
* add logic to hide tabs when a query rerun is executed

* remove double entry in the map
2018-09-18 17:56:37 -07:00
Abbie Petchtes
917f9eead3 Feat/add dom component (#2622)
* add dom component for model view

* formatting

* make css style hardcoded in dom.component

* comment out the unused CSS

* address comments

* address comment
2018-09-18 17:23:26 -07:00
Karl Burtram
08f2e72af8 Bump SQL Tools Service to 1.5.0-alpha.34 (#2621) 2018-09-18 14:57:59 -07:00
Aditya Bist
a2fb0ec029 fixed actual show plan command (#2620) 2018-09-18 13:42:14 -07:00
ranasaria
084042ad13 Bug/oetimeout Fix - When timeout happens while fetching node children, the node becomes unusable (#2616)
This commit fixes issue when multiple OE nodes are expanded simultaneously. While the error was getting displayed the node was left in incorrect state which was leading to the node being unusable in future. This commit repairs this defect.
2018-09-18 13:41:14 -07:00
AlexFsmn
8da3defe24 Added text underline CSS for DB NULL values when editing / showing data (#2597)
* Added text underline CSS for DB NULL values when editing / showing data
#217

* Changed db nulls styling to italic
2018-09-18 12:30:32 -07:00
Karl Burtram
58f9cd32a5 Update SQL Ops to 0.33.7 2018-09-18 12:12:23 -07:00
Anthony Dresser
f7abf5a2d5 Fix stating for scrolls (#2615)
* nearly working

* add accounting for the downsides to slickgrid
2018-09-17 17:55:52 -07:00
Anthony Dresser
c8c6d072f6 Respect message settings (#2614)
* add results view stating

* working through the bugs

* handle various resizing bugs

* gnale resizing better

* add configuration to state

* address comments
2018-09-17 17:55:38 -07:00
Anthony Dresser
4d9cc604b9 add view area options to pick up chart background fix (#2613) 2018-09-17 16:37:18 -07:00
Aditya Bist
1dc76fa171 Dashboard: Fixed all insight bugs (#2612)
* fixed all insight bugs

* removed unused imports

* added comment
2018-09-17 14:47:46 -07:00
Aditya Bist
1d37b9ae9c fixes scrolling in query plan (#2609) 2018-09-17 13:02:38 -07:00
Matt Irvine
26828602a8 Use version 2.0.9 of electron (#2606) 2018-09-17 12:08:50 -07:00
Matt Irvine
e253f3ac89 Fix/bump dependency versions (#2608) 2018-09-17 11:21:41 -07:00
AlexFsmn
4d59fdea1b The "New Query" context menu is now only available from the server & db (#2598)
#1890
2018-09-15 13:44:58 -07:00
AlexFsmn
98a313eb5b Changed the "Configure" link to "Learn How To Configure The Dashboard". (#2599)
* Changed the "Configure" link to "Learn How To Configure the Dashboard".
This inlcudes the command as well as the tile label
#1227

* Capitalizing first character in each word
2018-09-15 11:45:21 -07:00
Aditya Bist
77e1cd8b32 fixed insights crash (#2596) 2018-09-14 21:20:37 -07:00
Alan Ren
dede5c5ef5 edit data issue with column index handling (#2595) 2018-09-14 21:19:56 -07:00
Matt Irvine
d156c0be3d Fix crash when reverting in edit data with no changes (#2594) 2018-09-14 21:18:54 -07:00
Karl Burtram
dc0bc6e606 Update SQL Ops to 0.33.6 2018-09-13 23:48:40 -07:00
Matt Irvine
05040425df Add OE node refresh API method (#2578)
* Initial working commit for refreshing OE node via API

* Add test and fix up code

* Run tsfmt

* Fix test
2018-09-13 18:43:47 -07:00
Alan Ren
36f7c283b8 use latest slickgrid library (#2584) 2018-09-13 18:43:06 -07:00
Anthony Dresser
9fe4237033 Maintain Query State (#2571)
* add results view stating

* working through the bugs

* handle various resizing bugs

* gnale resizing better

* fix tests by adding missing node module

* formatting

* refactor interfaces out to get around testing restrictions

* more refactoring of importants to avoid loading errors
2018-09-13 18:42:29 -07:00
Alan Ren
b03c0a3e2d accessibility setting based select database dropdown (#2579) 2018-09-13 15:07:08 -07:00
Aditya Bist
87946996ed added context to chart buttons so they work (#2575) 2018-09-13 14:22:57 -07:00
AlexFsmn
cc55023440 Disabled connection name input when connecting to a server. (#2566)
* Disabled connection name input when connecting to a server.
#2557

* Fixed reset state of connection inputs
2018-09-13 12:36:28 -07:00
Karl Burtram
1f19dfc50d Update SQL Ops to 0.33.5 2018-09-13 08:06:27 -07:00
Alan Ren
950a440350 fix the connection issue when opening new query after connection (#2561) 2018-09-13 08:05:46 -07:00
Aditya Bist
c92b88bfaf Bug/extension contribution (#2560)
* revert 4ab5d84b94

* fixed extensions
2018-09-12 23:04:59 -07:00
Abbie Petchtes
10875f26dc add divcontainer in modelview (#2559)
* add divcontainer in modelview

* address comment
2018-09-12 20:18:40 -07:00
Kevin Cunnane
d62e809c18 Support isDirty flag for model view editors and begin plumb through of save support (#2547)
* Add dirty and save support to model view

* Add issue # for a TODO
2018-09-12 14:35:19 -07:00
Alan Ren
d85bf4f6dd fix the account not found error when creating firewall rules (#2543) 2018-09-12 13:32:40 -07:00
Anthony Dresser
801e201cc3 change active cell during change to fix focus shift (#2545) 2018-09-12 13:22:01 -07:00
Anthony Dresser
e18e0da0c1 Fix sizing error when switching windows (#2544)
* add work around for when we need to resize while we don't have a dimension to resize off of

* formatting
2018-09-12 13:21:51 -07:00
Karl Burtram
d046b0a412 Update SQL Ops to 0.33.4 2018-09-12 13:09:41 -07:00
Karl Burtram
72084b8fc1 Fix build break in Git extension (#2538) 2018-09-11 21:48:44 -07:00
Anthony Dresser
2639b2bd2c Fix bug around debounced event not being flushed in time (#2536)
* fix bug around debounced event not being flushed in time

* add comment
2018-09-11 21:22:25 -07:00
Anthony Dresser
82aa493dfd Reduce message panel min size to 0 (#2534)
* reduce message panel minimum size to 0; attempt to restore panel sizing on requery sizes; default grid panel size to 80%

* formatting
2018-09-11 21:22:06 -07:00
Karl Burtram
6c3c7c40b5 Turn-off Git missing prompt (#2533) 2018-09-11 21:21:44 -07:00
Anthony Dresser
5616751c04 fix grid action bar not updating (#2532) 2018-09-11 21:21:30 -07:00
Anthony Dresser
7d898ca34d Fix grid gaps (#2531)
* modifying grid gaps

* reduce gaps and increase gap for action bar
2018-09-11 21:21:06 -07:00
Kevin Cunnane
e26556b21a Fixes #2523 (#2528)
The IdGenerator was recreated each time and had a high likelihood of conflicts. Invitably after adding dozens or hundreds of icons you'll start seeing the CSS class replaced and overridden.

The solution is to do like elsewhere: have 1 const that is loaded on first import of the file and keeps a global track.

Side note is that it'd be a good idea to cache CSS rules with the same iconPath so we don't create lots of additional rules unnecessarily. If we reuse the same icon a bunch we should cache them - #2524 is tracking this.
2018-09-11 21:20:38 -07:00
Anthony Dresser
89e6d363e2 Selection in grid context (#2527)
* update action context on selection change

* correctly add ranges rather than a new range for every row

* add required functions to typings
2018-09-11 17:10:53 -07:00
Anthony Dresser
c559ac7be9 expand messages panel on error (#2519) 2018-09-11 17:09:40 -07:00
Anthony Dresser
b3fbe47f0a add min size for row num column (#2518) 2018-09-11 17:09:17 -07:00
Anthony Dresser
c73af4c480 add check for selection model in edit data (#2517) 2018-09-11 17:08:41 -07:00
Alan Ren
8887fe1eac fix the save and save all for untitled file (#2526) 2018-09-11 17:06:28 -07:00
Kevin Cunnane
ed861a6c96 Revert "Fixes #2523" (#2525)
This reverts commit e63bb6a8ec.
2018-09-11 16:42:27 -07:00
Kevin Cunnane
e63bb6a8ec Fixes #2523
The IdGenerator was recreated each time and had a high likelihood of conflicts. Invitably after adding dozens or hundreds of icons you'll start seeing the CSS class replaced and overridden.

The solution is to do like elsewhere: have 1 const that is loaded on first import of the file and keeps a global track.

Side note is that it'd be a good idea to cache CSS rules with the same iconPath so we don't create lots of additional rules unnecessarily. If we reuse the same icon a bunch we should cache them - #2524 is tracking this.
2018-09-11 16:36:17 -07:00
Anthony Dresser
8ec09d25ce add listener to change action bar on maximize change (#2505) 2018-09-11 12:42:06 -07:00
Anthony Dresser
a9a01ae479 fixes a rendering problem in splitview (#2512) 2018-09-11 12:18:03 -07:00
Karl Burtram
31a3864789 Disable the User Setup prompt (#2501) 2018-09-11 11:52:04 -07:00
Abbie Petchtes
a5c537197c add animation when button is clicked and fix title in button (#2488)
* add animation similar to toolbar in vscode and fix title in button

* remove bur method in button
2018-09-11 10:37:02 -07:00
Anthony Dresser
4ea13bdbc0 add select all handler to grid (#2496) 2018-09-10 21:18:39 -07:00
Anthony Dresser
b06ddf2dc7 change cursor in message panel to default (#2494) 2018-09-10 21:18:18 -07:00
Karl Burtram
2c45ac9df3 Reorder Connection Name field in Connection Dialog (#2498) 2018-09-10 21:17:57 -07:00
Anthony Dresser
7735f68502 Add check for potential failure in handling drag (#2499)
* add check for potential failure in handling drag

* move check to avoid ui glitches
2018-09-10 21:17:42 -07:00
Anthony Dresser
ffb0f5a1c7 add grid styles (#2483) 2018-09-10 21:17:26 -07:00
Anthony Dresser
80c7f9e855 add logic to hide and add grid panel based on size (#2481) 2018-09-10 21:16:54 -07:00
Alan Ren
709ef4e39f Alanren/icon overwrite issue (#2484)
* fix the error icon too large issue

* formatting
2018-09-10 15:55:40 -07:00
Anthony Dresser
4ceb869420 remove autosize and change column header css to properly respect column sizes (#2480) 2018-09-10 14:59:32 -07:00
Anthony Dresser
432a209184 fix error message formatting (#2477) 2018-09-10 14:58:51 -07:00
Karl Burtram
8444271c58 Fix Action Bar viewlet ordering (#2472) 2018-09-09 19:58:27 -07:00
Karl Burtram
2bc97c23d4 Bump Electron to 2.0.8 and SQL Ops to 0.33.3 (#2466) 2018-09-07 22:08:36 -07:00
112 changed files with 7618 additions and 6061 deletions

View File

@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron" disturl "https://atom.io/download/electron"
target "2.0.7" target "2.0.9"
runtime "electron" runtime "electron"

View File

@@ -13,7 +13,7 @@
"azure-storage": "^2.1.0", "azure-storage": "^2.1.0",
"decompress": "^4.2.0", "decompress": "^4.2.0",
"documentdb": "1.13.0", "documentdb": "1.13.0",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2", "service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"fs-extra-promise": "^1.0.1", "fs-extra-promise": "^1.0.1",
"mime": "^1.3.4", "mime": "^1.3.4",
"minimist": "^1.2.0", "minimist": "^1.2.0",

View File

@@ -1002,7 +1002,7 @@ hoek@4.x.x:
version "4.2.1" version "4.2.1"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
http-proxy-agent@^2.0.0: http-proxy-agent@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies: dependencies:
@@ -1025,7 +1025,7 @@ http-signature@~1.2.0:
jsprim "^1.2.2" jsprim "^1.2.2"
sshpk "^1.7.0" sshpk "^1.7.0"
https-proxy-agent@^2.1.1: https-proxy-agent@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies: dependencies:
@@ -1843,14 +1843,14 @@ semver@^5.4.1:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2": "service-downloader@github:anthonydresser/service-downloader#0.1.5":
version "0.1.2" version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798" resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
dependencies: dependencies:
decompress "^4.2.0" decompress "^4.2.0"
eventemitter2 "^5.0.1" eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^2.1.1" https-proxy-agent "^2.2.1"
mkdirp "^0.5.1" mkdirp "^0.5.1"
tmp "^0.0.33" tmp "^0.0.33"

View File

@@ -20,7 +20,7 @@ let localize = nls.loadMessageBundle();
export class AzureAccountProviderService implements vscode.Disposable { export class AzureAccountProviderService implements vscode.Disposable {
// CONSTANTS /////////////////////////////////////////////////////////////// // CONSTANTS ///////////////////////////////////////////////////////////////
private static CommandClearTokenCache = 'accounts.azure.clearTokenCache'; private static CommandClearTokenCache = 'accounts.clearTokenCache';
private static ConfigurationSection = 'accounts.azure'; private static ConfigurationSection = 'accounts.azure';
private static CredentialNamespace = 'azureAccountProviderCredentials'; private static CredentialNamespace = 'azureAccountProviderCredentials';

View File

@@ -7,7 +7,7 @@
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { ExtensionContext, workspace, window, Disposable, commands, Uri, OutputChannel } from 'vscode'; import { ExtensionContext, workspace, window, Disposable, commands, OutputChannel } from 'vscode';
import { findGit, Git, IGit } from './git'; import { findGit, Git, IGit } from './git';
import { Model } from './model'; import { Model } from './model';
import { CommandCenter } from './commands'; import { CommandCenter } from './commands';
@@ -98,28 +98,29 @@ export async function activate(context: ExtensionContext): Promise<API> {
throw err; throw err;
} }
const config = workspace.getConfiguration('git'); // {{SQL CARBON EDIT}} turn-off Git missing prompt
const shouldIgnore = config.get<boolean>('ignoreMissingGitWarning') === true; //const config = workspace.getConfiguration('git');
//const shouldIgnore = config.get<boolean>('ignoreMissingGitWarning') === true;
if (!shouldIgnore) { // if (!shouldIgnore) {
console.warn(err.message); // console.warn(err.message);
outputChannel.appendLine(err.message); // outputChannel.appendLine(err.message);
outputChannel.show(); // outputChannel.show();
const download = localize('downloadgit', "Download Git"); // const download = localize('downloadgit', "Download Git");
const neverShowAgain = localize('neverShowAgain', "Don't Show Again"); // const neverShowAgain = localize('neverShowAgain', "Don't Show Again");
const choice = await window.showWarningMessage( // const choice = await window.showWarningMessage(
localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."), // localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."),
download, // download,
neverShowAgain // neverShowAgain
); // );
if (choice === download) { // if (choice === download) {
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); // commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
} else if (choice === neverShowAgain) { // } else if (choice === neverShowAgain) {
await config.update('ignoreMissingGitWarning', true, true); // await config.update('ignoreMissingGitWarning', true, true);
} // }
} // }
return new NoopAPIImpl(); return new NoopAPIImpl();
} }

View File

@@ -82,7 +82,7 @@
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3", "opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4", "service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"vscode-extension-telemetry": "^0.0.5", "vscode-extension-telemetry": "^0.0.5",
"vscode-nls": "^3.2.1" "vscode-nls": "^3.2.1"
}, },

View File

@@ -177,14 +177,14 @@ graceful-fs@^4.1.10:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0: http-proxy-agent@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies: dependencies:
agent-base "4" agent-base "4"
debug "3.1.0" debug "3.1.0"
https-proxy-agent@^2.1.1: https-proxy-agent@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies: dependencies:
@@ -297,14 +297,14 @@ seek-bzip@^1.0.5:
dependencies: dependencies:
commander "~2.8.1" commander "~2.8.1"
"service-downloader@github:anthonydresser/service-downloader#0.1.4": "service-downloader@github:anthonydresser/service-downloader#0.1.5":
version "0.1.4" version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7" resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
dependencies: dependencies:
decompress "^4.2.0" decompress "^4.2.0"
eventemitter2 "^5.0.1" eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^2.1.1" https-proxy-agent "^2.2.1"
mkdirp "^0.5.1" mkdirp "^0.5.1"
tmp "^0.0.33" tmp "^0.0.33"

View File

@@ -20,7 +20,7 @@
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.8", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.8",
"opener": "^1.4.3", "opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4", "service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"vscode-extension-telemetry": "^0.0.15" "vscode-extension-telemetry": "^0.0.15"
}, },
"devDependencies": { "devDependencies": {

View File

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

View File

@@ -191,14 +191,14 @@ graceful-fs@^4.1.10:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0: http-proxy-agent@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies: dependencies:
agent-base "4" agent-base "4"
debug "3.1.0" debug "3.1.0"
https-proxy-agent@^2.1.1: https-proxy-agent@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies: dependencies:
@@ -315,14 +315,14 @@ semver@^5.3.0:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.4": "service-downloader@github:anthonydresser/service-downloader#0.1.5":
version "0.1.4" version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7" resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
dependencies: dependencies:
decompress "^4.2.0" decompress "^4.2.0"
eventemitter2 "^5.0.1" eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^2.1.1" https-proxy-agent "^2.2.1"
mkdirp "^0.5.1" mkdirp "^0.5.1"
tmp "^0.0.33" tmp "^0.0.33"

View File

@@ -1,6 +1,6 @@
{ {
"name": "sqlops", "name": "sqlops",
"version": "0.33.2", "version": "0.33.7",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee", "distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"
@@ -64,7 +64,7 @@
"reflect-metadata": "^0.1.8", "reflect-metadata": "^0.1.8",
"rxjs": "5.4.0", "rxjs": "5.4.0",
"semver": "^5.5.0", "semver": "^5.5.0",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.25", "slickgrid": "github:anthonydresser/SlickGrid#2.3.27",
"spdlog": "0.7.1", "spdlog": "0.7.1",
"sudo-prompt": "8.2.0", "sudo-prompt": "8.2.0",
"svg.js": "^2.2.5", "svg.js": "^2.2.5",
@@ -167,5 +167,8 @@
"windows-foreground-love": "0.1.0", "windows-foreground-love": "0.1.0",
"windows-mutex": "^0.2.0", "windows-mutex": "^0.2.0",
"windows-process-tree": "0.2.2" "windows-process-tree": "0.2.2"
},
"resolutions": {
"rc": "1.2.8"
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -518,7 +518,8 @@ export default class MainController implements vscode.Disposable {
let runButton = view.modelBuilder.button() let runButton = view.modelBuilder.button()
.withProperties({ .withProperties({
label: 'Run', label: 'Run',
iconPath: runIcon iconPath: runIcon,
title: 'Run title'
}).component(); }).component();
let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg')); let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
@@ -530,7 +531,8 @@ export default class MainController implements vscode.Disposable {
let monitorButton = view.modelBuilder.button() let monitorButton = view.modelBuilder.button()
.withProperties({ .withProperties({
label: 'Monitor', label: 'Monitor',
iconPath: monitorIcon iconPath: monitorIcon,
title: 'Monitor title'
}).component(); }).component();
let toolbarModel = view.modelBuilder.toolbarContainer() let toolbarModel = view.modelBuilder.toolbarContainer()
.withToolbarItems([{ .withToolbarItems([{

View File

@@ -23,6 +23,18 @@ export class Button extends vsButton {
this.$el.style('outline-color', this.buttonFocusOutline ? this.buttonFocusOutline.toString() : null); this.$el.style('outline-color', this.buttonFocusOutline ? this.buttonFocusOutline.toString() : null);
this.$el.style('outline-width', '1px'); this.$el.style('outline-width', '1px');
}); });
this.$el.on(DOM.EventType.MOUSE_DOWN, (e) => {
const mouseEvent = e as MouseEvent;
if (!this.$el.hasClass('disabled') && mouseEvent.button === 0) {
this.$el.addClass('active');
}
});
this.$el.on([DOM.EventType.MOUSE_UP], (e) => {
DOM.EventHelper.stop(e);
this.$el.removeClass('active');
});
} }
public style(styles: IButtonStyles): void { public style(styles: IButtonStyles): void {

View File

@@ -217,7 +217,7 @@ export abstract class Modal extends Disposable implements IThemable {
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) { if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
let builder = errorMessagesInFooter ? this._leftFooter : body; let builder = errorMessagesInFooter ? this._leftFooter : body;
builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => { builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => { errorMessageContainer.div({ class: 'sql icon error' }, (iconContainer) => {
this._errorIconElement = iconContainer.getHTMLElement(); this._errorIconElement = iconContainer.getHTMLElement();
this._errorIconElement.style.visibility = 'hidden'; this._errorIconElement.style.visibility = 'hidden';
}); });
@@ -518,5 +518,6 @@ export abstract class Modal extends Disposable implements IThemable {
public dispose() { public dispose() {
super.dispose(); super.dispose();
this._keydownListener.dispose(); this._keydownListener.dispose();
this._footerButtons = [];
} }
} }

View File

@@ -61,6 +61,8 @@ export class TabbedPanel extends Disposable implements IThemable {
private _onTabChange = new Emitter<PanelTabIdentifier>(); private _onTabChange = new Emitter<PanelTabIdentifier>();
public onTabChange: Event<PanelTabIdentifier> = this._onTabChange.event; public onTabChange: Event<PanelTabIdentifier> = this._onTabChange.event;
private tabHistory: string[] = [];
constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) { constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) {
super(); super();
this.$parent = this._register($('.tabbedPanel')); this.$parent = this._register($('.tabbedPanel'));
@@ -152,6 +154,7 @@ export class TabbedPanel extends Disposable implements IThemable {
} }
this._shownTab = id; this._shownTab = id;
this.tabHistory.push(id);
this.$body.clearChildren(); this.$body.clearChildren();
let tab = this._tabMap.get(this._shownTab); let tab = this._tabMap.get(this._shownTab);
this.$body.attr('aria-labelledby', tab.identifier); this.$body.attr('aria-labelledby', tab.identifier);
@@ -173,6 +176,26 @@ export class TabbedPanel extends Disposable implements IThemable {
} }
this._tabMap.get(tab).header.destroy(); this._tabMap.get(tab).header.destroy();
this._tabMap.delete(tab); this._tabMap.delete(tab);
if (this._shownTab === tab) {
this._shownTab = undefined;
while (this._shownTab === undefined && this.tabHistory.length > 0) {
let lastTab = this.tabHistory.shift();
if (this._tabMap.get(lastTab)) {
this.showTab(lastTab);
}
}
// this shouldn't happen but just in case
if (this._shownTab === undefined && this._tabMap.size > 0) {
this.showTab(this._tabMap.keys().next().value);
}
}
if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) {
this.$header.offDOM();
this._headerVisible = false;
this.layout(this._currentDimensions);
}
} }
public style(styles: IPanelStyles): void { public style(styles: IPanelStyles): void {
@@ -180,14 +203,16 @@ export class TabbedPanel extends Disposable implements IThemable {
} }
public layout(dimension: Dimension): void { public layout(dimension: Dimension): void {
this._currentDimensions = dimension; if (dimension) {
this.$parent.style('height', dimension.height + 'px'); this._currentDimensions = dimension;
this.$parent.style('width', dimension.width + 'px'); this.$parent.style('height', dimension.height + 'px');
this.$header.style('width', dimension.width + 'px'); this.$parent.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px'); this.$header.style('width', dimension.width + 'px');
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0); this.$body.style('width', dimension.width + 'px');
this.$body.style('height', bodyHeight + 'px'); const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight)); this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
}
} }
private _layoutCurrentTab(dimension: Dimension): void { private _layoutCurrentTab(dimension: Dimension): void {

View File

@@ -34,6 +34,8 @@ export interface IView extends HeightIView {
readonly onDidChange: Event<number | undefined>; readonly onDidChange: Event<number | undefined>;
render(container: HTMLElement, orientation: Orientation): void; render(container: HTMLElement, orientation: Orientation): void;
layout(size: number, orientation: Orientation): void; layout(size: number, orientation: Orientation): void;
onAdd?(): void;
onRemove?(): void;
} }
interface ISashEvent { interface ISashEvent {
@@ -48,6 +50,8 @@ interface IViewItem extends HeightIViewItem {
container: HTMLElement; container: HTMLElement;
disposable: IDisposable; disposable: IDisposable;
layout(): void; layout(): void;
onRemove: () => void;
onAdd: () => void;
} }
interface ISashItem { interface ISashItem {
@@ -109,6 +113,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
private _onDidSashReset = new Emitter<void>(); private _onDidSashReset = new Emitter<void>();
readonly onDidSashReset = this._onDidSashReset.event; readonly onDidSashReset = this._onDidSashReset.event;
private _onScroll = new Emitter<number>();
readonly onScroll = this._onScroll.event;
get length(): number { get length(): number {
return this.viewItems.length; return this.viewItems.length;
} }
@@ -124,6 +131,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => { debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
this.render(e.scrollTop, e.height); this.render(e.scrollTop, e.height);
this.relayout(); this.relayout();
this._onScroll.fire(e.scrollTop);
}); });
let domNode = this.scrollable.getDomNode(); let domNode = this.scrollable.getDomNode();
dom.addClass(this.el, 'monaco-scroll-split-view'); dom.addClass(this.el, 'monaco-scroll-split-view');
@@ -155,6 +163,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}); });
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]); const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const onAdd = view.onAdd ? () => view.onAdd() : () => { };
const onRemove = view.onRemove ? () => view.onRemove() : () => { };
const layoutContainer = this.orientation === Orientation.VERTICAL const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px` ? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`; : size => item.container.style.width = `${item.size}px`;
@@ -165,7 +176,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}; };
size = Math.round(size); size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 }; const item: IViewItem = { onRemove, onAdd, view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(viewIndex, 0, item); this.viewItems.splice(viewIndex, 0, item);
this.onInsertItems(new ArrayIterator([item]), viewIndex > 0 ? this.viewItems[viewIndex - 1].view.id : undefined); this.onInsertItems(new ArrayIterator([item]), viewIndex > 0 ? this.viewItems[viewIndex - 1].view.id : undefined);
@@ -220,6 +231,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}); });
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]); const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const onAdd = view.onAdd ? () => view.onAdd() : () => { };
const onRemove = view.onRemove ? () => view.onRemove() : () => { };
const layoutContainer = this.orientation === Orientation.VERTICAL const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px` ? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`; : size => item.container.style.width = `${item.size}px`;
@@ -230,7 +244,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}; };
size = Math.round(size); size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 }; const item: IViewItem = { onAdd, onRemove, view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(index, 0, item); this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined); this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
@@ -330,9 +344,16 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex); this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
} }
public setScrollPosition(position: number) {
this.scrollable.setScrollPosition({ scrollTop: position });
}
layout(size: number): void { layout(size: number): void {
const previousSize = Math.max(this.size, this.contentSize); const previousSize = this.size;
this.size = size; this.size = size;
this.contentSize = 0;
this.lastRenderHeight = undefined;
this.lastRenderTop = undefined;
this.resize(this.viewItems.length - 1, size - previousSize); this.resize(this.viewItems.length - 1, size - previousSize);
} }
@@ -484,6 +505,8 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
} }
item.layout(); item.layout();
item.onAdd();
return true; return true;
} }
@@ -493,6 +516,8 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
} }
this.el.removeChild(item.container); this.el.removeChild(item.container);
item.onRemove();
return true; return true;
} }

View File

@@ -7,6 +7,7 @@
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { range } from 'vs/base/common/arrays';
/** /**
* Implements the various additional navigation keybindings we want out of slickgrid * Implements the various additional navigation keybindings we want out of slickgrid
@@ -50,6 +51,14 @@ export class AdditionalKeyBindings<T> implements Slick.Plugin<T> {
} }
} else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) { } else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1); this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1);
} else if (event.equals(KeyCode.KEY_A | KeyMod.CtrlCmd)) {
// check if we can set the rows directly on the selectionModel, its cleaner
let selectionModel = this.grid.getSelectionModel();
if (selectionModel) {
selectionModel.setSelectedRanges([new Slick.Range(0, 0, this.grid.getDataLength() - 1, this.grid.getColumns().length - 1)]);
} else {
this.grid.setSelectedRows(range(this.grid.getDataLength()));
}
} else { } else {
handled = false; handled = false;
} }

View File

@@ -1,7 +1,5 @@
import { mixin } from 'vs/base/common/objects'; import { mixin } from 'vs/base/common/objects';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
const defaultOptions: ICellRangeSelectorOptions = { const defaultOptions: ICellRangeSelectorOptions = {
selectionCss: { selectionCss: {
'border': '2px dashed blue' 'border': '2px dashed blue'
@@ -44,6 +42,8 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>(); public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>();
constructor(private options: ICellRangeSelectorOptions) { constructor(private options: ICellRangeSelectorOptions) {
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
this.options = mixin(this.options, defaultOptions, false); this.options = mixin(this.options, defaultOptions, false);
} }
@@ -89,7 +89,7 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
this.canvas.classList.add(this.options.dragClass); this.canvas.classList.add(this.options.dragClass);
this.grid.focus(); this.grid.setActiveCell(cell.row, cell.cell);
let start = this.grid.getCellFromPoint( let start = this.grid.getCellFromPoint(
dd.startX - $(this.canvas).offset().left, dd.startX - $(this.canvas).offset().left,
@@ -128,8 +128,11 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
this.canvas.classList.remove(this.options.dragClass); this.canvas.classList.remove(this.options.dragClass);
this.dragging = false; this.dragging = false;
e.stopImmediatePropagation(); e.stopImmediatePropagation();
this.decorator.hide(); this.decorator.hide();
// if this happens to fast there is a chance we don't have the necessary information to actually do proper selection
if (!dd || !dd.range || !dd.range.start || !dd.range.end) {
return;
}
this.onCellRangeSelected.notify({ this.onCellRangeSelected.notify({
range: new Slick.Range( range: new Slick.Range(
dd.range.start.row, dd.range.start.row,

View File

@@ -6,8 +6,6 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector'; import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
export interface ICellSelectionModelOptions { export interface ICellSelectionModelOptions {
cellRangeSelector?: any; cellRangeSelector?: any;
selectActiveCell?: boolean; selectActiveCell?: boolean;

View File

@@ -38,8 +38,8 @@ export class CopyKeybind<T> implements Slick.Plugin<T> {
let selectionModel = this.grid.getSelectionModel(); let selectionModel = this.grid.getSelectionModel();
let ranges: Slick.Range[]; let ranges: Slick.Range[];
// check to see if we can get the range from the model directly // check to see if we can get the range from the model directly
if (selectionModel && (<any>selectionModel).getSelectedRanges) { if (selectionModel) {
ranges = (<any>selectionModel).getSelectedRanges(); ranges = selectionModel.getSelectedRanges();
} else { } else {
let selectedRows = this.grid.getSelectedRows(); let selectedRows = this.grid.getSelectedRows();
let startColumn = 0; let startColumn = 0;

View File

@@ -36,23 +36,31 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
private handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void { private handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void {
if (this.grid.getColumns()[args.cell].id === 'rowNumber') { if (this.grid.getColumns()[args.cell].id === 'rowNumber') {
this.grid.setActiveCell(args.row, 1); this.grid.setActiveCell(args.row, 1);
this.grid.setSelectedRows([args.row]); if (this.grid.getSelectionModel()) {
this.grid.setSelectedRows([args.row]);
}
} }
} }
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>): void { private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>): void {
if (args.column.id === 'rowNumber') { if (args.column.id === 'rowNumber') {
this.grid.setActiveCell(0, 1); this.grid.setActiveCell(0, 1);
this.grid.setSelectedRows(range(this.grid.getDataLength())); if (this.grid.getSelectionModel()) {
this.grid.setSelectedRows(range(this.grid.getDataLength()));
}
} }
} }
public getColumnDefinition(): Slick.Column<T> { public getColumnDefinition(): Slick.Column<T> {
// that smallest we can make it is 22 due to padding and margins in the cells
let columnWidth = Math.max(this.options.numberOfRows.toString().length * sizePerDigit, 22);
return { return {
id: 'rowNumber', id: 'rowNumber',
name: '', name: '',
field: 'rowNumber', field: 'rowNumber',
width: this.options.numberOfRows.toString().length * sizePerDigit, width: columnWidth,
minWidth: columnWidth,
maxWidth: columnWidth,
resizable: false, resizable: false,
cssClass: this.options.cssClass, cssClass: this.options.cssClass,
focusable: false, focusable: false,

View File

@@ -166,6 +166,10 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._grid.setData(this._data, true); this._grid.setData(this._data, true);
} }
getData(): Slick.DataProvider<T> {
return this._data;
}
get columns(): Slick.Column<T>[] { get columns(): Slick.Column<T>[] {
return this._grid.getColumns(); return this._grid.getColumns();
} }
@@ -244,7 +248,6 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
} }
} }
this.resizeCanvas(); this.resizeCanvas();
this.autosizeColumns();
} }
autosizeColumns() { autosizeColumns() {

View File

@@ -5,6 +5,7 @@
'use strict'; 'use strict';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
/** /**
* Implementation of vs/base/common/event/echo that is clearable * Implementation of vs/base/common/event/echo that is clearable
@@ -37,3 +38,52 @@ export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): {
clear clear
}; };
} }
/**
* Implementation of vs/base/common/event/debounceEvent that is clearable
*/
export function debounceEvent<T>(event: Event<T>, merger: (last: T, event: T) => T, delay?: number, leading?: boolean): { clear: () => void; event: Event<T> };
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay?: number, leading?: boolean): { clear: () => void; event: Event<O> };
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay: number = 100, leading = false): { clear: () => void; event: Event<O> } {
let subscription: IDisposable;
let output: O = undefined;
let handle: any = undefined;
let numDebouncedCalls = 0;
const clear = () => output = undefined;
const emitter = new Emitter<O>({
onFirstListenerAdd() {
subscription = event(cur => {
numDebouncedCalls++;
output = merger(output, cur);
if (leading && !handle) {
emitter.fire(output);
}
clearTimeout(handle);
handle = setTimeout(() => {
let _output = output;
output = undefined;
handle = undefined;
if (!leading || numDebouncedCalls > 1) {
emitter.fire(_output);
}
numDebouncedCalls = 0;
}, delay);
});
},
onLastListenerRemove() {
subscription.dispose();
}
});
return {
event: emitter.event,
clear
};
}

View File

@@ -63,21 +63,21 @@
background: url("globalerror.svg") center center no-repeat; background: url("globalerror.svg") center center no-repeat;
} }
.vs .icon.error, .vs .sql.icon.error,
.vs-dark .icon.error, .vs-dark .sql.icon.error,
.hc-black .icon.error { .hc-black .sql.icon.error {
content: url("status_error.svg"); content: url("status_error.svg");
} }
.vs .icon.warning, .vs .sql.icon.warning,
.vs-dark .icon.warning, .vs-dark .sql.icon.warning,
.hc-black .icon.warning { .hc-black .sql.icon.warning {
content: url("status_warning.svg"); content: url("status_warning.svg");
} }
.vs .icon.info, .vs .sql.icon.info,
.vs-dark .icon.info, .vs-dark .sql.icon.info,
.hc-black .icon.info { .hc-black .sql.icon.info {
content: url("status_info.svg"); content: url("status_info.svg");
} }

View File

@@ -117,7 +117,7 @@ export class AccountPicker extends Disposable {
// Create refresh account action // Create refresh account action
this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container')); this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container'));
DOM.append(this._refreshContainer, DOM.$('div.icon warning')); DOM.append(this._refreshContainer, DOM.$('div.sql icon warning'));
let actionBar = new ActionBar(this._refreshContainer, { animated: false }); let actionBar = new ActionBar(this._refreshContainer, { animated: false });
this._refreshAccountAction = this._instantiationService.createInstance(RefreshAccountAction); this._refreshAccountAction = this._instantiationService.createInstance(RefreshAccountAction);
actionBar.push(this._refreshAccountAction, { icon: false, label: true }); actionBar.push(this._refreshAccountAction, { icon: false, label: true });

View File

@@ -123,6 +123,7 @@ export class ConnectionStore {
.then(savedCred => { .then(savedCred => {
if (savedCred) { if (savedCred) {
credentialsItem.password = savedCred.password; credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
} }
resolve({ profile: credentialsItem, savedCred: !!savedCred }); resolve({ profile: credentialsItem, savedCred: !!savedCred });
}, },

View File

@@ -156,11 +156,6 @@ export class ConnectionWidget {
} }
private fillInConnectionForm(): void { private fillInConnectionForm(): void {
// Connection name
let connectionNameOption = this._optionsMaps[ConnectionOptionSpecialType.connectionName];
let connectionNameBuilder = DialogHelper.appendRow(this._tableContainer, connectionNameOption.displayName, 'connection-label', 'connection-input');
this._connectionNameInputBox = new InputBox(connectionNameBuilder.getHTMLElement(), this._contextViewService, { ariaLabel: connectionNameOption.displayName });
// Server name // Server name
let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName]; let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName];
let serverNameBuilder = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input'); let serverNameBuilder = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input');
@@ -222,6 +217,11 @@ export class ConnectionWidget {
let serverGroupBuilder = DialogHelper.appendRow(this._tableContainer, this._serverGroupDisplayString, 'connection-label', 'connection-input'); let serverGroupBuilder = DialogHelper.appendRow(this._tableContainer, this._serverGroupDisplayString, 'connection-label', 'connection-input');
DialogHelper.appendInputSelectBox(serverGroupBuilder, this._serverGroupSelectBox); DialogHelper.appendInputSelectBox(serverGroupBuilder, this._serverGroupSelectBox);
// Connection name
let connectionNameOption = this._optionsMaps[ConnectionOptionSpecialType.connectionName];
let connectionNameBuilder = DialogHelper.appendRow(this._tableContainer, connectionNameOption.displayName, 'connection-label', 'connection-input');
this._connectionNameInputBox = new InputBox(connectionNameBuilder.getHTMLElement(), this._contextViewService, { ariaLabel: connectionNameOption.displayName });
let AdvancedLabel = localize('advanced', 'Advanced...'); let AdvancedLabel = localize('advanced', 'Advanced...');
this._advancedButton = this.createAdvancedButton(this._tableContainer, AdvancedLabel); this._advancedButton = this.createAdvancedButton(this._tableContainer, AdvancedLabel);
} }
@@ -398,7 +398,7 @@ export class ConnectionWidget {
public focusOnOpen(): void { public focusOnOpen(): void {
this._handleClipboard(); this._handleClipboard();
this._connectionNameInputBox.focus(); this._serverNameInputBox.focus();
this.focusPasswordIfNeeded(); this.focusPasswordIfNeeded();
this.clearValidationMessages(); this.clearValidationMessages();
} }
@@ -492,6 +492,7 @@ export class ConnectionWidget {
this._databaseNameInputBox.enabled = false; this._databaseNameInputBox.enabled = false;
this._userNameInputBox.disable(); this._userNameInputBox.disable();
this._passwordInputBox.disable(); this._passwordInputBox.disable();
this._connectionNameInputBox.disable();
this._rememberPasswordCheckBox.enabled = false; this._rememberPasswordCheckBox.enabled = false;
if (this._authTypeSelectBox) { if (this._authTypeSelectBox) {
this._authTypeSelectBox.disable(); this._authTypeSelectBox.disable();
@@ -503,6 +504,7 @@ export class ConnectionWidget {
this._serverGroupSelectBox.enable(); this._serverGroupSelectBox.enable();
this._serverNameInputBox.enable(); this._serverNameInputBox.enable();
this._connectionNameInputBox.enable();
this._databaseNameInputBox.enabled = true; this._databaseNameInputBox.enabled = true;
let currentAuthType: AuthenticationType = undefined; let currentAuthType: AuthenticationType = undefined;
if (this._authTypeSelectBox) { if (this._authTypeSelectBox) {

View File

@@ -10,6 +10,7 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { memoize, unmemoize } from 'sql/base/common/decorators'; import { memoize, unmemoize } from 'sql/base/common/decorators';
import { mixin } from 'sql/base/common/objects'; import { mixin } from 'sql/base/common/objects';
import { LegendPosition, DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import * as colors from 'vs/platform/theme/common/colorRegistry'; import * as colors from 'vs/platform/theme/common/colorRegistry';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
@@ -21,29 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
declare var Chart: any; declare var Chart: any;
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export function customMixin(destination: any, source: any, overwrite?: boolean): any { export function customMixin(destination: any, source: any, overwrite?: boolean): any {
if (types.isObject(source)) { if (types.isObject(source)) {
mixin(destination, source, overwrite, customMixin); mixin(destination, source, overwrite, customMixin);

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export enum DataType {
Number = 'number',
Point = 'point'
}

View File

@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ChartInsight, ChartType, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { ChartInsight, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { mixin } from 'sql/base/common/objects'; import { mixin } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry'; import * as colors from 'vs/platform/theme/common/colorRegistry';

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import PieChart from './pieChart.component'; import PieChart from './pieChart.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class DoughnutChart extends PieChart { export default class DoughnutChart extends PieChart {
protected readonly chartType: ChartType = ChartType.Doughnut; protected readonly chartType: ChartType = ChartType.Doughnut;

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import BarChart from './barChart.component'; import BarChart from './barChart.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class HorizontalBarChart extends BarChart { export default class HorizontalBarChart extends BarChart {
protected readonly chartType: ChartType = ChartType.HorizontalBar; protected readonly chartType: ChartType = ChartType.HorizontalBar;

View File

@@ -3,16 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ChartType, customMixin, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { mixin } from 'vs/base/common/objects';
import { defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import BarChart, { IBarChartConfig } from './barChart.component'; import BarChart, { IBarChartConfig } from './barChart.component';
import { memoize, unmemoize } from 'sql/base/common/decorators'; import { memoize, unmemoize } from 'sql/base/common/decorators';
import { mixin } from 'vs/base/common/objects';
import { clone } from 'sql/base/common/objects'; import { clone } from 'sql/base/common/objects';
import { ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export enum DataType {
Number = 'number',
Point = 'point'
}
export interface ILineConfig extends IBarChartConfig { export interface ILineConfig extends IBarChartConfig {
dataType?: DataType; dataType?: DataType;

View File

@@ -3,7 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ChartInsight, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { ChartInsight } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class PieChart extends ChartInsight { export default class PieChart extends ChartInsight {
protected readonly chartType: ChartType = ChartType.Pie; protected readonly chartType: ChartType = ChartType.Pie;

View File

@@ -3,11 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import LineChart, { ILineConfig } from './lineChart.component'; import LineChart, { ILineConfig } from './lineChart.component';
import { clone } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { mixin } from 'vs/base/common/objects'; import { mixin } from 'vs/base/common/objects';
import { clone } from 'sql/base/common/objects';
const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig; const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;

View File

@@ -3,9 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import LineChart, { ILineConfig } from './lineChart.component'; import LineChart, { ILineConfig } from './lineChart.component';
import { clone } from 'sql/base/common/objects'; import { clone } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { mixin } from 'vs/base/common/objects'; import { mixin } from 'vs/base/common/objects';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';

View File

@@ -86,7 +86,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
}).filter(i => !!i); }).filter(i => !!i);
} }
this._tasks = tasks.map(i => TaskRegistry.getCommandActionById(i)).filter(v => !!v); this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
} }
ngOnInit() { ngOnInit() {

View File

@@ -65,7 +65,7 @@
<div class="option check" #encryptCheckContainer> <div class="option check" #encryptCheckContainer>
</div> </div>
<div class="option" #encryptWarningContainer> <div class="option" #encryptWarningContainer>
<div class="icon warning"> <div class="sql icon warning">
</div> </div>
<div class="warning-message"> <div class="warning-message">
{{localizedStrings.NO_ENCRYPTOR_WARNING}} {{localizedStrings.NO_ENCRYPTOR_WARNING}}

View File

@@ -25,7 +25,7 @@ classes should alter those!
overflow: hidden; overflow: hidden;
-o-text-overflow: ellipsis; -o-text-overflow: ellipsis;
text-overflow: ellipsis; text-overflow: ellipsis;
height: 16px; height: 28px;
line-height: 16px; line-height: 16px;
margin: 0; margin: 0;
padding: 4px; padding: 4px;
@@ -35,7 +35,7 @@ classes should alter those!
border-bottom: 2px solid #bbb; border-bottom: 2px solid #bbb;
float: left; float: left;
background-color: #eee; background-color: #eee;
box-sizing: content-box; box-sizing: border-box;
} }
.slick-headerrow-column.ui-state-default, .slick-footerrow-column.ui-state-default { .slick-headerrow-column.ui-state-default, .slick-footerrow-column.ui-state-default {
@@ -104,6 +104,11 @@ classes should alter those!
white-space: nowrap; white-space: nowrap;
cursor: default; cursor: default;
} }
.slick-cell .grid-cell-value-container.missing-value {
font-style: italic;
}
.slick-cell, .slick-headerrow-column{ .slick-cell, .slick-headerrow-column{
border-bottom-color: silver; border-bottom-color: silver;
} }

View File

@@ -166,7 +166,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onCellEditEnd = (event: Slick.OnCellChangeEventArgs<any>): void => { this.onCellEditEnd = (event: Slick.OnCellChangeEventArgs<any>): void => {
// Store the value that was set // Store the value that was set
self.currentEditCellValue = event.item[event.cell - 1]; self.currentEditCellValue = event.item[event.cell];
}; };
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => { this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
@@ -270,18 +270,18 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
return self.dataService.updateCell(sessionRowId, self.currentCell.column - 1, self.currentEditCellValue) return self.dataService.updateCell(sessionRowId, self.currentCell.column - 1, self.currentEditCellValue)
.then( .then(
result => { result => {
// Cell update was successful, update the flags // Cell update was successful, update the flags
self.currentEditCellValue = null; self.currentEditCellValue = null;
self.setCellDirtyState(row, self.currentCell.column, result.cell.isDirty); self.setCellDirtyState(row, self.currentCell.column, result.cell.isDirty);
self.setRowDirtyState(row, result.isRowDirty); self.setRowDirtyState(row, result.isRowDirty);
return Promise.resolve(); return Promise.resolve();
}, },
error => { error => {
// Cell update failed, jump back to the last cell we were on // Cell update failed, jump back to the last cell we were on
self.focusCell(self.currentCell.row, self.currentCell.column, true); self.focusCell(self.currentCell.row, self.currentCell.column, true);
return Promise.reject(null); return Promise.reject(null);
} }
); );
}); });
} }
@@ -377,10 +377,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
index => { return {}; } index => { return {}; }
), ),
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => { columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let columnIndex = (i + 1).toString();
return { return {
id: i.toString(), id: columnIndex,
name: escape(c.columnName), name: escape(c.columnName),
field: i.toString(), field: columnIndex,
formatter: Services.textFormatter, formatter: Services.textFormatter,
isEditable: c.isUpdatable isEditable: c.isUpdatable
}; };
@@ -464,7 +465,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
} else { } else {
try { try {
// Perform a revert row operation // Perform a revert row operation
if (this.currentCell) { if (this.currentCell && this.currentCell.row !== undefined && this.currentCell.row !== null) {
await this.dataService.revertRow(this.currentCell.row); await this.dataService.revertRow(this.currentCell.row);
} }
} finally { } finally {

View File

@@ -16,7 +16,7 @@ import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { DataType, ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component'; import { ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import * as PathUtilities from 'sql/common/pathUtilities'; import * as PathUtilities from 'sql/common/pathUtilities';
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions'; import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
@@ -24,10 +24,11 @@ import * as Constants from 'sql/parts/query/common/constants';
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component'; import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { LegendPosition, DataDirection, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
/* Insights */ /* Insights */
import { import {
ChartInsight, DataDirection, LegendPosition ChartInsight
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';

View File

@@ -188,8 +188,16 @@ export class InsightsDialogView extends Modal {
for (let i = 0; i < this._model.columns.length; i++) { for (let i = 0; i < this._model.columns.length; i++) {
resourceArray.push({ label: this._model.columns[i], value: element.data[i], data: element.data }); resourceArray.push({ label: this._model.columns[i], value: element.data[i], data: element.data });
} }
this._bottomTableData.clear(); this._bottomTableData.clear();
this._bottomTableData.push(resourceArray); this._bottomTableData.push(resourceArray);
// this table view has to be collapsed and expanded
// because the initial expand doesn't have the
// loaded data
if (bottomTableView.isExpanded()) {
bottomTableView.collapse();
bottomTableView.expand();
}
this._enableTaskButtons(true); this._enableTaskButtons(true);
} else { } else {
this._enableTaskButtons(false); this._enableTaskButtons(false);
@@ -334,6 +342,7 @@ export class InsightsDialogView extends Modal {
this.hide(); this.hide();
dispose(this._taskButtonDisposables); dispose(this._taskButtonDisposables);
this._taskButtonDisposables = []; this._taskButtonDisposables = [];
this.dispose();
} }
protected onClose(e: StandardKeyboardEvent) { protected onClose(e: StandardKeyboardEvent) {

View File

@@ -70,8 +70,7 @@ export abstract class JobManagementView extends TabChild implements AfterContent
abstract onFirstVisible(); abstract onFirstVisible();
protected openContextMenu(event): void { protected openContextMenu(event): void {
let grid = this._table.grid; let rowIndex = event.cell.row;
let rowIndex = grid.getCellFromEvent(event).row;
let targetObject = this.getCurrentTableObject(rowIndex); let targetObject = this.getCurrentTableObject(rowIndex);
let actions = this.getTableActions(); let actions = this.getTableActions();

View File

@@ -13,11 +13,11 @@ import * as sqlops from 'sqlops';
import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase'; import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { attachButtonStyler } from 'sql/common/theme/styler'; import { attachButtonStyler } from 'sql/common/theme/styler';
import { Button } from 'sql/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry';
import { Button } from 'sql/base/browser/ui/button/button';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
@@ -101,6 +101,7 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
super.setProperties(properties); super.setProperties(properties);
this._button.enabled = this.enabled; this._button.enabled = this.enabled;
this._button.label = this.label; this._button.label = this.label;
this._button.title = this.title;
if (this.width) { if (this.width) {
this._button.setWidth(this.convertSize(this.width.toString())); this._button.setWidth(this.convertSize(this.width.toString()));
} }
@@ -165,4 +166,13 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
private setFileProperties(properties: sqlops.ButtonProperties, isFile: boolean): void { private setFileProperties(properties: sqlops.ButtonProperties, isFile: boolean): void {
properties.isFile = isFile; properties.isFile = isFile;
} }
private get title(): string {
return this.getPropertyOrDefault<sqlops.ButtonProperties, string>((props) => props.title, '');
}
private set title(newValue: string) {
this.setPropertyFromUI<sqlops.ButtonProperties, string>((properties, title) => { properties.title = title; }, newValue);
}
} }

View File

@@ -22,6 +22,8 @@ export class ItemDescriptor<T> {
constructor(public descriptor: IComponentDescriptor, public config: T) { } constructor(public descriptor: IComponentDescriptor, public config: T) { }
} }
const ids = new IdGenerator('model-view-component-icon-');
export abstract class ComponentWithIconBase extends ComponentBase { export abstract class ComponentWithIconBase extends ComponentBase {
protected _iconClass: string; protected _iconClass: string;
@@ -42,7 +44,6 @@ export abstract class ComponentWithIconBase extends ComponentBase {
if (this.iconPath && this.iconPath !== this._iconPath) { if (this.iconPath && this.iconPath !== this._iconPath) {
this._iconPath = this.iconPath; this._iconPath = this.iconPath;
if (!this._iconClass) { if (!this._iconClass) {
const ids = new IdGenerator('model-view-component-icon-' + Math.round(Math.random() * 1000));
this._iconClass = ids.nextId(); this._iconClass = ids.nextId();
} }

View File

@@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import DivContainer from './divContainer.component';
import FlexContainer from './flexContainer.component'; import FlexContainer from './flexContainer.component';
import FormContainer from './formContainer.component'; import FormContainer from './formContainer.component';
import ToolbarContainer from './toolbarContainer.component'; import ToolbarContainer from './toolbarContainer.component';
@@ -22,9 +22,13 @@ import TextComponent from './text.component';
import LoadingComponent from './loadingComponent.component'; import LoadingComponent from './loadingComponent.component';
import FileBrowserTreeComponent from './fileBrowserTree.component'; import FileBrowserTreeComponent from './fileBrowserTree.component';
import EditorComponent from './editor.component'; import EditorComponent from './editor.component';
import DomComponent from './dom.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry'; import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes'; import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
export const DIV_CONTAINER = 'div-container';
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
export const FLEX_CONTAINER = 'flex-container'; export const FLEX_CONTAINER = 'flex-container';
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer); registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);
@@ -82,3 +86,6 @@ registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowser
export const EDITOR_COMPONENT = 'editor-component'; export const EDITOR_COMPONENT = 'editor-component';
registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorComponent); registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorComponent);
export const DOM_COMPONENT = 'dom-component';
registerComponentType(DOM_COMPONENT, ModelComponentTypes.Dom, DomComponent);

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* 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!./divContainer';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
} from '@angular/core';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import * as sqlops from 'sqlops';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
import types = require('vs/base/common/types');
class DivItem {
constructor(public descriptor: IComponentDescriptor, public config: sqlops.DivItemLayout) { }
}
@Component({
template: `
<div #divContainer *ngIf="items" class="divContainer" [style.height]="height" [style.width]="width">
<div *ngFor="let item of items" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
</div>
`
})
export default class DivContainer extends ContainerBase<sqlops.DivItemLayout> implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
@ViewChild('divContainer', { read: ElementRef }) divContainer;
private _height: string;
private _width: string;
private _overflowY: string;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef
) {
super(changeRef, el);
this._overflowY = ''; // default
}
ngOnInit(): void {
this.baseInit();
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public setLayout(layout: sqlops.DivLayout): void {
this._height = this.convertSize(layout.height);
this._width = this.convertSize(layout.width);
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (this.overflowY !== this._overflowY) {
this.updateOverflowY();
}
this.updateScroll();
}
private updateOverflowY() {
this._overflowY = this.overflowY;
if (this._overflowY) {
let element = <HTMLElement> this.divContainer.nativeElement;
element.style.overflowY = this._overflowY;
}
}
private updateScroll() {
let element = <HTMLElement> this.divContainer.nativeElement;
element.scrollTop = element.scrollTop - this.yOffsetChange;
element.dispatchEvent(new Event('scroll'));
}
// CSS-bound properties
public get height(): string {
return this._height;
}
public get width(): string {
return this._width;
}
// CSS-bound properties
public get overflowY(): string {
return this.getPropertyOrDefault<sqlops.DivContainerProperties, any>((props) => props.overflowY, '');
}
public set overflowY(newValue: string) {
this.setPropertyFromUI<sqlops.DivContainerProperties, any>((properties, newValue) => { properties.overflowY = newValue; }, newValue);
}
public get yOffsetChange(): number {
return this.getPropertyOrDefault<sqlops.DivContainerProperties, any>((props) => props.yOffsetChange, 0);
}
public set yOffsetChange(newValue: number) {
this.setPropertyFromUI<sqlops.DivContainerProperties, any>((properties, newValue) => { properties.yOffsetChange = newValue; }, newValue);
}
private getItemOrder(item: DivItem): number {
return item.config ? item.config.order : 0;
}
private getItemStyles(item: DivItem): { [key: string]: string } {
return item.config && item.config.CSSStyles ? item.config.CSSStyles : {};
}
}

View File

@@ -0,0 +1,5 @@
.divContainer {
display: block;
height: 100%;
}

View File

@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* 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!./dom';
import 'vs/css!./highlight';
import 'vs/css!./markdown';
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 { $, Builder } from 'vs/base/browser/builder';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
@Component({
template: '',
selector: 'modelview-dom-component'
})
export default class DomComponent extends ComponentBase implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _renderedHtml: string;
private _rootElement: Builder;
private _bodyElement: Builder;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef
) {
super(changeRef, el);
}
ngOnInit(): void {
this.baseInit();
this.createDomElement();
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
this.layout();
}));
}
ngOnDestroy(): void {
this.baseDestroy();
}
private createDomElement() {
this._rootElement = new Builder(this._el.nativeElement);
this._bodyElement = $('.dom-body');
this._rootElement.append(this._bodyElement);
}
/// Dom Functions
private setHtml(): void {
if (this.html) {
this._renderedHtml = this.html;
this._bodyElement.innerHtml(this._renderedHtml);
}
}
/// IComponent implementation
public layout(): void {
super.layout();
let element = <HTMLElement>this._el.nativeElement;
element.style.width = this.getWidth();
element.style.height = this.getHeight();
}
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.html !== this._renderedHtml) {
this.setHtml();
}
}
// CSS-bound properties
public get html(): string {
return this.getPropertyOrDefault<sqlops.DomProperties, string>((props) => props.html, '');
}
public set html(newValue: string) {
this.setPropertyFromUI<sqlops.DomProperties, string>((properties, html) => { properties.html = html; }, newValue);
}
}

View File

@@ -0,0 +1,9 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
modelview-dom-component {
display: block;
-webkit-user-select: text;
}

View File

@@ -0,0 +1,183 @@
/*
https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css
*/
/*
* Visual Studio 2015 dark style
* Author: Nicolas LLOBERA <nllobera@gmail.com>
*/
modelview-dom-component .hljs-keyword,
modelview-dom-component .hljs-literal,
modelview-dom-component .hljs-symbol,
modelview-dom-component .hljs-name {
color: #569CD6;
}
modelview-dom-component .hljs-link {
color: #569CD6;
text-decoration: underline;
}
modelview-dom-component .hljs-built_in,
modelview-dom-component .hljs-type {
color: #4EC9B0;
}
modelview-dom-component .hljs-number,
modelview-dom-component .hljs-class {
color: #B8D7A3;
}
modelview-dom-component .hljs-string,
modelview-dom-component .hljs-meta-string {
color: #D69D85;
}
modelview-dom-component .hljs-regexp,
modelview-dom-component .hljs-template-tag {
color: #9A5334;
}
modelview-dom-component .hljs-subst,
modelview-dom-component .hljs-function,
modelview-dom-component .hljs-title,
modelview-dom-component .hljs-params,
modelview-dom-component .hljs-formula {
color: #DCDCDC;
}
modelview-dom-component .hljs-comment,
modelview-dom-component .hljs-quote {
color: #57A64A;
font-style: italic;
}
modelview-dom-component .hljs-doctag {
color: #608B4E;
}
modelview-dom-component .hljs-meta,
modelview-dom-component .hljs-meta-keyword,
modelview-dom-component .hljs-tag {
color: #9B9B9B;
}
modelview-dom-component .hljs-variable,
modelview-dom-component .hljs-template-variable {
color: #BD63C5;
}
modelview-dom-component .hljs-attr,
modelview-dom-component .hljs-attribute,
modelview-dom-component .hljs-builtin-name {
color: #9CDCFE;
}
modelview-dom-component .hljs-section {
color: gold;
}
modelview-dom-component .hljs-emphasis {
font-style: italic;
}
modelview-dom-component .hljs-strong {
font-weight: bold;
}
/*.hljs-code {
font-family:'Monospace';
}*/
modelview-dom-component .hljs-bullet,
modelview-dom-component .hljs-selector-tag,
modelview-dom-component .hljs-selector-id,
modelview-dom-component .hljs-selector-class,
modelview-dom-component .hljs-selector-attr,
modelview-dom-component .hljs-selector-pseudo {
color: #D7BA7D;
}
modelview-dom-component .hljs-addition {
background-color: #144212;
display: inline-block;
width: 100%;
}
modelview-dom-component .hljs-deletion {
background-color: #600;
display: inline-block;
width: 100%;
}
/*
From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css
*/
/*
Visual Studio-like style based on original C# coloring by Jason Diamond <jason@diamond.name>
*/
/*
.vscode-light .hljs-function,
.vscode-light .hljs-params {
color: inherit;
}
.vscode-light .hljs-comment,
.vscode-light .hljs-quote,
.vscode-light .hljs-variable {
color: #008000;
}
.vscode-light .hljs-keyword,
.vscode-light .hljs-selector-tag,
.vscode-light .hljs-built_in,
.vscode-light .hljs-name,
.vscode-light .hljs-tag {
color: #00f;
}
.vscode-light .hljs-string,
.vscode-light .hljs-title,
.vscode-light .hljs-section,
.vscode-light .hljs-attribute,
.vscode-light .hljs-literal,
.vscode-light .hljs-template-tag,
.vscode-light .hljs-template-variable,
.vscode-light .hljs-type,
.vscode-light .hljs-addition {
color: #a31515;
}
.vscode-light .hljs-deletion,
.vscode-light .hljs-selector-attr,
.vscode-light .hljs-selector-pseudo,
.vscode-light .hljs-meta {
color: #2b91af;
}
.vscode-light .hljs-doctag {
color: #808080;
}
.vscode-light .hljs-attr {
color: #f00;
}
.vscode-light .hljs-symbol,
.vscode-light .hljs-bullet,
.vscode-light .hljs-link {
color: #00b0e8;
}
.vscode-light .hljs-emphasis {
font-style: italic;
}
.vscode-light .hljs-strong {
font-weight: bold;
}
*/

View File

@@ -0,0 +1,238 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
modelview-dom-component {
font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
font-size: 14px;
padding: 0 26px;
line-height: 22px;
word-wrap: break-word;
}
modelview-dom-component #code-csp-warning {
position: fixed;
top: 0;
right: 0;
color: white;
margin: 16px;
text-align: center;
font-size: 12px;
font-family: sans-serif;
background-color:#444444;
cursor: pointer;
padding: 6px;
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}
modelview-dom-component #code-csp-warning:hover {
text-decoration: none;
background-color:#007acc;
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}
modelview-dom-component .scrollBeyondLastLine {
margin-bottom: calc(100vh - 22px);
}
modelview-dom-component .showEditorSelection .code-line {
position: relative;
}
modelview-dom-component .showEditorSelection .code-active-line:before,
modelview-dom-component .showEditorSelection .code-line:hover:before {
content: "";
display: block;
position: absolute;
top: 0;
left: -12px;
height: 100%;
}
modelview-dom-component .showEditorSelection li.code-active-line:before,
modelview-dom-component .showEditorSelection li.code-line:hover:before {
left: -30px;
}
modelview-dom-component .showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(0, 0, 0, 0.15);
}
modelview-dom-component .showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(0, 0, 0, 0.40);
}
modelview-dom-component .showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.vs-dark .monaco-workbench modelview-dom-component .showEditorSelection .code-active-line:before
.hc-black .monaco-workbench modelview-dom-component .showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 255, 255, 0.4);
}
.vs-dark .monaco-workbench modelview-dom-component .showEditorSelection .code-line:hover:before
.hc-black .monaco-workbench modelview-dom-component .showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 255, 255, 0.60);
}
.vs-dark .monaco-workbench modelview-dom-component .showEditorSelection .code-line .code-line:hover:before
.hc-black .monaco-workbench modelview-dom-component .showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.hc-black modelview-dom-component .showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 160, 0, 0.7);
}
.hc-black modelview-dom-component .showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 160, 0, 1);
}
modelview-dom-component .showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
modelview-dom-component img {
max-width: 100%;
max-height: 100%;
}
modelview-dom-component a, modelview-dom-component a:link{
text-decoration: none;
}
modelview-dom-component a:hover, modelview-dom-component a:link {
text-decoration: underline;
}
modelview-dom-component a:focus,
modelview-dom-component input:focus,
modelview-dom-component select:focus,
modelview-dom-component textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
modelview-dom-component hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
modelview-dom-component h1 {
padding-bottom: 0.3em;
line-height: 1.2;
border-bottom-width: 1px;
border-bottom-style: solid;
}
modelview-dom-component h1,
modelview-dom-component h2,
modelview-dom-component h3 {
font-weight: normal;
}
modelview-dom-component h1 code,
modelview-dom-component h2 code,
modelview-dom-component h3 code,
modelview-dom-component h4 code,
modelview-dom-component h5 code,
modelview-dom-component h6 code {
font-size: inherit;
line-height: auto;
}
modelview-dom-component table {
border-collapse: collapse;
}
modelview-dom-component table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
modelview-dom-component table > thead > tr > th,
modelview-dom-component table > thead > tr > td,
modelview-dom-component table > tbody > tr > th,
modelview-dom-component table > tbody > tr > td {
padding: 5px 10px;
}
modelview-dom-component table > tbody > tr + tr > td {
border-top: 1px solid;
}
modelview-dom-component blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
modelview-dom-component code {
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
font-size: 14px;
line-height: 19px;
}
modelview-dom-component .wordWrap pre {
white-space: pre-wrap;
}
modelview-dom-component .mac code {
font-size: 12px;
line-height: 18px;
}
modelview-dom-component pre:not(.hljs),
modelview-dom-component pre.hljs code > div {
padding: 16px;
border-radius: 3px;
overflow: auto;
}
/** Theming */
modelview-dom-component pre code {
color: var(--vscode-editor-foreground);
}
modelview-dom-component pre {
background-color: rgba(220, 220, 220, 0.4);
}
.vs-dark .monaco-workbench modelview-dom-component pre {
background-color: rgba(10, 10, 10, 0.4);
}
.hc-black .monaco-workbench modelview-dom-component pre {
background-color: rgb(0, 0, 0);
}
.hc-black .monaco-workbench modelview-dom-component h1 {
border-color: rgb(0, 0, 0);
}
modelview-dom-component table > thead > tr > th {
border-color: rgba(0, 0, 0, 0.69);
}
.vs-dark .monaco-workbench modelview-dom-component table > thead > tr > th {
border-color: rgba(255, 255, 255, 0.69);
}
modelview-dom-component h1,
modelview-dom-component hr,
modelview-dom-component table > tbody > tr + tr > td {
border-color: rgba(0, 0, 0, 0.18);
}
.vs-dark .monaco-workbench modelview-dom-component h1,
.vs-dark .monaco-workbench modelview-dom-component hr,
.vs-dark .monaco-workbench modelview-dom-component table > tbody > tr + tr > td {
border-color: rgba(255, 255, 255, 0.18);
}

View File

@@ -3,16 +3,50 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as sqlops from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorModel } from 'vs/platform/editor/common/editor'; import { IEditorModel } from 'vs/platform/editor/common/editor';
import { EditorInput } from 'vs/workbench/common/editor'; import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/editor';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { DialogPane } from 'sql/platform/dialog/dialogPane'; import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { Emitter, Event } from 'vs/base/common/event';
import * as sqlops from 'sqlops'; export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
export class ModelViewInputModel extends EditorModel {
private dirty: boolean;
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
get onDidChangeDirty(): Event<void> { return this._onDidChangeDirty.event; }
constructor(public readonly modelViewId, private readonly handle: number, private saveHandler?: ModeViewSaveHandler) {
super();
this.dirty = false;
}
get isDirty(): boolean {
return this.dirty;
}
public setDirty(dirty: boolean): void {
if (this.dirty === dirty) {
return;
}
this.dirty = dirty;
this._onDidChangeDirty.fire();
}
save(): TPromise<boolean> {
if (this.saveHandler) {
return TPromise.wrap(this.saveHandler(this.handle));
}
return TPromise.wrap(true);
}
}
export class ModelViewInput extends EditorInput { export class ModelViewInput extends EditorInput {
public static ID: string = 'workbench.editorinputs.ModelViewEditorInput'; public static ID: string = 'workbench.editorinputs.ModelViewEditorInput';
@@ -20,14 +54,15 @@ export class ModelViewInput extends EditorInput {
private _dialogPaneContainer: HTMLElement; private _dialogPaneContainer: HTMLElement;
private _dialogPane: DialogPane; private _dialogPane: DialogPane;
constructor(private _title: string, private _modelViewId: string, constructor(private _title: string, private _model: ModelViewInputModel,
private _options: sqlops.ModelViewEditorOptions, private _options: sqlops.ModelViewEditorOptions,
@IInstantiationService private _instantiationService: IInstantiationService, @IInstantiationService private _instantiationService: IInstantiationService,
@IPartService private readonly _partService: IPartService @IPartService private readonly _partService: IPartService
) { ) {
super(); super();
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
this._container = document.createElement('div'); this._container = document.createElement('div');
this._container.id = `modelView-${_modelViewId}`; this._container.id = `modelView-${_model.modelViewId}`;
this._partService.getContainer(Parts.EDITOR_PART).appendChild(this._container); this._partService.getContainer(Parts.EDITOR_PART).appendChild(this._container);
} }
@@ -37,7 +72,7 @@ export class ModelViewInput extends EditorInput {
} }
public get modelViewId(): string { public get modelViewId(): string {
return this._modelViewId; return this._model.modelViewId;
} }
public getTypeId(): string { public getTypeId(): string {
@@ -85,6 +120,31 @@ export class ModelViewInput extends EditorInput {
return this._options; return this._options;
} }
/**
* An editor that is dirty will be asked to be saved once it closes.
*/
isDirty(): boolean {
return this._model.isDirty;
}
/**
* Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result.
*/
confirmSave(): TPromise<ConfirmResult> {
// TODO #2530 support save on close / confirm save. This is significantly more work
// as we need to either integrate with textFileService (seems like this isn't viable)
// or register our own complimentary service that handles the lifecycle operations such
// as close all, auto save etc.
return TPromise.wrap(ConfirmResult.DONT_SAVE);
}
/**
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
*/
save(): TPromise<boolean> {
return this._model.save();
}
public dispose(): void { public dispose(): void {
if (this._dialogPane) { if (this._dialogPane) {
this._dialogPane.dispose(); this._dialogPane.dispose();
@@ -93,6 +153,9 @@ export class ModelViewInput extends EditorInput {
this._container.remove(); this._container.remove();
this._container = undefined; this._container = undefined;
} }
if (this._model) {
this._model.dispose();
}
super.dispose(); super.dispose();
} }
} }

View File

@@ -29,7 +29,7 @@ export class ToolbarItem {
template: ` template: `
<div #container *ngIf="items" [class]="toolbarClass" > <div #container *ngIf="items" [class]="toolbarClass" >
<ng-container *ngFor="let item of items"> <ng-container *ngFor="let item of items">
<div class="modelview-toolbar-item" [title]="getItemTitle(item)" [style.paddingTop]="paddingTop" tabindex="0"> <div class="modelview-toolbar-item" [style.paddingTop]="paddingTop">
<div *ngIf="shouldShowTitle(item)" class="modelview-toolbar-title" > <div *ngIf="shouldShowTitle(item)" class="modelview-toolbar-title" >
{{getItemTitle(item)}} {{getItemTitle(item)}}
</div> </div>

View File

@@ -62,4 +62,12 @@
margin-right: 0.3em; margin-right: 0.3em;
background-position: 50% 50%; background-position: 50% 50%;
} }
.modelview-toolbar-container .modelview-toolbar-component modelview-button .monaco-text-button.active {
-ms-transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
-webkit-transform: scale(1.272019649, 1.272019649);
-moz-transform: scale(1.272019649, 1.272019649);
-o-transform: scale(1.272019649, 1.272019649);
transform: scale(1.272019649, 1.272019649);
}

View File

@@ -72,6 +72,8 @@ export interface IObjectExplorerService {
getActiveConnectionNodes(): TreeNode[]; getActiveConnectionNodes(): TreeNode[];
getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>; getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>;
refreshNodeInView(connectionId: string, nodePath: string): Thenable<TreeNode>;
} }
interface SessionStatus { interface SessionStatus {
@@ -476,6 +478,20 @@ export class ObjectExplorerService implements IObjectExplorerService {
return Object.values(this._activeObjectExplorerNodes); return Object.values(this._activeObjectExplorerNodes);
} }
public async refreshNodeInView(connectionId: string, nodePath: string): Promise<TreeNode> {
// Get the tree node and call refresh from the provider
let treeNode = await this.getTreeNode(connectionId, nodePath);
await this.refreshTreeNode(treeNode.getSession(), treeNode);
// Get the new tree node, refresh it in the view, and expand it if needed
treeNode = await this.getTreeNode(connectionId, nodePath);
await this._serverTreeView.refreshElement(treeNode);
if (treeNode.children.length > 0) {
await treeNode.setExpandedState(TreeItemCollapsibleState.Expanded);
}
return treeNode;
}
private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise<void> { private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise<void> {
treeNode = await this.getUpdatedTreeNode(treeNode); treeNode = await this.getUpdatedTreeNode(treeNode);
let expandNode = this.getTreeItem(treeNode); let expandNode = this.getTreeItem(treeNode);

View File

@@ -39,7 +39,7 @@ const viewletDescriptor = new ViewletDescriptor(
VIEWLET_ID, VIEWLET_ID,
'Servers', 'Servers',
'connectionViewlet', 'connectionViewlet',
-100 0
); );
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);

View File

@@ -167,12 +167,12 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
if (TreeUpdateUtils.isDatabaseNode(treeNode)) { if (TreeUpdateUtils.isDatabaseNode(treeNode)) {
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) { if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree)); actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree));
this.addNewQueryAction(context, actions);
} else { } else {
return actions; return actions;
} }
} }
this.addNewQueryAction(context, actions);
this.addScriptingActions(context, actions); this.addScriptingActions(context, actions);
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, treeNode)); actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, treeNode));

View File

@@ -7,7 +7,7 @@
import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode'; import { TreeNode, TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode';
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService'; import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils'; import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
@@ -72,9 +72,13 @@ export class ServerTreeDataSource implements IDataSource {
if (node.children) { if (node.children) {
resolve(node.children); resolve(node.children);
} else { } else {
// These similar changes are probably needed for a ConnectionProfile group element as well. However, we do not have a repro of a failiure in that scenario so they will be tackled in a future checkin.
// It has been tested for connecting to the server in profile itself and things work fine there.
this._objectExplorerService.resolveTreeNodeChildren(node.getSession(), node).then(() => { this._objectExplorerService.resolveTreeNodeChildren(node.getSession(), node).then(() => {
resolve(node.children); resolve(node.children);
}, expandError => { }, expandError => {
node.setExpandedState(TreeItemCollapsibleState.Collapsed);
node.errorStateMessage = expandError;
this.showError(expandError); this.showError(expandError);
resolve([]); resolve([]);
}); });

View File

@@ -138,7 +138,7 @@ export class ServerTreeView {
let expandGroups: boolean = self._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG]; let expandGroups: boolean = self._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
if (expandGroups) { if (expandGroups) {
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root)); self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
} }
if (root && !root.hasValidConnections) { if (root && !root.hasValidConnections) {
@@ -244,6 +244,10 @@ export class ServerTreeView {
TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService); TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
} }
public refreshElement(element: any): Thenable<void> {
return this._tree.refresh(element);
}
/** /**
* Filter connections based on view (recent/active) * Filter connections based on view (recent/active)
*/ */

View File

@@ -10,6 +10,27 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { GridPanelState } from 'sql/parts/query/editor/gridPanel';
import { MessagePanelState } from 'sql/parts/query/editor/messagePanel';
import { QueryPlanState } from 'sql/parts/queryPlan/queryPlan';
import { ChartState } from 'sql/parts/query/editor/charting/chartView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class ResultsViewState {
public gridPanelState: GridPanelState = new GridPanelState();
public messagePanelState: MessagePanelState = new MessagePanelState(this.configurationService);
public chartState: ChartState = new ChartState();
public queryPlanState: QueryPlanState = new QueryPlanState();
public gridPanelSize: number;
public messagePanelSize: number;
public activeTab: string;
public visibleTabs: Set<string> = new Set<string>();
constructor(@IConfigurationService private configurationService: IConfigurationService) {
}
}
/** /**
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of * Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
* data in the results grid. * data in the results grid.
@@ -29,7 +50,11 @@ export class QueryResultsInput extends EditorInput {
public readonly onRestoreViewStateEmitter = new Emitter<void>(); public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>(); public readonly onSaveViewStateEmitter = new Emitter<void>();
constructor(private _uri: string) { public readonly state = new ResultsViewState(this.configurationService);
constructor(private _uri: string,
@IConfigurationService private configurationService: IConfigurationService
) {
super(); super();
this._visible = false; this._visible = false;
this._hasBootstrapped = false; this._hasBootstrapped = false;

View File

@@ -11,13 +11,14 @@ import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import QueryRunner from 'sql/parts/query/execution/queryRunner'; import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { SaveFormat } from 'sql/parts/grid/common/interfaces'; import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { Table } from 'sql/base/browser/ui/table/table'; import { Table } from 'sql/base/browser/ui/table/table';
import { GridTableState } from 'sql/parts/query/editor/gridPanel'; import { GridTableState } from 'sql/parts/query/editor/gridPanel';
import { QueryEditor } from './queryEditor'; import { QueryEditor } from './queryEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
export interface IGridActionContext { export interface IGridActionContext {
cell: { row: number; cell: number; }; cell: { row: number; cell: number; };
@@ -26,6 +27,7 @@ export interface IGridActionContext {
batchId: number; batchId: number;
resultId: number; resultId: number;
table: Table<any>; table: Table<any>;
selectionModel: CellSelectionModel<any>;
tableState: GridTableState; tableState: GridTableState;
} }
@@ -113,7 +115,7 @@ export class SelectAllGridAction extends Action {
} }
public run(context: IGridActionContext): TPromise<boolean> { public run(context: IGridActionContext): TPromise<boolean> {
context.table.setSelectedRows(true); context.selectionModel.setSelectedRanges([new Slick.Range(0, 0, context.table.getData().getLength() - 1, context.table.columns.length - 1)]);
return TPromise.as(true); return TPromise.as(true);
} }
} }
@@ -167,13 +169,13 @@ export class MaximizeTableAction extends Action {
} }
} }
export class MinimizeTableAction extends Action { export class RestoreTableAction extends Action {
public static ID = 'grid.minimize'; public static ID = 'grid.restore';
public static LABEL = localize('minimize', 'Minimize'); public static LABEL = localize('restore', 'Restore');
public static ICON = 'exitFullScreen'; public static ICON = 'exitFullScreen';
constructor() { constructor() {
super(MinimizeTableAction.ID, MinimizeTableAction.LABEL, MinimizeTableAction.ICON); super(RestoreTableAction.ID, RestoreTableAction.LABEL, RestoreTableAction.ICON);
} }
public run(context: IGridActionContext): TPromise<boolean> { public run(context: IGridActionContext): TPromise<boolean> {
@@ -221,5 +223,4 @@ export class ShowQueryPlanAction extends Action {
return TPromise.as(false); return TPromise.as(false);
} }
} }
} }

View File

@@ -8,10 +8,9 @@
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { InsightType, IInsightOptions } from './insights/interfaces'; import { InsightType, IInsightOptions } from './insights/interfaces';
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution); const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);

View File

@@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
export class ChartTab implements IPanelTab { export class ChartTab implements IPanelTab {
public readonly title = localize('chartTabTitle', 'Chart'); public readonly title = localize('chartTabTitle', 'Chart');
public readonly identifier = generateUuid(); public readonly identifier = 'ChartTab';
public readonly view: ChartView; public readonly view: ChartView;
constructor(@IInstantiationService instantiationService: IInstantiationService) { constructor(@IInstantiationService instantiationService: IInstantiationService) {

View File

@@ -12,12 +12,13 @@ import { Insight } from './insights/insight';
import QueryRunner from 'sql/parts/query/execution/queryRunner'; import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { ChartOptions, IChartOption, ControlType } from './chartOptions'; import { ChartOptions, IChartOption, ControlType } from './chartOptions';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { IInsightOptions } from './insights/interfaces'; import { IInsightOptions } from './insights/interfaces';
import { CopyAction, SaveImageAction, CreateInsightAction, IChartActionContext } from './actions'; import { CopyAction, SaveImageAction, CreateInsightAction, IChartActionContext } from './actions';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { Registry } from 'vs/platform/registry/common/platform';
import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom'; import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -27,11 +28,21 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isUndefinedOrNull } from 'vs/base/common/types';
export class ChartState {
dataId: { batchId: number, resultId: number };
options: IInsightOptions = {
type: ChartType.Bar
};
}
declare class Proxy { declare class Proxy {
constructor(object, handler); constructor(object, handler);
} }
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
export class ChartView implements IPanelView { export class ChartView implements IPanelView {
private insight: Insight; private insight: Insight;
private _queryRunner: QueryRunner; private _queryRunner: QueryRunner;
@@ -43,6 +54,8 @@ export class ChartView implements IPanelView {
private _copyAction: CopyAction; private _copyAction: CopyAction;
private _saveAction: SaveImageAction; private _saveAction: SaveImageAction;
private _state: ChartState;
private options: IInsightOptions = { private options: IInsightOptions = {
type: ChartType.Bar type: ChartType.Bar
}; };
@@ -61,7 +74,7 @@ export class ChartView implements IPanelView {
private chartingContainer: HTMLElement; private chartingContainer: HTMLElement;
private optionDisposables: IDisposable[] = []; private optionDisposables: IDisposable[] = [];
private optionMap: { [x: string]: HTMLElement } = {}; private optionMap: { [x: string]: { element: HTMLElement; set: (val) => void } } = {};
constructor( constructor(
@IContextViewService private _contextViewService: IContextViewService, @IContextViewService private _contextViewService: IContextViewService,
@@ -95,6 +108,10 @@ export class ChartView implements IPanelView {
} }
let result = Reflect.set(target, key, value, receiver); let result = Reflect.set(target, key, value, receiver);
// mirror the change in our state
if (self.state) {
Reflect.set(self.state.options, key, value);
}
if (change) { if (change) {
self.taskbar.context = <IChartActionContext>{ options: self.options, insight: self.insight ? self.insight.insight : undefined }; self.taskbar.context = <IChartActionContext>{ options: self.options, insight: self.insight ? self.insight.insight : undefined };
@@ -109,6 +126,7 @@ export class ChartView implements IPanelView {
} }
}) as IInsightOptions; }) as IInsightOptions;
ChartOptions.general[0].options = insightRegistry.getAllIds();
ChartOptions.general.map(o => { ChartOptions.general.map(o => {
this.createOption(o, generalControls); this.createOption(o, generalControls);
}); });
@@ -138,6 +156,7 @@ export class ChartView implements IPanelView {
} }
public chart(dataId: { batchId: number, resultId: number }) { public chart(dataId: { batchId: number, resultId: number }) {
this.state.dataId = dataId;
this._currentData = dataId; this._currentData = dataId;
this.shouldGraph(); this.shouldGraph();
} }
@@ -172,7 +191,7 @@ export class ChartView implements IPanelView {
}); });
} }
} }
// if we have the necessary information but the information isn't avaiable yet, // if we have the necessary information but the information isn't available yet,
// we should be smart and retrying when the information might be available // we should be smart and retrying when the information might be available
} }
} }
@@ -180,7 +199,9 @@ export class ChartView implements IPanelView {
private buildOptions() { private buildOptions() {
dispose(this.optionDisposables); dispose(this.optionDisposables);
this.optionDisposables = []; this.optionDisposables = [];
this.optionMap = {}; this.optionMap = {
'type': this.optionMap['type']
};
new Builder(this.typeControls).clearChildren(); new Builder(this.typeControls).clearChildren();
this.updateActionbar(); this.updateActionbar();
@@ -200,9 +221,9 @@ export class ChartView implements IPanelView {
let option = ChartOptions[this.options.type].find(e => e.configEntry === key); let option = ChartOptions[this.options.type].find(e => e.configEntry === key);
if (option && option.if) { if (option && option.if) {
if (option.if(this.options)) { if (option.if(this.options)) {
new Builder(this.optionMap[key]).show(); new Builder(this.optionMap[key].element).show();
} else { } else {
new Builder(this.optionMap[key]).hide(); new Builder(this.optionMap[key].element).hide();
} }
} }
} }
@@ -211,6 +232,7 @@ export class ChartView implements IPanelView {
private updateActionbar() { private updateActionbar() {
if (this.insight && this.insight.isCopyable) { if (this.insight && this.insight.isCopyable) {
this.taskbar.context = { insight: this.insight.insight, options: this.options };
this.taskbar.setContent([ this.taskbar.setContent([
{ action: this._createInsightAction }, { action: this._createInsightAction },
{ action: this._copyAction }, { action: this._copyAction },
@@ -226,57 +248,104 @@ export class ChartView implements IPanelView {
label.innerText = option.label; label.innerText = option.label;
let optionContainer = $('div.option-container'); let optionContainer = $('div.option-container');
optionContainer.appendChild(label); optionContainer.appendChild(label);
let setFunc: (val) => void;
let value = this.state ? this.state.options[option.configEntry] || option.default : option.default;
switch (option.type) { switch (option.type) {
case ControlType.checkbox: case ControlType.checkbox:
let checkbox = new Checkbox(optionContainer, { let checkbox = new Checkbox(optionContainer, {
label: '', label: '',
ariaLabel: option.label, ariaLabel: option.label,
checked: option.default, checked: value,
onChange: () => { onChange: () => {
if (this.options[option.configEntry] !== checkbox.checked) { if (this.options[option.configEntry] !== checkbox.checked) {
this.options[option.configEntry] = checkbox.checked; this.options[option.configEntry] = checkbox.checked;
this.insight.options = this.options; if (this.insight) {
this.insight.options = this.options;
}
} }
} }
}); });
setFunc = (val: boolean) => {
checkbox.checked = val;
};
break; break;
case ControlType.combo: case ControlType.combo:
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService); let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
dropdown.select(option.options.indexOf(option.default)); dropdown.select(option.options.indexOf(value));
dropdown.render(optionContainer); dropdown.render(optionContainer);
dropdown.onDidSelect(e => { dropdown.onDidSelect(e => {
if (this.options[option.configEntry] !== option.options[e.index]) { if (this.options[option.configEntry] !== option.options[e.index]) {
this.options[option.configEntry] = option.options[e.index]; this.options[option.configEntry] = option.options[e.index];
this.insight.options = this.options; if (this.insight) {
this.insight.options = this.options;
}
} }
}); });
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
dropdown.select(option.options.indexOf(val));
}
};
this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService)); this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService));
break; break;
case ControlType.input: case ControlType.input:
let input = new InputBox(optionContainer, this._contextViewService); let input = new InputBox(optionContainer, this._contextViewService);
input.value = option.default || ''; input.value = value || '';
input.onDidChange(e => { input.onDidChange(e => {
if (this.options[option.configEntry] !== e) { if (this.options[option.configEntry] !== e) {
this.options[option.configEntry] = e; this.options[option.configEntry] = e;
this.insight.options = this.options; if (this.insight) {
this.insight.options = this.options;
}
} }
}); });
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
input.value = val;
}
};
this.optionDisposables.push(attachInputBoxStyler(input, this._themeService)); this.optionDisposables.push(attachInputBoxStyler(input, this._themeService));
break; break;
case ControlType.numberInput: case ControlType.numberInput:
let numberInput = new InputBox(optionContainer, this._contextViewService, { type: 'number' }); let numberInput = new InputBox(optionContainer, this._contextViewService, { type: 'number' });
numberInput.value = option.default || ''; numberInput.value = value || '';
numberInput.onDidChange(e => { numberInput.onDidChange(e => {
if (this.options[option.configEntry] !== Number(e)) { if (this.options[option.configEntry] !== Number(e)) {
this.options[option.configEntry] = Number(e); this.options[option.configEntry] = Number(e);
this.insight.options = this.options; if (this.insight) {
this.insight.options = this.options;
}
} }
}); });
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
numberInput.value = val;
}
};
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService)); this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
break; break;
} }
this.optionMap[option.configEntry] = optionContainer; this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
container.appendChild(optionContainer); container.appendChild(optionContainer);
this.options[option.configEntry] = option.default; this.options[option.configEntry] = value;
} }
}
public set state(val: ChartState) {
this._state = val;
if (this.state.options) {
for (let key in this.state.options) {
if (this.state.options.hasOwnProperty(key) && this.optionMap[key]) {
this.options[key] = this.state.options[key];
this.optionMap[key].set(this.state.options[key]);
}
}
}
if (this.state.dataId) {
this.chart(this.state.dataId);
}
}
public get state(): ChartState {
return this._state;
}
}

View File

@@ -13,9 +13,9 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { IInsightOptions, IInsight } from './interfaces'; import { IInsightOptions, IInsight } from './interfaces';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie]; const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
@@ -149,6 +149,8 @@ export class Graph implements IInsight {
let foreground = foregroundColor ? foregroundColor.toString() : null; let foreground = foregroundColor ? foregroundColor.toString() : null;
let gridLinesColor = this._theme.getColor(editorLineNumbers); let gridLinesColor = this._theme.getColor(editorLineNumbers);
let gridLines = gridLinesColor ? gridLinesColor.toString() : null; let gridLines = gridLinesColor ? gridLinesColor.toString() : null;
let backgroundColor = this._theme.getColor(colors.editorBackground);
let background = backgroundColor ? backgroundColor.toString() : null;
if (options) { if (options) {
retval.scales = {}; retval.scales = {};
@@ -187,12 +189,20 @@ export class Graph implements IInsight {
}]; }];
} }
retval.legend = { retval.legend = <ChartJs.ChartLegendOptions>{
position: options.legendPosition as ChartJs.PositionType, position: options.legendPosition as ChartJs.PositionType,
display: options.legendPosition !== LegendPosition.None display: options.legendPosition !== LegendPosition.None,
labels: {
fontColor: foreground
}
}; };
} }
// these are custom options that will throw compile errors
(<any>retval).viewArea = {
backgroundColor: background
};
return retval; return retval;
} }

View File

@@ -6,15 +6,15 @@
'use strict'; 'use strict';
import { Graph } from './graphInsight'; import { Graph } from './graphInsight';
import { ChartType, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { Builder } from 'vs/base/browser/builder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ImageInsight } from './imageInsight'; import { ImageInsight } from './imageInsight';
import { TableInsight } from './tableInsight'; import { TableInsight } from './tableInsight';
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces'; import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
import { CountInsight } from './countInsight'; import { CountInsight } from './countInsight';
import { Builder } from 'vs/base/browser/builder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Dimension } from 'vs/base/browser/dom'; import { Dimension } from 'vs/base/browser/dom';
const defaultOptions: IInsightOptions = { const defaultOptions: IInsightOptions = {

View File

@@ -4,11 +4,10 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
'use strict'; 'use strict';
import { Dimension } from 'vs/base/browser/dom';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { ChartType, LegendPosition, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { Dimension } from 'vs/base/browser/dom';
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
export interface IInsightOptions { export interface IInsightOptions {
type: InsightType | ChartType; type: InsightType | ChartType;

View File

@@ -12,11 +12,13 @@ import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scr
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin'; import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin'; import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { SaveFormat } from 'sql/parts/grid/common/interfaces'; import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, MinimizeTableAction, ChartDataAction, ShowQueryPlanAction } from 'sql/parts/query/editor/actions'; import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, RestoreTableAction, ChartDataAction, ShowQueryPlanAction } from 'sql/parts/query/editor/actions';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin'; import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin'; import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { escape } from 'sql/base/common/strings'; import { escape } from 'sql/base/common/strings';
import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices'; import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices';
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
@@ -34,25 +36,30 @@ import { $ } from 'vs/base/browser/builder';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { Dimension, getContentWidth } from 'vs/base/browser/dom'; import { Dimension, getContentWidth, isInDOM } from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin'; import { IAction } from 'vs/base/common/actions';
const ROW_HEIGHT = 29; const ROW_HEIGHT = 29;
const HEADER_HEIGHT = 26; const HEADER_HEIGHT = 26;
const MIN_GRID_HEIGHT_ROWS = 8; const MIN_GRID_HEIGHT_ROWS = 8;
const ESTIMATED_SCROLL_BAR_HEIGHT = 10; const ESTIMATED_SCROLL_BAR_HEIGHT = 10;
const BOTTOM_PADDING = 5; const BOTTOM_PADDING = 15;
const ACTIONBAR_WIDTH = 26; const ACTIONBAR_WIDTH = 26;
// minimum height needed to show the full actionbar // minimum height needed to show the full actionbar
const ACTIONBAR_HEIGHT = 100; const ACTIONBAR_HEIGHT = 100;
// this handles min size if rows is greater than the min grid visible rows // this handles min size if rows is greater than the min grid visible rows
const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT + BOTTOM_PADDING; const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
export class GridPanelState {
public tableStates: GridTableState[] = [];
public scrollPosition: number;
public collapsed = false;
}
export interface IGridTableState { export interface IGridTableState {
canBeMaximized: boolean; canBeMaximized: boolean;
@@ -66,13 +73,29 @@ export class GridTableState {
private _onMaximizedChange = new Emitter<boolean>(); private _onMaximizedChange = new Emitter<boolean>();
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event; public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
public canBeMaximized: boolean; private _onCanBeMaximizedChange = new Emitter<boolean>();
public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event;
constructor(state?: IGridTableState) { private _canBeMaximized: boolean;
if (state) {
this._maximized = state.maximized; /* The top row of the current scroll */
this.canBeMaximized = state.canBeMaximized; public scrollPosition = 0;
public selection: Slick.Range[];
public activeCell: Slick.Cell;
constructor(public readonly resultId: number, public readonly batchId: number) {
}
public get canBeMaximized(): boolean {
return this._canBeMaximized;
}
public set canBeMaximized(val: boolean) {
if (val === this._canBeMaximized) {
return;
} }
this._canBeMaximized = val;
this._onCanBeMaximizedChange.fire(val);
} }
public get maximized(): boolean { public get maximized(): boolean {
@@ -86,10 +109,6 @@ export class GridTableState {
this._maximized = val; this._maximized = val;
this._onMaximizedChange.fire(val); this._onMaximizedChange.fire(val);
} }
public clone(): GridTableState {
return new GridTableState({ canBeMaximized: this.canBeMaximized, maximized: this.maximized });
}
} }
export class GridPanel extends ViewletPanel { export class GridPanel extends ViewletPanel {
@@ -103,6 +122,7 @@ export class GridPanel extends ViewletPanel {
private runner: QueryRunner; private runner: QueryRunner;
private maximizedGrid: GridTable<any>; private maximizedGrid: GridTable<any>;
private _state: GridPanelState;
constructor( constructor(
options: IViewletPanelOptions, options: IViewletPanelOptions,
@@ -114,6 +134,16 @@ export class GridPanel extends ViewletPanel {
) { ) {
super(options, keybindingService, contextMenuService, configurationService); super(options, keybindingService, contextMenuService, configurationService);
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false }); this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
this.splitView.onScroll(e => {
if (this.state) {
this.state.scrollPosition = e;
}
});
this.onDidChange(e => {
if (this.state) {
this.state.collapsed = !this.isExpanded();
}
});
} }
protected renderBody(container: HTMLElement): void { protected renderBody(container: HTMLElement): void {
@@ -138,7 +168,12 @@ export class GridPanel extends ViewletPanel {
this.queryRunnerDisposables = []; this.queryRunnerDisposables = [];
this.runner = runner; this.runner = runner;
this.queryRunnerDisposables.push(this.runner.onResultSet(e => this.onResultSet(e))); this.queryRunnerDisposables.push(this.runner.onResultSet(e => this.onResultSet(e)));
this.queryRunnerDisposables.push(this.runner.onQueryStart(() => this.reset())); this.queryRunnerDisposables.push(this.runner.onQueryStart(() => {
if (this.state) {
this.state.tableStates = [];
}
this.reset();
}));
} }
private onResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) { private onResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
@@ -151,6 +186,10 @@ export class GridPanel extends ViewletPanel {
this.maximumBodySize = this.tables.reduce((p, c) => { this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize; return p + c.maximumSize;
}, 0); }, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
} }
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) { private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
@@ -164,8 +203,18 @@ export class GridPanel extends ViewletPanel {
let tables: GridTable<any>[] = []; let tables: GridTable<any>[] = [];
for (let set of resultsToAdd) { for (let set of resultsToAdd) {
let tableState = new GridTableState(); let tableState: GridTableState;
let table = this.instantiationService.createInstance(GridTable, this.runner, tableState, set); if (this._state) {
tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id);
}
if (!tableState) {
tableState = new GridTableState(set.id, set.batchId);
if (this._state) {
this._state.tableStates.push(tableState);
}
}
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
table.state = tableState;
tableState.onMaximizedChange(e => { tableState.onMaximizedChange(e => {
if (e) { if (e) {
this.maximizeTable(table.id); this.maximizeTable(table.id);
@@ -191,7 +240,6 @@ export class GridPanel extends ViewletPanel {
for (let i = this.splitView.length - 1; i >= 0; i--) { for (let i = this.splitView.length - 1; i >= 0; i--) {
this.splitView.removeView(i); this.splitView.removeView(i);
} }
dispose(this.tables); dispose(this.tables);
this.tables = []; this.tables = [];
@@ -224,6 +272,24 @@ export class GridPanel extends ViewletPanel {
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize)); this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
} }
} }
public set state(val: GridPanelState) {
this._state = val;
this.tables.map(t => {
let state = this.state.tableStates.find(s => s.batchId === t.resultSet.batchId && s.resultId === t.resultSet.id);
if (!state) {
this.state.tableStates.push(t.state);
}
if (state) {
t.state = state;
}
});
this.setExpanded(!this.state.collapsed);
}
public get state(): GridPanelState {
return this._state;
}
} }
class GridTable<T> extends Disposable implements IView { class GridTable<T> extends Disposable implements IView {
@@ -242,13 +308,16 @@ class GridTable<T> extends Disposable implements IView {
public id = generateUuid(); public id = generateUuid();
readonly element: HTMLElement = this.container; readonly element: HTMLElement = this.container;
private _state: GridTableState;
private scrolled = false;
// this handles if the row count is small, like 4-5 rows // this handles if the row count is small, like 4-5 rows
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT + BOTTOM_PADDING; private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
constructor( constructor(
private runner: QueryRunner, private runner: QueryRunner,
public state: GridTableState, public readonly resultSet: sqlops.ResultSetSummary,
private resultSet: sqlops.ResultSetSummary,
@IContextMenuService private contextMenuService: IContextMenuService, @IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService, @IInstantiationService private instantiationService: IInstantiationService,
@IEditorService private editorService: IEditorService, @IEditorService private editorService: IEditorService,
@@ -257,7 +326,7 @@ class GridTable<T> extends Disposable implements IView {
super(); super();
this.container.style.width = '100%'; this.container.style.width = '100%';
this.container.style.height = '100%'; this.container.style.height = '100%';
this.container.style.marginBottom = BOTTOM_PADDING + 'px'; // this.container.style.marginBottom = BOTTOM_PADDING + 'px';
this.container.className = 'grid-panel'; this.container.className = 'grid-panel';
this.columns = this.resultSet.columnInfo.map((c, i) => { this.columns = this.resultSet.columnInfo.map((c, i) => {
@@ -274,6 +343,11 @@ class GridTable<T> extends Disposable implements IView {
}); });
} }
public onRemove() {
// when we are removed slickgrid acts badly so we need to account for that
this.scrolled = false;
}
public render(container: HTMLElement, orientation: Orientation): void { public render(container: HTMLElement, orientation: Orientation): void {
container.appendChild(this.container); container.appendChild(this.container);
} }
@@ -294,15 +368,7 @@ class GridTable<T> extends Disposable implements IView {
let numberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount }); let numberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount });
let copyHandler = new CopyKeybind(); let copyHandler = new CopyKeybind();
copyHandler.onCopy(e => { copyHandler.onCopy(e => {
new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run({ new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run(this.generateContext());
selection: e,
batchId: this.resultSet.batchId,
resultId: this.resultSet.id,
cell: this.table.grid.getActiveCell(),
runner: this.runner,
table: this.table,
tableState: this.state
});
}); });
this.columns.unshift(numberColumn.getColumnDefinition()); this.columns.unshift(numberColumn.getColumnDefinition());
let tableOptions: Slick.GridOptions<T> = { let tableOptions: Slick.GridOptions<T> = {
@@ -317,6 +383,7 @@ class GridTable<T> extends Disposable implements IView {
this.table.registerPlugin(new AutoColumnSize()); this.table.registerPlugin(new AutoColumnSize());
this.table.registerPlugin(copyHandler); this.table.registerPlugin(copyHandler);
this.table.registerPlugin(numberColumn); this.table.registerPlugin(numberColumn);
this.table.registerPlugin(new AdditionalKeyBindings());
this._register(this.table.onContextMenu(this.contextMenu, this)); this._register(this.table.onContextMenu(this.contextMenu, this));
this._register(this.table.onClick(this.onTableClick, this)); this._register(this.table.onClick(this.onTableClick, this));
@@ -324,22 +391,7 @@ class GridTable<T> extends Disposable implements IView {
this.table.style(this.styles); this.table.style(this.styles);
} }
let actions = []; let actions = this.getCurrentActions();
if (this.state.canBeMaximized) {
if (this.state.maximized) {
actions.splice(1, 0, new MinimizeTableAction());
} else {
actions.splice(1, 0, new MaximizeTableAction());
}
}
actions.push(
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
this.instantiationService.createInstance(ChartDataAction)
);
let actionBarContainer = document.createElement('div'); let actionBarContainer = document.createElement('div');
actionBarContainer.style.width = ACTIONBAR_WIDTH + 'px'; actionBarContainer.style.width = ACTIONBAR_WIDTH + 'px';
@@ -356,7 +408,65 @@ class GridTable<T> extends Disposable implements IView {
tableState: this.state tableState: this.state
} }
}); });
// update context before we run an action
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
this.actionBar.context = this.generateContext();
});
this.actionBar.push(actions, { icon: true, label: false }); this.actionBar.push(actions, { icon: true, label: false });
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
if (this.state) {
this.state.selection = this.selectionModel.getSelectedRanges();
}
});
this.table.grid.onScroll.subscribe((e, data) => {
if (!this.scrolled && this.state.scrollPosition && isInDOM(this.container)) {
this.scrolled = true;
this.table.grid.scrollTo(this.state.scrollPosition);
}
if (this.state && isInDOM(this.container)) {
this.state.scrollPosition = data.scrollTop;
}
});
this.table.grid.onActiveCellChanged.subscribe(e => {
if (this.state) {
this.state.activeCell = this.table.grid.getActiveCell();
}
});
this.setupState();
}
private setupState() {
// change actionbar on maximize change
this.state.onMaximizedChange(this.rebuildActionBar, this);
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
if (this.state.scrollPosition) {
// most of the time this won't do anything
this.table.grid.scrollTo(this.state.scrollPosition);
// the problem here is that the scrolling state slickgrid uses
// doesn't work with it offDOM.
}
if (this.state.selection) {
this.selectionModel.setSelectedRanges(this.state.selection);
}
if (this.state.activeCell) {
this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell);
}
}
public get state(): GridTableState {
return this._state;
}
public set state(val: GridTableState) {
this._state = val;
} }
private onTableClick(event: ITableMouseEvent) { private onTableClick(event: ITableMouseEvent) {
@@ -372,6 +482,48 @@ class GridTable<T> extends Disposable implements IView {
} }
} }
private generateContext(cell?: Slick.Cell): IGridActionContext {
const selection = this.selectionModel.getSelectedRanges();
return <IGridActionContext>{
cell,
selection,
runner: this.runner,
batchId: this.resultSet.batchId,
resultId: this.resultSet.id,
table: this.table,
tableState: this.state,
selectionModel: this.selectionModel
};
}
private rebuildActionBar() {
let actions = this.getCurrentActions();
this.actionBar.clear();
this.actionBar.push(actions, { icon: true, label: false });
}
private getCurrentActions(): IAction[] {
let actions = [];
if (this.state.canBeMaximized) {
if (this.state.maximized) {
actions.splice(1, 0, new RestoreTableAction());
} else {
actions.splice(1, 0, new MaximizeTableAction());
}
}
actions.push(
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
this.instantiationService.createInstance(ChartDataAction)
);
return actions;
}
public layout(size?: number): void { public layout(size?: number): void {
if (!this.table) { if (!this.table) {
this.build(); this.build();
@@ -384,24 +536,27 @@ class GridTable<T> extends Disposable implements IView {
this.table.layout( this.table.layout(
new Dimension( new Dimension(
getContentWidth(this.container) - ACTIONBAR_WIDTH, getContentWidth(this.container) - ACTIONBAR_WIDTH,
size - BOTTOM_PADDING size
) )
); );
} }
public get minimumSize(): number { public get minimumSize(): number {
// clamp between ensuring we can show the actionbar, while also making sure we don't take too much space // clamp between ensuring we can show the actionbar, while also making sure we don't take too much space
return Math.max(Math.min(this.maxSize, MIN_GRID_HEIGHT), ACTIONBAR_HEIGHT); return Math.max(Math.min(this.maxSize, MIN_GRID_HEIGHT), ACTIONBAR_HEIGHT + BOTTOM_PADDING);
} }
public get maximumSize(): number { public get maximumSize(): number {
return Math.max(this.maxSize, ACTIONBAR_HEIGHT); return Math.max(this.maxSize, ACTIONBAR_HEIGHT + BOTTOM_PADDING);
} }
private loadData(offset: number, count: number): Thenable<T[]> { private loadData(offset: number, count: number): Thenable<T[]> {
return this.runner.getQueryRows(offset, count, this.resultSet.batchId, this.resultSet.id).then(response => { return this.runner.getQueryRows(offset, count, this.resultSet.batchId, this.resultSet.id).then(response => {
if (this.runner.isQueryPlan) { if (this.runner.isQueryPlan) {
this.instantiationService.createInstance(ShowQueryPlanAction).run(response.resultSubset.rows[0][0].displayValue); // it's a show plan response
if (response.resultSubset.rowCount === 1) {
this.instantiationService.createInstance(ShowQueryPlanAction).run(response.resultSubset.rows[0][0].displayValue);
}
} }
return response.resultSubset.rows.map(r => { return response.resultSubset.rows.map(r => {
let dataWithSchema = {}; let dataWithSchema = {};
@@ -419,7 +574,6 @@ class GridTable<T> extends Disposable implements IView {
} }
private contextMenu(e: ITableMouseEvent): void { private contextMenu(e: ITableMouseEvent): void {
const selection = this.selectionModel.getSelectedRanges();
const { cell } = e; const { cell } = e;
this.contextMenuService.showContextMenu({ this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor, getAnchor: () => e.anchor,
@@ -437,7 +591,7 @@ class GridTable<T> extends Disposable implements IView {
if (this.state.canBeMaximized) { if (this.state.canBeMaximized) {
if (this.state.maximized) { if (this.state.maximized) {
actions.splice(1, 0, new MinimizeTableAction()); actions.splice(1, 0, new RestoreTableAction());
} else { } else {
actions.splice(1, 0, new MaximizeTableAction()); actions.splice(1, 0, new MaximizeTableAction());
} }
@@ -446,15 +600,7 @@ class GridTable<T> extends Disposable implements IView {
return TPromise.as(actions); return TPromise.as(actions);
}, },
getActionsContext: () => { getActionsContext: () => {
return <IGridActionContext>{ return this.generateContext(cell);
cell,
selection,
runner: this.runner,
batchId: this.resultSet.batchId,
resultId: this.resultSet.id,
table: this.table,
tableState: this.state
};
} }
}); });
} }

View File

@@ -13,18 +13,28 @@
word-break: break-all; word-break: break-all;
} }
.monaco-workbench .message-tree .monaco-tree .monaco-tree-rows>.monaco-tree-row {
cursor: default;
}
.message-tree .time-stamp { .message-tree .time-stamp {
width: 100px; width: 100px;
display: inline-block; display: inline-block;
} }
.message-tree .message, .message-tree .message,
.message-tree .batch-start { .message-tree .batch-start,
.message-tree .error-message {
display: inline-block; display: inline-block;
} }
.message-tree .batch-start { .message-tree .batch-start {
text-decoration: underline; text-decoration: underline;
cursor: pointer;
}
.message-tree .error-message {
color: red;
} }
.message-tree .batch-start:hover { .message-tree .batch-start:hover {

View File

@@ -0,0 +1,5 @@
.monaco-select-box {
cursor: pointer;
min-width: 150px;
padding: 2px;
}

View File

@@ -8,7 +8,7 @@ import 'vs/css!./media/messagePanel';
import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions'; import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions';
import QueryRunner from 'sql/parts/query/execution/queryRunner'; import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { IResultMessage, BatchSummary, ISelectionData } from 'sqlops'; import { IResultMessage, ISelectionData } from 'sqlops';
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
@@ -25,11 +25,11 @@ import { OpenMode, ClickBehavior, ICancelableEvent, IControllerOptions } from 'v
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { $ } from 'vs/base/browser/builder'; import { $ } from 'vs/base/browser/builder';
import { isArray } from 'vs/base/common/types'; import { isArray, isUndefinedOrNull } from 'vs/base/common/types';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor } from 'vs/editor/common/editorCommon'; import { IEditor } from 'vs/editor/common/editorCommon';
import { QueryInput } from 'sql/parts/query/common/queryInput';
export interface IResultMessageIntern extends IResultMessage { export interface IResultMessageIntern extends IResultMessage {
id?: string; id?: string;
@@ -56,9 +56,22 @@ interface IBatchTemplate extends IMessageTemplate {
const TemplateIds = { const TemplateIds = {
MESSAGE: 'message', MESSAGE: 'message',
BATCH: 'batch', BATCH: 'batch',
MODEL: 'model' MODEL: 'model',
ERROR: 'error'
}; };
export class MessagePanelState {
public scrollPosition: number;
public collapsed = false;
constructor(@IConfigurationService configurationService: IConfigurationService) {
let messagesOpenedSettings = configurationService.getValue<boolean>('sql.messagesDefaultOpen');
if (!isUndefinedOrNull(messagesOpenedSettings)) {
this.collapsed = !messagesOpenedSettings;
}
}
}
export class MessagePanel extends ViewletPanel { export class MessagePanel extends ViewletPanel {
private ds = new MessageDataSource(); private ds = new MessageDataSource();
private renderer = new MessageRenderer(); private renderer = new MessageRenderer();
@@ -67,6 +80,7 @@ export class MessagePanel extends ViewletPanel {
private container = $('div message-tree').getHTMLElement(); private container = $('div message-tree').getHTMLElement();
private queryRunnerDisposables: IDisposable[] = []; private queryRunnerDisposables: IDisposable[] = [];
private _state: MessagePanelState;
private tree: ITree; private tree: ITree;
@@ -86,6 +100,16 @@ export class MessagePanel extends ViewletPanel {
renderer: this.renderer, renderer: this.renderer,
controller: this.controller controller: this.controller
}, { keyboardSupport: false }); }, { keyboardSupport: false });
this.tree.onDidScroll(e => {
if (this.state) {
this.state.scrollPosition = this.tree.getScrollPosition();
}
});
this.onDidChange(e => {
if (this.state) {
this.state.collapsed = !this.isExpanded();
}
});
} }
protected renderBody(container: HTMLElement): void { protected renderBody(container: HTMLElement): void {
@@ -99,8 +123,12 @@ export class MessagePanel extends ViewletPanel {
protected layoutBody(size: number): void { protected layoutBody(size: number): void {
const previousScrollPosition = this.tree.getScrollPosition(); const previousScrollPosition = this.tree.getScrollPosition();
this.tree.layout(size); this.tree.layout(size);
if (previousScrollPosition === 1) { if (this.state && this.state.scrollPosition) {
this.tree.setScrollPosition(1); this.tree.setScrollPosition(this.state.scrollPosition);
} else {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
} }
} }
@@ -113,17 +141,30 @@ export class MessagePanel extends ViewletPanel {
} }
private onMessage(message: IResultMessage | IResultMessage[]) { private onMessage(message: IResultMessage | IResultMessage[]) {
let hasError = false;
if (isArray(message)) { if (isArray(message)) {
hasError = message.find(e => e.isError) ? true : false;
this.model.messages.push(...message); this.model.messages.push(...message);
} else { } else {
hasError = message.isError;
this.model.messages.push(message); this.model.messages.push(message);
} }
const previousScrollPosition = this.tree.getScrollPosition(); if (hasError) {
this.tree.refresh(this.model).then(() => { this.setExpanded(true);
if (previousScrollPosition === 1) { }
if (this.state.scrollPosition) {
this.tree.refresh(this.model).then(() => {
this.tree.setScrollPosition(1); this.tree.setScrollPosition(1);
} });
}); } else {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
this.maximumBodySize = this.model.messages.length * 22;
} }
private reset() { private reset() {
@@ -131,6 +172,17 @@ export class MessagePanel extends ViewletPanel {
this.model.totalExecuteMessage = undefined; this.model.totalExecuteMessage = undefined;
this.tree.refresh(this.model); this.tree.refresh(this.model);
} }
public set state(val: MessagePanelState) {
this._state = val;
if (this.state.scrollPosition) {
this.tree.setScrollPosition(this.state.scrollPosition);
}
this.setExpanded(!this.state.collapsed);
}
public get state(): MessagePanelState {
return this._state;
}
} }
class MessageDataSource implements IDataSource { class MessageDataSource implements IDataSource {
@@ -176,6 +228,8 @@ class MessageRenderer implements IRenderer {
return TemplateIds.MODEL; return TemplateIds.MODEL;
} else if (element.selection) { } else if (element.selection) {
return TemplateIds.BATCH; return TemplateIds.BATCH;
} else if (element.isError) {
return TemplateIds.ERROR;
} else { } else {
return TemplateIds.MESSAGE; return TemplateIds.MESSAGE;
} }
@@ -191,15 +245,19 @@ class MessageRenderer implements IRenderer {
const timeStamp = $('div.time-stamp').appendTo(container).getHTMLElement(); const timeStamp = $('div.time-stamp').appendTo(container).getHTMLElement();
const message = $('div.batch-start').appendTo(container).getHTMLElement(); const message = $('div.batch-start').appendTo(container).getHTMLElement();
return { message, timeStamp }; return { message, timeStamp };
} else if (templateId === TemplateIds.ERROR) {
$('div.time-stamp').appendTo(container);
const message = $('div.error-message').appendTo(container).getHTMLElement();
return { message };
} else { } else {
return undefined; return undefined;
} }
} }
renderElement(tree: ITree, element: IResultMessage, templateId: string, templateData: IMessageTemplate | IBatchTemplate): void { renderElement(tree: ITree, element: IResultMessage, templateId: string, templateData: IMessageTemplate | IBatchTemplate): void {
if (templateId === TemplateIds.MESSAGE) { if (templateId === TemplateIds.MESSAGE || templateId === TemplateIds.ERROR) {
let data: IMessageTemplate = templateData; let data: IMessageTemplate = templateData;
data.message.innerText = element.message; data.message.innerText = element.message.replace(/(\r\n|\n|\r)/g, ' ');
} else if (templateId === TemplateIds.BATCH) { } else if (templateId === TemplateIds.BATCH) {
let data = templateData as IBatchTemplate; let data = templateData as IBatchTemplate;
data.timeStamp.innerText = element.time; data.timeStamp.innerText = element.time;
@@ -245,14 +303,8 @@ export class MessageController extends WorkbenchTreeController {
if (element.selection) { if (element.selection) {
let selection: ISelectionData = element.selection; let selection: ISelectionData = element.selection;
// this is a batch statement // this is a batch statement
let control = this.workbenchEditorService.activeControl.getControl() as IEditor; let input = this.workbenchEditorService.activeEditor as QueryInput;
control.setSelection({ input.updateSelection(selection);
startColumn: selection.startColumn + 1,
endColumn: selection.endColumn + 1,
endLineNumber: selection.endLine + 1,
startLineNumber: selection.startLine + 1
});
control.focus();
} }
return true; return true;

View File

@@ -43,7 +43,6 @@ import {
import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService'; import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -378,6 +377,26 @@ export class QueryEditor extends BaseEditor {
return undefined; return undefined;
} }
public getAllSelection(): ISelectionData {
if (this._sqlEditor && this._sqlEditor.getControl()) {
let control = this._sqlEditor.getControl();
let codeEditor: ICodeEditor = <ICodeEditor>control;
if (codeEditor) {
let model = codeEditor.getModel();
let totalLines = model.getLineCount();
let endColumn = model.getLineMaxColumn(totalLines);
let selection: ISelectionData = {
startLine: 0,
startColumn: 0,
endLine: totalLines - 1,
endColumn: endColumn - 1,
};
return selection;
}
}
return undefined;
}
public getSelectionText(): string { public getSelectionText(): string {
if (this._sqlEditor && this._sqlEditor.getControl()) { if (this._sqlEditor && this._sqlEditor.getControl()) {
let control = this._sqlEditor.getControl(); let control = this._sqlEditor.getControl();
@@ -500,7 +519,7 @@ export class QueryEditor extends BaseEditor {
public get listDatabasesActionItem(): ListDatabasesActionItem { public get listDatabasesActionItem(): ListDatabasesActionItem {
if (!this._listDatabasesActionItem) { if (!this._listDatabasesActionItem) {
this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction); this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction);
this._register(attachEditableDropdownStyler(this._listDatabasesActionItem, this.themeService)); this._register(this._listDatabasesActionItem.attachStyler(this.themeService));
} }
return this._listDatabasesActionItem; return this._listDatabasesActionItem;
} }

View File

@@ -93,6 +93,7 @@ export class QueryResultsEditor extends BaseEditor {
protected _input: QueryResultsInput; protected _input: QueryResultsInput;
private resultsView: QueryResultsView; private resultsView: QueryResultsView;
private styleSheet = DOM.createStyleSheet();
constructor( constructor(
@ITelemetryService telemetryService: ITelemetryService, @ITelemetryService telemetryService: ITelemetryService,
@@ -103,12 +104,13 @@ export class QueryResultsEditor extends BaseEditor {
) { ) {
super(QueryResultsEditor.ID, telemetryService, themeService); super(QueryResultsEditor.ID, telemetryService, themeService);
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel()); this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
// this._configurationService.onDidChangeConfiguration(e => { this._configurationService.onDidChangeConfiguration(e => {
// if (e.affectsConfiguration('resultsGrid')) { if (e.affectsConfiguration('resultsGrid')) {
// this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel()); this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
// this.applySettings(); this.applySettings();
// } }
// }); });
this.applySettings();
} }
public get input(): QueryResultsInput { public get input(): QueryResultsInput {
@@ -116,23 +118,20 @@ export class QueryResultsEditor extends BaseEditor {
} }
private applySettings() { private applySettings() {
if (this.input && this.input.container) { let cssRuleText = '';
if (!this.input.css) { if (types.isNumber(this._rawOptions.cellPadding)) {
this.input.css = DOM.createStyleSheet(this.input.container); cssRuleText = this._rawOptions.cellPadding + 'px';
} } else {
let cssRuleText = ''; cssRuleText = this._rawOptions.cellPadding.join('px ') + 'px;';
if (types.isNumber(this._rawOptions.cellPadding)) {
cssRuleText = this._rawOptions.cellPadding + 'px';
} else {
cssRuleText = this._rawOptions.cellPadding.join('px ') + 'px;';
}
let content = `.grid .slick-cell { padding: ${cssRuleText}; }`;
content += `.grid { ${getBareResultsGridInfoStyles(this._rawOptions)} }`;
this.input.css.innerHTML = content;
} }
let content = `.grid-panel .monaco-table .slick-cell { padding: ${cssRuleText} }`;
content += `.grid-panel .monaco-table { ${getBareResultsGridInfoStyles(this._rawOptions)} }`;
this.styleSheet.innerHTML = content;
} }
createEditor(parent: HTMLElement): void { createEditor(parent: HTMLElement): void {
this.styleSheet.remove();
parent.appendChild(this.styleSheet);
if (!this.resultsView) { if (!this.resultsView) {
this.resultsView = new QueryResultsView(parent, this._instantiationService, this._queryModelService); this.resultsView = new QueryResultsView(parent, this._instantiationService, this._queryModelService);
} }

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
'use strict'; 'use strict';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; import { QueryResultsInput, ResultsViewState } from 'sql/parts/query/common/queryResultsInput';
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel'; import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
import { IQueryModelService } from '../execution/queryModel'; import { IQueryModelService } from '../execution/queryModel';
import QueryRunner from 'sql/parts/query/execution/queryRunner'; import QueryRunner from 'sql/parts/query/execution/queryRunner';
@@ -14,11 +14,11 @@ import { ChartTab } from './charting/chartTab';
import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan'; import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import * as UUID from 'vs/base/common/uuid';
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet'; import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { once } from 'vs/base/common/event'; import { once, anyEvent } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
class ResultsView implements IPanelView { class ResultsView implements IPanelView {
private panelViewlet: PanelViewlet; private panelViewlet: PanelViewlet;
@@ -26,25 +26,72 @@ class ResultsView implements IPanelView {
private messagePanel: MessagePanel; private messagePanel: MessagePanel;
private container = document.createElement('div'); private container = document.createElement('div');
private currentDimension: DOM.Dimension; private currentDimension: DOM.Dimension;
private needsGridResize = false;
private _state: ResultsViewState;
constructor(instantiationService: IInstantiationService) { constructor(private instantiationService: IInstantiationService) {
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.gridPanel = instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results') }); this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.messagePanel = instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages') }); this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
this.gridPanel.render(); this.gridPanel.render();
this.messagePanel.render(); this.messagePanel.render();
this.panelViewlet.create(this.container).then(() => { this.panelViewlet.create(this.container).then(() => {
this.gridPanel.setVisible(false);
this.panelViewlet.addPanels([ this.panelViewlet.addPanels([
{ panel: this.gridPanel, size: 1000, index: 0 },
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 } { panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
]); ]);
}); });
let gridResizeList = this.gridPanel.onDidChange(e => { anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => {
this.panelViewlet.resizePanel(this.gridPanel, this.gridPanel.maximumSize); let size = this.gridPanel.maximumBodySize;
if (size < 1 && this.gridPanel.isVisible()) {
this.gridPanel.setVisible(false);
this.panelViewlet.removePanels([this.gridPanel]);
this.gridPanel.layout(0);
} else if (size > 0 && !this.gridPanel.isVisible()) {
this.gridPanel.setVisible(true);
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]);
}
}); });
let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => {
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
if (this.state.messagePanelSize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
}
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
})
// once the user changes the sash we should stop trying to resize the grid // once the user changes the sash we should stop trying to resize the grid
once(this.panelViewlet.onDidSashChange)(e => { once(this.panelViewlet.onDidSashChange)(e => {
gridResizeList.dispose(); this.needsGridResize = false;
resizeList.dispose();
});
this.panelViewlet.onDidSashChange(e => {
if (this.state) {
if (this.gridPanel.isExpanded()) {
this.state.gridPanelSize = this.panelViewlet.getPanelSize(this.gridPanel);
}
if (this.messagePanel.isExpanded()) {
this.state.messagePanelSize = this.panelViewlet.getPanelSize(this.messagePanel);
}
}
}); });
} }
@@ -59,6 +106,9 @@ class ResultsView implements IPanelView {
this.gridPanel.layout(dimension.height); this.gridPanel.layout(dimension.height);
} }
this.currentDimension = dimension; this.currentDimension = dimension;
if (this.needsGridResize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.gridPanelSize || Math.round(this.currentDimension.height * .7));
}
} }
remove(): void { remove(): void {
@@ -73,11 +123,21 @@ class ResultsView implements IPanelView {
public hideResultHeader() { public hideResultHeader() {
this.gridPanel.headerVisible = false; this.gridPanel.headerVisible = false;
} }
public set state(val: ResultsViewState) {
this._state = val;
this.gridPanel.state = val.gridPanelState;
this.messagePanel.state = val.messagePanelState;
}
public get state(): ResultsViewState {
return this._state;
}
} }
class ResultsTab implements IPanelTab { class ResultsTab implements IPanelTab {
public readonly title = nls.localize('resultsTabTitle', 'Results'); public readonly title = nls.localize('resultsTabTitle', 'Results');
public readonly identifier = UUID.generateUuid(); public readonly identifier = 'resultsTab';
public readonly view: ResultsView; public readonly view: ResultsView;
constructor(instantiationService: IInstantiationService) { constructor(instantiationService: IInstantiationService) {
@@ -96,6 +156,8 @@ export class QueryResultsView {
private chartTab: ChartTab; private chartTab: ChartTab;
private qpTab: QueryPlanTab; private qpTab: QueryPlanTab;
private runnerDisposables: IDisposable[];
constructor( constructor(
container: HTMLElement, container: HTMLElement,
@IInstantiationService instantiationService: IInstantiationService, @IInstantiationService instantiationService: IInstantiationService,
@@ -105,6 +167,12 @@ export class QueryResultsView {
this.chartTab = new ChartTab(instantiationService); this.chartTab = new ChartTab(instantiationService);
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false }); this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
this.qpTab = new QueryPlanTab(); this.qpTab = new QueryPlanTab();
this._panelView.pushTab(this.resultsTab);
this._panelView.onTabChange(e => {
if (this.input) {
this.input.state.activeTab = e;
}
});
} }
public style() { public style() {
@@ -112,11 +180,32 @@ export class QueryResultsView {
public set input(input: QueryResultsInput) { public set input(input: QueryResultsInput) {
this._input = input; this._input = input;
dispose(this.runnerDisposables);
this.runnerDisposables = [];
this.resultsTab.view.state = this.input.state;
this.qpTab.view.state = this.input.state.queryPlanState;
this.chartTab.view.state = this.input.state.chartState;
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner; let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
this.resultsTab.queryRunner = queryRunner; this.resultsTab.queryRunner = queryRunner;
this.chartTab.queryRunner = queryRunner; this.chartTab.queryRunner = queryRunner;
if (!this._panelView.contains(this.resultsTab)) { this.runnerDisposables.push(queryRunner.onQueryStart(e => {
this._panelView.pushTab(this.resultsTab); this.hideChart();
this.hidePlan();
this.input.state.visibleTabs = new Set();
this.input.state.activeTab = this.resultsTab.identifier;
}));
if (this.input.state.visibleTabs.has(this.chartTab.identifier)) {
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
}
if (this.input.state.visibleTabs.has(this.qpTab.identifier)) {
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}
}
if (this.input.state.activeTab) {
this._panelView.showTab(this.input.state.activeTab);
} }
} }
@@ -133,6 +222,7 @@ export class QueryResultsView {
} }
public chartData(dataId: { resultId: number, batchId: number }): void { public chartData(dataId: { resultId: number, batchId: number }): void {
this.input.state.visibleTabs.add(this.chartTab.identifier);
if (!this._panelView.contains(this.chartTab)) { if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab); this._panelView.pushTab(this.chartTab);
} }
@@ -141,7 +231,14 @@ export class QueryResultsView {
this.chartTab.chart(dataId); this.chartTab.chart(dataId);
} }
public hideChart() {
if (this._panelView.contains(this.chartTab)) {
this._panelView.removeTab(this.chartTab.identifier);
}
}
public showPlan(xml: string) { public showPlan(xml: string) {
this.input.state.visibleTabs.add(this.qpTab.identifier);
if (!this._panelView.contains(this.qpTab)) { if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab); this._panelView.pushTab(this.qpTab);
} }
@@ -149,4 +246,10 @@ export class QueryResultsView {
this._panelView.showTab(this.qpTab.identifier); this._panelView.showTab(this.qpTab.identifier);
this.qpTab.view.showPlan(xml); this.qpTab.view.showPlan(xml);
} }
public hidePlan() {
if (this._panelView.contains(this.qpTab)) {
this._panelView.removeTab(this.qpTab.identifier);
}
}
} }

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/parts/query/editor/media/queryActions';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { Builder, $ } from 'vs/base/browser/builder'; import { Builder, $ } from 'vs/base/browser/builder';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown'; import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
@@ -12,6 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/common/theme/styler';
import { ISelectionData } from 'sqlops'; import { ISelectionData } from 'sqlops';
import { import {
@@ -25,6 +27,8 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
/** /**
* Action class that query-based Actions will extend. This base class automatically handles activating and * Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -267,7 +271,11 @@ export class ActualQueryPlanAction extends QueryTaskbarAction {
} }
if (this.isConnected(editor)) { if (this.isConnected(editor)) {
editor.currentQueryInput.runQuery(editor.getSelection(), { let selection = editor.getSelection();
if (!selection) {
selection = editor.getAllSelection();
}
editor.currentQueryInput.runQuery(selection, {
displayActualQueryPlan: true displayActualQueryPlan: true
}); });
} }
@@ -431,6 +439,9 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private _isConnected: boolean; private _isConnected: boolean;
private $databaseListDropdown: Builder; private $databaseListDropdown: Builder;
private _dropdown: Dropdown; private _dropdown: Dropdown;
private _databaseSelectBox: SelectBox;
private _isInAccessibilityMode: boolean;
private readonly _selectDatabaseString: string = nls.localize("selectDatabase", "Select Database");
// CONSTRUCTOR ///////////////////////////////////////////////////////// // CONSTRUCTOR /////////////////////////////////////////////////////////
constructor( constructor(
@@ -439,23 +450,33 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IContextViewService contextViewProvider: IContextViewService, @IContextViewService contextViewProvider: IContextViewService,
@IThemeService themeService: IThemeService @IThemeService themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) { ) {
super(); super();
this._toDispose = []; this._toDispose = [];
this.$databaseListDropdown = $('.databaseListDropdown'); this.$databaseListDropdown = $('.databaseListDropdown');
let selectString = nls.localize("selectDatabase", "Select Database"); this._isInAccessibilityMode = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true, if (this._isInAccessibilityMode) {
placeholder: selectString, this._databaseSelectBox = new SelectBox([this._selectDatabaseString], this._selectDatabaseString, contextViewProvider, undefined, { ariaLabel: this._selectDatabaseString });
ariaLabel: selectString, this._databaseSelectBox.render(this.$databaseListDropdown.getHTMLElement());
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown') this._databaseSelectBox.onDidSelect(e => { this.databaseSelected(e.selected); });
}); this._databaseSelectBox.disable();
this._dropdown.onValueChange(s => this.databaseSelected(s));
} else {
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true,
placeholder: this._selectDatabaseString,
ariaLabel: this._selectDatabaseString,
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
}
// Register event handlers // Register event handlers
let self = this; let self = this;
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); })); this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); }));
} }
@@ -465,7 +486,12 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
} }
public style(styles) { public style(styles) {
this._dropdown.style(styles); if (this._isInAccessibilityMode) {
this._databaseSelectBox.style(styles);
}
else {
this._dropdown.style(styles);
}
} }
public setActionContext(context: any): void { public setActionContext(context: any): void {
@@ -477,11 +503,27 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
} }
public focus(): void { public focus(): void {
this._dropdown.focus(); if (this._isInAccessibilityMode) {
this._databaseSelectBox.focus();
} else {
this._dropdown.focus();
}
} }
public blur(): void { public blur(): void {
this._dropdown.blur(); if (this._isInAccessibilityMode) {
this._databaseSelectBox.blur();
} else {
this._dropdown.blur();
}
}
public attachStyler(themeService: IThemeService): IDisposable {
if (this._isInAccessibilityMode) {
return attachSelectBoxStyler(this, themeService);
} else {
return attachEditableDropdownStyler(this, themeService);
}
} }
public dispose(): void { public dispose(): void {
@@ -496,9 +538,15 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
public onDisconnect(): void { public onDisconnect(): void {
this._isConnected = false; this._isConnected = false;
this._dropdown.enabled = false;
this._currentDatabaseName = undefined; this._currentDatabaseName = undefined;
this._dropdown.value = '';
if (this._isInAccessibilityMode) {
this._databaseSelectBox.disable();
this._databaseSelectBox.setOptions([this._selectDatabaseString]);
} else {
this._dropdown.enabled = false;
this._dropdown.value = '';
}
} }
// PRIVATE HELPERS ///////////////////////////////////////////////////// // PRIVATE HELPERS /////////////////////////////////////////////////////
@@ -515,22 +563,22 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
this._connectionManagementService.changeDatabase(this._editor.uri, dbName) this._connectionManagementService.changeDatabase(this._editor.uri, dbName)
.then( .then(
result => { result => {
if (!result) { if (!result) {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database")
});
}
},
error => {
this.resetDatabaseName(); this.resetDatabaseName();
this._notificationService.notify({ this._notificationService.notify({
severity: Severity.Error, severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database") message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
}); });
}
},
error => {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
}); });
});
} }
private getCurrentDatabaseName() { private getCurrentDatabaseName() {
@@ -545,7 +593,11 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
} }
private resetDatabaseName() { private resetDatabaseName() {
this._dropdown.value = this.getCurrentDatabaseName(); if (this._isInAccessibilityMode) {
this._databaseSelectBox.selectWithOptionName(this.getCurrentDatabaseName());
} else {
this._dropdown.value = this.getCurrentDatabaseName();
}
} }
private onConnectionChanged(connParams: IConnectionParams): void { private onConnectionChanged(connParams: IConnectionParams): void {
@@ -579,9 +631,26 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private updateConnection(databaseName: string) { private updateConnection(databaseName: string) {
this._isConnected = true; this._isConnected = true;
this._dropdown.enabled = true;
this._currentDatabaseName = databaseName; this._currentDatabaseName = databaseName;
this._dropdown.value = databaseName;
if (this._isInAccessibilityMode) {
this._databaseSelectBox.enable();
let self = this;
let uri = self._editor.connectedUri;
if (!uri) {
return;
}
self._connectionManagementService.listDatabases(uri)
.then(result => {
if (result && result.databaseNames) {
this._databaseSelectBox.setOptions(result.databaseNames);
}
this._databaseSelectBox.selectWithOptionName(databaseName);
});
} else {
this._dropdown.enabled = true;
this._dropdown.value = databaseName;
}
} }
// TESTING PROPERTIES ////////////////////////////////////////////////// // TESTING PROPERTIES //////////////////////////////////////////////////

View File

@@ -12,7 +12,7 @@ import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import * as Utils from 'sql/parts/connection/common/utils'; import * as Utils from 'sql/parts/connection/common/utils';
import { SaveFormat } from 'sql/parts/grid/common/interfaces'; import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { echo } from 'sql/base/common/event'; import { echo, debounceEvent } from 'sql/base/common/event';
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
@@ -22,9 +22,10 @@ import * as types from 'vs/base/common/types';
import { EventEmitter } from 'sql/base/common/eventEmitter'; import { EventEmitter } from 'sql/base/common/eventEmitter';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { Emitter, debounceEvent, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer'; import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
import { TPromise } from 'vs/base/common/winjs.base';
export interface IEditSessionReadyEvent { export interface IEditSessionReadyEvent {
ownerUri: string; ownerUri: string;
@@ -73,25 +74,27 @@ export default class QueryRunner {
public get isQueryPlan(): boolean { return this._isQueryPlan; } public get isQueryPlan(): boolean { return this._isQueryPlan; }
private _onMessage = new Emitter<sqlops.IResultMessage>(); private _onMessage = new Emitter<sqlops.IResultMessage>();
private _echoedMessages = echo(debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => { private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
// on first run // on first run
if (types.isUndefinedOrNull(l)) { if (types.isUndefinedOrNull(l)) {
return [e]; return [e];
} else { } else {
return l.concat(e); return l.concat(e);
} }
})); });
private _echoedMessages = echo(this._debouncedMessage.event);
public readonly onMessage = this._echoedMessages.event; public readonly onMessage = this._echoedMessages.event;
private _onResultSet = new Emitter<sqlops.ResultSetSummary>(); private _onResultSet = new Emitter<sqlops.ResultSetSummary>();
private _echoedResultSet = echo(debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => { private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
// on first run // on first run
if (types.isUndefinedOrNull(l)) { if (types.isUndefinedOrNull(l)) {
return [e]; return [e];
} else { } else {
return l.concat(e); return l.concat(e);
} }
})); });
private _echoedResultSet = echo(this._debouncedResultSet.event);
public readonly onResultSet = this._echoedResultSet.event; public readonly onResultSet = this._echoedResultSet.event;
private _onQueryStart = new Emitter<void>(); private _onQueryStart = new Emitter<void>();
@@ -171,8 +174,13 @@ export default class QueryRunner {
private doRunQuery(input: string, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>; private doRunQuery(input: string, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
private doRunQuery(input: sqlops.ISelectionData, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>; private doRunQuery(input: sqlops.ISelectionData, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
private doRunQuery(input, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void> { private doRunQuery(input, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void> {
if (this.isExecuting) {
return TPromise.as(undefined);
}
this._echoedMessages.clear(); this._echoedMessages.clear();
this._echoedResultSet.clear(); this._echoedResultSet.clear();
this._debouncedMessage.clear();
this._debouncedResultSet.clear();
let ownerUri = this.uri; let ownerUri = this.uri;
this._batchSets = []; this._batchSets = [];
this._hasCompleted = false; this._hasCompleted = false;

View File

@@ -13,9 +13,13 @@ import { localize } from 'vs/nls';
import * as UUID from 'vs/base/common/uuid'; import * as UUID from 'vs/base/common/uuid';
import { Builder } from 'vs/base/browser/builder'; import { Builder } from 'vs/base/browser/builder';
export class QueryPlanState {
xml: string;
}
export class QueryPlanTab implements IPanelTab { export class QueryPlanTab implements IPanelTab {
public readonly title = localize('queryPlanTitle', 'Query Plan'); public readonly title = localize('queryPlanTitle', 'Query Plan');
public readonly identifier = UUID.generateUuid(); public readonly identifier = 'QueryPlanTab';
public readonly view: QueryPlanView; public readonly view: QueryPlanView;
constructor() { constructor() {
@@ -27,6 +31,7 @@ export class QueryPlanView implements IPanelView {
private qp: QueryPlan; private qp: QueryPlan;
private xml: string; private xml: string;
private container = document.createElement('div'); private container = document.createElement('div');
private _state: QueryPlanState;
public render(container: HTMLElement): void { public render(container: HTMLElement): void {
if (!this.qp) { if (!this.qp) {
@@ -36,6 +41,7 @@ export class QueryPlanView implements IPanelView {
} }
} }
container.appendChild(this.container); container.appendChild(this.container);
container.style.overflow = 'scroll';
} }
public layout(dimension: Dimension): void { public layout(dimension: Dimension): void {
@@ -47,6 +53,20 @@ export class QueryPlanView implements IPanelView {
} else { } else {
this.xml = xml; this.xml = xml;
} }
if (this.state) {
this.state.xml = xml;
}
}
public set state(val: QueryPlanState) {
this._state = val;
if (this.state.xml) {
this.showPlan(this.state.xml);
}
}
public get state(): QueryPlanState {
return this._state;
} }
} }

View File

@@ -93,7 +93,7 @@ const viewletDescriptor = new ViewletDescriptor(
VIEWLET_ID, VIEWLET_ID,
'Task History', 'Task History',
'taskHistoryViewlet', 'taskHistoryViewlet',
-90 1
); );
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);

View File

@@ -48,9 +48,7 @@ export abstract class Task {
id: this.id, id: this.id,
handler: (accessor, profile, args) => this.runTask(accessor, profile, args), handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
description: this._description, description: this._description,
iconClass: this._iconClass, iconClass: this._iconClass
iconPath: this.opts.iconPath,
title: this.title
}; };
} }
@@ -62,10 +60,8 @@ export abstract class Task {
}; };
} }
public registerTask(showInCommandPalette: boolean = true): IDisposable { public registerTask(): IDisposable {
if (showInCommandPalette) { MenuRegistry.addCommand(this.toCommandAction());
MenuRegistry.addCommand(this.toCommandAction());
}
return TaskRegistry.registerTask(this.toITask()); return TaskRegistry.registerTask(this.toITask());
} }
@@ -100,8 +96,6 @@ export interface ITask {
precondition?: ContextKeyExpr; precondition?: ContextKeyExpr;
description?: ITaskHandlerDescription; description?: ITaskHandlerDescription;
iconClass?: string; iconClass?: string;
iconPath?: { dark: string; light?: string; };
title?: string;
} }
export interface ITaskRegistry { export interface ITaskRegistry {
@@ -110,7 +104,6 @@ export interface ITaskRegistry {
getTasks(): string[]; getTasks(): string[];
getOrCreateTaskIconClassName(item: ICommandAction): string; getOrCreateTaskIconClassName(item: ICommandAction): string;
onTaskRegistered: Event<string>; onTaskRegistered: Event<string>;
getCommandActionById(id: string): ICommandAction;
} }
const ids = new IdGenerator('task-icon-'); const ids = new IdGenerator('task-icon-');
@@ -121,7 +114,6 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
private _onTaskRegistered = new Emitter<string>(); private _onTaskRegistered = new Emitter<string>();
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event; public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>(); private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
private taskIdToCommandActionMap: Map<string, ICommandAction> = new Map<string, ICommandAction>();
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable { registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
let disposable: IDisposable; let disposable: IDisposable;
@@ -133,16 +125,6 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
if (idOrTask.iconClass) { if (idOrTask.iconClass) {
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass); this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
} }
if (idOrTask.iconPath && idOrTask.title) {
this.taskIdToCommandActionMap.set(idOrTask.id, {
iconLocation: {
dark: URI.parse(idOrTask.iconPath.dark),
light: URI.parse(idOrTask.iconPath.light),
},
id: idOrTask.id,
title: idOrTask.title
});
}
disposable = CommandsRegistry.registerCommand(idOrTask); disposable = CommandsRegistry.registerCommand(idOrTask);
id = idOrTask.id; id = idOrTask.id;
} }
@@ -177,8 +159,4 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
getTasks(): string[] { getTasks(): string[] {
return this._tasks.slice(0); return this._tasks.slice(0);
} }
getCommandActionById(taskId: string): ICommandAction {
return this.taskIdToCommandActionMap.get(taskId);
}
}; };

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

@@ -185,6 +185,11 @@ declare module 'sqlops' {
* Get the parent node. Returns undefined if there is none. * Get the parent node. Returns undefined if there is none.
*/ */
getParent(): Thenable<ObjectExplorerNode>; getParent(): Thenable<ObjectExplorerNode>;
/**
* Refresh the node, expanding it if it has children
*/
refresh(): Thenable<void>;
} }
} }

View File

@@ -17,7 +17,9 @@ declare module 'sqlops' {
*/ */
export interface ModelBuilder { export interface ModelBuilder {
navContainer(): ContainerBuilder<NavContainer, any, any>; navContainer(): ContainerBuilder<NavContainer, any, any>;
divContainer(): DivBuilder;
flexContainer(): FlexBuilder; flexContainer(): FlexBuilder;
dom(): ComponentBuilder<DomComponent>
card(): ComponentBuilder<CardComponent>; card(): ComponentBuilder<CardComponent>;
inputBox(): ComponentBuilder<InputBoxComponent>; inputBox(): ComponentBuilder<InputBoxComponent>;
checkBox(): ComponentBuilder<CheckBoxComponent>; checkBox(): ComponentBuilder<CheckBoxComponent>;
@@ -72,6 +74,10 @@ declare module 'sqlops' {
} }
export interface DivBuilder extends ContainerBuilder<DivContainer, DivLayout, DivItemLayout> {
}
export interface GroupBuilder extends ContainerBuilder<GroupContainer, GroupLayout, GroupItemLayout> { export interface GroupBuilder extends ContainerBuilder<GroupContainer, GroupLayout, GroupItemLayout> {
} }
@@ -346,6 +352,33 @@ declare module 'sqlops' {
export interface GroupItemLayout { export interface GroupItemLayout {
} }
export interface DivLayout {
/**
* Container Height
*/
height?: number | string;
/**
* Container Width
*/
width?: number | string;
}
export interface DivItemLayout {
/**
* Matches the order CSS property and its available values.
*/
order?: number;
/**
* Matches the CSS style key and its available values.
*/
CSSStyles?: { [key: string]: string };
}
export interface DivContainer extends Container<DivLayout, DivItemLayout>, DivContainerProperties {
}
export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> { export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> {
} }
@@ -543,6 +576,13 @@ declare module 'sqlops' {
options?: vscode.WebviewOptions; options?: vscode.WebviewOptions;
} }
export interface DomProperties extends ComponentProperties {
/**
* Contents of the DOM component.
*/
html?: string;
}
/** /**
* Editor properties for the editor component * Editor properties for the editor component
*/ */
@@ -561,17 +601,35 @@ declare module 'sqlops' {
label?: string; label?: string;
isFile?: boolean; isFile?: boolean;
fileContent?: string; fileContent?: string;
title?: string;
} }
export interface LoadingComponentProperties { export interface LoadingComponentProperties {
loading?: boolean; loading?: boolean;
} }
export interface DivContainerProperties extends ComponentProperties {
/**
* Matches the overflow-y CSS property and its available values.
*/
overflowY?: string;
/**
* Setting the scroll based on the y offset
* This is used when its child component is webview
*/
yOffsetChange?: number;
}
export interface CardComponent extends Component, CardProperties { export interface CardComponent extends Component, CardProperties {
onDidActionClick: vscode.Event<ActionDescriptor>; onDidActionClick: vscode.Event<ActionDescriptor>;
onCardSelectedChanged: vscode.Event<any>; onCardSelectedChanged: vscode.Event<any>;
} }
export interface DomComponent extends Component, DomProperties {
}
export interface TextComponent extends Component { export interface TextComponent extends Component {
value: string; value: string;
} }
@@ -662,8 +720,22 @@ declare module 'sqlops' {
} }
export interface ButtonComponent extends Component, ButtonProperties { export interface ButtonComponent extends Component, ButtonProperties {
/**
* The label for the button
*/
label: string; label: string;
/**
* The title for the button. This title will show when it hovers
*/
title: string;
/**
* Icon Path for the button.
*/
iconPath: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri }; iconPath: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
/**
* An event called when the button is clicked
*/
onDidClick: vscode.Event<any>; onDidClick: vscode.Event<any>;
} }
@@ -1100,11 +1172,22 @@ declare module 'sqlops' {
export function createModelViewEditor(title: string, options?: ModelViewEditorOptions): ModelViewEditor; export function createModelViewEditor(title: string, options?: ModelViewEditorOptions): ModelViewEditor;
export interface ModelViewEditor extends window.modelviewdialog.ModelViewPanel { export interface ModelViewEditor extends window.modelviewdialog.ModelViewPanel {
/**
* `true` if there are unpersisted changes.
* This is editable to support extensions updating the dirty status.
*/
isDirty: boolean;
/** /**
* Opens the editor * Opens the editor
*/ */
openEditor(position?: vscode.ViewColumn): Thenable<void>; openEditor(position?: vscode.ViewColumn): Thenable<void>;
/**
* Registers a save handler for this editor. This will be called if [supportsSave](#ModelViewEditorOptions.supportsSave)
* is set to true and the editor is marked as dirty
*/
registerSaveHandler(handler: () => Thenable<boolean>);
} }
} }
@@ -1113,6 +1196,11 @@ declare module 'sqlops' {
* Should the model view editor's context be kept around even when the editor is no longer visible? It is false by default * Should the model view editor's context be kept around even when the editor is no longer visible? It is false by default
*/ */
readonly retainContextWhenHidden?: boolean; readonly retainContextWhenHidden?: boolean;
/**
* Does this model view editor support save?
*/
readonly supportsSave?: boolean;
} }
export enum DataProviderType { export enum DataProviderType {

View File

@@ -131,6 +131,7 @@ export enum FrequencyRelativeIntervals {
export enum ModelComponentTypes { export enum ModelComponentTypes {
NavContainer, NavContainer,
DivContainer,
FlexContainer, FlexContainer,
Card, Card,
InputBox, InputBox,
@@ -151,7 +152,8 @@ export enum ModelComponentTypes {
LoadingComponent, LoadingComponent,
TreeComponent, TreeComponent,
FileBrowserTree, FileBrowserTree,
Editor Editor,
Dom
} }
export interface IComponentShape { export interface IComponentShape {

View File

@@ -90,14 +90,16 @@ export class ExtHostAccountManagement extends ExtHostAccountManagementShape {
} }
public $getSecurityToken(account: sqlops.Account): Thenable<{}> { public $getSecurityToken(account: sqlops.Account): Thenable<{}> {
for (const handle in this._accounts) { return this.$getAllAccounts().then(() => {
const providerHandle = parseInt(handle); for (const handle in this._accounts) {
if (this._accounts[handle].findIndex((acct) => acct.key.accountId === account.key.accountId) !== -1) { const providerHandle = parseInt(handle);
return this._withProvider(providerHandle, (provider: sqlops.AccountProvider) => provider.getSecurityToken(account)); if (this._accounts[handle].findIndex((acct) => acct.key.accountId === account.key.accountId) !== -1) {
return this._withProvider(providerHandle, (provider: sqlops.AccountProvider) => provider.getSecurityToken(account));
}
} }
}
throw new Error(`Account ${account.key.accountId} not found.`); throw new Error(`Account ${account.key.accountId} not found.`);
});
} }
public get onDidChangeAccounts(): Event<sqlops.DidChangeAccountsParams> { public get onDidChangeAccounts(): Event<sqlops.DidChangeAccountsParams> {

View File

@@ -32,14 +32,21 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> { navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> {
let id = this.getNextComponentId(); let id = this.getNextComponentId();
let container: ContainerBuilderImpl<sqlops.NavContainer, any, any> = new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id); let container: GenericContainerBuilder<sqlops.NavContainer, any, any> = new GenericContainerBuilder(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
this._componentBuilders.set(id, container);
return container;
}
divContainer(): sqlops.DivBuilder {
let id = this.getNextComponentId();
let container = new DivContainerBuilder(this._proxy, this._handle, ModelComponentTypes.DivContainer, id);
this._componentBuilders.set(id, container); this._componentBuilders.set(id, container);
return container; return container;
} }
flexContainer(): sqlops.FlexBuilder { flexContainer(): sqlops.FlexBuilder {
let id = this.getNextComponentId(); let id = this.getNextComponentId();
let container: ContainerBuilderImpl<sqlops.FlexContainer, any, any> = new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id); let container: GenericContainerBuilder<sqlops.FlexContainer, any, any> = new GenericContainerBuilder<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
this._componentBuilders.set(id, container); this._componentBuilders.set(id, container);
return container; return container;
} }
@@ -60,7 +67,7 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
groupContainer(): sqlops.GroupBuilder { groupContainer(): sqlops.GroupBuilder {
let id = this.getNextComponentId(); let id = this.getNextComponentId();
let container: ContainerBuilderImpl<sqlops.GroupContainer, any, any> = new ContainerBuilderImpl<sqlops.GroupContainer, sqlops.GroupLayout, sqlops.GroupItemLayout>(this._proxy, this._handle, ModelComponentTypes.Group, id); let container: GenericContainerBuilder<sqlops.GroupContainer, any, any> = new GenericContainerBuilder<sqlops.GroupContainer, sqlops.GroupLayout, sqlops.GroupItemLayout>(this._proxy, this._handle, ModelComponentTypes.Group, id);
this._componentBuilders.set(id, container); this._componentBuilders.set(id, container);
return container; return container;
} }
@@ -184,6 +191,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder; return builder;
} }
dom(): sqlops.ComponentBuilder<sqlops.DomComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.DomComponent> = this.getComponentBuilder(new DomComponentWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
getComponentBuilder<T extends sqlops.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> { getComponentBuilder<T extends sqlops.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> {
let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component); let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component);
this._componentBuilders.set(id, componentBuilder); this._componentBuilders.set(id, componentBuilder);
@@ -241,17 +255,9 @@ class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.Compone
} }
} }
class GenericComponentBuilder<T extends sqlops.Component> extends ComponentBuilderImpl<T> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
}
}
class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> extends ComponentBuilderImpl<T> implements sqlops.ContainerBuilder<T, TLayout, TItemLayout> { class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> extends ComponentBuilderImpl<T> implements sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) { constructor(componentWrapper: ComponentWrapper) {
super(new ComponentWrapper(proxy, handle, type, id)); super(componentWrapper);
} }
withLayout(layout: TLayout): sqlops.ContainerBuilder<T, TLayout, TItemLayout> { withLayout(layout: TLayout): sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
@@ -268,7 +274,19 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
} }
} }
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder { class GenericContainerBuilder<T extends sqlops.Component, TLayout, TItemLayout> extends ContainerBuilderImpl<T, TLayout, TItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
}
}
class DivContainerBuilder extends ContainerBuilderImpl<sqlops.DivContainer, sqlops.DivLayout, sqlops.DivItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new DivContainerWrapper(proxy, handle, type, id));
}
}
class FormContainerBuilder extends GenericContainerBuilder<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string, private _builder: ModelBuilderImpl) { constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string, private _builder: ModelBuilderImpl) {
super(proxy, handle, type, id); super(proxy, handle, type, id);
} }
@@ -376,7 +394,7 @@ class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sq
} }
} }
class ToolbarContainerBuilder extends ContainerBuilderImpl<sqlops.ToolbarContainer, sqlops.ToolbarLayout, any> implements sqlops.ToolbarBuilder { class ToolbarContainerBuilder extends GenericContainerBuilder<sqlops.ToolbarContainer, sqlops.ToolbarLayout, any> implements sqlops.ToolbarBuilder {
withToolbarItems(components: sqlops.ToolbarComponent[]): sqlops.ContainerBuilder<sqlops.ToolbarContainer, any, any> { withToolbarItems(components: sqlops.ToolbarComponent[]): sqlops.ContainerBuilder<sqlops.ToolbarContainer, any, any> {
this._component.itemConfigs = components.map(item => { this._component.itemConfigs = components.map(item => {
return this.convertToItemConfig(item); return this.convertToItemConfig(item);
@@ -829,8 +847,8 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
public get html(): string { public get html(): string {
return this.properties['html']; return this.properties['html'];
} }
public set html(v: string) { public set html(html: string) {
this.setProperty('html', v); this.setProperty('html', html);
} }
public get onMessage(): vscode.Event<any> { public get onMessage(): vscode.Event<any> {
@@ -844,9 +862,21 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
public set options(o: vscode.WebviewOptions) { public set options(o: vscode.WebviewOptions) {
this.setProperty('options', o); this.setProperty('options', o);
} }
}
class DomComponentWrapper extends ComponentWrapper implements sqlops.DomComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.Dom, id);
this.properties = {};
}
public get html(): string {
return this.properties['html'];
}
public set html(html: string) {
this.setProperty('html', html);
}
} }
class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent { class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
@@ -1103,6 +1133,13 @@ class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
this.setProperty('iconPath', v); this.setProperty('iconPath', v);
} }
public get title(): string {
return this.properties['title'];
}
public set title(v: string) {
this.setProperty('title', v);
}
public get onDidClick(): vscode.Event<any> { public get onDidClick(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onDidClick); let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
return emitter && emitter.event; return emitter && emitter.event;
@@ -1155,6 +1192,24 @@ class FileBrowserTreeComponentWrapper extends ComponentWrapper implements sqlops
} }
} }
class DivContainerWrapper extends ComponentWrapper implements sqlops.DivContainer {
public get overflowY(): string {
return this.properties['overflowY'];
}
public set overflowY(value: string) {
this.setProperty('overflowY', value);
}
public get yOffsetChange(): number {
return this.properties['yOffsetChange'];
}
public set yOffsetChange(value: number) {
this.setProperty('yOffsetChange', value);
}
}
class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeComponent<T> { class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeComponent<T> {
constructor( constructor(

View File

@@ -75,6 +75,9 @@ class ModelViewPanelImpl implements sqlops.window.modelviewdialog.ModelViewPanel
} }
class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace.ModelViewEditor { class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace.ModelViewEditor {
private _isDirty: boolean;
private _saveHandler: () => Thenable<boolean>;
constructor( constructor(
extHostModelViewDialog: ExtHostModelViewDialog, extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape, extHostModelView: ExtHostModelViewShape,
@@ -84,10 +87,32 @@ class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace
private _options: sqlops.ModelViewEditorOptions private _options: sqlops.ModelViewEditorOptions
) { ) {
super('modelViewEditor', extHostModelViewDialog, extHostModelView, extensionLocation); super('modelViewEditor', extHostModelViewDialog, extHostModelView, extensionLocation);
this._isDirty = false;
} }
public openEditor(position?: vscode.ViewColumn): Thenable<void> { public openEditor(position?: vscode.ViewColumn): Thenable<void> {
return this._proxy.$openEditor(this._modelViewId, this._title, this._options, position); return this._proxy.$openEditor(this.handle, this._modelViewId, this._title, this._options, position);
}
public get isDirty(): boolean {
return this._isDirty;
}
public set isDirty(value: boolean) {
this._isDirty = value;
this._proxy.$setDirty(this.handle, value);
}
registerSaveHandler(handler: () => Thenable<boolean>) {
this._saveHandler = handler;
}
public handleSave(): Thenable<boolean> {
if (this._saveHandler) {
return Promise.resolve(this._saveHandler());
} else {
return Promise.resolve(true);
}
} }
} }
@@ -470,6 +495,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
return dialog.validateClose(); return dialog.validateClose();
} }
public $handleSave(handle: number): Thenable<boolean> {
let editor = this._objectsByHandle.get(handle) as ModelViewEditorImpl;
return editor.handleSave();
}
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void { public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
let handle = this.getHandle(dialog); let handle = this.getHandle(dialog);
this.updateDialogContent(dialog); this.updateDialogContent(dialog);

View File

@@ -9,7 +9,7 @@ import { ExtHostObjectExplorerShape, SqlMainContext, MainThreadObjectExplorerSha
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape { export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
private _proxy: MainThreadObjectExplorerShape; private _proxy: MainThreadObjectExplorerShape;
@@ -44,7 +44,7 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
public errorMessage: string; public errorMessage: string;
constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) { constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value); this.getDetailsFromInfo(nodeInfo);
this.connectionId = connectionId; this.connectionId = connectionId;
} }
@@ -71,4 +71,12 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
} }
return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined); return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined);
} }
refresh(): Thenable<void> {
return this._proxy.$refresh(this.connectionId, this.nodePath).then(nodeInfo => this.getDetailsFromInfo(nodeInfo));
}
private getDetailsFromInfo(nodeInfo: sqlops.NodeInfo): void {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
}
} }

View File

@@ -6,6 +6,7 @@
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditor } from 'vs/workbench/common/editor';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -14,7 +15,7 @@ import { MainThreadModelViewDialogShape, SqlMainContext, ExtHostModelViewDialogS
import { Dialog, DialogTab, DialogButton, WizardPage, Wizard } from 'sql/platform/dialog/dialogTypes'; import { Dialog, DialogTab, DialogButton, WizardPage, Wizard } from 'sql/platform/dialog/dialogTypes';
import { CustomDialogService } from 'sql/platform/dialog/customDialogService'; import { CustomDialogService } from 'sql/platform/dialog/customDialogService';
import { IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails, IModelViewWizardPageDetails, IModelViewWizardDetails } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails, IModelViewWizardPageDetails, IModelViewWizardDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ModelViewInput } from 'sql/parts/modelComponents/modelEditor/modelViewInput'; import { ModelViewInput, ModelViewInputModel, ModeViewSaveHandler } from 'sql/parts/modelComponents/modelEditor/modelViewInput';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
@@ -28,6 +29,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
private readonly _wizardPages = new Map<number, WizardPage>(); private readonly _wizardPages = new Map<number, WizardPage>();
private readonly _wizardPageHandles = new Map<WizardPage, number>(); private readonly _wizardPageHandles = new Map<WizardPage, number>();
private readonly _wizards = new Map<number, Wizard>(); private readonly _wizards = new Map<number, Wizard>();
private readonly _editorInputModels = new Map<number, ModelViewInputModel>();
private _dialogService: CustomDialogService; private _dialogService: CustomDialogService;
constructor( constructor(
@@ -43,15 +45,18 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
public $openEditor(modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void> { public $openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void> {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
let input = this._instatiationService.createInstance(ModelViewInput, title, modelViewId, options); let saveHandler: ModeViewSaveHandler = options && options.supportsSave ? (h) => this.handleSave(h) : undefined;
let model = new ModelViewInputModel(modelViewId, handle, saveHandler);
let input = this._instatiationService.createInstance(ModelViewInput, title, model, options);
let editorOptions = { let editorOptions = {
preserveFocus: true, preserveFocus: true,
pinned: true pinned: true
}; };
this._editorService.openEditor(input, editorOptions, position as any).then(() => { this._editorService.openEditor(input, editorOptions, position as any).then((editor) => {
this._editorInputModels.set(handle, model);
resolve(); resolve();
}, error => { }, error => {
reject(error); reject(error);
@@ -59,6 +64,10 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
}); });
} }
private handleSave(handle: number): Thenable<boolean> {
return this._proxy.$handleSave(handle);
}
public $openDialog(handle: number): Thenable<void> { public $openDialog(handle: number): Thenable<void> {
let dialog = this.getDialog(handle); let dialog = this.getDialog(handle);
this._dialogService.showDialog(dialog); this._dialogService.showDialog(dialog);
@@ -213,6 +222,21 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
return Promise.resolve(); return Promise.resolve();
} }
$setDirty(handle: number, isDirty: boolean): void {
let model = this.getEditor(handle);
if (model) {
model.setDirty(isDirty);
}
}
private getEditor(handle: number): ModelViewInputModel {
let model = this._editorInputModels.get(handle);
if (!model) {
throw new Error('No editor matching the given handle');
}
return model;
}
private getDialog(handle: number): Dialog { private getDialog(handle: number): Dialog {
let dialog = this._dialogs.get(handle); let dialog = this._dialogs.get(handle);
if (!dialog) { if (!dialog) {

View File

@@ -15,6 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode';
@extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer) @extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer)
export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape { export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
@@ -50,7 +51,7 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
public $getActiveConnectionNodes(): Thenable<NodeInfoWithConnection[]> { public $getActiveConnectionNodes(): Thenable<NodeInfoWithConnection[]> {
let connectionNodes = this._objectExplorerService.getActiveConnectionNodes(); let connectionNodes = this._objectExplorerService.getActiveConnectionNodes();
return Promise.resolve(connectionNodes.map(node => { return Promise.resolve(connectionNodes.map(node => {
return {connectionId: node.connection.id, nodeInfo: node.toNodeInfo()}; return { connectionId: node.connection.id, nodeInfo: node.toNodeInfo() };
})); }));
} }
@@ -73,4 +74,8 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
public $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]> { public $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]> {
return this._objectExplorerService.findNodes(connectionId, type, schema, name, database, parentObjectNames); return this._objectExplorerService.findNodes(connectionId, type, schema, name, database, parentObjectNames);
} }
public $refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo> {
return this._objectExplorerService.refreshNodeInView(connectionId, nodePath).then(node => node.toNodeInfo());
}
} }

View File

@@ -36,7 +36,7 @@ export abstract class ExtHostAccountManagementShape {
export abstract class ExtHostConnectionManagementShape { export abstract class ExtHostConnectionManagementShape {
$onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; } $onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; }
} }
export abstract class ExtHostDataProtocolShape { export abstract class ExtHostDataProtocolShape {
@@ -663,6 +663,7 @@ export interface MainThreadObjectExplorerShape extends IDisposable {
$getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]>; $getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]>;
$isExpanded(connectionId: string, nodePath: string): Thenable<boolean>; $isExpanded(connectionId: string, nodePath: string): Thenable<boolean>;
$findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>; $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>;
$refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo>;
} }
export interface ExtHostModelViewDialogShape { export interface ExtHostModelViewDialogShape {
@@ -672,10 +673,11 @@ export interface ExtHostModelViewDialogShape {
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void; $updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
$validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>; $validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>;
$validateDialogClose(handle: number): Thenable<boolean>; $validateDialogClose(handle: number): Thenable<boolean>;
$handleSave(handle: number): Thenable<boolean>;
} }
export interface MainThreadModelViewDialogShape extends IDisposable { export interface MainThreadModelViewDialogShape extends IDisposable {
$openEditor(modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>; $openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>;
$openDialog(handle: number): Thenable<void>; $openDialog(handle: number): Thenable<void>;
$closeDialog(handle: number): Thenable<void>; $closeDialog(handle: number): Thenable<void>;
$setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>; $setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>;
@@ -688,6 +690,7 @@ export interface MainThreadModelViewDialogShape extends IDisposable {
$addWizardPage(wizardHandle: number, pageHandle: number, pageIndex: number): Thenable<void>; $addWizardPage(wizardHandle: number, pageHandle: number, pageIndex: number): Thenable<void>;
$removeWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>; $removeWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>;
$setWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>; $setWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>;
$setDirty(handle: number, isDirty: boolean): void;
} }
export interface ExtHostQueryEditorShape { export interface ExtHostQueryEditorShape {
} }

View File

@@ -16,8 +16,8 @@ import { ShowCurrentReleaseNotesAction } from 'sql/workbench/update/releaseNotes
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
new Actions.BackupAction().registerTask(false); new Actions.BackupAction().registerTask();
new Actions.RestoreAction().registerTask(false); new Actions.RestoreAction().registerTask();
new Actions.NewQueryAction().registerTask(); new Actions.NewQueryAction().registerTask();
new Actions.ConfigureDashboardAction().registerTask(); new Actions.ConfigureDashboardAction().registerTask();

View File

@@ -302,6 +302,12 @@ export class BackupAction extends Task {
} }
runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> { runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> {
if (!profile) {
let objectExplorerService = accessor.get<IObjectExplorerService>(IObjectExplorerService);
let connectionManagementService = accessor.get<IConnectionManagementService>(IConnectionManagementService);
let workbenchEditorService = accessor.get<IEditorService>(IEditorService);
profile = TaskUtilities.getCurrentGlobalConnection(objectExplorerService, connectionManagementService, workbenchEditorService);
}
let configurationService = accessor.get<IWorkspaceConfigurationService>(IWorkspaceConfigurationService); let configurationService = accessor.get<IWorkspaceConfigurationService>(IWorkspaceConfigurationService);
let previewFeaturesEnabled: boolean = configurationService.getValue('workbench')['enablePreviewFeatures']; let previewFeaturesEnabled: boolean = configurationService.getValue('workbench')['enablePreviewFeatures'];
if (!previewFeaturesEnabled) { if (!previewFeaturesEnabled) {
@@ -435,7 +441,7 @@ export class NewDatabaseAction extends Action {
export class ConfigureDashboardAction extends Task { export class ConfigureDashboardAction extends Task {
public static readonly ID = 'configureDashboard'; public static readonly ID = 'configureDashboard';
public static readonly LABEL = nls.localize('configureDashboard', 'Configure'); public static readonly LABEL = nls.localize('configureDashboard', 'Learn How To Configure The Dashboard');
public static readonly ICON = 'configure-dashboard'; public static readonly ICON = 'configure-dashboard';
private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig'; private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig';

View File

@@ -108,13 +108,13 @@ export class ErrorMessageDialog extends Modal {
private updateIconTitle(): void { private updateIconTitle(): void {
switch (this._severity) { switch (this._severity) {
case Severity.Error: case Severity.Error:
this.titleIconClassName = 'icon error'; this.titleIconClassName = 'sql icon error';
break; break;
case Severity.Warning: case Severity.Warning:
this.titleIconClassName = 'icon warning'; this.titleIconClassName = 'sql icon warning';
break; break;
case Severity.Info: case Severity.Info:
this.titleIconClassName = 'icon info'; this.titleIconClassName = 'sql icon info';
break; break;
} }
} }

View File

@@ -301,7 +301,8 @@ suite('SQL Object Explorer Service tests', () => {
reveal: element => Promise.resolve() as Thenable<void>, reveal: element => Promise.resolve() as Thenable<void>,
setSelected: (element, selected, clearOtherSelections) => undefined, setSelected: (element, selected, clearOtherSelections) => undefined,
isExpanded: element => undefined, isExpanded: element => undefined,
onSelectionOrFocusChange: Event.None onSelectionOrFocusChange: Event.None,
refreshElement: (element) => Promise.resolve() as Thenable<void>
} as ServerTreeView); } as ServerTreeView);
}); });
@@ -738,4 +739,22 @@ suite('SQL Object Explorer Service tests', () => {
}, err => done(err)); }, err => done(err));
}); });
}); });
test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => {
// Set up the session and tree view
await objectExplorerService.createNewSession('MSSQL', connection);
objectExplorerService.onSessionCreated(1, objectExplorerSession);
serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
// Refresh the node
let nodePath = objectExplorerSession.rootNode.nodePath;
let refreshedNode = await objectExplorerService.refreshNodeInView(connection.id, nodePath);
// Verify that it was refreshed, expanded, and the refreshed detailed were returned
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(refreshNode => refreshNode.nodePath === nodePath)), TypeMoq.Times.once());
refreshedNode.children.forEach((childNode, index) => {
assert.equal(childNode.nodePath, objectExplorerExpandInfoRefresh.nodes[index].nodePath);
});
});
}); });

View File

@@ -28,6 +28,7 @@ import { ConnectionManagementService } from 'sql/parts/connection/common/connect
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { TestThemeService } from 'sqltest/stubs/themeTestService'; import { TestThemeService } from 'sqltest/stubs/themeTestService';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import * as TypeMoq from 'typemoq'; import * as TypeMoq from 'typemoq';
import * as assert from 'assert'; import * as assert from 'assert';
@@ -40,6 +41,7 @@ suite('SQL QueryAction Tests', () => {
let editor: TypeMoq.Mock<QueryEditor>; let editor: TypeMoq.Mock<QueryEditor>;
let calledRunQueryOnInput: boolean = undefined; let calledRunQueryOnInput: boolean = undefined;
let testQueryInput: TypeMoq.Mock<QueryInput>; let testQueryInput: TypeMoq.Mock<QueryInput>;
let configurationService: TypeMoq.Mock<ConfigurationService>;
setup(() => { setup(() => {
// Setup a reusable mock QueryInput // Setup a reusable mock QueryInput
@@ -56,6 +58,13 @@ suite('SQL QueryAction Tests', () => {
editor.setup(x => x.getSelection()).returns(() => undefined); editor.setup(x => x.getSelection()).returns(() => undefined);
editor.setup(x => x.getSelection(false)).returns(() => undefined); editor.setup(x => x.getSelection(false)).returns(() => undefined);
editor.setup(x => x.isSelectionEmpty()).returns(() => false); editor.setup(x => x.isSelectionEmpty()).returns(() => false);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return {};
});
}); });
test('setClass sets child CSS class correctly', (done) => { test('setClass sets child CSS class correctly', (done) => {
@@ -463,7 +472,7 @@ suite('SQL QueryAction Tests', () => {
}); });
// If I query without having initialized anything, state should be clear // If I query without having initialized anything, state should be clear
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined); listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected'); assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected');
assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected'); assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected');
@@ -498,7 +507,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected // ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected(); listItem.onConnected();
// If: I raise a connection changed event // If: I raise a connection changed event
@@ -522,7 +531,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected // ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected(); listItem.onConnected();
// If: I raise a connection changed event for the 'wrong' URI // If: I raise a connection changed event for the 'wrong' URI
@@ -549,7 +558,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
// ... Create a database dropdown // ... Create a database dropdown
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
// If: I raise a connection changed event // If: I raise a connection changed event
let eventParams = <IConnectionParams>{ let eventParams = <IConnectionParams>{

View File

@@ -93,7 +93,7 @@ suite('SQL QueryEditor Tests', () => {
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => { instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => {
if (classDef.ID) { if (classDef.ID) {
if (classDef.ID === 'listDatabaseQueryActionItem') { if (classDef.ID === 'listDatabaseQueryActionItem') {
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined); return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
} }
} }
// Default // Default
@@ -110,18 +110,26 @@ suite('SQL QueryEditor Tests', () => {
editorDescriptorService = TypeMoq.Mock.ofType(EditorDescriptorService, TypeMoq.MockBehavior.Loose); editorDescriptorService = TypeMoq.Mock.ofType(EditorDescriptorService, TypeMoq.MockBehavior.Loose);
editorDescriptorService.setup(x => x.getEditor(TypeMoq.It.isAny())).returns(() => descriptor); editorDescriptorService.setup(x => x.getEditor(TypeMoq.It.isAny())).returns(() => descriptor);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return { enablePreviewFeatures: true };
});
// Create a QueryInput // Create a QueryInput
let filePath = 'someFile.sql'; let filePath = 'someFile.sql';
let uri: URI = URI.parse(filePath); let uri: URI = URI.parse(filePath);
let fileInput = new UntitledEditorInput(uri, false, '', '', '', instantiationService.object, undefined, undefined, undefined); let fileInput = new UntitledEditorInput(uri, false, '', '', '', instantiationService.object, undefined, undefined, undefined);
let queryResultsInput: QueryResultsInput = new QueryResultsInput(uri.fsPath); let queryResultsInput: QueryResultsInput = new QueryResultsInput(uri.fsPath, configurationService.object);
queryInput = new QueryInput('first', fileInput, queryResultsInput, undefined, undefined, undefined, undefined, undefined); queryInput = new QueryInput('first', fileInput, queryResultsInput, undefined, undefined, undefined, undefined, undefined);
// Create a QueryInput to compare to the previous one // Create a QueryInput to compare to the previous one
let filePath2 = 'someFile2.sql'; let filePath2 = 'someFile2.sql';
let uri2: URI = URI.parse(filePath2); let uri2: URI = URI.parse(filePath2);
let fileInput2 = new UntitledEditorInput(uri2, false, '', '', '', instantiationService.object, undefined, undefined, undefined); let fileInput2 = new UntitledEditorInput(uri2, false, '', '', '', instantiationService.object, undefined, undefined, undefined);
let queryResultsInput2: QueryResultsInput = new QueryResultsInput(uri2.fsPath); let queryResultsInput2: QueryResultsInput = new QueryResultsInput(uri2.fsPath, configurationService.object);
queryInput2 = new QueryInput('second', fileInput2, queryResultsInput2, undefined, undefined, undefined, undefined, undefined); queryInput2 = new QueryInput('second', fileInput2, queryResultsInput2, undefined, undefined, undefined, undefined, undefined);
// Mock IMessageService // Mock IMessageService
@@ -136,14 +144,6 @@ suite('SQL QueryEditor Tests', () => {
// Create a QueryModelService // Create a QueryModelService
queryModelService = new QueryModelService(instantiationService.object, notificationService.object); queryModelService = new QueryModelService(instantiationService.object, notificationService.object);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return { enablePreviewFeatures: true };
});
}); });
test('createEditor creates only the taskbar', (done) => { test('createEditor creates only the taskbar', (done) => {
@@ -344,7 +344,7 @@ suite('SQL QueryEditor Tests', () => {
queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((definition, editor, action, selectBox) => { .returns((definition, editor, action, selectBox) => {
if (definition.ID === 'listDatabaseQueryActionItem') { if (definition.ID === 'listDatabaseQueryActionItem') {
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined); let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined,configurationService.object);
return item; return item;
} }
// Default // Default
@@ -393,7 +393,7 @@ suite('SQL QueryEditor Tests', () => {
done(); done();
}); });
test('Test that we attempt to dispose query when the queryInput is disposed', (done) => { test('Test that we attempt to dispose query when the queryInput is disposed', (done) => {
let queryResultsInput = new QueryResultsInput('testUri'); let queryResultsInput = new QueryResultsInput('testUri', configurationService.object);
queryInput['_results'] = queryResultsInput; queryInput['_results'] = queryResultsInput;
queryInput.dispose(); queryInput.dispose();
queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once());

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