Compare commits

...

74 Commits

Author SHA1 Message Date
Karl Burtram
9717b7516d Bump Agent, Import and Profiler extensions versions (#2330) 2018-08-24 19:16:11 -04:00
AlexFsmn
b92c23df2b Added functionality for adding any file to import wizard (#2329)
#2316
2018-08-24 19:01:22 -04:00
Aditya Bist
df617f19e0 Feature: Usage metrics (#2227)
* added user stats telemetry

* finished daily, weekly and monthly events

* code review comments

* added wekly and monthly start dates

* fixed the day condition using testing
2018-08-24 12:11:22 -07:00
Alan Ren
fb565c2548 insights widget accessibility support (#2324) 2018-08-24 10:22:58 -07:00
Kevin Cunnane
2bfc3a6c85 Support position property in editor component (#2314)
* Support position property in editor component
- This needs to be set directly on the editor component so cannot just use CSSStyles feature
- Given its importance to this scenario, it also warrants a dedicated property.

* Fix window resize when action bar clicked

* Renamed per Abbie's suggestion

* Changed name to clarify the comments
2018-08-23 17:32:13 -07:00
Alan Ren
4ab5d84b94 hide the backup restore commands from command palette (#2317) 2018-08-23 17:08:54 -07:00
Karl Burtram
154213b705 Bump Electron to 1.7.16 (#2312) 2018-08-23 19:55:40 -04:00
Anthony Dresser
4cce29ea9d change sqlops to be dev dep (#2311) 2018-08-23 14:15:08 -07:00
Alan Yu
9d5d00aa8f Updated Profiler readme with getting started instructions 2018-08-23 13:09:13 -07:00
Karl Burtram
70d47c1757 Prompt to create Profiler session per server (#2303)
* Null check getXEventSessions return value

* Profiler state WIP

* Fix various bugs getting session state
2018-08-23 15:33:23 -04:00
Anthony Dresser
befa34790f Refactor results grid (#2147)
* got a basic ui

* working on message panel

* done with messages moving to grids

* formatting

* working on multiple grids

* it does work

* styling

* formatting

* formatting

* fixed reset methods

* moved for scrollable

* formatting

* fixing scrolling

* making progress

* formatting

* fixed scrolling

* fix horizontal scrolling and size

* fix columns for tables

* integrate view item

* implementing heightmap scrolling

* add context menu and fix scrolling

* formatting

* revert slickgrid

* add actions to message pane

* formatting

* formatting

* bottom padding for tables

* minimized and maximized table actions

* add timestamp

* added batch start message  with selection

* updating

* formatting

* formatting

* fix execution time

* formatting

* fix problems

* fix rendering issues, add icons

* formatting

* formatting

* added commit change

* fix performance, message scrolling, etc

* formatting

* formatting

* fixing performance

* formatting

* update package

* tring to fix bugs

* reworking

* the problem is the 1st sash is always the first sash visible

* remove resizing from grid panels

* add missing files

* trying to get edit to work

* fix editdata

* formatting

* update angular2-slickgrid
2018-08-23 12:32:47 -07:00
Kevin Cunnane
84da9d289b Support vertical orientation for toolbar (#2308)
* Support vertical orientation for toolbar modelview component

* Add tab support for each button

* Fix padding and simplify styles
2018-08-23 10:54:20 -07:00
Matt Irvine
7a30d535e8 Make it possible to tab to result export buttons (#2302) 2018-08-23 10:27:48 -07:00
Kevin Cunnane
199701d26b Temporarily roll back SqlToolsService update to fix #2304 (#2305)
- Credential Service and Azure / Resource Provider Services are all broken in 1.5.0-alpha.29 so rolling back until fixed
2018-08-22 20:03:23 -07:00
Alan Ren
3ac2cfa528 fix the active cell's bottom border disappearing issue (#2301) 2018-08-22 16:56:32 -07:00
Karl Burtram
78d68aa1b9 Fix typo in SQL Tools Service config.json 2018-08-22 13:57:52 -07:00
Cory Rivera
8042b78f1e Check whether page is valid before enabling next button. (#2293) 2018-08-22 12:12:33 -07:00
Matt Irvine
000d064276 Show no account message after deleting all azure accounts (#2294) 2018-08-22 11:57:29 -07:00
Karl Burtram
600a78f35f Update SQL Tools Service to 1.5.0-alpha.29 2018-08-21 22:12:04 -07:00
Alan Ren
6f5e4c30dc fix edit data issue (#2285) 2018-08-21 18:29:21 -07:00
Karl Burtram
8e7457911e Bump DMP client to pick-up build connection string LIB (#2284) 2018-08-21 20:17:16 -04:00
Aditya Bist
43b3207937 changed desktop icon flag (#2281) 2018-08-21 16:50:30 -07:00
Abbie Petchtes
22f2151c21 fix the layout issue in model view (#2280) 2018-08-21 16:16:15 -07:00
Matt Irvine
7c744f2307 Put newline between multiple selections when copying results (#2279) 2018-08-21 14:04:46 -07:00
Karl Burtram
08d57fed86 Check-in current XLF files (#2275)
* Check-in current XLF files

* Remove duplicate IDs from SQL.XLF file

* Add comment why line is being disabled
2018-08-21 16:57:22 -04:00
AlexFsmn
6e8a0fe0ef Fixed background issue when copying a chart to clipboard (#2215)
#183
2018-08-20 19:35:49 -04:00
Anthony Dresser
6668ec4b5d Fix accessibility bugs in Chart Viewer and Advanced properties (#2240)
* fix accessibility bugs in chart viewer

* add advanced properties

* add comments for changes in vs
2018-08-20 16:19:29 -07:00
Alan Ren
08a8288293 fix the server group disapearing issue #1835 (#2269)
* fix the server group disapearing issue #1835

* address review comments
2018-08-20 15:43:16 -04:00
Matt Irvine
3001640eec Fix getUriForConnection API returning wrong URIs (#2202)
* Fix connection URI api to return working URI

* run tsfmt

* Keep using hand-built connection string for now in import

* Use connection ID instead of URI to get connection string
2018-08-20 15:41:06 -04:00
Aditya Bist
efa3658ced added preview messages (#2208) 2018-08-20 15:01:29 -04:00
AlexFsmn
92bc253cf7 Fixed problem where vertical charts didn't display labels correctly. (#2263)
#2017
2018-08-20 15:01:00 -04:00
AlexFsmn
a190190843 Fixed Initial values for charts to match visuals (#2266)
#2265
2018-08-20 14:59:43 -04:00
Kevin Cunnane
9c40bd1a23 Fix error where rename controller wasn't in InstantiationService (#2243) 2018-08-20 11:58:40 -07:00
Abbie Petchtes
dc2193138d fix the layout issue in model view dialog (#2242) 2018-08-20 11:53:47 -07:00
Anthony Dresser
033c8cb8b1 Adds build connection info feature (#2192)
* connection string

* formatting

* change serailize reponse type to match connect params

* add connection string serialization

* readd the connection string to the connection widget

* format

* remove unnecessary change

* update serializer to require provider

* update name of function

* fix function name

* bump dataprotocol and sqltools

* revert unnecessary change

* remove more unnecessary chagnes

* bump sqltoolsserivce

* adde configuration for auto parsing the clipboard
2018-08-20 14:50:16 -04:00
Aditya Bist
21c4429c6e Agent: Added support for high contrast theme (#2229)
* added support for high contrast theme

* initial theming

* programmatically change theme colors in agent
2018-08-20 10:08:00 -07:00
AlexFsmn
348a96b033 Renamed chart option labels (#2264)
#2262
2018-08-19 21:12:16 -04:00
AlexFsmn
0c930d7c0f Added feature for opening file after exporting to CSV/XLS/JSON & query files (#2216)
* Fix #746.
Added prompt for opening saved file location/file after save.
This fix includes saving of JSON/CSV/Excel & saving of a new SQL file.

* Changed var to let.
Moved code from vs dir to sql.
Removed support for showing file location after file save.
(Will be moved to another PR).
#746
2018-08-17 20:28:00 -04:00
Karl Burtram
98aca2b988 Bump SQL Ops to 0.32.6 (#2256) 2018-08-17 20:24:39 -04:00
Karl Burtram
8d7f497e0c Switch back DB icon but use default colors (#2254) 2018-08-17 18:49:03 -04:00
Leila Lali
1b6328b451 reverting a change in declarative table (#2246) 2018-08-17 14:50:41 -07:00
Karl Burtram
d10e08e63e Update SQL Ops Studio to 0.32.4 2018-08-16 17:35:54 -07:00
Anthony Dresser
a8f21b56f0 Fix context menus in grids (#2245)
* fix context menus

* fix edit data context menu
2018-08-16 20:34:52 -04:00
Karl Burtram
173842510c Bump yarn.lock package reference (#2248) 2018-08-16 20:33:19 -04:00
Alan Ren
19c08fe0eb Alanren/fixfor1782 (#2247)
* fix for issue #1782, refresh the cached data

* pick up the latest version of slickgrid and tools service
2018-08-16 16:54:07 -07:00
Amir Ali Omidi
db817a7192 Required fields and labelling of buttons (#2237)
* Required fields and labelling of buttons

* Update the readme.

* Change localized string
2018-08-16 11:16:06 -07:00
Amir Ali Omidi
2c8e93cc96 Support right clicking the database to start the importer task (#2233) 2018-08-15 14:11:54 -07:00
Alan Ren
44e9a97f09 fix for issue: Results Grid Row Indicator Zero Based #2152 (#2232) 2018-08-14 17:38:00 -07:00
Abbie Petchtes
298ddc4195 fix layout and add css styling for flex item (#2231) 2018-08-14 15:33:03 -07:00
Alan Ren
d9134d6085 Fix for: https://github.com/Microsoft/sqlopsstudio/issues/1317 (#2228)
use culture invariant display value for number conversion
2018-08-14 13:57:22 -07:00
Amir Ali Omidi
b17b4ce880 Entry Point updates (#2222)
* Entry point changes

* Navigator validation changes.

* Minor modifications to how the validator is called and setup
2018-08-14 13:18:07 -07:00
Leila Lali
2304c32453 fixed the issue caused by my latest check in in connection service (#2220) 2018-08-13 11:56:32 -07:00
Abbie Petchtes
2b68e4a7df fix issue where tree item doesn't expand when collapsible state is expanded (#2212) 2018-08-13 09:59:26 -07:00
Abbie Petchtes
8f06e72318 add aria label to the title of dialog (#2210) 2018-08-10 15:34:59 -07:00
Aditya Bist
5fa740ead4 added fix for disabling dropdown (#2203) 2018-08-10 11:07:54 -07:00
Leila Lali
e5096e61e5 Feature/ext connection dialog (#2201)
* Connection Dialog API for extensions
2018-08-10 09:29:46 -07:00
Amir Ali Omidi
2a3195636e Feat/import/language used (#2204)
Updated language used by flat file import
2018-08-09 15:44:58 -07:00
Amir Ali Omidi
5fb9b8ccd3 Clean importer startup (#2197)
* Cleans up issues from: https://github.com/Microsoft/sqlopsstudio/issues/2183
2018-08-09 14:34:17 -07:00
Amir Ali Omidi
7089e2299a Switches distribution of OSX binaries: https://github.com/Microsoft/sqlopsstudio/issues/2188 (#2198) 2018-08-09 14:00:23 -07:00
Anthony Dresser
b553cbb68c update build to use yarn to fix errors (#2191) 2018-08-08 15:09:12 -07:00
Amir Ali Omidi
c712411e77 Fixes the build issues. (#2190) 2018-08-08 14:21:21 -07:00
Leila Lali
48d5cc554c fixed the bug with collapsibleState (#2189) 2018-08-08 12:27:55 -07:00
Amir Ali Omidi
39bfd69dc9 Import Wizard (#2130)
Flat File Importer
2018-08-07 17:27:07 -07:00
Karl Burtram
d690b80493 Avoid null ref when workspace folder uri is undefined (#2179) 2018-08-07 19:48:26 -04:00
Karl Burtram
eb48a9f993 Fix Profiler missing title property error message (#2176) 2018-08-07 19:06:45 -04:00
Leila Lali
5a54abaf44 fixed the issue with tree component layout (#2174) 2018-08-07 14:10:57 -07:00
Karl Burtram
47c161f9f1 Bump SQL Ops to 0.32.4 and bump extensions (#2169) 2018-08-07 14:51:10 -04:00
Karl Burtram
807f8e68f3 Fix drag-n-drop null ref in OE (#2163) 2018-08-07 13:57:59 -04:00
Karl Burtram
fba8536c33 Update active grid tracking to fix copy bug (#2162) 2018-08-07 13:57:24 -04:00
Aditya Bist
36fc1bb71a added option for desktop icon (#2155) 2018-08-07 08:41:46 -07:00
Cory Rivera
ccaa96c81e Add >= and <= operators for context key expressions. (#2160) 2018-08-06 18:24:18 -07:00
Anthony Dresser
ac2f279c88 Fix edit data bugs (#2157)
* fix edit data bugs

* formatting
2018-08-06 20:42:52 -04:00
Aditya Bist
91cb99b8c8 changed linux packaging comment (#2158) 2018-08-06 17:25:21 -07:00
Aditya Bist
02b1a525e3 made the tree single click mode (#2159) 2018-08-06 17:17:05 -07:00
176 changed files with 11707 additions and 2490 deletions

View File

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

View File

@@ -78,6 +78,7 @@ const sqlBuiltInExtensions = [
// Add SQL built-in extensions here.
// the extension will be excluded from SQLOps package and will have separate vsix packages
'agent',
'import',
'profiler'
];
@@ -273,7 +274,8 @@ function packageBuiltInExtensions() {
console.info('Creating vsix for ' + element.path + ' result:' + packagePath);
vsce.createVSIX({
cwd: element.path,
packagePath: packagePath
packagePath: packagePath,
useYarn: true
});
});
}
@@ -412,7 +414,7 @@ function packageTask(platform, arch, opts) {
license,
watermark,
api,
// {{SQL CARBON EDIT}}
// {{SQL CARBON EDIT}}
copiedModules,
dataApi,
sources,
@@ -526,7 +528,9 @@ gulp.task('vscode-translations-push-test', ['optimize-vscode'], function () {
gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
// {{SQL CARBON EDIT}}
// disable since function makes calls to VS Code Transifex API
// ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
).pipe(vfs.dest('../vscode-transifex-input'));
});

View File

@@ -60,6 +60,7 @@ Type: files; Name: "{app}\resources\app\Credits_45.0.2454.85.html"; Check: IsNot
Type: filesandordirs; Name: "{app}\_"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce;
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"

View File

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

View File

@@ -367,11 +367,18 @@ export class AlertDialog extends AgentDialog<AlertData> {
title: AlertDialog.NewJobButtonLabel
}], { componentWidth: '100%'}).component();
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.notifyOperatorsCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.NotifyOperatorsTextBoxLabel
}).component();
this.notifyOperatorsCheckBox.enabled = false;
this.operatorsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -423,6 +430,9 @@ export class AlertDialog extends AgentDialog<AlertData> {
}, {
component: executeJobContainer,
title: ''
}, {
component: previewTag,
title: ''
}, {
component: this.notifyOperatorsCheckBox,
title: ''

View File

@@ -125,7 +125,6 @@ export class JobDialog extends AgentDialog<JobData> {
this.initializeSchedulesTab();
this.initializeNotificationsTab();
this.dialog.content = [this.generalTab, this.stepsTab, this.schedulesTab, this.alertsTab, this.notificationsTab];
this.dialog.registerCloseValidator(() => {
this.updateModel();
let validationResult = this.model.validate();
@@ -194,6 +193,10 @@ export class JobDialog extends AgentDialog<JobData> {
private initializeStepsTab() {
this.stepsTab.registerContent(async view => {
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.stepsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -246,8 +249,14 @@ export class JobDialog extends AgentDialog<JobData> {
width: 80
}).component();
this.stepsTable.enabled = false;
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: previewTag,
title: ''
},
{
component: this.stepsTable,
title: this.JobStepsTopLabelString,
actions: [this.moveStepUpButton, this.moveStepDownButton, this.newStepButton, this.editStepButton, this.deleteStepButton]
@@ -258,6 +267,10 @@ export class JobDialog extends AgentDialog<JobData> {
private initializeAlertsTab() {
this.alertsTab.registerContent(async view => {
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -282,6 +295,9 @@ export class JobDialog extends AgentDialog<JobData> {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: previewTag,
title: ''
}, {
component: this.alertsTable,
title: this.AlertsTopLabelString,
actions: [this.newAlertButton]

View File

@@ -368,6 +368,10 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
private initializeNotificationTab() {
this.notificationsTab.registerContent(async view => {
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -381,6 +385,9 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: previewTag,
title: ''
}, {
component: this.alertsTable,
title: OperatorDialog.AlertsTableLabel
}]).withLayout({ width: '100%' }).component();

1
extensions/import/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
flatfileimportservice/

View File

@@ -0,0 +1,2 @@
client/src/**
client/tsconfig.json

View File

@@ -0,0 +1,25 @@
# Microsoft SQL Server Import for SQL Operations Studio
Import Flat File Wizard is a simple way to copy data from a flat file (.csv, .txt) to a destination. This overview describes the reasons for using this wizard, how to find this wizard, and a simple example to follow.
![image](https://user-images.githubusercontent.com/30873802/43433347-c958ed28-942b-11e8-8bbc-f4f2529c3978.png)
## Why would I use this wizard?
This wizard was created to improve the current import experience leveraging an intelligent framework known as Program Synthesis using Examples ([PROSE](https://microsoft.github.io/prose/)). For a user without specialized domain knowledge, importing data can often be a complex, error prone, and tedious task. This wizard streamlines the import process as simple as selecting an input file and unique table name, and the PROSE framework handles the rest.
PROSE analyzes data patterns in your input file to infer column names, types, delimiters, and more. This framework learns the structure of the file and does all of the hard work so users don't have to.
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Privacy Statement
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt).

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#231f20;}.cls-3{fill:#0095d7;}</style></defs><title>importflatfile_inverse</title><path class="cls-1" d="M13.34,1.57c-2.81,0-7.52.53-7.65,2.49v3.2L7,8.54V5.66a17.11,17.11,0,0,0,6.37,1,17.1,17.1,0,0,0,6.38-1V17.58c-.17.46-2.55,1.35-6.38,1.35a19.63,19.63,0,0,1-3.43-.27V20a23.78,23.78,0,0,0,3.43.25c2.86,0,7.66-.57,7.66-2.64V4.06C20.87,2.1,16.16,1.57,13.34,1.57Zm6.38,2.55c-.2.45-2.56,1.28-6.38,1.28S7.24,4.6,7,4.14c.27-.47,2.6-1.29,6.37-1.29s6.16.85,6.38,1.25h0Z"/><polygon class="cls-2" points="18.55 3.06 18.53 3.07 18.53 3.04 18.55 3.06"/><path class="cls-1" d="M7,10,5.69,8.68,5,8H0V19.85H8.91v-8ZM5.2,9.24l.49.49L7,11l.67.67H5.2Zm3,9.86H.74V8.71H4.46v3.71H8.17Z"/><path class="cls-3" d="M16.5,15a.27.27,0,0,1-.08.2L14.2,17.4a.26.26,0,0,1-.19.08.28.28,0,0,1-.2-.08.26.26,0,0,1-.08-.2.82.82,0,0,1,0-.14l.06-.25.08-.32.08-.32.07-.27,0-.17H4.5v-1.5h9.59l0-.17L14,13.79l-.08-.32-.08-.31-.06-.25a.91.91,0,0,1,0-.14.26.26,0,0,1,.08-.2.28.28,0,0,1,.2-.08.26.26,0,0,1,.19.08l2.22,2.22A.26.26,0,0,1,16.5,15Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21"><defs><style>.cls-1{fill:#212121;}.cls-2{fill:#231f20;}.cls-3{fill:#00539c;}</style></defs><title>importflatfile</title><path class="cls-1" d="M13.34,1.3c-2.81,0-7.52.53-7.65,2.49V7L7,8.27V5.39a17.11,17.11,0,0,0,6.37,1,17.1,17.1,0,0,0,6.38-1V17.31c-.17.46-2.55,1.35-6.38,1.35a19.63,19.63,0,0,1-3.43-.27V19.7a23.78,23.78,0,0,0,3.43.25C16.2,20,21,19.38,21,17.31V3.79C20.87,1.83,16.16,1.3,13.34,1.3Zm6.38,2.55c-.2.45-2.56,1.28-6.38,1.28S7.24,4.33,7,3.87c.27-.47,2.6-1.29,6.37-1.29s6.16.85,6.38,1.25h0Z"/><polygon class="cls-2" points="18.55 2.79 18.53 2.81 18.53 2.78 18.55 2.79"/><path class="cls-1" d="M7,9.69,5.69,8.41,5,7.7H0V19.58H8.91v-8ZM5.2,9l.49.49L7,10.74l.67.67H5.2Zm3,9.86H.74V8.44H4.46v3.71H8.17Z"/><path class="cls-3" d="M16.5,14.72a.27.27,0,0,1-.08.2L14.2,17.14a.26.26,0,0,1-.19.08.28.28,0,0,1-.2-.08.26.26,0,0,1-.08-.2.82.82,0,0,1,0-.14l.06-.25.08-.32.08-.32.07-.27,0-.17H4.5V14h9.59l0-.17L14,13.53l-.08-.32-.08-.31-.06-.25a.91.91,0,0,1,0-.14.26.26,0,0,1,.08-.2.28.28,0,0,1,.2-.08.26.26,0,0,1,.19.08l2.22,2.22A.26.26,0,0,1,16.5,14.72Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,91 @@
{
"name": "import",
"displayName": "SQL Server Import",
"description": "Imports data from a flat file.",
"version": "0.0.2",
"publisher": "Microsoft",
"preview": true,
"engines": {
"vscode": "^1.25.0",
"sqlops": "*"
},
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
"icon": "images/sqlserver.png",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"activationEvents": [
"*"
],
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/sqlopsstudio.git"
},
"extensionDependencies": [
"Microsoft.mssql"
],
"contributes": {
"commands": [
{
"command": "flatFileImport.start",
"title": "Import wizard",
"category": "Flat File Import",
"icon": {
"light": "./images/light_icon.svg",
"dark": "./images/dark_icon.svg"
}
}
],
"keybindings": [
{
"command": "flatFileImport.start",
"key": "ctrl+i",
"mac": "ctrl+i"
}
],
"dashboard.tabs": [
{
"id": "flat-file-import",
"title": "Flat File Import",
"description": "The flat file importer.",
"container": {
"flat-file-import-container": {}
}
}
],
"dashboard.containers": [
{
"id": "flat-file-import-container",
"container": {
"widgets-container": [
{
"name": "Tasks",
"widget": {
"tasks-widget": [
"flatFileImport.start"
]
}
}
]
}
}
],
"menus": {
"objectExplorer/item/context": [
{
"command": "flatFileImport.start",
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
"group": "import"
}
]
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.5",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
}
}

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export const extensionConfigSectionName = 'flatFileImport';
export const serviceName = 'Flat File Import Service';
export const providerId = 'FlatFileImport';
export const configLogDebugInfo = 'logDebugInfo';
export const sqlConfigSectionName = 'sql';
export const serviceCrashLink = 'https://github.com/Microsoft/sqlopsstudio/issues/2090';

View File

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

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as constants from '../constants';
import * as sqlops from 'sqlops';
import ControllerBase from './controllerBase';
import * as vscode from 'vscode';
import { FlatFileWizard } from '../wizard/flatFileWizard';
import { ServiceClient } from '../services/serviceClient';
import { ApiType, managerInstance } from '../services/serviceApiManager';
import { FlatFileProvider } from '../services/contracts';
/**
* The main controller class that initializes the extension
*/
export default class MainController extends ControllerBase {
public constructor(context: vscode.ExtensionContext) {
super(context);
}
/**
*/
public deactivate(): void {
}
public activate(): Promise<boolean> {
const outputChannel = vscode.window.createOutputChannel(constants.serviceName);
new ServiceClient(outputChannel).startService(this._context);
managerInstance.onRegisteredApi<FlatFileProvider>(ApiType.FlatFileProvider)(provider => {
this.initializeFlatFileProvider(provider);
});
return Promise.resolve(true);
}
private initializeFlatFileProvider(provider: FlatFileProvider) {
sqlops.tasks.registerTask('flatFileImport.start', (profile: sqlops.IConnectionProfile, ...args: any[]) => new FlatFileWizard(provider).start(profile, args));
}
}

View File

@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import ControllerBase from './controllers/controllerBase';
import MainController from './controllers/mainController';
let controllers: ControllerBase[] = [];
export function activate(context: vscode.ExtensionContext) {
let activations: Promise<boolean>[] = [];
// Start the main controller
let mainController = new MainController(context);
controllers.push(mainController);
context.subscriptions.push(mainController);
activations.push(mainController.activate());
return Promise.all(activations)
.then((results: boolean[]) => {
for (let result of results) {
if (!result) {
return false;
}
}
return true;
});
}
export function deactivate() {
for (let controller of controllers) {
controller.deactivate();
}
}

View File

@@ -0,0 +1,16 @@
{
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/extensions/import/{#fileName#}",
"useDefaultLinuxRuntime": true,
"version": "0.0.1",
"downloadFileNames": {
"Windows_64": "win-x64.zip",
"Windows_86": "win-x86.zip",
"OSX": "osx.tar.gz",
"Linux_64": "linux-x64.tar.gz"
},
"installDirectory": "flatfileimportservice/{#platform#}/{#version#}",
"executableFiles": [
"MicrosoftSqlToolsFlatFileImport",
"MicrosoftSqlToolsFlatFileImport.exe"
]
}

View File

@@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RequestType, NotificationType } from 'vscode-languageclient';
/**
* @interface IMessage
*/
export interface IMessage {
jsonrpc: string;
}
// ------------------------------- < Telemetry Sent Event > ------------------------------------
/**
* Event sent when the language service send a telemetry event
*/
export namespace TelemetryNotification {
export const type = new NotificationType<TelemetryParams, void>('telemetry/sqlevent');
}
/**
* Update event parameters
*/
export class TelemetryParams {
public params: {
eventName: string;
properties: ITelemetryEventProperties;
measures: ITelemetryEventMeasures;
};
}
export interface ITelemetryEventProperties {
[key: string]: string;
}
export interface ITelemetryEventMeasures {
[key: string]: number;
}
/**
* Contract Classes
*/
export interface Result {
success: boolean;
errorMessage: string;
}
export interface ColumnInfo {
name: string;
sqlType: string;
isNullable: boolean;
}
/**
* PROSEDiscoveryRequest
* Send this request to create a new PROSE session with a new file and preview it
*/
const proseDiscoveryRequestName = 'flatfile/proseDiscovery';
export interface PROSEDiscoveryParams {
filePath: string;
tableName: string;
schemaName?: string;
fileType?: string;
}
export interface PROSEDiscoveryResponse {
dataPreview: string[][];
columnInfo: ColumnInfo[];
}
/**
* InsertDataRequest
*/
const insertDataRequestName = 'flatfile/insertData';
export interface InsertDataParams {
connectionString: string;
batchSize: number;
}
export interface InsertDataResponse {
result: Result;
}
/**
* GetColumnInfoRequest
*/
const getColumnInfoRequestName = 'flatfile/getColumnInfo';
export interface GetColumnInfoParams {
}
export interface GetColumnInfoResponse {
columnInfo: ColumnInfo[];
}
/**
* ChangeColumnSettingsRequest
*/
const changeColumnSettingsRequestName = 'flatfile/changeColumnSettings';
export interface ChangeColumnSettingsParams {
index: number;
newName?: string;
newDataType?: string;
newNullable?: boolean;
newInPrimaryKey?: boolean;
}
export interface ChangeColumnSettingsResponse {
result: Result;
}
/**
* Requests
*/
export namespace PROSEDiscoveryRequest {
export const type = new RequestType<PROSEDiscoveryParams, PROSEDiscoveryResponse, void, void>(proseDiscoveryRequestName);
}
export namespace InsertDataRequest {
export const type = new RequestType<InsertDataParams, InsertDataResponse, void, void>(insertDataRequestName);
}
export namespace GetColumnInfoRequest {
export const type = new RequestType<GetColumnInfoParams, GetColumnInfoResponse, void, void>(getColumnInfoRequestName);
}
export namespace ChangeColumnSettingsRequest {
export const type = new RequestType<ChangeColumnSettingsParams, ChangeColumnSettingsResponse, void, void>(changeColumnSettingsRequestName);
}
export interface FlatFileProvider {
providerId?: string;
sendPROSEDiscoveryRequest(params: PROSEDiscoveryParams): Thenable<PROSEDiscoveryResponse>;
sendInsertDataRequest(params: InsertDataParams): Thenable<InsertDataResponse>;
sendGetColumnInfoRequest(params: GetColumnInfoParams): Thenable<GetColumnInfoResponse>;
sendChangeColumnSettingsRequest(params: ChangeColumnSettingsParams): Thenable<ChangeColumnSettingsResponse>;
}

View File

@@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client';
import {
ClientCapabilities,
StaticFeature,
RPCMessageType,
ServerCapabilities
} from 'vscode-languageclient';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import { Disposable } from 'vscode';
import { Telemetry } from './telemetry';
import * as serviceUtils from './serviceUtils';
import * as Contracts from './contracts';
import { managerInstance, ApiType } from './serviceApiManager';
export class TelemetryFeature implements StaticFeature {
constructor(private _client: SqlOpsDataClient) {
}
fillClientCapabilities(capabilities: ClientCapabilities): void {
serviceUtils.ensure(capabilities, 'telemetry')!.telemetry = true;
}
initialize(): void {
this._client.onNotification(Contracts.TelemetryNotification.type, e => {
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
});
}
}
export class FlatFileImportFeature extends SqlOpsFeature<undefined> {
private static readonly messagesTypes: RPCMessageType[] = [
Contracts.PROSEDiscoveryRequest.type
];
constructor(client: SqlOpsDataClient) {
super(client, FlatFileImportFeature.messagesTypes);
}
public fillClientCapabilities(capabilities: ClientCapabilities): void {
}
public initialize(capabilities: ServerCapabilities): void {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected registerProvider(options: undefined): Disposable {
const client = this._client;
let requestSender = (requestType, params) => {
return client.sendRequest(requestType, params).then(
r => {
return r as any;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.reject(e);
}
);
};
let sendPROSEDiscoveryRequest = (params: Contracts.PROSEDiscoveryParams): Thenable<Contracts.PROSEDiscoveryResponse> => {
return requestSender(Contracts.PROSEDiscoveryRequest.type, params);
};
let sendInsertDataRequest = (params: Contracts.InsertDataParams): Thenable<Contracts.InsertDataResponse> => {
return requestSender(Contracts.InsertDataRequest.type, params);
};
let sendGetColumnInfoRequest = (params: Contracts.GetColumnInfoParams): Thenable<Contracts.GetColumnInfoResponse> => {
return requestSender(Contracts.GetColumnInfoRequest.type, params);
};
let sendChangeColumnSettingsRequest = (params: Contracts.ChangeColumnSettingsParams): Thenable<Contracts.ChangeColumnSettingsResponse> => {
return requestSender(Contracts.ChangeColumnSettingsRequest.type, params);
};
return managerInstance.registerApi<Contracts.FlatFileProvider>(ApiType.FlatFileProvider, {
providerId: client.providerId,
sendPROSEDiscoveryRequest,
sendChangeColumnSettingsRequest,
sendGetColumnInfoRequest,
sendInsertDataRequest
});
}
}

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import * as contracts from './contracts';
import { SqlOpsDataClient } from 'dataprotocol-client/lib/main';
export enum ApiType {
FlatFileProvider = 'FlatFileProvider'
}
export interface IServiceApi {
onRegisteredApi<T>(type: ApiType): vscode.Event<T>;
registerApi<T>(type: ApiType, feature: T): vscode.Disposable;
}
export interface IModelViewDefinition {
id: string;
modelView: sqlops.ModelView;
}
export class ServiceApiManager implements IServiceApi {
private modelViewRegistrations: { [id: string]: boolean } = {};
private featureEventChannels: { [type: string]: vscode.EventEmitter<any> } = {};
private _onRegisteredModelView = new vscode.EventEmitter<IModelViewDefinition>();
public onRegisteredApi<T>(type: ApiType): vscode.Event<T> {
let featureEmitter = this.featureEventChannels[type];
if (!featureEmitter) {
featureEmitter = new vscode.EventEmitter<T>();
this.featureEventChannels[type] = featureEmitter;
}
return featureEmitter.event;
}
public registerApi<T>(type: ApiType, feature: T): vscode.Disposable {
let featureEmitter = this.featureEventChannels[type];
if (featureEmitter) {
featureEmitter.fire(feature);
}
// TODO handle unregistering API on close
return {
dispose: () => undefined
};
}
public get onRegisteredModelView(): vscode.Event<IModelViewDefinition> {
return this._onRegisteredModelView.event;
}
public registerModelView(id: string, modelView: sqlops.ModelView): void {
this._onRegisteredModelView.fire({
id: id,
modelView: modelView
});
}
}
export let managerInstance = new ServiceApiManager();

View File

@@ -0,0 +1,165 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
import { IConfig, ServerProvider, Events } from 'service-downloader';
import { ServerOptions, TransportKind } from 'vscode-languageclient';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import * as path from 'path';
import { EventAndListener } from 'eventemitter2';
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
import * as Constants from '../constants';
import { TelemetryFeature, FlatFileImportFeature } from './features';
import * as serviceUtils from './serviceUtils';
const baseConfig = require('./config.json');
export class ServiceClient {
private statusView: vscode.StatusBarItem;
constructor(private outputChannel: vscode.OutputChannel) {
this.statusView = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
}
public startService(context: vscode.ExtensionContext): Promise<SqlOpsDataClient> {
let config: IConfig = JSON.parse(JSON.stringify(baseConfig));
config.installDirectory = path.join(context.extensionPath, config.installDirectory);
config.proxy = vscode.workspace.getConfiguration('http').get('proxy');
config.strictSSL = vscode.workspace.getConfiguration('http').get('proxyStrictSSL') || true;
const serverdownloader = new ServerProvider(config);
serverdownloader.eventEmitter.onAny(this.generateHandleServerProviderEvent());
let clientOptions: ClientOptions = this.createClientOptions();
const installationStart = Date.now();
let client: SqlOpsDataClient;
return new Promise((resolve, reject) => {
serverdownloader.getOrDownloadServer().then(e => {
const installationComplete = Date.now();
let serverOptions = this.generateServerOptions(e);
client = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
const processStart = Date.now();
client.onReady().then(() => {
const processEnd = Date.now();
this.statusView.text = localize('serviceStarted', 'Service Started');
setTimeout(() => {
this.statusView.hide();
}, 1500);
Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', {
installationTime: String(installationComplete - installationStart),
processStartupTime: String(processEnd - processStart),
totalTime: String(processEnd - installationStart),
beginningTimestamp: String(installationStart)
});
});
this.statusView.show();
this.statusView.text = localize('serviceStarting', 'Starting service');
let disposable = client.start();
context.subscriptions.push(disposable);
resolve(client);
}, e => {
Telemetry.sendTelemetryEvent('ServiceInitializingFailed');
vscode.window.showErrorMessage(localize('flatFileImport.serviceStartFailed', 'Failed to start Import service{0}', e));
// Just resolve to avoid unhandled promise. We show the error to the user.
resolve(undefined);
});
});
}
private createClientOptions(): ClientOptions {
return {
providerId: Constants.providerId,
errorHandler: new LanguageClientErrorHandler(),
synchronize: {
configurationSection: [Constants.extensionConfigSectionName, Constants.sqlConfigSectionName]
},
features: [
// we only want to add new features
TelemetryFeature,
FlatFileImportFeature
],
outputChannel: new CustomOutputChannel()
};
}
private generateServerOptions(executablePath: string): ServerOptions {
let launchArgs = [];
launchArgs.push('--log-dir');
let logFileLocation = path.join(serviceUtils.getDefaultLogLocation(), 'flatfileimport');
launchArgs.push(logFileLocation);
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
if (config) {
let logDebugInfo = config[Constants.configLogDebugInfo];
if (logDebugInfo) {
launchArgs.push('--enable-logging');
}
}
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
}
private generateHandleServerProviderEvent(): EventAndListener {
let dots = 0;
return (e: string, ...args: any[]) => {
this.outputChannel.show();
this.statusView.show();
switch (e) {
case Events.INSTALL_START:
this.outputChannel.appendLine(localize('installingServiceDetailed', 'Installing {0} service to {1}', Constants.serviceName, args[0]));
this.statusView.text = localize('installingService', 'Installing Service');
break;
case Events.INSTALL_END:
this.outputChannel.appendLine(localize('serviceInstalled', 'Installed'));
break;
case Events.DOWNLOAD_START:
this.outputChannel.appendLine(localize('downloadingService', 'Downloading {0}', args[0]));
this.outputChannel.append(`(${Math.ceil(args[1] / 1024)} KB)`);
this.statusView.text = localize('downloadingServiceStatus', 'Downloading Service');
break;
case Events.DOWNLOAD_PROGRESS:
let newDots = Math.ceil(args[0] / 5);
if (newDots > dots) {
this.outputChannel.append('.'.repeat(newDots - dots));
dots = newDots;
}
break;
case Events.DOWNLOAD_END:
this.outputChannel.appendLine(localize('downloadingServiceComplete', 'Done!'));
break;
default:
break;
}
};
}
}
class CustomOutputChannel implements vscode.OutputChannel {
name: string;
append(value: string): void {
}
appendLine(value: string): void {
}
// tslint:disable-next-line:no-empty
clear(): void {
}
show(preserveFocus?: boolean): void;
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
// tslint:disable-next-line:no-empty
show(column?: any, preserveFocus?: any): void {
}
// tslint:disable-next-line:no-empty
hide(): void {
}
// tslint:disable-next-line:no-empty
dispose(): void {
}
}

View File

@@ -0,0 +1,156 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as crypto from 'crypto';
import * as os from 'os';
const baseConfig = require('./config.json');
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
// work for now because the extension is running in different process.
export function getAppDataPath(): string {
let platform = process.platform;
switch (platform) {
case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming');
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
default: throw new Error('Platform not supported');
}
}
export function getDefaultLogLocation(): string {
return path.join(getAppDataPath(), 'sqlops');
}
export function ensure(target: object, key: string): any {
if (target[key] === void 0) {
target[key] = {} as any;
}
return target[key];
}
export interface IPackageInfo {
name: string;
version: string;
aiKey: string;
}
export function getPackageInfo(packageJson: any): IPackageInfo {
if (packageJson) {
return {
name: packageJson.name,
version: packageJson.version,
aiKey: packageJson.aiKey
};
}
}
export function generateUserId(): Promise<string> {
return new Promise<string>(resolve => {
try {
let interfaces = os.networkInterfaces();
let mac;
for (let key of Object.keys(interfaces)) {
let item = interfaces[key][0];
if (!item.internal) {
mac = item.mac;
break;
}
}
if (mac) {
resolve(crypto.createHash('sha256').update(mac + os.homedir(), 'utf8').digest('hex'));
} else {
resolve(generateGuid());
}
} catch (err) {
resolve(generateGuid()); // fallback
}
});
}
export function generateGuid(): string {
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
let oct: string = '';
let tmp: number;
/* tslint:disable:no-bitwise */
for (let a: number = 0; a < 4; a++) {
tmp = (4294967296 * Math.random()) | 0;
oct += hexValues[tmp & 0xF] +
hexValues[tmp >> 4 & 0xF] +
hexValues[tmp >> 8 & 0xF] +
hexValues[tmp >> 12 & 0xF] +
hexValues[tmp >> 16 & 0xF] +
hexValues[tmp >> 20 & 0xF] +
hexValues[tmp >> 24 & 0xF] +
hexValues[tmp >> 28 & 0xF];
}
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
/* tslint:enable:no-bitwise */
}
export function verifyPlatform(): Thenable<boolean> {
if (os.platform() === 'darwin' && parseFloat(os.release()) < 16.0) {
return Promise.resolve(false);
} else {
return Promise.resolve(true);
}
}
export function getServiceInstallConfig(basePath?: string): any {
if (!basePath) {
basePath = __dirname;
}
let config = JSON.parse(JSON.stringify(baseConfig));
config.installDirectory = path.join(basePath, config.installDirectory);
return config;
}
export function getResolvedServiceInstallationPath(runtime: Runtime, basePath?: string): string {
let config = getServiceInstallConfig(basePath);
let dir = config.installDirectory;
dir = dir.replace('{#version#}', config.version);
dir = dir.replace('{#platform#}', getRuntimeDisplayName(runtime));
return dir;
}
export function getRuntimeDisplayName(runtime: Runtime): string {
switch (runtime) {
case Runtime.Windows_64:
return 'Windows';
case Runtime.Windows_86:
return 'Windows';
case Runtime.OSX:
return 'OSX';
case Runtime.Linux_64:
return 'Linux';
default:
return 'Unknown';
}
}
export enum Runtime {
Unknown = <any>'Unknown',
Windows_86 = <any>'Windows_86',
Windows_64 = <any>'Windows_64',
OSX = <any>'OSX',
CentOS_7 = <any>'CentOS_7',
Debian_8 = <any>'Debian_8',
Fedora_23 = <any>'Fedora_23',
OpenSUSE_13_2 = <any>'OpenSUSE_13_2',
SLES_12_2 = <any>'SLES_12_2',
RHEL_7 = <any>'RHEL_7',
Ubuntu_14 = <any>'Ubuntu_14',
Ubuntu_16 = <any>'Ubuntu_16',
Linux_64 = <any>'Linux_64',
Linux_86 = <any>'Linux-86'
}

View File

@@ -0,0 +1,216 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ErrorAction, CloseAction } from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { PlatformInformation } from 'service-downloader/out/platform';
import * as opener from 'opener';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import * as constants from '../constants';
import * as serviceUtils from './serviceUtils';
import { IMessage, ITelemetryEventProperties, ITelemetryEventMeasures } from './contracts';
/**
* Handle Language Service client errors
* @class LanguageClientErrorHandler
*/
export class LanguageClientErrorHandler {
/**
* Creates an instance of LanguageClientErrorHandler.
* @memberOf LanguageClientErrorHandler
*/
constructor() {
}
/**
* Show an error message prompt with a link to known issues wiki page
* @memberOf LanguageClientErrorHandler
*/
showOnErrorPrompt(): void {
// TODO add telemetry
// Telemetry.sendTelemetryEvent('SqlToolsServiceCrash');
let crashButtonText = localize('import.serviceCrashButton', 'Give Feedback');
vscode.window.showErrorMessage(
localize('serviceCrashMessage', 'service component could not start'),
crashButtonText
).then(action => {
if (action && action === crashButtonText) {
opener(constants.serviceCrashLink);
}
});
}
/**
* Callback for language service client error
*
* @param {Error} error
* @param {Message} message
* @param {number} count
* @returns {ErrorAction}
*
* @memberOf LanguageClientErrorHandler
*/
error(error: Error, message: IMessage, count: number): ErrorAction {
this.showOnErrorPrompt();
// we don't retry running the service since crashes leave the extension
// in a bad, unrecovered state
return ErrorAction.Shutdown;
}
/**
* Callback for language service client closed
*
* @returns {CloseAction}
*
* @memberOf LanguageClientErrorHandler
*/
closed(): CloseAction {
this.showOnErrorPrompt();
// we don't retry running the service since crashes leave the extension
// in a bad, unrecovered state
return CloseAction.DoNotRestart;
}
}
/**
* Filters error paths to only include source files. Exported to support testing
*/
export function FilterErrorPath(line: string): string {
if (line) {
let values: string[] = line.split('/out/');
if (values.length <= 1) {
// Didn't match expected format
return line;
} else {
return values[1];
}
}
}
export class Telemetry {
private static reporter: TelemetryReporter;
private static userId: string;
private static platformInformation: PlatformInformation;
private static disabled: boolean;
// Get the unique ID for the current user of the extension
public static getUserId(): Promise<string> {
return new Promise<string>(resolve => {
// Generate the user id if it has not been created already
if (typeof this.userId === 'undefined') {
let id = serviceUtils.generateUserId();
id.then(newId => {
this.userId = newId;
resolve(this.userId);
});
} else {
resolve(this.userId);
}
});
}
public static getPlatformInformation(): Promise<PlatformInformation> {
if (this.platformInformation) {
return Promise.resolve(this.platformInformation);
} else {
return new Promise<PlatformInformation>(resolve => {
PlatformInformation.getCurrent().then(info => {
this.platformInformation = info;
resolve(this.platformInformation);
});
});
}
}
/**
* Disable telemetry reporting
*/
public static disable(): void {
this.disabled = true;
}
/**
* Initialize the telemetry reporter for use.
*/
public static initialize(): void {
if (typeof this.reporter === 'undefined') {
// Check if the user has opted out of telemetry
if (!vscode.workspace.getConfiguration('telemetry').get<boolean>('enableTelemetry', true)) {
this.disable();
return;
}
let packageInfo = vscode.extensions.getExtension('Microsoft.import').packageJSON;
this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
}
}
/**
* Send a telemetry event for an exception
*/
public static sendTelemetryEventForException(
err: any, methodName: string, extensionConfigName: string): void {
try {
let stackArray: string[];
let firstLine: string = '';
if (err !== undefined && err.stack !== undefined) {
stackArray = err.stack.split('\n');
if (stackArray !== undefined && stackArray.length >= 2) {
firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event
firstLine = FilterErrorPath(firstLine);
}
}
// Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII
this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine });
// Utils.logDebug('Unhandled Exception occurred. error: ' + err + ' method: ' + methodName, extensionConfigName);
} catch (telemetryErr) {
// If sending telemetry event fails ignore it so it won't break the extension
// Utils.logDebug('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName);
}
}
/**
* Send a telemetry event using application insights
*/
public static sendTelemetryEvent(
eventName: string,
properties?: ITelemetryEventProperties,
measures?: ITelemetryEventMeasures): void {
if (typeof this.disabled === 'undefined') {
this.disabled = false;
}
if (this.disabled || typeof (this.reporter) === 'undefined') {
// Don't do anything if telemetry is disabled
return;
}
if (!properties || typeof properties === 'undefined') {
properties = {};
}
// Augment the properties structure with additional common properties before sending
Promise.all([this.getUserId(), this.getPlatformInformation()]).then(() => {
properties['userId'] = this.userId;
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';
this.reporter.sendTelemetryEvent(eventName, properties, measures);
});
}
}
Telemetry.initialize();

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.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
/// <reference types='@types/node'/>

View File

@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ImportDataModel } from './models';
import * as sqlops from 'sqlops';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
export abstract class ImportPage {
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
protected readonly instance: FlatFileWizard;
protected readonly model: ImportDataModel;
protected readonly view: sqlops.ModelView;
protected readonly provider: FlatFileProvider;
protected constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
this.instance = instance;
this.wizardPage = wizardPage;
this.model = model;
this.view = view;
this.provider = provider;
}
/**
* This method constructs all the elements of the page.
* @returns {Promise<boolean>}
*/
public async abstract start(): Promise<boolean>;
/**
* This method is called when the user is entering the page.
* @returns {Promise<boolean>}
*/
public async abstract onPageEnter(): Promise<boolean>;
/**
* This method is called when the user is leaving the page.
* @returns {Promise<boolean>}
*/
public async abstract onPageLeave(): Promise<boolean>;
/**
* Sets up a navigation validator.
* This will be called right before onPageEnter().
*/
public abstract setupNavigationValidator();
/**
* Override this method to cleanup what you don't need cached in the page.
* @returns {Promise<boolean>}
*/
public async cleanup(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
/**
* The main data model that communicates between the pages.
*/
export interface ImportDataModel {
ownerUri: string;
proseColumns: ColumnMetadata[];
proseDataPreview: string[][];
server: sqlops.connection.Connection;
serverId: string;
database: string;
table: string;
schema: string;
filePath: string;
fileType: string;
}
/**
* Metadata of a column
*/
export interface ColumnMetadata {
columnName: string;
dataType: string;
primaryKey: boolean;
nullable: boolean;
}

View File

@@ -0,0 +1,144 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import { FlatFileProvider } from '../services/contracts';
import { ImportDataModel } from './api/models';
import { ImportPage } from './api/importPage';
// pages
import { FileConfigPage } from './pages/fileConfigPage';
import { ProsePreviewPage } from './pages/prosePreviewPage';
import { ModifyColumnsPage } from './pages/modifyColumnsPage';
import { SummaryPage } from './pages/summaryPage';
const localize = nls.loadMessageBundle();
export class FlatFileWizard {
private readonly provider: FlatFileProvider;
private wizard: sqlops.window.modelviewdialog.Wizard;
private importAnotherFileButton: sqlops.window.modelviewdialog.Button;
constructor(provider: FlatFileProvider) {
this.provider = provider;
}
public async start(p: any, ...args: any[]) {
let model = <ImportDataModel>{};
let profile = <sqlops.IConnectionProfile>p.connectionProfile;
if (profile) {
model.serverId = profile.id;
model.database = profile.databaseName;
}
let pages: Map<number, ImportPage> = new Map<number, ImportPage>();
let connections = await sqlops.connection.getActiveConnections();
if (!connections || connections.length === 0) {
vscode.window.showErrorMessage(localize('import.needConnection', 'Please connect to a server before using this wizard.'));
return;
}
this.wizard = sqlops.window.modelviewdialog.createWizard(localize('flatFileImport.wizardName', 'Import flat file wizard'));
let page1 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page1Name', 'Specify Input File'));
let page2 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page2Name', 'Preview Data'));
let page3 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page3Name', 'Modify Columns'));
let page4 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page4Name', 'Summary'));
let fileConfigPage: FileConfigPage;
page1.registerContent(async (view) => {
fileConfigPage = new FileConfigPage(this, page1, model, view, this.provider);
pages.set(0, fileConfigPage);
await fileConfigPage.start().then(() => {
fileConfigPage.setupNavigationValidator();
fileConfigPage.onPageEnter();
});
});
let prosePreviewPage: ProsePreviewPage;
page2.registerContent(async (view) => {
prosePreviewPage = new ProsePreviewPage(this, page2, model, view, this.provider);
pages.set(1, prosePreviewPage);
await prosePreviewPage.start();
});
let modifyColumnsPage: ModifyColumnsPage;
page3.registerContent(async (view) => {
modifyColumnsPage = new ModifyColumnsPage(this, page3, model, view, this.provider);
pages.set(2, modifyColumnsPage);
await modifyColumnsPage.start();
});
let summaryPage: SummaryPage;
page4.registerContent(async (view) => {
summaryPage = new SummaryPage(this, page4, model, view, this.provider);
pages.set(3, summaryPage);
await summaryPage.start();
});
this.importAnotherFileButton = sqlops.window.modelviewdialog.createButton(localize('flatFileImport.importNewFile', 'Import new file'));
this.importAnotherFileButton.onClick(() => {
//TODO replace this with proper cleanup for all the pages
this.wizard.close();
pages.forEach((page) => page.cleanup());
this.wizard.open();
});
this.importAnotherFileButton.hidden = true;
this.wizard.customButtons = [this.importAnotherFileButton];
this.wizard.onPageChanged(async (event) => {
let idx = event.newPage;
let page = pages.get(idx);
if (page) {
page.setupNavigationValidator();
page.onPageEnter();
}
});
this.wizard.onPageChanged(async (event) => {
let idx = event.lastPage;
let page = pages.get(idx);
if (page) {
page.onPageLeave();
}
});
//not needed for this wizard
this.wizard.generateScriptButton.hidden = true;
this.wizard.pages = [page1, page2, page3, page4];
this.wizard.open();
}
public setImportAnotherFileVisibility(visibility: boolean) {
this.importAnotherFileButton.hidden = !visibility;
}
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean) {
this.wizard.registerNavigationValidator(validator);
}
public changeNextButtonLabel(label: string) {
this.wizard.nextButton.label = label;
}
}

View File

@@ -0,0 +1,411 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class FileConfigPage extends ImportPage {
private serverDropdown: sqlops.DropDownComponent;
private databaseDropdown: sqlops.DropDownComponent;
private fileTextBox: sqlops.InputBoxComponent;
private fileButton: sqlops.ButtonComponent;
private tableNameTextBox: sqlops.InputBoxComponent;
private schemaDropdown: sqlops.DropDownComponent;
private form: sqlops.FormContainer;
private databaseLoader: sqlops.LoadingComponent;
private schemaLoader: sqlops.LoadingComponent;
private tableNames: string[] = [];
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, wizardPage, model, view, provider);
}
async start(): Promise<boolean> {
let schemaComponent = await this.createSchemaDropdown();
let tableNameComponent = await this.createTableNameBox();
let fileBrowserComponent = await this.createFileBrowser();
let databaseComponent = await this.createDatabaseDropdown();
let serverComponent = await this.createServerDropdown();
this.form = this.view.modelBuilder.formContainer()
.withFormItems(
[
serverComponent,
databaseComponent,
fileBrowserComponent,
tableNameComponent,
schemaComponent
]).component();
await this.view.initializeModel(this.form);
return true;
}
async onPageEnter(): Promise<boolean> {
let r1 = await this.populateServerDropdown();
let r2 = await this.populateDatabaseDropdown();
let r3 = await this.populateSchemaDropdown();
return r1 && r2 && r3;
}
async onPageLeave(): Promise<boolean> {
delete this.model.serverId;
return true;
}
public async cleanup(): Promise<boolean> {
delete this.model.filePath;
delete this.model.table;
return true;
}
public setupNavigationValidator() {
this.instance.registerNavigationValidator((info) => {
if (this.schemaLoader.loading || this.databaseLoader.loading) {
return false;
}
return true;
});
}
private async createServerDropdown(): Promise<sqlops.FormComponent> {
this.serverDropdown = this.view.modelBuilder.dropDown().withProperties({
required: true
}).component();
// Handle server changes
this.serverDropdown.onValueChanged(async (params) => {
this.model.server = (this.serverDropdown.value as ConnectionDropdownValue).connection;
await this.populateDatabaseDropdown();
await this.populateSchemaDropdown();
});
return {
component: this.serverDropdown,
title: localize('flatFileImport.serverDropdownTitle', 'Server the database is in')
};
}
private async populateServerDropdown(): Promise<boolean> {
let cons = await sqlops.connection.getActiveConnections();
// This user has no active connections ABORT MISSION
if (!cons || cons.length === 0) {
return true;
}
let count = -1;
let idx = -1;
let values = cons.map(c => {
// Handle the code to remember what the user's choice was from before
count++;
if (idx === -1) {
if (this.model.server && c.connectionId === this.model.server.connectionId) {
idx = count;
} else if (this.model.serverId && c.connectionId === this.model.serverId) {
idx = count;
}
}
let db = c.options.databaseDisplayName;
let usr = c.options.user;
let srv = c.options.server;
if (!db) {
db = '<default>';
}
if (!usr) {
usr = 'default';
}
let finalName = `${srv}, ${db} (${usr})`;
return {
connection: c,
displayName: finalName,
name: c.connectionId
};
});
if (idx >= 0) {
let tmp = values[0];
values[0] = values[idx];
values[idx] = tmp;
} else {
delete this.model.server;
delete this.model.serverId;
delete this.model.database;
delete this.model.schema;
}
this.model.server = values[0].connection;
this.serverDropdown.updateProperties({
values: values
});
return true;
}
private async createDatabaseDropdown(): Promise<sqlops.FormComponent> {
this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({
required: true
}).component();
// Handle database changes
this.databaseDropdown.onValueChanged(async (db) => {
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
//this.populateTableNames();
this.populateSchemaDropdown();
});
this.databaseLoader = this.view.modelBuilder.loadingComponent().withItem(this.databaseDropdown).component();
return {
component: this.databaseLoader,
title: localize('flatFileImport.databaseDropdownTitle', 'Database the table is created in')
};
}
private async populateDatabaseDropdown(): Promise<boolean> {
this.databaseLoader.loading = true;
this.databaseDropdown.updateProperties({ values: [] });
this.schemaDropdown.updateProperties({ values: [] });
if (!this.model.server) {
//TODO handle error case
this.databaseLoader.loading = false;
return false;
}
let idx = -1;
let count = -1;
let values = (await sqlops.connection.listDatabases(this.model.server.connectionId)).map(db => {
count++;
if (this.model.database && db === this.model.database) {
idx = count;
}
return {
displayName: db,
name: db
};
});
if (idx >= 0) {
let tmp = values[0];
values[0] = values[idx];
values[idx] = tmp;
} else {
delete this.model.database;
delete this.model.schema;
}
this.model.database = values[0].name;
this.databaseDropdown.updateProperties({
values: values
});
this.databaseLoader.loading = false;
return true;
}
private async createFileBrowser(): Promise<sqlops.FormComponent> {
this.fileTextBox = this.view.modelBuilder.inputBox().withProperties({
required: true
}).component();
this.fileButton = this.view.modelBuilder.button().withProperties({
label: localize('flatFileImport.browseFiles', 'Browse'),
}).component();
this.fileButton.onDidClick(async (click) => {
let fileUris = await vscode.window.showOpenDialog(
{
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
openLabel: localize('flatFileImport.openFile', 'Open'),
filters: {
'CSV/TXT Files': ['csv', 'txt'],
'All Files': ['*']
}
}
);
if (!fileUris || fileUris.length === 0) {
return;
}
let fileUri = fileUris[0];
this.fileTextBox.value = fileUri.fsPath;
// Get the name of the file.
let nameStart = fileUri.path.lastIndexOf('/');
let nameEnd = fileUri.path.lastIndexOf('.');
// Handle files without extensions
if (nameEnd === 0) {
nameEnd = fileUri.path.length;
}
this.model.fileType = 'TXT';
let extension = fileUri.path.substring(nameEnd + 1, fileUri.path.length);
if (extension.toLowerCase() === 'json') {
this.model.fileType = 'JSON';
}
this.tableNameTextBox.value = fileUri.path.substring(nameStart + 1, nameEnd);
this.model.table = this.tableNameTextBox.value;
this.tableNameTextBox.validate();
// Let then model know about the file path
this.model.filePath = fileUri.fsPath;
});
return {
component: this.fileTextBox,
title: localize('flatFileImport.fileTextboxTitle', 'Location of the file to be imported'),
actions: [this.fileButton]
};
}
private async createTableNameBox(): Promise<sqlops.FormComponent> {
this.tableNameTextBox = this.view.modelBuilder.inputBox().withValidation((name) => {
let tableName = name.value;
if (!tableName || tableName.length === 0) {
return false;
}
// This won't actually do anything until table names are brought back in.
if (this.tableNames.indexOf(tableName) !== -1) {
return false;
}
return true;
}).withProperties({
required: true,
}).component();
this.tableNameTextBox.onTextChanged((tableName) => {
this.model.table = tableName;
});
return {
component: this.tableNameTextBox,
title: localize('flatFileImport.tableTextboxTitle', 'New table name'),
};
}
private async createSchemaDropdown(): Promise<sqlops.FormComponent> {
this.schemaDropdown = this.view.modelBuilder.dropDown().withProperties({
required: true
}).component();
this.schemaLoader = this.view.modelBuilder.loadingComponent().withItem(this.schemaDropdown).component();
this.schemaDropdown.onValueChanged(() => {
this.model.schema = (<sqlops.CategoryValue>this.schemaDropdown.value).name;
});
return {
component: this.schemaLoader,
title: localize('flatFileImport.schemaTextboxTitle', 'Table schema'),
};
}
private async populateSchemaDropdown(): Promise<boolean> {
this.schemaLoader.loading = true;
let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
let query = `SELECT name FROM sys.schemas`;
let results = await queryProvider.runQueryAndReturn(connectionUri, query);
let idx = -1;
let count = -1;
let values = results.rows.map(row => {
let schemaName = row[0].displayValue;
count++;
if (this.model.schema && schemaName === this.model.schema) {
idx = count;
}
let val = row[0].displayValue;
return {
name: val,
displayName: val
};
});
if (idx >= 0) {
let tmp = values[0];
values[0] = values[idx];
values[idx] = tmp;
}
this.model.schema = values[0].name;
this.schemaDropdown.updateProperties({
values: values
});
this.schemaLoader.loading = false;
return true;
}
// private async populateTableNames(): Promise<boolean> {
// this.tableNames = [];
// let databaseName = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
//
// if (!databaseName || databaseName.length === 0) {
// this.tableNames = [];
// return false;
// }
//
// let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
// let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
// let results: sqlops.SimpleExecuteResult;
//
// try {
// //let query = sqlstring.format('USE ?; SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = \'BASE TABLE\'', [databaseName]);
// //results = await queryProvider.runQueryAndReturn(connectionUri, query);
// } catch (e) {
// return false;
// }
//
// this.tableNames = results.rows.map(row => {
// return row[0].displayValue;
// });
//
// return true;
// }
}
interface ConnectionDropdownValue extends sqlops.CategoryValue {
connection: sqlops.connection.Connection;
}

View File

@@ -0,0 +1,171 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import { ColumnMetadata, ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class ModifyColumnsPage extends ImportPage {
private readonly categoryValues = [
{ name: 'bigint', displayName: 'bigint' },
{ name: 'binary(50)', displayName: 'binary(50)' },
{ name: 'bit', displayName: 'bit' },
{ name: 'char(10)', displayName: 'char(10)' },
{ name: 'date', displayName: 'date' },
{ name: 'datetime', displayName: 'datetime' },
{ name: 'datetime2(7)', displayName: 'datetime2(7)' },
{ name: 'datetimeoffset(7)', displayName: 'datetimeoffset(7)' },
{ name: 'decimal(18, 10)', displayName: 'decimal(18, 10)' },
{ name: 'float', displayName: 'float' },
{ name: 'geography', displayName: 'geography' },
{ name: 'geometry', displayName: 'geometry' },
{ name: 'hierarchyid', displayName: 'hierarchyid' },
{ name: 'int', displayName: 'int' },
{ name: 'money', displayName: 'money' },
{ name: 'nchar(10)', displayName: 'nchar(10)' },
{ name: 'ntext', displayName: 'ntext' },
{ name: 'numeric(18, 0)', displayName: 'numeric(18, 0)' },
{ name: 'nvarchar(50)', displayName: 'nvarchar(50)' },
{ name: 'nvarchar(MAX)', displayName: 'nvarchar(MAX)' },
{ name: 'real', displayName: 'real' },
{ name: 'smalldatetime', displayName: 'smalldatetime' },
{ name: 'smallint', displayName: 'smallint' },
{ name: 'smallmoney', displayName: 'smallmoney' },
{ name: 'sql_variant', displayName: 'sql_variant' },
{ name: 'text', displayName: 'text' },
{ name: 'time(7)', displayName: 'time(7)' },
{ name: 'timestamp', displayName: 'timestamp' },
{ name: 'tinyint', displayName: 'tinyint' },
{ name: 'uniqueidentifier', displayName: 'uniqueidentifier' },
{ name: 'varbinary(50)', displayName: 'varbinary(50)' },
{ name: 'varbinary(MAX)', displayName: 'varbinary(MAX)' },
{ name: 'varchar(50)', displayName: 'varchar(50)' },
{ name: 'varchar(MAX)', displayName: 'varchar(MAX)' }
];
private table: sqlops.DeclarativeTableComponent;
private loading: sqlops.LoadingComponent;
private text: sqlops.TextComponent;
private form: sqlops.FormContainer;
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, wizardPage, model, view, provider);
}
private static convertMetadata(column: ColumnMetadata): any[] {
return [column.columnName, column.dataType, false, column.nullable];
}
async start(): Promise<boolean> {
this.loading = this.view.modelBuilder.loadingComponent().component();
this.table = this.view.modelBuilder.declarativeTable().component();
this.text = this.view.modelBuilder.text().component();
this.table.onDataChanged((e) => {
this.model.proseColumns = [];
this.table.data.forEach((row) => {
this.model.proseColumns.push({
columnName: row[0],
dataType: row[1],
primaryKey: row[2],
nullable: row[3]
});
});
});
this.form = this.view.modelBuilder.formContainer()
.withFormItems(
[
{
component: this.text,
title: ''
},
{
component: this.table,
title: ''
}
], {
horizontal: false,
componentWidth: '100%'
}).component();
this.loading.component = this.form;
await this.view.initializeModel(this.form);
return true;
}
async onPageEnter(): Promise<boolean> {
this.loading.loading = true;
await this.populateTable();
this.instance.changeNextButtonLabel(localize('flatFileImport.importData', 'Import Data'));
this.loading.loading = false;
return true;
}
async onPageLeave(): Promise<boolean> {
this.instance.changeNextButtonLabel(localize('flatFileImport.next', 'Next'));
return undefined;
}
async cleanup(): Promise<boolean> {
delete this.model.proseColumns;
this.instance.changeNextButtonLabel(localize('flatFileImport.next', 'Next'));
return true;
}
public setupNavigationValidator() {
this.instance.registerNavigationValidator((info) => {
return !this.loading.loading;
});
}
private async populateTable() {
let data: any[][] = [];
this.model.proseColumns.forEach((column) => {
data.push(ModifyColumnsPage.convertMetadata(column));
});
this.table.updateProperties({
height: 400,
columns: [{
displayName: localize('flatFileImport.columnName', 'Column Name'),
valueType: sqlops.DeclarativeDataType.string,
width: '150px',
isReadOnly: false
}, {
displayName: localize('flatFileImport.dataType', 'Data Type'),
valueType: sqlops.DeclarativeDataType.editableCategory,
width: '150px',
isReadOnly: false,
categoryValues: this.categoryValues
}, {
displayName: localize('flatFileImport.primaryKey', 'Primary Key'),
valueType: sqlops.DeclarativeDataType.boolean,
width: '100px',
isReadOnly: false
}, {
displayName: localize('flatFileImport.allowNulls', 'Allow Nulls'),
valueType: sqlops.DeclarativeDataType.boolean,
isReadOnly: false,
width: '100px'
}],
data: data
});
}
}

View File

@@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import { ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class ProsePreviewPage extends ImportPage {
private table: sqlops.TableComponent;
private loading: sqlops.LoadingComponent;
private form: sqlops.FormContainer;
private refresh: sqlops.ButtonComponent;
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, wizardPage, model, view, provider);
}
async start(): Promise<boolean> {
this.table = this.view.modelBuilder.table().component();
this.refresh = this.view.modelBuilder.button().withProperties({
label: localize('flatFileImport.refresh', 'Refresh'),
isFile: false
}).component();
this.refresh.onDidClick(async () => {
this.onPageEnter();
});
this.loading = this.view.modelBuilder.loadingComponent().component();
this.form = this.view.modelBuilder.formContainer().withFormItems([
{
component: this.table,
title: localize('flatFileImport.prosePreviewMessage', 'This operation analyzed the input file structure to generate the preview below for up to the first 50 rows.'),
actions: [this.refresh]
}
]).component();
this.loading.component = this.form;
await this.view.initializeModel(this.loading);
return true;
}
async onPageEnter(): Promise<boolean> {
this.loading.loading = true;
await this.handleProse();
await this.populateTable(this.model.proseDataPreview, this.model.proseColumns.map(c => c.columnName));
this.loading.loading = false;
return true;
}
async onPageLeave(): Promise<boolean> {
await this.emptyTable();
return true;
}
async cleanup(): Promise<boolean> {
delete this.model.proseDataPreview;
return true;
}
public setupNavigationValidator() {
this.instance.registerNavigationValidator((info) => {
return !this.loading.loading;
});
}
private async handleProse() {
await this.provider.sendPROSEDiscoveryRequest({
filePath: this.model.filePath,
tableName: this.model.table,
schemaName: this.model.schema,
fileType: this.model.fileType
}).then((result) => {
this.model.proseDataPreview = result.dataPreview;
this.model.proseColumns = [];
result.columnInfo.forEach((column) => {
this.model.proseColumns.push({
columnName: column.name,
dataType: column.sqlType,
primaryKey: false,
nullable: column.isNullable
});
});
});
}
private async populateTable(tableData: string[][], columnHeaders: string[]) {
let rows;
let rowsLength = tableData.length;
if (rowsLength > 50) {
rows = tableData;
}
else {
rows = tableData.slice(0, rowsLength);
}
this.table.updateProperties({
data: rows,
columns: columnHeaders,
height: 400,
width: '700',
});
}
private async emptyTable() {
this.table.updateProperties([]);
}
}

View File

@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import { ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider, InsertDataResponse } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class SummaryPage extends ImportPage {
private table: sqlops.TableComponent;
private statusText: sqlops.TextComponent;
private loading: sqlops.LoadingComponent;
private form: sqlops.FormContainer;
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, wizardPage, model, view, provider);
}
async start(): Promise<boolean> {
this.table = this.view.modelBuilder.table().component();
this.statusText = this.view.modelBuilder.text().component();
this.loading = this.view.modelBuilder.loadingComponent().withItem(this.statusText).component();
this.form = this.view.modelBuilder.formContainer().withFormItems(
[
{
component: this.table,
title: localize('flatFileImport.importInformation', 'Import information')
},
{
component: this.loading,
title: localize('flatFileImport.importStatus', 'Import status')
}
]
).component();
await this.view.initializeModel(this.form);
return true;
}
async onPageEnter(): Promise<boolean> {
this.loading.loading = true;
this.populateTable();
await this.handleImport();
this.loading.loading = false;
this.instance.setImportAnotherFileVisibility(true);
return true;
}
async onPageLeave(): Promise<boolean> {
this.instance.setImportAnotherFileVisibility(false);
return true;
}
public setupNavigationValidator() {
this.instance.registerNavigationValidator((info) => {
return !this.loading.loading;
});
}
private populateTable() {
this.table.updateProperties({
data: [
[localize('flatFileImport.serverName', 'Server name'), this.model.server.providerName],
[localize('flatFileImport.databaseName', 'Database name'), this.model.database],
[localize('flatFileImport.tableName', 'Table name'), this.model.table],
[localize('flatFileImport.tableSchema', 'Table schema'), this.model.schema],
[localize('flatFileImport.fileImport', 'File to be imported'), this.model.filePath]],
columns: ['Object type', 'Name'],
width: 600,
height: 200
});
}
private async handleImport(): Promise<boolean> {
let changeColumnResults = [];
this.model.proseColumns.forEach((val, i, arr) => {
let columnChangeParams = {
index: i,
newName: val.columnName,
newDataType: val.dataType,
newNullable: val.nullable,
newInPrimaryKey: val.primaryKey
};
changeColumnResults.push(this.provider.sendChangeColumnSettingsRequest(columnChangeParams));
});
let result: InsertDataResponse;
let err;
try {
result = await this.provider.sendInsertDataRequest({
connectionString: await this.getConnectionString(),
//TODO check what SSMS uses as batch size
batchSize: 500
});
} catch (e) {
err = e.toString();
}
let updateText: string;
if (!result || !result.result.success) {
updateText = '✗ ';
if (!result) {
updateText += err;
} else {
updateText += result.result.errorMessage;
}
} else {
// TODO: When sql statements are in, implement this.
//let rows = await this.getCountRowsInserted();
//if (rows < 0) {
updateText = localize('flatFileImport.success.norows', '✔ You have successfully inserted the data into a table.');
//} else {
//updateText = localize('flatFileImport.success.rows', '✔ You have successfully inserted {0} rows.', rows);
//}
}
this.statusText.updateProperties({
value: updateText
});
return true;
}
/**
* Gets the connection string to send to the middleware
* @returns {Promise<string>}
*/
private async getConnectionString(): Promise<string> {
let options = this.model.server.options;
let connectionString: string;
if (options.authenticationType === 'Integrated') {
connectionString = `Data Source=${options.server + (options.port ? `,${options.port}` : '')};Initial Catalog=${this.model.database};Integrated Security=True`;
} else {
let credentials = await sqlops.connection.getCredentials(this.model.server.connectionId);
connectionString = `Data Source=${options.server + (options.port ? `,${options.port}` : '')};Initial Catalog=${this.model.database};Integrated Security=False;User Id=${options.user};Password=${credentials.password}`;
}
// TODO: Fix this, it's returning undefined string.
//await sqlops.connection.getConnectionString(this.model.server.connectionId, true);
return connectionString;
}
// private async getCountRowsInserted(): Promise<Number> {
// let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
// let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
// try {
// let query = sqlstring.format('USE ?; SELECT COUNT(*) FROM ?', [this.model.database, this.model.table]);
// let results = await queryProvider.runQueryAndReturn(connectionUri, query);
// let cell = results.rows[0][0];
// if (!cell || cell.isNull) {
// return -1;
// }
// let numericCell = Number(cell.displayValue);
// if (isNaN(numericCell)) {
// return -1;
// }
// return numericCell;
// } catch (e) {
// return -1;
// }
// }
}

View File

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

409
extensions/import/yarn.lock Normal file
View File

@@ -0,0 +1,409 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
agent-base@4, agent-base@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
dependencies:
es6-promisify "^5.0.0"
applicationinsights@0.15.6:
version "0.15.6"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.15.6.tgz#201a0682c0704fe4bdd9a92d0b2cbe34d2ae5972"
base64-js@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
bl@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
dependencies:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
buffer-alloc@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
buffer@^3.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
dependencies:
base64-js "0.0.8"
ieee754 "^1.1.4"
isarray "^1.0.0"
commander@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
dependencies:
graceful-readlink ">= 1.0.0"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.7":
version "0.2.6"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/85653d8b305af8aef334728d71f07bdc240dfcb7"
dependencies:
vscode-languageclient "3.5.1"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
dependencies:
file-type "^5.2.0"
is-stream "^1.1.0"
tar-stream "^1.5.2"
decompress-tarbz2@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
dependencies:
decompress-tar "^4.1.0"
file-type "^6.1.0"
is-stream "^1.1.0"
seek-bzip "^1.0.5"
unbzip2-stream "^1.0.9"
decompress-targz@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
dependencies:
decompress-tar "^4.1.1"
file-type "^5.2.0"
is-stream "^1.1.0"
decompress-unzip@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
dependencies:
file-type "^3.8.0"
get-stream "^2.2.0"
pify "^2.3.0"
yauzl "^2.4.2"
decompress@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
dependencies:
decompress-tar "^4.0.0"
decompress-tarbz2 "^4.0.0"
decompress-targz "^4.0.0"
decompress-unzip "^4.0.1"
graceful-fs "^4.1.10"
make-dir "^1.0.0"
pify "^2.3.0"
strip-dirs "^2.0.0"
end-of-stream@^1.0.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
dependencies:
once "^1.4.0"
es6-promise@^4.0.3:
version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
dependencies:
es6-promise "^4.0.3"
eventemitter2@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
dependencies:
pend "~1.2.0"
file-type@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
file-type@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
file-type@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
dependencies:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
graceful-fs@^4.1.10:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
agent-base "^4.1.0"
debug "^3.1.0"
ieee754@^1.1.4:
version "1.1.12"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
make-dir@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
dependencies:
pify "^3.0.0"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
readable-stream@^2.3.0, readable-stream@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
seek-bzip@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
dependencies:
commander "~2.8.1"
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
mkdirp "^0.5.1"
tmp "^0.0.33"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
dependencies:
safe-buffer "~5.1.0"
strip-dirs@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
dependencies:
is-natural-number "^4.0.1"
tar-stream@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
dependencies:
bl "^1.0.0"
buffer-alloc "^1.1.0"
end-of-stream "^1.0.0"
fs-constants "^1.0.0"
readable-stream "^2.3.0"
to-buffer "^1.1.0"
xtend "^4.0.0"
through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
dependencies:
os-tmpdir "~1.0.2"
to-buffer@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
unbzip2-stream@^1.0.9:
version "1.2.5"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
dependencies:
buffer "^3.0.1"
through "^2.3.6"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
vscode-extension-telemetry@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.5.tgz#21e2abb4cbce3326e469ddbb322123b3702f3f85"
dependencies:
applicationinsights "0.15.6"
winreg "0.0.13"
vscode-jsonrpc@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-languageclient@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.1.tgz#c78e582459c24e58f88020dfa34065e976186a98"
dependencies:
vscode-languageserver-protocol "3.5.1"
vscode-languageserver-protocol@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.1.tgz#5144a3a9eeccbd83fe2745bd4ed75fad6cc45f0d"
dependencies:
vscode-jsonrpc "3.5.0"
vscode-languageserver-types "3.5.0"
vscode-languageserver-types@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
vscode-nls@^3.2.1:
version "3.2.4"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
winreg@0.0.13:
version "0.0.13"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-0.0.13.tgz#76bfe02e1dd0c9c8275fb9fdf17a9f36846e3483"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
xtend@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
yauzl@^2.4.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"

View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

View File

@@ -17,6 +17,14 @@
"compile": "gulp compile-extension:mssql-client",
"update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json"
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {
},
"contributes": {
"languages": [
{
@@ -656,19 +664,5 @@
}
]
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.4",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {
},
"resolutions": {
"vscode-jsonrpc": "3.5.0",
"vscode-languageclient": "3.5.0",
"vscode-languageserver-protocol": "3.5.0",
"vscode-languageserver-types": "3.5.0"
}
}

View File

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

View File

@@ -3,8 +3,8 @@
agent-base@4, agent-base@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce"
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
dependencies:
es6-promisify "^5.0.0"
@@ -27,10 +27,25 @@ bl@^1.0.0:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
buffer-alloc@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
buffer@^3.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
@@ -49,11 +64,11 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.4":
version "0.2.4"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/b4e2696563d0075e1b674ac8b179b3b8c441f59f"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.7":
version "0.2.6"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/85653d8b305af8aef334728d71f07bdc240dfcb7"
dependencies:
vscode-languageclient "3.5.0"
vscode-languageclient "3.5.1"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
@@ -139,9 +154,9 @@ eventemitter2@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
dependencies:
pend "~1.2.0"
@@ -157,6 +172,10 @@ file-type@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
@@ -180,15 +199,15 @@ http-proxy-agent@^2.0.0:
debug "3.1.0"
https-proxy-agent@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz#7fbba856be8cd677986f42ebd3664f6317257887"
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
agent-base "^4.1.0"
debug "^3.1.0"
ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
version "1.1.12"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
inherits@~2.0.3:
version "2.0.3"
@@ -207,8 +226,8 @@ isarray@^1.0.0, isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
make-dir@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
dependencies:
pify "^3.0.0"
@@ -270,21 +289,21 @@ process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
readable-stream@^2.0.0, readable-stream@^2.3.5:
version "2.3.5"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d"
readable-stream@^2.3.0, readable-stream@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.0.3"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
seek-bzip@^1.0.5:
version "1.0.5"
@@ -307,9 +326,9 @@ semver@^5.3.0:
mkdirp "^0.5.1"
tmp "^0.0.33"
string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
dependencies:
safe-buffer "~5.1.0"
@@ -320,12 +339,15 @@ strip-dirs@^2.0.0:
is-natural-number "^4.0.1"
tar-stream@^1.5.2:
version "1.5.5"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
version "1.6.1"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
dependencies:
bl "^1.0.0"
buffer-alloc "^1.1.0"
end-of-stream "^1.0.0"
readable-stream "^2.0.0"
fs-constants "^1.0.0"
readable-stream "^2.3.0"
to-buffer "^1.1.0"
xtend "^4.0.0"
through@^2.3.6:
@@ -338,6 +360,10 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
to-buffer@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
unbzip2-stream@^1.0.9:
version "1.2.5"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
@@ -355,24 +381,24 @@ vscode-extension-telemetry@^0.0.15:
dependencies:
applicationinsights "1.0.1"
vscode-jsonrpc@3.5.0, vscode-jsonrpc@^3.5.0:
vscode-jsonrpc@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-languageclient@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.0.tgz#36d02cc186a8365a4467719a290fb200a9ae490a"
vscode-languageclient@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.1.tgz#c78e582459c24e58f88020dfa34065e976186a98"
dependencies:
vscode-languageserver-protocol "^3.5.0"
vscode-languageserver-protocol "3.5.1"
vscode-languageserver-protocol@3.5.0, vscode-languageserver-protocol@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
vscode-languageserver-protocol@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.1.tgz#5144a3a9eeccbd83fe2745bd4ed75fad6cc45f0d"
dependencies:
vscode-jsonrpc "^3.5.0"
vscode-languageserver-types "^3.5.0"
vscode-jsonrpc "3.5.0"
vscode-languageserver-types "3.5.0"
vscode-languageserver-types@3.5.0, vscode-languageserver-types@^3.5.0:
vscode-languageserver-types@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
@@ -385,11 +411,11 @@ xtend@^4.0.0:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
yauzl@^2.4.2:
version "2.9.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f"
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.0.1"
fd-slicer "~1.1.0"
zone.js@0.7.6:
version "0.7.6"

View File

@@ -10,6 +10,17 @@ Common SQL Profiler use-cases taken from https://docs.microsoft.com/en-us/sql/to
- Monitoring the performance of SQL Server to tune workloads.
- Correlating performance counters to diagnose problems.
## Getting Started:
To launch SQL Server Profiler, you have to first make a connection to a server.
Open Profiler by pressing **Alt+P** on Windows, and **Ctrl+Alt+P** on macOS.
To Start/Stop Profiler, click the Start button or press **Alt+S** on Windows, or **Ctrl+Alt+S** on macOS.
Otherwise, open the command palette and type 'Profiler.'
For more info, [check out our documentation.](https://docs.microsoft.com/en-us/sql/sql-operations-studio/sql-server-profiler-extension?view=sql-server-2017)
## SQL Server Profiler 0.1.1 Release
The SQL Server Profiler for SQL Operations Studio *Preview* extension is now available. This is the initial preview release for a new lightweight XEvent-based profiler. The SQL Server Profiler extension tries to make it simple to quickly trace server activity for troubleshooting and monitoring.

View File

@@ -2,7 +2,7 @@
"name": "profiler",
"displayName": "SQL Server Profiler",
"description": "SQL Server Profiler for SQL Operations Studio",
"version": "0.1.2",
"version": "0.1.5",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
@@ -45,6 +45,7 @@
},
{
"command": "profiler.openCreateSessionDialog",
"title": "Create Profiler Session",
"category": "Profiler"
}
],

View File

@@ -232,11 +232,11 @@
},
"_db_light": {
"fontCharacter": "\\E01C",
"fontColor": "#dd4b78"
"fontColor": "#bfc2c1"
},
"_db": {
"fontCharacter": "\\E01C",
"fontColor": "#f55385"
"fontColor": "#d4d7d6"
},
"_default_light": {
"fontCharacter": "\\E01D",
@@ -1411,7 +1411,7 @@
"rust": "_rust",
"scss": "_sass",
"shellscript": "_shell",
"sql": "_default",
"sql": "_db",
"swift": "_swift",
"typescript": "_typescript",
"typescriptreact": "_react",
@@ -1616,7 +1616,7 @@
"rust": "_rust_light",
"scss": "_sass_light",
"shellscript": "_shell_light",
"sql": "_default_light",
"sql": "_db_light",
"swift": "_swift_light",
"typescript": "_typescript_light",
"typescriptreact": "_react_light",

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.32.3",
"version": "0.32.6",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -34,7 +34,7 @@
"@angular/router": "~4.1.3",
"@angular/upgrade": "~4.1.3",
"angular2-grid": "2.0.6",
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.3.12",
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.4",
"applicationinsights": "0.18.0",
"chart.js": "^2.6.0",
"fast-plist": "0.1.2",
@@ -149,7 +149,7 @@
"underscore": "^1.8.3",
"vinyl": "^0.4.5",
"vinyl-fs": "^2.4.3",
"vsce": "1.33.2",
"vsce": "1.46.0",
"vscode-nls-dev": "3.0.7"
},
"repository": {

View File

@@ -35,6 +35,7 @@
"date": "2017-12-15T12:00:00.000Z",
"recommendedExtensions": [
"Microsoft.agent",
"Microsoft.import",
"Microsoft.profiler",
"Microsoft.server-report",
"Microsoft.whoisactive",

View File

@@ -1,6 +1,6 @@
[Desktop Entry]
Name=@@NAME_LONG@@
Comment=SQL Operations Studio
Comment=Data Management Tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
GenericName=Text Editor
Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F
Icon=@@ICON@@

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,589 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="extensions/agent/out/dialogs/scheduleDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="scheduleDialog.newSchedule">
<source xml:lang="en">New Schedule</source>
</trans-unit>
<trans-unit id="scheduleDialog.ok">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="scheduleDialog.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="scheduleDialog.scheduleName">
<source xml:lang="en">Schedule Name</source>
</trans-unit>
<trans-unit id="scheduleDialog.schedules">
<source xml:lang="en">Schedules</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/proxyDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="createProxy.createProxy">
<source xml:lang="en">Create Proxy</source>
</trans-unit>
<trans-unit id="createProxy.editProxy">
<source xml:lang="en">Edit Proxy</source>
</trans-unit>
<trans-unit id="createProxy.General">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="createProxy.ProxyName">
<source xml:lang="en">Proxy name</source>
</trans-unit>
<trans-unit id="createProxy.CredentialName">
<source xml:lang="en">Credential name</source>
</trans-unit>
<trans-unit id="createProxy.Description">
<source xml:lang="en">Description</source>
</trans-unit>
<trans-unit id="createProxy.SubsystemName">
<source xml:lang="en">Subsystem</source>
</trans-unit>
<trans-unit id="createProxy.OperatingSystem">
<source xml:lang="en">Operating system (CmdExec)</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationSnapshot">
<source xml:lang="en">Replication Snapshot</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationTransactionLog">
<source xml:lang="en">Replication Transaction-Log Reader</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationDistributor">
<source xml:lang="en">Replication Distributor</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationMerge">
<source xml:lang="en">Replication Merge</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationQueueReader">
<source xml:lang="en">Replication Queue Reader</source>
</trans-unit>
<trans-unit id="createProxy.SSASQueryLabel">
<source xml:lang="en">SQL Server Analysis Services Query</source>
</trans-unit>
<trans-unit id="createProxy.SSASCommandLabel">
<source xml:lang="en">SQL Server Analysis Services Command</source>
</trans-unit>
<trans-unit id="createProxy.SSISPackage">
<source xml:lang="en">SQL Server Integration Services Package</source>
</trans-unit>
<trans-unit id="createProxy.PowerShell">
<source xml:lang="en">PowerShell</source>
</trans-unit>
<trans-unit id="createProxy.subSystemHeading">
<source xml:lang="en">Active to the following subsytems</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/pickScheduleDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="pickSchedule.jobSchedules">
<source xml:lang="en">Job Schedules</source>
</trans-unit>
<trans-unit id="pickSchedule.ok">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="pickSchedule.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="pickSchedule.scheduleName">
<source xml:lang="en">Schedule Name</source>
</trans-unit>
<trans-unit id="pickSchedule.schedules">
<source xml:lang="en">Schedules</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/operatorDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="createOperator.createOperator">
<source xml:lang="en">Create Operator</source>
</trans-unit>
<trans-unit id="createOperator.editOperator">
<source xml:lang="en">Edit Operator</source>
</trans-unit>
<trans-unit id="createOperator.General">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="createOperator.Notifications">
<source xml:lang="en">Notifications</source>
</trans-unit>
<trans-unit id="createOperator.Name">
<source xml:lang="en">Name</source>
</trans-unit>
<trans-unit id="createOperator.Enabled">
<source xml:lang="en">Enabled</source>
</trans-unit>
<trans-unit id="createOperator.EmailName">
<source xml:lang="en">E-mail Name</source>
</trans-unit>
<trans-unit id="createOperator.PagerEmailName">
<source xml:lang="en">Pager E-mail Name</source>
</trans-unit>
<trans-unit id="createOperator.PagerMondayCheckBox">
<source xml:lang="en">Monday</source>
</trans-unit>
<trans-unit id="createOperator.PagerTuesdayCheckBox">
<source xml:lang="en">Tuesday</source>
</trans-unit>
<trans-unit id="createOperator.PagerWednesdayCheckBox">
<source xml:lang="en">Wednesday</source>
</trans-unit>
<trans-unit id="createOperator.PagerThursdayCheckBox">
<source xml:lang="en">Thursday</source>
</trans-unit>
<trans-unit id="createOperator.PagerFridayCheckBox">
<source xml:lang="en">Friday </source>
</trans-unit>
<trans-unit id="createOperator.PagerSaturdayCheckBox">
<source xml:lang="en">Saturday</source>
</trans-unit>
<trans-unit id="createOperator.PagerSundayCheckBox">
<source xml:lang="en">Sunday</source>
</trans-unit>
<trans-unit id="createOperator.workdayBegin">
<source xml:lang="en">Workday begin</source>
</trans-unit>
<trans-unit id="createOperator.workdayEnd">
<source xml:lang="en">Workday end</source>
</trans-unit>
<trans-unit id="createOperator.PagerDutySchedule">
<source xml:lang="en">Pager on duty schdule</source>
</trans-unit>
<trans-unit id="createOperator.AlertListHeading">
<source xml:lang="en">Alert list</source>
</trans-unit>
<trans-unit id="createOperator.AlertNameColumnLabel">
<source xml:lang="en">Alert name</source>
</trans-unit>
<trans-unit id="createOperator.AlertEmailColumnLabel">
<source xml:lang="en">E-mail</source>
</trans-unit>
<trans-unit id="createOperator.AlertPagerColumnLabel">
<source xml:lang="en">Pager</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/jobStepDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="jobStepDialog.newJobStep">
<source xml:lang="en">New Job Step</source>
</trans-unit>
<trans-unit id="jobStepDialog.fileBrowserTitle">
<source xml:lang="en">Locate Database Files - </source>
</trans-unit>
<trans-unit id="jobStepDialog.ok">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="jobStepDialog.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="jobStepDialog.general">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="jobStepDialog.advanced">
<source xml:lang="en">Advanced</source>
</trans-unit>
<trans-unit id="jobStepDialog.open">
<source xml:lang="en">Open...</source>
</trans-unit>
<trans-unit id="jobStepDialog.parse">
<source xml:lang="en">Parse</source>
</trans-unit>
<trans-unit id="jobStepDialog.next">
<source xml:lang="en">Next</source>
</trans-unit>
<trans-unit id="jobStepDialog.previous">
<source xml:lang="en">Previous</source>
</trans-unit>
<trans-unit id="jobStepDialog.successParse">
<source xml:lang="en">The command was successfully parsed.</source>
</trans-unit>
<trans-unit id="jobStepDialog.failParse">
<source xml:lang="en">The command failed.</source>
</trans-unit>
<trans-unit id="jobStepDialog.blankStepName">
<source xml:lang="en">The step name cannot be left blank</source>
</trans-unit>
<trans-unit id="jobStepDialog.stepNameLabel">
<source xml:lang="en">Step Name</source>
</trans-unit>
<trans-unit id="jobStepDialog.typeLabel">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="jobStepDialog.runAsLabel">
<source xml:lang="en">Run as</source>
</trans-unit>
<trans-unit id="jobStepDialog.databaseLabel">
<source xml:lang="en">Database</source>
</trans-unit>
<trans-unit id="jobStepDialog.commandLabel">
<source xml:lang="en">Command</source>
</trans-unit>
<trans-unit id="jobStepDialog.successAction">
<source xml:lang="en">On success action</source>
</trans-unit>
<trans-unit id="jobStepDialog.failureAction">
<source xml:lang="en">On failure action</source>
</trans-unit>
<trans-unit id="jobStepDialog.runAsUser">
<source xml:lang="en">Run as user</source>
</trans-unit>
<trans-unit id="jobStepDialog.retryAttempts">
<source xml:lang="en">Retry Attempts</source>
</trans-unit>
<trans-unit id="jobStepDialog.retryInterval">
<source xml:lang="en">Retry Interval (minutes)</source>
</trans-unit>
<trans-unit id="jobStepDialog.logToTable">
<source xml:lang="en">Log to table</source>
</trans-unit>
<trans-unit id="jobStepDialog.appendExistingTableEntry">
<source xml:lang="en">Append output to exisiting entry in table</source>
</trans-unit>
<trans-unit id="jobStepDialog.includeStepOutputHistory">
<source xml:lang="en">Include step output in history</source>
</trans-unit>
<trans-unit id="jobStepDialog.outputFile">
<source xml:lang="en">Output File</source>
</trans-unit>
<trans-unit id="jobStepDialog.appendOutputToFile">
<source xml:lang="en">Append output to existing file</source>
</trans-unit>
<trans-unit id="jobStepDialog.selectedPath">
<source xml:lang="en">Selected path</source>
</trans-unit>
<trans-unit id="jobStepDialog.filesOfType">
<source xml:lang="en">Files of type</source>
</trans-unit>
<trans-unit id="jobStepDialog.fileName">
<source xml:lang="en">File name</source>
</trans-unit>
<trans-unit id="jobStepDialog.allFiles">
<source xml:lang="en">All Files (*)</source>
</trans-unit>
<trans-unit id="jobStepDialog.TSQL">
<source xml:lang="en">Transact-SQL script (T-SQL)</source>
</trans-unit>
<trans-unit id="jobStepDialog.agentServiceAccount">
<source xml:lang="en">SQL Server Agent Service Account</source>
</trans-unit>
<trans-unit id="jobStepDialog.nextStep">
<source xml:lang="en">Go to the next step</source>
</trans-unit>
<trans-unit id="jobStepDialog.quitJobSuccess">
<source xml:lang="en">Quit the job reporting success</source>
</trans-unit>
<trans-unit id="jobStepDialog.quitJobFailure">
<source xml:lang="en">Quit the job reporting failure</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/jobDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="jobDialog.general">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="jobDialog.steps">
<source xml:lang="en">Steps</source>
</trans-unit>
<trans-unit id="jobDialog.schedules">
<source xml:lang="en">Schedules</source>
</trans-unit>
<trans-unit id="jobDialog.alerts">
<source xml:lang="en">Alerts</source>
</trans-unit>
<trans-unit id="jobDialog.notifications">
<source xml:lang="en">Notifications</source>
</trans-unit>
<trans-unit id="jobDialog.blankJobNameError">
<source xml:lang="en">The name of the job cannot be blank.</source>
</trans-unit>
<trans-unit id="jobDialog.name">
<source xml:lang="en">Name</source>
</trans-unit>
<trans-unit id="jobDialog.owner">
<source xml:lang="en">Owner</source>
</trans-unit>
<trans-unit id="jobDialog.category">
<source xml:lang="en">Category</source>
</trans-unit>
<trans-unit id="jobDialog.description">
<source xml:lang="en">Description</source>
</trans-unit>
<trans-unit id="jobDialog.enabled">
<source xml:lang="en">Enabled</source>
</trans-unit>
<trans-unit id="jobDialog.jobStepList">
<source xml:lang="en">Job step list</source>
</trans-unit>
<trans-unit id="jobDialog.step">
<source xml:lang="en">Step</source>
</trans-unit>
<trans-unit id="jobDialog.type">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="jobDialog.onSuccess">
<source xml:lang="en">On Success</source>
</trans-unit>
<trans-unit id="jobDialog.onFailure">
<source xml:lang="en">On Failure</source>
</trans-unit>
<trans-unit id="jobDialog.new">
<source xml:lang="en">New...</source>
</trans-unit>
<trans-unit id="jobDialog.edit">
<source xml:lang="en">Edit</source>
</trans-unit>
<trans-unit id="jobDialog.delete">
<source xml:lang="en">Delete</source>
</trans-unit>
<trans-unit id="jobDialog.moveUp">
<source xml:lang="en">Move Step Up</source>
</trans-unit>
<trans-unit id="jobDialog.moveDown">
<source xml:lang="en">Move Step Up</source>
</trans-unit>
<trans-unit id="jobDialog.notificationsTabTop">
<source xml:lang="en">Actions to perform when the job completes</source>
</trans-unit>
<trans-unit id="jobDialog.email">
<source xml:lang="en">Email</source>
</trans-unit>
<trans-unit id="jobDialog.page">
<source xml:lang="en">Page</source>
</trans-unit>
<trans-unit id="jobDialog.eventLogCheckBoxLabel">
<source xml:lang="en">Write to the Windows Application event log</source>
</trans-unit>
<trans-unit id="jobDialog.deleteJobLabel">
<source xml:lang="en">Automatically delete job</source>
</trans-unit>
<trans-unit id="jobDialog.schedulesaLabel">
<source xml:lang="en">Schedules list</source>
</trans-unit>
<trans-unit id="jobDialog.pickSchedule">
<source xml:lang="en">Pick Schedule</source>
</trans-unit>
<trans-unit id="jobDialog.scheduleNameLabel">
<source xml:lang="en">Schedule Name</source>
</trans-unit>
<trans-unit id="jobDialog.alertsList">
<source xml:lang="en">Alerts list</source>
</trans-unit>
<trans-unit id="jobDialog.newAlert">
<source xml:lang="en">New Alert</source>
</trans-unit>
<trans-unit id="jobDialog.alertNameLabel">
<source xml:lang="en">Alert Name</source>
</trans-unit>
<trans-unit id="jobDialog.newJob">
<source xml:lang="en">New Job</source>
</trans-unit>
<trans-unit id="jobDialog.editJob">
<source xml:lang="en">Edit Job</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/alertDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="alertDialog.createAlert">
<source xml:lang="en">Create Alert</source>
</trans-unit>
<trans-unit id="alertDialog.editAlert">
<source xml:lang="en">Edit Alert</source>
</trans-unit>
<trans-unit id="alertDialog.General">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="alertDialog.Response">
<source xml:lang="en">Response</source>
</trans-unit>
<trans-unit id="alertDialog.Options">
<source xml:lang="en">Options</source>
</trans-unit>
<trans-unit id="alertDialog.eventAlert">
<source xml:lang="en">Event alert definition</source>
</trans-unit>
<trans-unit id="alertDialog.Name">
<source xml:lang="en">Name</source>
</trans-unit>
<trans-unit id="alertDialog.Type">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="alertDialog.Enabled">
<source xml:lang="en">Enabled</source>
</trans-unit>
<trans-unit id="alertDialog.DatabaseName">
<source xml:lang="en">Database name</source>
</trans-unit>
<trans-unit id="alertDialog.ErrorNumber">
<source xml:lang="en">Error number</source>
</trans-unit>
<trans-unit id="alertDialog.Severity">
<source xml:lang="en">Severity</source>
</trans-unit>
<trans-unit id="alertDialog.RaiseAlertContains">
<source xml:lang="en">Raise alert when message contains</source>
</trans-unit>
<trans-unit id="alertDialog.MessageText">
<source xml:lang="en">Message text</source>
</trans-unit>
<trans-unit id="alertDialog.Severity001">
<source xml:lang="en">001 - Miscellaneous System Information</source>
</trans-unit>
<trans-unit id="alertDialog.Severity002">
<source xml:lang="en">002 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity003">
<source xml:lang="en">003 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity004">
<source xml:lang="en">004 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity005">
<source xml:lang="en">005 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity006">
<source xml:lang="en">006 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity007">
<source xml:lang="en">007 - Notification: Status Information</source>
</trans-unit>
<trans-unit id="alertDialog.Severity008">
<source xml:lang="en">008 - Notification: User Intervention Required</source>
</trans-unit>
<trans-unit id="alertDialog.Severity009">
<source xml:lang="en">009 - User Defined</source>
</trans-unit>
<trans-unit id="alertDialog.Severity010">
<source xml:lang="en">010 - Information</source>
</trans-unit>
<trans-unit id="alertDialog.Severity011">
<source xml:lang="en">011 - Specified Database Object Not Found</source>
</trans-unit>
<trans-unit id="alertDialog.Severity012">
<source xml:lang="en">012 - Unused</source>
</trans-unit>
<trans-unit id="alertDialog.Severity013">
<source xml:lang="en">013 - User Transaction Syntax Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity014">
<source xml:lang="en">014 - Insufficient Permission</source>
</trans-unit>
<trans-unit id="alertDialog.Severity015">
<source xml:lang="en">015 - Syntax Error in SQL Statements</source>
</trans-unit>
<trans-unit id="alertDialog.Severity016">
<source xml:lang="en">016 - Miscellaneous User Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity017">
<source xml:lang="en">017 - Insufficient Resources</source>
</trans-unit>
<trans-unit id="alertDialog.Severity018">
<source xml:lang="en">018 - Nonfatal Internal Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity019">
<source xml:lang="en">019 - Fatal Error in Resource</source>
</trans-unit>
<trans-unit id="alertDialog.Severity020">
<source xml:lang="en">020 - Fatal Error in Current Process</source>
</trans-unit>
<trans-unit id="alertDialog.Severity021">
<source xml:lang="en">021 - Fatal Error in Database Processes</source>
</trans-unit>
<trans-unit id="alertDialog.Severity022">
<source xml:lang="en">022 - Fatal Error: Table Integrity Suspect</source>
</trans-unit>
<trans-unit id="alertDialog.Severity023">
<source xml:lang="en">023 - Fatal Error: Database Integrity Suspect</source>
</trans-unit>
<trans-unit id="alertDialog.Severity024">
<source xml:lang="en">024 - Fatal Error: Hardware Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity025">
<source xml:lang="en">025 - Fatal Error</source>
</trans-unit>
<trans-unit id="alertDialog.AllDatabases">
<source xml:lang="en">&lt;all databases&gt;</source>
</trans-unit>
<trans-unit id="alertDialog.ExecuteJob">
<source xml:lang="en">Execute Job</source>
</trans-unit>
<trans-unit id="alertDialog.ExecuteJobName">
<source xml:lang="en">Job Name</source>
</trans-unit>
<trans-unit id="alertDialog.NotifyOperators">
<source xml:lang="en">Notify Operators</source>
</trans-unit>
<trans-unit id="alertDialog.NewJob">
<source xml:lang="en">New Job</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorList">
<source xml:lang="en">Operator List</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorName">
<source xml:lang="en">Operator</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorEmail">
<source xml:lang="en">E-mail</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorPager">
<source xml:lang="en">Pager</source>
</trans-unit>
<trans-unit id="alertDialog.NewOperator">
<source xml:lang="en">New Operator</source>
</trans-unit>
<trans-unit id="alertDialog.IncludeErrorInEmail">
<source xml:lang="en">Include alert error text in e-mail</source>
</trans-unit>
<trans-unit id="alertDialog.IncludeErrorInPager">
<source xml:lang="en">Include alert error text in pager</source>
</trans-unit>
<trans-unit id="alertDialog.AdditionalNotification">
<source xml:lang="en">Additional notification message to send</source>
</trans-unit>
<trans-unit id="alertDialog.DelayBetweenResponse">
<source xml:lang="en">Delay between responses</source>
</trans-unit>
<trans-unit id="alertDialog.DelayMinutes">
<source xml:lang="en">Delay Minutes</source>
</trans-unit>
<trans-unit id="alertDialog.DelaySeconds">
<source xml:lang="en">Delay Seconds</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/agentDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="agentDialog.OK">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="agentDialog.Cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/data/jobData" source-language="en" datatype="plaintext"><body>
<trans-unit id="jobData.whenJobCompletes">
<source xml:lang="en">When the job completes</source>
</trans-unit>
<trans-unit id="jobData.whenJobFails">
<source xml:lang="en">When the job fails</source>
</trans-unit>
<trans-unit id="jobData.whenJobSucceeds">
<source xml:lang="en">When the job succeeds</source>
</trans-unit>
<trans-unit id="jobData.jobNameRequired">
<source xml:lang="en">Job name must be provided</source>
</trans-unit>
<trans-unit id="jobData.saveErrorMessage">
<source xml:lang="en">Job update failed '{0}'</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/data/alertData" source-language="en" datatype="plaintext"><body>
<trans-unit id="alertData.saveErrorMessage">
<source xml:lang="en">Alert update failed '{0}'</source>
</trans-unit>
<trans-unit id="alertData.DefaultAlertTypString">
<source xml:lang="en">SQL Server event alert</source>
</trans-unit>
<trans-unit id="alertDialog.PerformanceCondition">
<source xml:lang="en">SQL Server performance condition alert</source>
</trans-unit>
<trans-unit id="alertDialog.WmiEvent">
<source xml:lang="en">WMI event alert</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/mainController" source-language="en" datatype="plaintext"><body>
<trans-unit id="mainController.notImplemented">
<source xml:lang="en">This feature is under development. Check-out the latest insiders build if you'd like to try out the most recent changes!</source>
</trans-unit>
</body></file>
</xliff>

View File

@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="extensions/import/out/wizard/pages/summaryPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.importInformation">
<source xml:lang="en">Import information</source>
</trans-unit>
<trans-unit id="flatFileImport.importStatus">
<source xml:lang="en">Import status</source>
</trans-unit>
<trans-unit id="flatFileImport.serverName">
<source xml:lang="en">Server name</source>
</trans-unit>
<trans-unit id="flatFileImport.databaseName">
<source xml:lang="en">Database name</source>
</trans-unit>
<trans-unit id="flatFileImport.tableName">
<source xml:lang="en">Table name</source>
</trans-unit>
<trans-unit id="flatFileImport.tableSchema">
<source xml:lang="en">Table schema</source>
</trans-unit>
<trans-unit id="flatFileImport.fileImport">
<source xml:lang="en">File to be imported</source>
</trans-unit>
<trans-unit id="flatFileImport.success.norows">
<source xml:lang="en">✔ You have successfully inserted the data into a table.</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/pages/prosePreviewPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.refresh">
<source xml:lang="en">Refresh</source>
</trans-unit>
<trans-unit id="flatFileImport.prosePreviewMessage">
<source xml:lang="en">This operation analyzed the input file structure to generate the preview below for up to the first 50 rows.</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/pages/modifyColumnsPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.importData">
<source xml:lang="en">Import Data</source>
</trans-unit>
<trans-unit id="flatFileImport.next">
<source xml:lang="en">Next</source>
</trans-unit>
<trans-unit id="flatFileImport.columnName">
<source xml:lang="en">Column Name</source>
</trans-unit>
<trans-unit id="flatFileImport.dataType">
<source xml:lang="en">Data Type</source>
</trans-unit>
<trans-unit id="flatFileImport.primaryKey">
<source xml:lang="en">Primary Key</source>
</trans-unit>
<trans-unit id="flatFileImport.allowNulls">
<source xml:lang="en">Allow Nulls</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/pages/fileConfigPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.serverDropdownTitle">
<source xml:lang="en">Server the database is in</source>
</trans-unit>
<trans-unit id="flatFileImport.databaseDropdownTitle">
<source xml:lang="en">Database the table is created in</source>
</trans-unit>
<trans-unit id="flatFileImport.browseFiles">
<source xml:lang="en">Browse</source>
</trans-unit>
<trans-unit id="flatFileImport.openFile">
<source xml:lang="en">Open</source>
</trans-unit>
<trans-unit id="flatFileImport.fileTextboxTitle">
<source xml:lang="en">Location of the file to be imported</source>
</trans-unit>
<trans-unit id="flatFileImport.tableTextboxTitle">
<source xml:lang="en">New table name</source>
</trans-unit>
<trans-unit id="flatFileImport.schemaTextboxTitle">
<source xml:lang="en">Table schema</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/services/telemetry" source-language="en" datatype="plaintext"><body>
<trans-unit id="import.serviceCrashButton">
<source xml:lang="en">Give Feedback</source>
</trans-unit>
<trans-unit id="serviceCrashMessage">
<source xml:lang="en">service component could not start</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/services/serviceClient" source-language="en" datatype="plaintext"><body>
<trans-unit id="serviceStarted">
<source xml:lang="en">Service Started</source>
</trans-unit>
<trans-unit id="serviceStarting">
<source xml:lang="en">Starting service</source>
</trans-unit>
<trans-unit id="flatFileImport.serviceStartFailed">
<source xml:lang="en">Failed to start Import service{0}</source>
</trans-unit>
<trans-unit id="installingServiceDetailed">
<source xml:lang="en">Installing {0} service to {1}</source>
</trans-unit>
<trans-unit id="installingService">
<source xml:lang="en">Installing Service</source>
</trans-unit>
<trans-unit id="serviceInstalled">
<source xml:lang="en">Installed</source>
</trans-unit>
<trans-unit id="downloadingService">
<source xml:lang="en">Downloading {0}</source>
</trans-unit>
<trans-unit id="downloadingServiceStatus">
<source xml:lang="en">Downloading Service</source>
</trans-unit>
<trans-unit id="downloadingServiceComplete">
<source xml:lang="en">Done!</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/flatFileWizard" source-language="en" datatype="plaintext"><body>
<trans-unit id="import.needConnection">
<source xml:lang="en">Please connect to a server before using this wizard.</source>
</trans-unit>
<trans-unit id="flatFileImport.wizardName">
<source xml:lang="en">Import flat file wizard</source>
</trans-unit>
<trans-unit id="flatFileImport.page1Name">
<source xml:lang="en">Specify Input File</source>
</trans-unit>
<trans-unit id="flatFileImport.page2Name">
<source xml:lang="en">Preview Data</source>
</trans-unit>
<trans-unit id="flatFileImport.page3Name">
<source xml:lang="en">Modify Columns</source>
</trans-unit>
<trans-unit id="flatFileImport.page4Name">
<source xml:lang="en">Summary</source>
</trans-unit>
<trans-unit id="flatFileImport.importNewFile">
<source xml:lang="en">Import new file</source>
</trans-unit>
</body></file>
</xliff>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="extensions/profiler/client\out/dialogs/profilerCreateSessionDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="createSessionDialog.newSession">
<source xml:lang="en">New Session</source>
</trans-unit>
<trans-unit id="createSessionDialog.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="createSessionDialog.create">
<source xml:lang="en">Create</source>
</trans-unit>
<trans-unit id="createSessionDialog.title">
<source xml:lang="en">Create New Profiler Session</source>
</trans-unit>
<trans-unit id="createSessionDialog.templatesInvalid">
<source xml:lang="en">Invalid templates list, cannot open dialog</source>
</trans-unit>
<trans-unit id="createSessionDialog.dialogOwnerInvalid">
<source xml:lang="en">Invalid dialog owner, cannot open dialog</source>
</trans-unit>
<trans-unit id="createSessionDialog.selectTemplates">
<source xml:lang="en">Select session template:</source>
</trans-unit>
<trans-unit id="createSessionDialog.enterSessionName">
<source xml:lang="en">Enter session name:</source>
</trans-unit>
</body></file>
</xliff>

File diff suppressed because it is too large Load Diff

View File

@@ -172,8 +172,7 @@
"dependencies": {
"fs-extra": "^5.0.0",
"handlebars": "^4.0.11",
"vscode-nls": "2.0.2",
"sqlops": "github:anthonydresser/sqlops-extension-sqlops"
"vscode-nls": "2.0.2"
},
"devDependencies": {
"@types/handlebars": "^4.0.11",
@@ -192,6 +191,7 @@
"typemoq": "^2.1.0",
"typescript": "^2.6.1",
"vscode": "^1.1.6",
"sqlops": "github:anthonydresser/sqlops-extension-sqlops",
"vsce": "1.36.2"
}
}

View File

@@ -21,6 +21,10 @@
"command": "sqlservices.openDialog",
"title": "sqlservices.openDialog"
},
{
"command": "sqlservices.openConnectionDialog",
"title": "sqlservices.openConnectionDialog"
},
{
"command": "sqlservices.openEditor",
"title": "sqlservices.openEditor"

View File

@@ -49,6 +49,13 @@ export default class MainController implements vscode.Disposable {
this.openDialog();
});
vscode.commands.registerCommand('sqlservices.openConnectionDialog', async () => {
let connection = await sqlops.connection.openConnectionDialog();
if (connection) {
console.info('Connection Opened: ' + connection.options['server']);
}
});
vscode.commands.registerCommand('sqlservices.openEditor', () => {
this.openEditor();
});
@@ -378,18 +385,20 @@ export default class MainController implements vscode.Disposable {
page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800);
});
/*
wizard.registerOperation({
displayName: 'test task',
description: 'task description',
isCancelable: true
}, op => {
op.updateStatus(sqlops.TaskStatus.InProgress);
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000);
});*/
isCancelable: true,
connection: undefined,
operation: op => {
op.updateStatus(sqlops.TaskStatus.InProgress);
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000);
}
});
wizard.pages = [page1, page2];
wizard.open();
}

View File

@@ -75,6 +75,14 @@ export class TreeNode implements sqlops.TreeComponentItem {
this.data.label = value;
}
public get collapsibleState(): vscode.TreeItemCollapsibleState {
if (!this._isAlwaysLeaf) {
return vscode.TreeItemCollapsibleState.Expanded;
} else {
vscode.TreeItemCollapsibleState.None;
}
}
public get label(): string {
return this.data.label;
}
@@ -265,6 +273,7 @@ export class TreeDataProvider implements sqlops.TreeComponentDataProvider<TreeNo
let item: sqlops.TreeComponentItem = {};
item.label = element.label;
item.checked = element.checked;
item.collapsibleState = element.collapsibleState;
item.iconPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
return item;
}

View File

@@ -20,7 +20,7 @@ export class Checkbox implements OnInit, OnChanges {
@Input() label: string;
@Input() enabled = true;
@Input() checked = true;
@Input() private ariaLabel: string;
@Input('aria-label') private ariaLabel: string;
@Output() onChange = new EventEmitter<boolean>();

View File

@@ -15,7 +15,6 @@ import { AngularDisposable } from 'sql/base/common/lifecycle';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
@Component({
@@ -29,7 +28,7 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
@Input() max: string;
@Input() type: string;
@Input() placeholder: string;
@Input() ariaLabel: string;
@Input('aria-label') ariaLabel: string;
@Input() value: string;
@Output() onDidChange = new EventEmitter<string | number>();

View File

@@ -135,7 +135,7 @@ export abstract class Modal extends Disposable implements IThemable {
private _name: string,
private _partService: IPartService,
private _telemetryService: ITelemetryService,
private _contextKeyService: IContextKeyService,
_contextKeyService: IContextKeyService,
options?: IModalOptions
) {
super();
@@ -228,7 +228,7 @@ export abstract class Modal extends Disposable implements IThemable {
}
// The builder builds the dialog. It append header, body and footer sections.
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
this._builder = $().div({ class: builderClass, 'role': 'dialog', 'aria-label': this._title }, (dialogContainer) => {
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
modalDialog.div({ class: 'modal-content' }, (modelContent) => {
parts.forEach((part) => {

View File

@@ -49,7 +49,7 @@ export function createOptionElement(option: sqlops.ServiceOption, rowContainer:
optionWidget.value = optionValue;
inputElement = findElement(rowContainer, 'input');
} else if (option.valueType === ServiceOptionType.category || option.valueType === ServiceOptionType.boolean) {
optionWidget = new SelectBox(possibleInputs, optionValue.toString(), contextViewService);
optionWidget = new SelectBox(possibleInputs, optionValue.toString(), contextViewService, undefined, { ariaLabel: option.displayName });
DialogHelper.appendInputSelectBox(rowContainer, optionWidget);
inputElement = findElement(rowContainer, 'monaco-select-box');
} else if (option.valueType === ServiceOptionType.string || option.valueType === ServiceOptionType.password) {

View File

@@ -4,11 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { IThemable } from 'vs/platform/theme/common/styler';
import * as objects from 'sql/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { Dimension } from 'vs/base/browser/dom';
import { Dimension, EventType } from 'vs/base/browser/dom';
import { $, Builder } from 'vs/base/browser/builder';
import { EventType } from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -17,12 +15,16 @@ import './panelStyles';
import { Disposable } from 'vs/base/common/lifecycle';
export interface IPanelStyles {
}
export interface IPanelOptions {
showHeaderWhenSingleView?: boolean;
}
export interface IPanelView {
render(container: HTMLElement): void;
layout(dimension: Dimension): void;
remove?(): void;
}
export interface IPanelTab {
@@ -36,6 +38,10 @@ interface IInternalPanelTab extends IPanelTab {
label: Builder;
}
const defaultOptions: IPanelOptions = {
showHeaderWhenSingleView: true
};
export type PanelTabIdentifier = string;
export class TabbedPanel extends Disposable implements IThemable {
@@ -49,11 +55,12 @@ export class TabbedPanel extends Disposable implements IThemable {
private _actionbar: ActionBar;
private _currentDimensions: Dimension;
private _collapsed = false;
private _headerVisible: boolean;
private _onTabChange = new Emitter<PanelTabIdentifier>();
public onTabChange: Event<PanelTabIdentifier> = this._onTabChange.event;
constructor(private container: HTMLElement) {
constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) {
super();
this.$parent = this._register($('.tabbedPanel'));
this.$parent.appendTo(container);
@@ -65,7 +72,12 @@ export class TabbedPanel extends Disposable implements IThemable {
let actionbarcontainer = $('.title-actions');
this._actionbar = new ActionBar(actionbarcontainer.getHTMLElement());
this.$header.append(actionbarcontainer);
this.$parent.append(this.$header);
if (options.showHeaderWhenSingleView) {
this._headerVisible = true;
this.$parent.append(this.$header);
} else {
this._headerVisible = false;
}
this.$body = $('tabBody');
this.$body.attr('role', 'tabpanel');
this.$body.attr('tabindex', '0');
@@ -73,12 +85,16 @@ export class TabbedPanel extends Disposable implements IThemable {
}
public pushTab(tab: IPanelTab): PanelTabIdentifier {
let internalTab = objects.clone(tab) as IInternalPanelTab;
let internalTab = tab as IInternalPanelTab;
this._tabMap.set(tab.identifier, internalTab);
this._createTab(internalTab);
if (!this._shownTab) {
this.showTab(tab.identifier);
}
if (this._tabMap.size > 1 && !this._headerVisible) {
this.$parent.append(this.$header, 0);
this._headerVisible = true;
}
return tab.identifier as PanelTabIdentifier;
}
@@ -139,6 +155,11 @@ export class TabbedPanel extends Disposable implements IThemable {
}
public removeTab(tab: PanelTabIdentifier) {
let actualTab = this._tabMap.get(tab);
actualTab.header.destroy();
if (actualTab.view.remove) {
actualTab.view.remove();
}
this._tabMap.get(tab).header.destroy();
this._tabMap.delete(tab);
}
@@ -151,8 +172,9 @@ export class TabbedPanel extends Disposable implements IThemable {
this._currentDimensions = dimension;
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
this.$body.style('height', (dimension.height - this.headersize) + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, dimension.height - this.headersize));
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
}
private _layoutCurrentTab(dimension: Dimension): void {

View File

@@ -0,0 +1,212 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INextIterator } from 'vs/base/common/iterator';
export interface IView {
id: string;
}
export interface IViewItem {
view: IView;
top: number;
height: number;
width: number;
}
export class HeightMap {
private heightMap: IViewItem[];
private indexes: { [item: string]: number; };
constructor() {
this.heightMap = [];
this.indexes = {};
}
public getContentHeight(): number {
let last = this.heightMap[this.heightMap.length - 1];
return !last ? 0 : last.top + last.height;
}
public onInsertItems(iterator: INextIterator<IViewItem>, afterItemId: string = null): number {
let viewItem: IViewItem;
let i: number, j: number;
let totalSize: number;
let sizeDiff = 0;
if (afterItemId === null) {
i = 0;
totalSize = 0;
} else {
i = this.indexes[afterItemId] + 1;
viewItem = this.heightMap[i - 1];
if (!viewItem) {
console.error('view item doesnt exist');
return undefined;
}
totalSize = viewItem.top + viewItem.height;
}
let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0);
let itemsToInsert: IViewItem[] = [];
while (viewItem = iterator.next()) {
viewItem.top = totalSize + sizeDiff;
this.indexes[viewItem.view.id] = i++;
itemsToInsert.push(viewItem);
sizeDiff += viewItem.height;
}
boundSplice.apply(this.heightMap, itemsToInsert);
for (j = i; j < this.heightMap.length; j++) {
viewItem = this.heightMap[j];
viewItem.top += sizeDiff;
this.indexes[viewItem.view.id] = j;
}
for (j = itemsToInsert.length - 1; j >= 0; j--) {
this.onInsertItem(itemsToInsert[j]);
}
for (j = this.heightMap.length - 1; j >= i; j--) {
this.onRefreshItem(this.heightMap[j]);
}
return sizeDiff;
}
public onInsertItem(item: IViewItem): void {
// noop
}
// Contiguous items
public onRemoveItems(iterator: INextIterator<string>): void {
let itemId: string;
let viewItem: IViewItem;
let startIndex: number = null;
let i: number;
let sizeDiff = 0;
while (itemId = iterator.next()) {
i = this.indexes[itemId];
viewItem = this.heightMap[i];
if (!viewItem) {
console.error('view item doesnt exist');
return;
}
sizeDiff -= viewItem.height;
delete this.indexes[itemId];
this.onRemoveItem(viewItem);
if (startIndex === null) {
startIndex = i;
}
}
if (sizeDiff === 0) {
return;
}
this.heightMap.splice(startIndex, i - startIndex + 1);
for (i = startIndex; i < this.heightMap.length; i++) {
viewItem = this.heightMap[i];
viewItem.top += sizeDiff;
this.indexes[viewItem.view.id] = i;
this.onRefreshItem(viewItem);
}
}
public onRemoveItem(item: IViewItem): void {
// noop
}
public onRefreshItem(item: IViewItem, needsRender: boolean = false): void {
// noop
}
protected updateSize(item: string, size: number): void {
let i = this.indexes[item];
let viewItem = this.heightMap[i];
viewItem.height = size;
}
protected updateTop(item: string, top: number): void {
let i = this.indexes[item];
let viewItem = this.heightMap[i];
viewItem.top = top;
}
public itemsCount(): number {
return this.heightMap.length;
}
public itemAt(position: number): string {
return this.heightMap[this.indexAt(position)].view.id;
}
public withItemsInRange(start: number, end: number, fn: (item: string) => void): void {
start = this.indexAt(start);
end = this.indexAt(end);
for (let i = start; i <= end; i++) {
fn(this.heightMap[i].view.id);
}
}
public indexAt(position: number): number {
let left = 0;
let right = this.heightMap.length;
let center: number;
let item: IViewItem;
// Binary search
while (left < right) {
center = Math.floor((left + right) / 2);
item = this.heightMap[center];
if (position < item.top) {
right = center;
} else if (position >= item.top + item.height) {
if (left === center) {
break;
}
left = center;
} else {
return center;
}
}
return this.heightMap.length;
}
public indexAfter(position: number): number {
return Math.min(this.indexAt(position) + 1, this.heightMap.length);
}
public itemAtIndex(index: number): IViewItem {
return this.heightMap[index];
}
public itemAfter(item: IViewItem): IViewItem {
return this.heightMap[this.indexes[item.view.id] + 1] || null;
}
public dispose(): void {
this.heightMap = null;
this.indexes = null;
}
}

View File

@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-scroll-split-view {
position: relative;
}

View File

@@ -0,0 +1,622 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./scrollableSplitview';
import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { mapEvent, Emitter, Event, debounceEvent } from 'vs/base/common/event';
import * as types from 'vs/base/common/types';
import * as dom from 'vs/base/browser/dom';
import { clamp } from 'vs/base/common/numbers';
import { range, firstIndex } from 'vs/base/common/arrays';
import { Sash, Orientation, ISashEvent as IBaseSashEvent } from 'vs/base/browser/ui/sash/sash';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { HeightMap, IView as HeightIView, IViewItem as HeightIViewItem } from './heightMap';
import { ArrayIterator } from 'vs/base/common/iterator';
import { mixin } from 'vs/base/common/objects';
export { Orientation } from 'vs/base/browser/ui/sash/sash';
export interface ISplitViewOptions {
orientation?: Orientation; // default Orientation.VERTICAL
enableResizing?: boolean;
}
const defaultOptions: ISplitViewOptions = {
enableResizing: true
};
export interface IView extends HeightIView {
readonly minimumSize: number;
readonly maximumSize: number;
readonly onDidChange: Event<number | undefined>;
render(container: HTMLElement, orientation: Orientation): void;
layout(size: number, orientation: Orientation): void;
}
interface ISashEvent {
sash: Sash;
start: number;
current: number;
}
interface IViewItem extends HeightIViewItem {
view: IView;
size: number;
container: HTMLElement;
disposable: IDisposable;
layout(): void;
}
interface ISashItem {
sash: Sash;
disposable: IDisposable;
}
interface ISashDragState {
index: number;
start: number;
sizes: number[];
}
enum State {
Idle,
Busy
}
function pushToEnd<T>(arr: T[], value: T): T[] {
let didFindValue = false;
const result = arr.filter(v => {
if (v === value) {
didFindValue = true;
return false;
}
return true;
});
if (didFindValue) {
result.push(value);
}
return result;
}
export class ScrollableSplitView extends HeightMap implements IDisposable {
private orientation: Orientation;
private el: HTMLElement;
private size = 0;
private contentSize = 0;
private viewItems: IViewItem[] = [];
private sashItems: ISashItem[] = [];
private sashDragState: ISashDragState;
private state: State = State.Idle;
private scrollable: ScrollableElement;
private options: ISplitViewOptions;
private dirtyState = false;
private lastRenderTop: number;
private lastRenderHeight: number;
private _onDidSashChange = new Emitter<void>();
readonly onDidSashChange = this._onDidSashChange.event;
private _onDidSashReset = new Emitter<void>();
readonly onDidSashReset = this._onDidSashReset.event;
get length(): number {
return this.viewItems.length;
}
constructor(container: HTMLElement, options: ISplitViewOptions = {}) {
super();
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
this.options = mixin(options, defaultOptions, false);
this.el = document.createElement('div');
this.scrollable = new ScrollableElement(this.el, {});
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
this.render(e.scrollTop, e.height);
this.relayout();
});
let domNode = this.scrollable.getDomNode();
dom.addClass(this.el, 'monaco-scroll-split-view');
dom.addClass(domNode, 'monaco-split-view2');
dom.addClass(domNode, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
container.appendChild(domNode);
}
addViews(views: IView[], sizes: number[], index = this.viewItems.length): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
for (let i = 0; i < views.length; i++) {
let view = views[i], size = sizes[i];
// Add view
const container = dom.$('.split-view-view');
const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));
const containerDisposable = toDisposable(() => {
if (container.parentElement) {
this.el.removeChild(container);
}
this.onRemoveItems(new ArrayIterator([item.view.id]));
});
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`;
const layout = () => {
layoutContainer(item.size);
item.view.layout(item.size, this.orientation);
};
size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
// Add sash
if (this.options.enableResizing && this.viewItems.length > 1) {
const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) };
const sash = new Sash(this.el, layoutProvider, { orientation });
const sashEventMapper = this.orientation === Orientation.VERTICAL
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY })
: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX });
const onStart = mapEvent(sash.onDidStart, sashEventMapper);
const onStartDisposable = onStart(this.onSashStart, this);
const onChange = mapEvent(sash.onDidChange, sashEventMapper);
const onSashChangeDisposable = onChange(this.onSashChange, this);
const onEnd = mapEvent<void, void>(sash.onDidEnd, () => null);
const onEndDisposable = onEnd(() => this._onDidSashChange.fire());
const onDidReset = mapEvent<void, void>(sash.onDidReset, () => null);
const onDidResetDisposable = onDidReset(() => this._onDidSashReset.fire());
const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, onEndDisposable, onDidResetDisposable, sash]);
const sashItem: ISashItem = { sash, disposable };
this.sashItems.splice(index - 1, 0, sashItem);
}
view.render(container, this.orientation);
}
this.relayout(index);
this.state = State.Idle;
}
addView(view: IView, size: number, index = this.viewItems.length): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
// Add view
const container = dom.$('.split-view-view');
const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));
const containerDisposable = toDisposable(() => {
if (container.parentElement) {
this.el.removeChild(container);
}
this.onRemoveItems(new ArrayIterator([item.view.id]));
});
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`;
const layout = () => {
layoutContainer(item.size);
item.view.layout(item.size, this.orientation);
};
size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
// Add sash
if (this.options.enableResizing && this.viewItems.length > 1) {
const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) };
const sash = new Sash(this.el, layoutProvider, { orientation });
const sashEventMapper = this.orientation === Orientation.VERTICAL
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY })
: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX });
const onStart = mapEvent(sash.onDidStart, sashEventMapper);
const onStartDisposable = onStart(this.onSashStart, this);
const onChange = mapEvent(sash.onDidChange, sashEventMapper);
const onSashChangeDisposable = onChange(this.onSashChange, this);
const onEnd = mapEvent<void, void>(sash.onDidEnd, () => null);
const onEndDisposable = onEnd(() => this._onDidSashChange.fire());
const onDidReset = mapEvent<void, void>(sash.onDidReset, () => null);
const onDidResetDisposable = onDidReset(() => this._onDidSashReset.fire());
const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, onEndDisposable, onDidResetDisposable, sash]);
const sashItem: ISashItem = { sash, disposable };
sash.hide();
this.sashItems.splice(index - 1, 0, sashItem);
}
view.render(container, this.orientation);
this.relayout(index);
this.state = State.Idle;
}
removeView(index: number): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
if (index < 0 || index >= this.viewItems.length) {
return;
}
// Remove view
const viewItem = this.viewItems.splice(index, 1)[0];
viewItem.disposable.dispose();
// Remove sash
if (this.options.enableResizing && this.viewItems.length >= 1) {
const sashIndex = Math.max(index - 1, 0);
const sashItem = this.sashItems.splice(sashIndex, 1)[0];
sashItem.disposable.dispose();
} else {
this.lastRenderHeight = NaN, this.lastRenderTop = NaN;
}
this.relayout();
this.state = State.Idle;
}
moveView(from: number, to: number): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
if (from < 0 || from >= this.viewItems.length) {
return;
}
if (to < 0 || to >= this.viewItems.length) {
return;
}
if (from === to) {
return;
}
const viewItem = this.viewItems.splice(from, 1)[0];
this.viewItems.splice(to, 0, viewItem);
if (to + 1 < this.viewItems.length) {
this.el.insertBefore(viewItem.container, this.viewItems[to + 1].container);
} else {
this.el.appendChild(viewItem.container);
}
this.layoutViews();
this.state = State.Idle;
}
private relayout(lowPriorityIndex?: number): void {
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
}
layout(size: number): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
this.resize(this.viewItems.length - 1, size - previousSize);
}
private render(scrollTop: number, viewHeight: number): void {
let i: number;
let stop: number;
let renderTop = scrollTop;
let renderBottom = scrollTop + viewHeight;
let thisRenderBottom = this.lastRenderTop + this.lastRenderHeight;
// when view scrolls down, start rendering from the renderBottom
for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) {
if (this.insertItemInDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
// when view scrolls up, start rendering from either this.renderTop or renderBottom
for (i = Math.min(this.indexAt(this.lastRenderTop), this.indexAfter(renderBottom)) - 1, stop = this.indexAt(renderTop); i >= stop; i--) {
if (this.insertItemInDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
// when view scrolls down, start unrendering from renderTop
for (i = this.indexAt(this.lastRenderTop), stop = Math.min(this.indexAt(renderTop), this.indexAfter(thisRenderBottom)); i < stop; i++) {
if (this.removeItemFromDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
// when view scrolls up, start unrendering from either renderBottom this.renderTop
for (i = Math.max(this.indexAfter(renderBottom), this.indexAt(this.lastRenderTop)), stop = this.indexAfter(thisRenderBottom); i < stop; i++) {
if (this.removeItemFromDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
let topItem = this.itemAtIndex(this.indexAt(renderTop));
if (topItem) {
this.el.style.top = (topItem.top - renderTop) + 'px';
}
this.lastRenderTop = renderTop;
this.lastRenderHeight = renderBottom - renderTop;
}
private onSashStart({ sash, start }: ISashEvent): void {
const index = firstIndex(this.sashItems, item => item.sash === sash);
const sizes = this.viewItems.map(i => i.size);
// const upIndexes = range(index, -1);
// const collapseUp = upIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0);
// const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0);
// const downIndexes = range(index + 1, this.viewItems.length);
// const collapseDown = downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0);
// const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0);
// const minDelta = -Math.min(collapseUp, expandDown);
// const maxDelta = Math.min(collapseDown, expandUp);
this.sashDragState = { start, index, sizes };
}
private onSashChange({ sash, current }: ISashEvent): void {
const { index, start, sizes } = this.sashDragState;
const delta = current - start;
this.resize(index, delta, sizes);
}
private onViewChange(item: IViewItem, size: number | undefined): void {
const index = this.viewItems.indexOf(item);
if (index < 0 || index >= this.viewItems.length) {
return;
}
size = typeof size === 'number' ? size : item.size;
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
item.size = size;
this.relayout(index);
}
resizeView(index: number, size: number): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
if (index < 0 || index >= this.viewItems.length) {
return;
}
const item = this.viewItems[index];
size = Math.round(size);
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
let delta = size - item.size;
if (delta !== 0 && index < this.viewItems.length - 1) {
const downIndexes = range(index + 1, this.viewItems.length);
const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
const deltaDown = clamp(delta, -expandDown, collapseDown);
this.resize(index, deltaDown);
delta -= deltaDown;
}
if (delta !== 0 && index > 0) {
const upIndexes = range(index - 1, -1);
const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
const deltaUp = clamp(-delta, -collapseUp, expandUp);
this.resize(index - 1, deltaUp);
}
this.state = State.Idle;
}
// DOM changes
private insertItemInDOM(item: IViewItem): boolean {
if (item.container.parentElement) {
return false;
}
let elementAfter: HTMLElement = null;
let itemAfter = <IViewItem>this.itemAfter(item);
if (itemAfter && itemAfter.container) {
elementAfter = itemAfter.container;
}
if (elementAfter === null) {
this.el.appendChild(item.container);
} else {
try {
this.el.insertBefore(item.container, elementAfter);
} catch (e) {
// console.warn('Failed to locate previous tree element');
this.el.appendChild(item.container);
}
}
item.layout();
return true;
}
private removeItemFromDOM(item: IViewItem): boolean {
if (!item || !item.container || !item.container.parentElement) {
return false;
}
this.el.removeChild(item.container);
return true;
}
getViewSize(index: number): number {
if (index < 0 || index >= this.viewItems.length) {
return -1;
}
return this.viewItems[index].size;
}
private resize(index: number, delta: number, sizes = this.viewItems.map(i => i.size), lowPriorityIndex?: number): void {
if (index < 0 || index >= this.viewItems.length) {
return;
}
if (delta !== 0) {
let upIndexes = range(index, -1);
let downIndexes = range(index + 1, this.viewItems.length);
if (typeof lowPriorityIndex === 'number') {
upIndexes = pushToEnd(upIndexes, lowPriorityIndex);
downIndexes = pushToEnd(downIndexes, lowPriorityIndex);
}
const upItems = upIndexes.map(i => this.viewItems[i]);
const upSizes = upIndexes.map(i => sizes[i]);
const downItems = downIndexes.map(i => this.viewItems[i]);
const downSizes = downIndexes.map(i => sizes[i]);
for (let i = 0, deltaUp = delta; deltaUp !== 0 && i < upItems.length; i++) {
const item = upItems[i];
const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - upSizes[i];
deltaUp -= viewDelta;
item.size = size;
}
for (let i = 0, deltaDown = delta; deltaDown !== 0 && i < downItems.length; i++) {
const item = downItems[i];
const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - downSizes[i];
deltaDown += viewDelta;
item.size = size;
}
}
let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
let emptyDelta = this.size - contentSize;
for (let i = this.viewItems.length - 1; emptyDelta > 0 && i >= 0; i--) {
const item = this.viewItems[i];
const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - item.size;
emptyDelta -= viewDelta;
item.size = size;
}
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.scrollable.setScrollDimensions({
scrollHeight: this.contentSize,
height: this.size
});
this.layoutViews();
}
private layoutViews(): void {
if (this.dirtyState) {
for (let i = this.indexAt(this.lastRenderTop); i <= this.indexAfter(this.lastRenderTop + this.lastRenderHeight) - 1; i++) {
this.viewItems[i].layout();
if (this.options.enableResizing) {
this.sashItems[i].sash.layout();
}
}
this.dirtyState = false;
}
// Update sashes enablement
// let previous = false;
// const collapsesDown = this.viewItems.map(i => previous = (i.size - i.view.minimumSize > 0) || previous);
// previous = false;
// const expandsDown = this.viewItems.map(i => previous = (i.view.maximumSize - i.size > 0) || previous);
// const reverseViews = [...this.viewItems].reverse();
// previous = false;
// const collapsesUp = reverseViews.map(i => previous = (i.size - i.view.minimumSize > 0) || previous).reverse();
// previous = false;
// const expandsUp = reverseViews.map(i => previous = (i.view.maximumSize - i.size > 0) || previous).reverse();
// this.sashItems.forEach((s, i) => {
// if ((collapsesDown[i] && expandsUp[i + 1]) || (expandsDown[i] && collapsesUp[i + 1])) {
// s.sash.enable();
// } else {
// s.sash.disable();
// }
// });
}
private getSashPosition(sash: Sash): number {
let position = 0;
for (let i = 0; i < this.sashItems.length; i++) {
position += this.viewItems[i].size;
if (this.sashItems[i].sash === sash) {
return position;
}
}
return 0;
}
dispose(): void {
this.viewItems.forEach(i => i.disposable.dispose());
this.viewItems = [];
this.sashItems.forEach(i => i.disposable.dispose());
this.sashItems = [];
}
}

View File

@@ -28,6 +28,7 @@ export class SelectBox extends AngularDisposable implements OnInit, OnChanges {
@Input() options: string[];
@Input() selectedOption: string;
@Input() onlyEmitOnChange = false;
@Input('aria-label') ariaLabel: string;
@Output() onDidSelect = new EventEmitter<ISelectData>();
@@ -42,7 +43,7 @@ export class SelectBox extends AngularDisposable implements OnInit, OnChanges {
}
ngOnInit(): void {
this._selectbox = new vsSelectBox(this.options, this.selectedOption, this.contextViewService);
this._selectbox = new vsSelectBox(this.options, this.selectedOption, this.contextViewService, undefined, { ariaLabel: this.ariaLabel });
this._selectbox.render(this._el.nativeElement);
this._selectbox.onDidSelect(e => {
if (this.onlyEmitOnChange) {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox';
import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox';
import { Color } from 'vs/base/common/color';
import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import * as dom from 'vs/base/browser/dom';
@@ -46,8 +46,8 @@ export class SelectBox extends vsSelectBox {
private inputValidationErrorBackground: Color;
private element: HTMLElement;
constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement) {
super(options, 0, contextViewProvider);
constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement, selectBoxOptions?: ISelectBoxOptions) {
super(options, 0, contextViewProvider, undefined, selectBoxOptions);
this._optionsDictionary = new Array();
for (var i = 0; i < options.length; i++) {
this._optionsDictionary[options[i]] = i;
@@ -84,6 +84,7 @@ export class SelectBox extends vsSelectBox {
this.inputValidationWarningBackground = styles.inputValidationWarningBackground;
this.inputValidationErrorBorder = styles.inputValidationErrorBorder;
this.inputValidationErrorBackground = styles.inputValidationErrorBackground;
this.applyStyles();
}
public selectWithOptionName(optionName: string): void {

View File

@@ -0,0 +1,215 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IObservableCollection<T> {
getLength(): number;
at(index: number): T;
getRange(start: number, end: number): T[];
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void;
}
export interface IGridDataRow {
row?: number;
values: any[];
}
export enum CollectionChange {
ItemsReplaced
}
class LoadCancellationToken {
isCancelled: boolean;
}
class DataWindow<TData> {
private _dataSourceLength: number;
private _data: TData[];
private _length: number = 0;
private _offsetFromDataSource: number = -1;
private loadFunction: (offset: number, count: number) => Thenable<TData[]>;
private lastLoadCancellationToken: LoadCancellationToken;
private loadCompleteCallback: (start: number, end: number) => void;
private placeholderItemGenerator: (index: number) => TData;
constructor(dataSourceLength: number,
loadFunction: (offset: number, count: number) => Thenable<TData[]>,
placeholderItemGenerator: (index: number) => TData,
loadCompleteCallback: (start: number, end: number) => void) {
this._dataSourceLength = dataSourceLength;
this.loadFunction = loadFunction;
this.placeholderItemGenerator = placeholderItemGenerator;
this.loadCompleteCallback = loadCompleteCallback;
}
getStartIndex(): number {
return this._offsetFromDataSource;
}
getEndIndex(): number {
return this._offsetFromDataSource + this._length;
}
contains(dataSourceIndex: number): boolean {
return dataSourceIndex >= this.getStartIndex() && dataSourceIndex < this.getEndIndex();
}
getItem(index: number): TData {
if (!this._data) {
return this.placeholderItemGenerator(index);
}
return this._data[index - this._offsetFromDataSource];
}
positionWindow(offset: number, length: number): void {
this._offsetFromDataSource = offset;
this._length = length;
this._data = undefined;
if (this.lastLoadCancellationToken) {
this.lastLoadCancellationToken.isCancelled = true;
}
if (length === 0) {
return;
}
let cancellationToken = new LoadCancellationToken();
this.lastLoadCancellationToken = cancellationToken;
this.loadFunction(offset, length).then(data => {
if (!cancellationToken.isCancelled) {
this._data = data;
this.loadCompleteCallback(this._offsetFromDataSource, this._offsetFromDataSource + this._length);
}
});
}
}
export class VirtualizedCollection<TData> implements IObservableCollection<TData> {
private _length: number;
private _windowSize: number;
private _bufferWindowBefore: DataWindow<TData>;
private _window: DataWindow<TData>;
private _bufferWindowAfter: DataWindow<TData>;
private collectionChangedCallback: (change: CollectionChange, startIndex: number, count: number) => void;
constructor(windowSize: number,
length: number,
loadFn: (offset: number, count: number) => Thenable<TData[]>,
private _placeHolderGenerator: (index: number) => TData) {
this._windowSize = windowSize;
this._length = length;
let loadCompleteCallback = (start: number, end: number) => {
if (this.collectionChangedCallback) {
this.collectionChangedCallback(CollectionChange.ItemsReplaced, start, end - start);
}
};
this._bufferWindowBefore = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
this._window = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
this._bufferWindowAfter = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
}
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void {
this.collectionChangedCallback = callback;
}
getLength(): number {
return this._length;
}
at(index: number): TData {
return this.getRange(index, index + 1)[0];
}
getRange(start: number, end: number): TData[] {
// current data may contain placeholders
let currentData = this.getRangeFromCurrent(start, end);
// only shift window and make promise of refreshed data in following condition:
if (start < this._bufferWindowBefore.getStartIndex() || end > this._bufferWindowAfter.getEndIndex()) {
// jump, reset
this.resetWindowsAroundIndex(start);
} else if (end <= this._bufferWindowBefore.getEndIndex()) {
// scroll up, shift up
let windowToRecycle = this._bufferWindowAfter;
this._bufferWindowAfter = this._window;
this._window = this._bufferWindowBefore;
this._bufferWindowBefore = windowToRecycle;
let newWindowOffset = Math.max(0, this._window.getStartIndex() - this._windowSize);
this._bufferWindowBefore.positionWindow(newWindowOffset, this._window.getStartIndex() - newWindowOffset);
} else if (start >= this._bufferWindowAfter.getStartIndex()) {
// scroll down, shift down
let windowToRecycle = this._bufferWindowBefore;
this._bufferWindowBefore = this._window;
this._window = this._bufferWindowAfter;
this._bufferWindowAfter = windowToRecycle;
let newWindowOffset = Math.min(this._window.getStartIndex() + this._windowSize, this._length);
let newWindowLength = Math.min(this._length - newWindowOffset, this._windowSize);
this._bufferWindowAfter.positionWindow(newWindowOffset, newWindowLength);
}
return currentData;
}
private getRangeFromCurrent(start: number, end: number): TData[] {
let currentData = [];
for (let i = 0; i < end - start; i++) {
currentData.push(this.getDataFromCurrent(start + i));
}
return currentData;
}
private getDataFromCurrent(index: number): TData {
if (this._bufferWindowBefore.contains(index)) {
return this._bufferWindowBefore.getItem(index);
} else if (this._bufferWindowAfter.contains(index)) {
return this._bufferWindowAfter.getItem(index);
} else if (this._window.contains(index)) {
return this._window.getItem(index);
}
return this._placeHolderGenerator(index);
}
private resetWindowsAroundIndex(index: number): void {
let bufferWindowBeforeStart = Math.max(0, index - this._windowSize * 1.5);
let bufferWindowBeforeEnd = Math.max(0, index - this._windowSize / 2);
this._bufferWindowBefore.positionWindow(bufferWindowBeforeStart, bufferWindowBeforeEnd - bufferWindowBeforeStart);
let mainWindowStart = bufferWindowBeforeEnd;
let mainWindowEnd = Math.min(mainWindowStart + this._windowSize, this._length);
this._window.positionWindow(mainWindowStart, mainWindowEnd - mainWindowStart);
let bufferWindowAfterStart = mainWindowEnd;
let bufferWindowAfterEnd = Math.min(bufferWindowAfterStart + this._windowSize, this._length);
this._bufferWindowAfter.positionWindow(bufferWindowAfterStart, bufferWindowAfterEnd - bufferWindowAfterStart);
}
}
export class AsyncDataProvider<TData extends IGridDataRow> implements Slick.DataProvider<TData> {
constructor(private dataRows: IObservableCollection<TData>) { }
public getLength(): number {
return this.dataRows ? this.dataRows.getLength() : 0;
}
public getItem(index: number): TData {
return !this.dataRows ? undefined : this.dataRows.at(index);
}
public getRange(start: number, end: number): TData[] {
return !this.dataRows ? undefined : this.dataRows.getRange(start, end);
}
}

View File

@@ -46,7 +46,8 @@
background-image: url('down.svg');
}
.vs-dark .slick-header-menubutton {
.vs-dark .slick-header-menubutton,
.hc-black .slick-header-menubutton {
background-image: url('down-inverse.svg');
}
@@ -54,7 +55,8 @@
background-image: url('filter.svg');
}
.vs-dark .slick-header-menubutton.filtered {
.vs-dark .slick-header-menubutton.filtered,
.hc-black .slick-header-menubutton.filtered {
background-image: url('filter_inverse.svg');
}
@@ -74,6 +76,10 @@
background: none repeat scroll 0 0 #333333;
}
.hc-black .slick-header-menu {
background: none repeat scroll 0 0 #000000;
}
.slick-header-menu a.monaco-button.monaco-text-button {
width: 60px;
margin: 6px 6px 6px 6px;
@@ -144,4 +150,8 @@ label {
.vs-dark .slick-header-menu > input.input {
color: #4a4a4a;
}
.hc-black .slick-header-menu > input.input {
color: #000000;
}

View File

@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as DOM from 'vs/base/browser/dom';
import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { mixin } from 'vs/base/common/objects';
const SCROLL_WHEEL_SENSITIVITY = 50;
export interface IMouseWheelSupportOptions {
scrollSpeed?: number;
}
const defaultOptions: IMouseWheelSupportOptions = {
scrollSpeed: SCROLL_WHEEL_SENSITIVITY
};
export class MouseWheelSupport implements Slick.Plugin<any> {
private viewport: HTMLElement;
private canvas: HTMLElement;
private options: IMouseWheelSupportOptions;
private _disposables: IDisposable[] = [];
constructor(options: IMouseWheelSupportOptions = {}) {
this.options = mixin(options, defaultOptions);
}
public init(grid: Slick.Grid<any>): void {
this.canvas = grid.getCanvasNode();
this.viewport = this.canvas.parentElement;
let onMouseWheel = (browserEvent: MouseWheelEvent) => {
let e = new StandardMouseWheelEvent(browserEvent);
this._onMouseWheel(e);
};
this._disposables.push(DOM.addDisposableListener(this.viewport, 'mousewheel', onMouseWheel));
this._disposables.push(DOM.addDisposableListener(this.viewport, 'DOMMouseScroll', onMouseWheel));
}
private _onMouseWheel(event: StandardMouseWheelEvent) {
const scrollHeight = this.canvas.clientHeight;
const height = this.viewport.clientHeight;
const scrollDown = Math.sign(event.deltaY) === -1;
if (scrollDown) {
if ((this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed)) + height > scrollHeight) {
this.viewport.scrollTop = scrollHeight - height;
this.viewport.dispatchEvent(new Event('scroll'));
} else {
this.viewport.scrollTop = this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed);
this.viewport.dispatchEvent(new Event('scroll'));
event.stopPropagation();
event.preventDefault();
}
} else {
if ((this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed)) < 0) {
this.viewport.scrollTop = 0;
this.viewport.dispatchEvent(new Event('scroll'));
} else {
this.viewport.scrollTop = this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed);
this.viewport.dispatchEvent(new Event('scroll'));
event.stopPropagation();
event.preventDefault();
}
}
}
destroy() {
dispose(this._disposables);
}
}

View File

@@ -63,7 +63,9 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
private formatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
if (dataContext) {
return `<span>${row}</span>`;
// row is zero-based, we need make it 1 based for display in the result grid
//
return `<span>${row + 1}</span>`;
}
return null;
}

View File

@@ -11,9 +11,17 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import * as DOM from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Widget } from 'vs/base/browser/ui/widget';
import { isArray, isBoolean } from 'vs/base/common/types';
import { Event, Emitter } from 'vs/base/common/event';
import { range } from 'vs/base/common/arrays';
export interface ITableContextMenuEvent {
anchor: HTMLElement | { x: number, y: number };
cell?: { row: number, cell: number };
}
export interface ITableStyles extends IListStyles {
tableHeaderBackground?: Color;
@@ -27,30 +35,46 @@ function getDefaultOptions<T>(): Slick.GridOptions<T> {
};
}
export class Table<T extends Slick.SlickData> extends Widget implements IThemable {
export interface ITableSorter<T> {
sort(args: Slick.OnSortEventArgs<T>);
}
export interface ITableConfiguration<T> {
dataProvider?: Slick.DataProvider<T> | Array<T>;
columns?: Slick.Column<T>[];
sorter?: ITableSorter<T>;
}
export class Table<T extends Slick.SlickData> extends Widget implements IThemable, IDisposable {
private styleElement: HTMLStyleElement;
private idPrefix: string;
private _grid: Slick.Grid<T>;
private _columns: Slick.Column<T>[];
private _data: TableDataView<T>;
private _data: Slick.DataProvider<T>;
private _sorter: ITableSorter<T>;
private _autoscroll: boolean;
private _onRowCountChangeListener: IDisposable;
private _container: HTMLElement;
private _tableContainer: HTMLElement;
private _classChangeTimeout: number;
constructor(parent: HTMLElement, data?: Array<T> | TableDataView<T>, columns?: Slick.Column<T>[], options?: Slick.GridOptions<T>) {
private _disposables: IDisposable[] = [];
private _onContextMenu = new Emitter<ITableContextMenuEvent>();
public readonly onContextMenu: Event<ITableContextMenuEvent> = this._onContextMenu.event;
constructor(parent: HTMLElement, configuration?: ITableConfiguration<T>, options?: Slick.GridOptions<T>) {
super();
if (data instanceof TableDataView) {
this._data = data;
if (!configuration || isArray(configuration.dataProvider)) {
this._data = new TableDataView<T>(configuration && configuration.dataProvider as Array<T>);
} else {
this._data = new TableDataView<T>(data);
this._data = configuration.dataProvider;
}
if (columns) {
this._columns = columns;
if (configuration.columns) {
this._columns = configuration.columns;
} else {
this._columns = new Array<Slick.Column<T>>();
}
@@ -81,15 +105,33 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._grid = new Slick.Grid<T>(this._tableContainer, this._data, this._columns, newOptions);
this.idPrefix = this._tableContainer.classList[0];
DOM.addClass(this._container, this.idPrefix);
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
this._grid.onSort.subscribe((e, args) => {
this._data.sort(args);
this._grid.invalidate();
this._grid.render();
if (configuration.sorter) {
this._sorter = configuration.sorter;
this._grid.onSort.subscribe((e, args) => {
this._sorter.sort(args);
this._grid.invalidate();
this._grid.render();
});
}
this._grid.onContextMenu.subscribe((e: JQuery.Event) => {
const originalEvent = e.originalEvent;
const cell = this._grid.getCellFromEvent(originalEvent);
const anchor = originalEvent instanceof MouseEvent ? { x: originalEvent.x, y: originalEvent.y } : originalEvent.srcElement as HTMLElement;
this._onContextMenu.fire({ anchor, cell });
});
}
private _handleRowCountChange() {
public dispose() {
dispose(this._disposables);
}
public invalidateRows(rows: number[], keepEditor: boolean) {
this._grid.invalidateRows(rows, keepEditor);
this._grid.render();
}
public updateRowCount() {
this._grid.updateRowCount();
this._grid.render();
if (this._autoscroll) {
@@ -113,20 +155,22 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
} else {
this._data = new TableDataView<T>(data);
}
this._onRowCountChangeListener.dispose();
this._grid.setData(this._data, true);
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
}
get columns(): Slick.Column<T>[] {
return this._grid.getColumns();
}
setSelectedRows(rows: number[]) {
this._grid.setSelectedRows(rows);
public setSelectedRows(rows: number[] | boolean) {
if (isBoolean(rows)) {
this._grid.setSelectedRows(range(this._grid.getDataLength()));
} else {
this._grid.setSelectedRows(rows);
}
}
getSelectedRows(): number[] {
public getSelectedRows(): number[] {
return this._grid.getSelectedRows();
}
@@ -143,21 +187,6 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
};
}
onContextMenu(fn: (e: Slick.EventData, data: Slick.OnContextMenuEventArgs<T>) => any): IDisposable;
onContextMenu(fn: (e: DOMEvent, data: Slick.OnContextMenuEventArgs<T>) => any): IDisposable;
onContextMenu(fn: any): IDisposable {
this._grid.onContextMenu.subscribe(fn);
return {
dispose: () => {
this._grid.onContextMenu.unsubscribe(fn);
}
};
}
getCellFromEvent(e: DOMEvent): Slick.Cell {
return this._grid.getCellFromEvent(e);
}
setSelectionModel(model: Slick.SelectionModel<T, Array<Slick.Range>>) {
this._grid.setSelectionModel(model);
}
@@ -194,7 +223,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._tableContainer.style.width = sizing.width + 'px';
this._tableContainer.style.height = sizing.height + 'px';
} else {
if (orientation === Orientation.HORIZONTAL) {
if (orientation === Orientation.VERTICAL) {
this._container.style.width = '100%';
this._container.style.height = sizing + 'px';
this._tableContainer.style.width = '100%';
@@ -207,6 +236,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
}
}
this.resizeCanvas();
this.autosizeColumns();
}
autosizeColumns() {

View File

@@ -25,7 +25,7 @@ export class TableBasicView<T> extends View {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
this._table = new Table<T>(this._container, { dataProvider: data, columns }, tableOpts);
}
public get table(): Table<T> {
@@ -59,7 +59,7 @@ export class TableHeaderView<T> extends HeaderView {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
this._table = new Table<T>(this._container, { dataProvider: data, columns }, tableOpts);
}
public get table(): Table<T> {
@@ -76,7 +76,7 @@ export class TableHeaderView<T> extends HeaderView {
}
protected layoutBody(size: number): void {
this._table.layout(size, Orientation.HORIZONTAL);
this._table.layout(size, Orientation.VERTICAL);
}
focus(): void {
@@ -99,7 +99,7 @@ export class TableCollapsibleView<T> extends AbstractCollapsibleView {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
this._table = new Table<T>(this._container, { dataProvider: data, columns }, tableOpts);
}
public render(container: HTMLElement, orientation: Orientation): void {
@@ -143,6 +143,6 @@ export class TableCollapsibleView<T> extends AbstractCollapsibleView {
}
protected layoutBody(size: number): void {
this._table.layout(size, Orientation.HORIZONTAL);
this._table.layout(size, Orientation.VERTICAL);
}
}

View File

@@ -42,7 +42,10 @@ export function resolveFilePath(uri: string, filePath: string, rootPath: string)
export function getRootPath(contextService: IWorkspaceContextService): string {
let isWorkspace = contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
if (isWorkspace) {
return contextService.getWorkspace().folders[0].uri.fsPath;
let folder = contextService.getWorkspace().folders[0];
if (folder && folder.uri) {
return folder.uri.fsPath;
}
}
return undefined;

View File

@@ -13,4 +13,11 @@ export const disabledInputBackground = registerColor('input.disabled.background'
export const disabledInputForeground = registerColor('input.disabled.foreground', { dark: '#888888', light: '#888888', hc: foreground }, nls.localize('disabledInputBoxForeground', "Disabled Input box foreground."));
export const buttonFocusOutline = registerColor('button.focusOutline', { dark: '#eaeaea', light: '#666666', hc: null }, nls.localize('buttonFocusOutline', "Button outline color when focused."));
export const listFocusAndSelectionBackground = registerColor('list.focusAndSelectionBackground', { dark: '#2c3295', light: '#2c3295', hc: null }, nls.localize('listFocusAndSelectionBackground', "List/Table background color for the selected and focus item when the list/table is active"));
export const listFocusAndSelectionBackground = registerColor('list.focusAndSelectionBackground', { dark: '#2c3295', light: '#2c3295', hc: null }, nls.localize('listFocusAndSelectionBackground', "List/Table background color for the selected and focus item when the list/table is active"));
// SQL Agent Colors
export const tableBackground = registerColor('agent.tableBackground', { light: '#fffffe', dark: '#333333', hc: Color.black }, nls.localize('agentTableBackground', "SQL Agent Table background color."));
export const cellBackground = registerColor('agent.cellBackground', { light: '#faf5f8', dark: Color.black, hc: Color.black }, nls.localize('agentCellBackground', "SQL Agent table cell background color."));
export const tableHoverBackground = registerColor('agent.tableHoverColor', { light: '#dcdcdc', dark: '#444444', hc: null }, nls.localize('agentTableHoverBackground', "SQL Agent table hover background color."));
export const jobsHeadingBackground = registerColor('agent.jobsHeadingColor', { light: '#f4f4f4', dark: '#444444', hc: '#2b56f2' }, nls.localize('agentJobsHeadingColor', "SQL Agent heading background color."));
export const cellBorderColor = registerColor('agent.cellBorderColor', { light: null, dark: null, hc: '#2b56f2' }, nls.localize('agentCellBorderColor', "SQL Agent table cell border color."));

View File

@@ -171,13 +171,17 @@ export class AccountDialog extends Modal {
if (!this.isEmptyLinkedAccount()) {
this.showSplitView();
} else {
this._splitViewContainer.hidden = true;
this._noaccountViewContainer.hidden = false;
this._addAccountButton.focus();
this.showNoAccountContainer();
}
}
private showNoAccountContainer() {
this._splitViewContainer.hidden = true;
this._noaccountViewContainer.hidden = false;
this._addAccountButton.focus();
}
private showSplitView() {
this._splitViewContainer.hidden = false;
this._noaccountViewContainer.hidden = true;
@@ -298,6 +302,10 @@ export class AccountDialog extends Modal {
this.showSplitView();
}
if (this.isEmptyLinkedAccount() && this._noaccountViewContainer.hidden) {
this.showNoAccountContainer();
}
this.layout();
}
}

View File

@@ -92,5 +92,10 @@ configurationRegistry.registerConfiguration({
'description': localize('sql.defaultEngineDescription', 'Default SQL Engine to use. This drives default language provider in .sql files and the default to use when creating a new connection. Valid option is currently MSSQL'),
'default': 'MSSQL'
},
'connection.parseClipboardForConnectionString': {
'type': 'boolean',
'default': true,
'description': localize('connection.parseClipboardForConnectionStringDescription', 'Attempt to parse the contents of the clipboard when the connection dialog is opened or a paste is performed.')
}
}
});
});

View File

@@ -159,7 +159,8 @@ export class GetCurrentConnectionStringAction extends Action {
if (activeInput && (activeInput instanceof QueryInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput)
&& this._connectionManagementService.isConnected(activeInput.uri)) {
let includePassword = false;
this._connectionManagementService.getConnectionString(activeInput.uri, includePassword).then(result => {
let connectionProfile = this._connectionManagementService.getConnectionProfile(activeInput.uri);
this._connectionManagementService.getConnectionString(connectionProfile.id, includePassword).then(result => {
let message = result
? result
: nls.localize('connectionAction.connectionString', "Connection string not available");

View File

@@ -59,6 +59,7 @@ export interface IConnectionResult {
errorCode: number;
callStack: string;
errorHandled?: boolean;
connectionProfile?: IConnectionProfile;
}
export interface IConnectionCallbacks {
@@ -130,15 +131,15 @@ export interface IConnectionManagementService {
onConnectionChangedNotification(handle: number, changedConnInfo: sqlops.ChangedConnectionInfo);
getConnectionGroups(): ConnectionProfileGroup[];
getConnectionGroups(providers?: string[]): ConnectionProfileGroup[];
getRecentConnections(): ConnectionProfile[];
getRecentConnections(providers?: string[]): ConnectionProfile[];
clearRecentConnectionsList(): void;
clearRecentConnection(connectionProfile: IConnectionProfile): void;
getActiveConnections(): ConnectionProfile[];
getActiveConnections(providers?: string[]): ConnectionProfile[];
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string>;
@@ -262,15 +263,37 @@ export interface IConnectionManagementService {
getActiveConnectionCredentials(profileId: string): { [name: string]: string };
/**
* Get the connection string for the provided connection profile
* Get the connection string for the provided connection ID
*/
getConnectionString(ownerUri: string, includePassword: boolean): Thenable<string>;
getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>;
/**
* Serialize connection string with optional provider
*/
buildConnectionInfo(connectionString: string, provider?: string): Thenable<sqlops.ConnectionInfo>;
}
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');
export interface IConnectionDialogService {
_serviceBrand: any;
/**
* Opens the connection dialog and returns the promise for successfully opening the dialog
* @param connectionManagementService
* @param params
* @param model
* @param connectionResult
*/
showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<void>;
/**
* Opens the connection dialog and returns the promise when connection is made
* or dialog is closed
* @param connectionManagementService
* @param params
* @param model
* @param connectionResult
*/
openDialogAndWait(connectionManagementService: IConnectionManagementService, params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<IConnectionProfile>;
}
export interface IServerGroupDialogCallbacks {
@@ -304,6 +327,7 @@ export interface INewConnectionParams {
runQueryOnCompletion?: RunQueryOnConnectionMode;
querySelection?: sqlops.ISelectionData;
showDashboard?: boolean;
providers?: string[];
}
export interface IConnectableInput {

View File

@@ -627,12 +627,12 @@ export class ConnectionManagementService extends Disposable implements IConnecti
}
}
public getConnectionGroups(): ConnectionProfileGroup[] {
return this._connectionStore.getConnectionProfileGroups();
public getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] {
return this._connectionStore.getConnectionProfileGroups(false, providers);
}
public getRecentConnections(): ConnectionProfile[] {
return this._connectionStore.getRecentlyUsedConnections();
public getRecentConnections(providers?: string[]): ConnectionProfile[] {
return this._connectionStore.getRecentlyUsedConnections(providers);
}
@@ -644,14 +644,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
}
public getActiveConnections(): ConnectionProfile[] {
public getActiveConnections(providers?: string[]): ConnectionProfile[] {
return this._connectionStatusManager.getActiveConnectionProfiles();
}
public getConnectionUriFromId(connectionId: string): string {
let connection = this.getActiveConnections().find(connection => connection.id === connectionId);
if (connection) {
return this.getConnectionUri(connection);
let connectionInfo = this._connectionStatusManager.findConnectionByProfileId(connectionId);
if (connectionInfo) {
return connectionInfo.ownerUri;
} else {
return undefined;
}
@@ -1002,14 +1002,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
let connectionMngInfo = this._connectionStatusManager.findConnection(uri);
if (connectionMngInfo && connectionMngInfo.deleted) {
this._connectionStatusManager.deleteConnection(uri);
resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true });
resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true, connectionProfile: connection });
} else {
if (errorMessage) {
// Connection to the server failed
this._connectionStatusManager.deleteConnection(uri);
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack });
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection });
} else {
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack });
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection });
}
}
});
@@ -1348,9 +1348,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
}
/**
* Get the connection string for the provided connection profile
* Get the connection string for the provided connection ID
*/
public getConnectionString(ownerUri: string, includePassword: boolean = false): Thenable<string> {
public getConnectionString(connectionId: string, includePassword: boolean = false): Thenable<string> {
let ownerUri = this.getConnectionUriFromId(connectionId);
if (!ownerUri) {
return Promise.resolve(undefined);
}
@@ -1366,4 +1368,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
});
});
}
/**
* Serialize connection with options provider
* TODO this could be a map reduce operation
*/
public buildConnectionInfo(connectionString: string, provider: string): Thenable<sqlops.ConnectionInfo> {
return this._providers.get(provider).onReady.then(e => {
return e.buildConnectionInfo(connectionString);
});
}
}

View File

@@ -153,6 +153,19 @@ export class ConnectionProfileGroup implements IConnectionProfileGroup {
return this.parent;
}
public isAncestorOf(node: ConnectionProfileGroup | ConnectionProfile): boolean {
let isAncestor = false;
let currentNode = node;
while (currentNode) {
if (currentNode.parent && currentNode.parent.id === this.id) {
isAncestor = true;
break;
}
currentNode = currentNode.parent;
}
return isAncestor;
}
public static getGroupFullNameParts(groupFullName: string): string[] {
groupFullName = groupFullName ? groupFullName : '';
let groupNames: string[] = groupFullName.split(ConnectionProfileGroup.GroupNameSeparator);

View File

@@ -22,14 +22,18 @@ export class ConnectionStatusManager {
this._providerCapabilitiesMap = {};
}
public findConnection(id: string): ConnectionManagementInfo {
if (id in this._connections) {
return this._connections[id];
public findConnection(uri: string): ConnectionManagementInfo {
if (uri in this._connections) {
return this._connections[uri];
} else {
return undefined;
}
}
public findConnectionByProfileId(profileId: string): ConnectionManagementInfo {
return Object.values(this._connections).find((connection: ConnectionManagementInfo) => connection.connectionProfile.id === profileId);
}
public findConnectionProfile(connectionProfile: IConnectionProfile): ConnectionManagementInfo {
let id = Utils.generateUri(connectionProfile);
return this.findConnection(id);
@@ -193,9 +197,14 @@ export class ConnectionStatusManager {
/**
* Get a list of the active connection profiles managed by the status manager
*/
public getActiveConnectionProfiles(): ConnectionProfile[] {
public getActiveConnectionProfiles(providers?: string[]): ConnectionProfile[] {
let profiles = Object.values(this._connections).map((connectionInfo: ConnectionManagementInfo) => connectionInfo.connectionProfile);
// Remove duplicate profiles that may be listed multiple times under different URIs by filtering for profiles that don't have the same ID as an earlier profile in the list
return profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index);
profiles = profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index);
if (providers) {
profiles = profiles.filter(f => providers.includes(f.providerName));
}
return profiles;
}
}

View File

@@ -213,13 +213,16 @@ export class ConnectionStore {
*
* @returns {sqlops.ConnectionInfo} the array of connections, empty if none are found
*/
public getRecentlyUsedConnections(): ConnectionProfile[] {
public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] {
let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections];
if (!configValues) {
configValues = [];
}
configValues = configValues.filter(c => !!(c));
if (providers && providers.length > 0) {
configValues = configValues.filter(c => providers.includes(c.providerName));
}
return this.convertConfigValuesToConnectionProfiles(configValues);
}
@@ -429,10 +432,13 @@ export class ConnectionStore {
});
}
public getConnectionProfileGroups(withoutConnections?: boolean): ConnectionProfileGroup[] {
public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] {
let profilesInConfiguration: ConnectionProfile[];
if (!withoutConnections) {
profilesInConfiguration = this._connectionConfig.getConnections(true);
if (providers && providers.length > 0) {
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName));
}
}
let groups = this._connectionConfig.getAllGroups();

View File

@@ -132,8 +132,8 @@ export class ConnectionController implements IConnectionComponentController {
}
}
private getAllServerGroups(): IConnectionProfileGroup[] {
var connectionGroupRoot = this._connectionManagementService.getConnectionGroups();
private getAllServerGroups(providers?: string[]): IConnectionProfileGroup[] {
var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(providers);
var connectionGroupNames: IConnectionProfileGroup[] = [];
if (connectionGroupRoot && connectionGroupRoot.length > 0) {
this.getServerGroupHelper(connectionGroupRoot[0], connectionGroupNames);
@@ -149,8 +149,8 @@ export class ConnectionController implements IConnectionComponentController {
return connectionGroupNames;
}
public initDialog(connectionInfo: IConnectionProfile): void {
this._connectionWidget.updateServerGroup(this.getAllServerGroups());
public initDialog(providers: string[], connectionInfo: IConnectionProfile): void {
this._connectionWidget.updateServerGroup(this.getAllServerGroups(providers));
this._model = connectionInfo;
this._model.providerName = this._providerName;
let appNameOption = this._providerOptions.find(option => option.specialValueType === ConnectionOptionSpecialType.appName);

View File

@@ -33,6 +33,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as types from 'vs/base/common/types';
import { trim } from 'vs/base/common/strings';
import { Deferred } from 'sql/base/common/promise';
export interface IConnectionValidateResult {
isValid: boolean;
@@ -49,7 +50,7 @@ export interface IConnectionComponentCallbacks {
export interface IConnectionComponentController {
showUiComponent(container: HTMLElement): void;
initDialog(model: IConnectionProfile): void;
initDialog(providers: string[], model: IConnectionProfile): void;
validateConnection(): IConnectionValidateResult;
fillInConnectionInputs(connectionInfo: IConnectionProfile): void;
handleOnConnecting(): void;
@@ -75,6 +76,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
private _currentProviderType: string = 'Microsoft SQL Server';
private _connecting: boolean = false;
private _connectionErrorTitle = localize('connectionError', 'Connection error');
private _dialogDeferredPromise: Deferred<IConnectionProfile>;
constructor(
@IPartService private _partService: IPartService,
@@ -82,17 +84,24 @@ export class ConnectionDialogService implements IConnectionDialogService {
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IWindowsService private _windowsService: IWindowsService,
@IClipboardService private _clipboardService: IClipboardService,
@ICommandService private _commandService: ICommandService
) { }
private getDefaultProviderName() {
if (this._workspaceConfigurationService) {
let defaultProvider = WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, Constants.defaultEngine);
let defaultProvider: string;
if (this._providerNameToDisplayNameMap) {
let keys = Object.keys(this._providerNameToDisplayNameMap);
if (keys && keys.length > 0) {
defaultProvider = keys[0];
}
}
if (!defaultProvider && this._workspaceConfigurationService) {
defaultProvider = WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, Constants.defaultEngine);
}
// as a fallback, default to MSSQL if the value from settings is not available
return Constants.mssqlProviderName;
return defaultProvider || Constants.mssqlProviderName;
}
private handleOnConnect(params: INewConnectionParams, profile?: IConnectionProfile): void {
@@ -148,26 +157,32 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._connecting = false;
}
this.uiController.databaseDropdownExpanded = false;
if (this._dialogDeferredPromise) {
this._dialogDeferredPromise.resolve(undefined);
}
}
private handleDefaultOnConnect(params: INewConnectionParams, connection: IConnectionProfile): Thenable<void> {
let fromEditor = params && params.connectionType === ConnectionType.editor;
let uri: string = undefined;
if (fromEditor && params.input) {
if (fromEditor && params && params.input) {
uri = params.input.uri;
}
let options: IConnectionCompletionOptions = {
params: params,
saveTheConnection: !fromEditor,
showDashboard: params.showDashboard !== undefined ? params.showDashboard : !fromEditor,
showDashboard: params && params.showDashboard !== undefined ? params.showDashboard : !fromEditor,
showConnectionDialogOnError: false,
showFirewallRuleOnError: true
};
return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params.input).then(connectionResult => {
return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params && params.input).then(connectionResult => {
this._connecting = false;
if (connectionResult && connectionResult.connected) {
this._connectionDialog.close();
if (this._dialogDeferredPromise) {
this._dialogDeferredPromise.resolve(connectionResult.connectionProfile);
}
} else if (connectionResult && connectionResult.errorHandled) {
this._connectionDialog.resetConnection();
} else {
@@ -213,7 +228,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
}
private handleInitDialog() {
this.uiController.initDialog(this._model);
this.uiController.initDialog(this._params && this._params.providers, this._model);
}
private handleFillInConnectionInputs(connectionInfo: IConnectionProfile): void {
@@ -265,9 +280,25 @@ export class ConnectionDialogService implements IConnectionDialogService {
});
}
public openDialogAndWait(connectionManagementService: IConnectionManagementService,
params?: INewConnectionParams,
model?: IConnectionProfile,
connectionResult?: IConnectionResult): Thenable<IConnectionProfile> {
this._dialogDeferredPromise = new Deferred<IConnectionProfile>();
this.showDialog(connectionManagementService,
params,
model,
connectionResult).then(() => {
}, error => {
this._dialogDeferredPromise.reject(error);
});
return this._dialogDeferredPromise;
}
public showDialog(
connectionManagementService: IConnectionManagementService,
params: INewConnectionParams,
params?: INewConnectionParams,
model?: IConnectionProfile,
connectionResult?: IConnectionResult): Thenable<void> {
@@ -297,11 +328,12 @@ export class ConnectionDialogService implements IConnectionDialogService {
});
}
private doShowDialog(params: INewConnectionParams): TPromise<void> {
if (!this._connectionDialog) {
let container = document.getElementById(this._partService.getWorkbenchElementId()).parentElement;
this._container = container;
this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName]);
this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName], this._providerNameToDisplayNameMap);
this._connectionDialog.onCancel(() => {
this._connectionDialog.databaseDropdownExpanded = this.uiController.databaseDropdownExpanded;
this.handleOnCancel(this._connectionDialog.newConnectionParams);
@@ -316,7 +348,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._connectionDialog.newConnectionParams = params;
return new TPromise<void>(() => {
this._connectionDialog.open(this._connectionManagementService.getRecentConnections().length > 0);
this._connectionDialog.open(this._connectionManagementService.getRecentConnections(params.providers).length > 0);
this.uiController.focusOnOpen();
});
}

View File

@@ -34,7 +34,6 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as styler from 'vs/platform/theme/common/styler';
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -59,6 +58,7 @@ export class ConnectionDialogWidget extends Modal {
private $connectionUIContainer: Builder;
private _databaseDropdownExpanded: boolean;
private _actionbar: ActionBar;
private _providers: string[];
private _panel: TabbedPanel;
private _recentConnectionTabId: PanelTabIdentifier;
@@ -84,6 +84,7 @@ export class ConnectionDialogWidget extends Modal {
constructor(
private providerTypeOptions: string[],
private selectedProviderType: string,
private providerNameToDisplayNameMap: { [providerDisplayName: string]: string },
@IInstantiationService private _instantiationService: IInstantiationService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IWorkbenchThemeService private _themeService: IWorkbenchThemeService,
@@ -96,6 +97,22 @@ export class ConnectionDialogWidget extends Modal {
super(localize('connection', 'Connection'), TelemetryKeys.Connection, _partService, telemetryService, contextKeyService, { hasSpinner: true, hasErrors: true });
}
public refresh(): void {
let filteredProviderTypes = this.providerTypeOptions;
if (this._newConnectionParams && this._newConnectionParams.providers) {
let validProviderNames = Object.keys(this.providerNameToDisplayNameMap).filter(x => this.includeProvider(x, this._newConnectionParams));
if (validProviderNames && validProviderNames.length > 0) {
filteredProviderTypes = filteredProviderTypes.filter(x => validProviderNames.find( v => this.providerNameToDisplayNameMap[v] === x) !== undefined);
}
}
this._providerTypeSelectBox.setOptions(filteredProviderTypes);
}
private includeProvider(providerName: string, params?: INewConnectionParams): Boolean {
return params === undefined || params.providers === undefined || params.providers.find(x => x === providerName) !== undefined;
}
protected renderBody(container: HTMLElement): void {
let connectionContainer = $('.connection-dialog');
container.appendChild(connectionContainer.getHTMLElement());
@@ -147,7 +164,7 @@ export class ConnectionDialogWidget extends Modal {
this._panel.onTabChange(c => {
if (c === savedConnectionTabId && this._savedConnectionTree.getContentHeight() === 0) {
// Update saved connection tree
TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService);
TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService, this._providers);
if (this._savedConnectionTree.getContentHeight() > 0) {
this._noSavedConnectionBuilder.hide();
@@ -358,6 +375,7 @@ export class ConnectionDialogWidget extends Modal {
*/
public open(recentConnections: boolean) {
this._panel.showTab(this._recentConnectionTabId);
this.show();
if (recentConnections) {
this._noRecentConnectionBuilder.hide();
@@ -366,7 +384,7 @@ export class ConnectionDialogWidget extends Modal {
this._recentConnectionBuilder.hide();
this._noRecentConnectionBuilder.show();
}
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService);
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService, this._providers);
// reset saved connection tree
this._savedConnectionTree.setInput([]);
@@ -415,6 +433,8 @@ export class ConnectionDialogWidget extends Modal {
public set newConnectionParams(params: INewConnectionParams) {
this._newConnectionParams = params;
this._providers = params && params.providers;
this.refresh();
}
public updateProvider(displayName: string) {

View File

@@ -19,6 +19,9 @@ import * as Constants from 'sql/parts/connection/common/constants';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { attachInputBoxStyler, attachButtonStyler, attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from '../common/connectionProfile';
import * as sqlops from 'sqlops';
@@ -32,6 +35,8 @@ import { OS, OperatingSystem } from 'vs/base/common/platform';
import { Builder, $ } from 'vs/base/browser/builder';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class ConnectionWidget {
private _builder: Builder;
@@ -85,7 +90,12 @@ export class ConnectionWidget {
callbacks: IConnectionComponentCallbacks,
providerName: string,
@IThemeService private _themeService: IThemeService,
@IContextViewService private _contextViewService: IContextViewService) {
@IContextViewService private _contextViewService: IContextViewService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IClipboardService private _clipboardService: IClipboardService,
@IConfigurationService private _configurationService: IConfigurationService
) {
this._callbacks = callbacks;
this._toDispose = [];
this._optionsMaps = {};
@@ -120,9 +130,29 @@ export class ConnectionWidget {
if (this._authTypeSelectBox) {
this.onAuthTypeSelected(this._authTypeSelectBox.value);
}
DOM.addDisposableListener(container, 'paste', e => {
this._handleClipboard();
});
DOM.append(container, this._builder.getHTMLElement());
}
private _handleClipboard(): void {
if (this._configurationService.getValue<boolean>('connection.parseClipboardForConnectionString')) {
let paste = this._clipboardService.readText();
this._connectionManagementService.buildConnectionInfo(paste, this._providerName).then(e => {
if (e) {
let profile = new ConnectionProfile(this._capabilitiesService, this._providerName);
profile.options = e.options;
if (profile.serverName) {
this.initDialog(profile);
}
}
});
}
}
private fillInConnectionForm(): void {
let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName];
let serverNameBuilder = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input');
@@ -130,7 +160,7 @@ export class ConnectionWidget {
validationOptions: {
validation: (value: string) => {
if (!value) {
return ({ type: MessageType.ERROR, content: localize('connectionWidget.missingRequireField', '{0} is required.', serverNameOption.displayName)});
return ({ type: MessageType.ERROR, content: localize('connectionWidget.missingRequireField', '{0} is required.', serverNameOption.displayName) });
} else if (startsWith(value, ' ') || endsWith(value, ' ')) {
return ({ type: MessageType.WARNING, content: localize('connectionWidget.fieldWillBeTrimmed', '{0} will be trimmed.', serverNameOption.displayName) });
}
@@ -173,7 +203,7 @@ export class ConnectionWidget {
placeholder: this._defaultDatabaseName,
maxHeight: 125,
ariaLabel: databaseOption.displayName,
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
actionLabel: localize('connectionWidget.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
let serverGroupLabel = localize('serverGroup', 'Server group');
@@ -354,6 +384,7 @@ export class ConnectionWidget {
}
public focusOnOpen(): void {
this._handleClipboard();
this._serverNameInputBox.focus();
this.focusPasswordIfNeeded();
this.clearValidationMessages();

View File

@@ -69,7 +69,7 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardContainerContrib | IDashboar
return;
}
if (Object.keys(container).length !== 1) {
extension.collector.error(localize('dashboardTab.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space.'));
extension.collector.error(localize('dashboardContainer.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space.'));
return;
}

View File

@@ -33,6 +33,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { TPromise } from 'vs/base/common/winjs.base';
import { toDisposable } from 'vs/base/common/lifecycle';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
@@ -72,7 +73,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IStorageService) private storageService: IStorageService,
@Inject(IWorkspaceContextService) private workspaceContextService: IWorkspaceContextService,
@Inject(IConfigurationService) private readonly _configurationService: IConfigurationService
) {
super();
this.insightConfig = <IInsightsConfig>this._config.widget['insights-widget'];
@@ -231,7 +232,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
if (componentInstance.setConfig) {
componentInstance.setConfig(this.insightConfig.type[this._typeKey]);
}
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) };
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => (item.invariantCultureDisplayValue === null || item.invariantCultureDisplayValue === undefined) ? item.displayValue : item.invariantCultureDisplayValue)) };
if (componentInstance.init) {
componentInstance.init();
@@ -275,6 +276,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
this._typeKey = Object.keys(this.insightConfig.type)[0];
// When the editor.accessibilitySupport setting is on, we will force the chart type to be table.
// so that the information is accessible to the user.
// count chart type is already a text based chart, we don't have to apply this rule for it.
let isAccessibilitySupportOn = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
if (isAccessibilitySupportOn && this._typeKey !== 'count') {
this._typeKey = 'table';
}
if (types.isStringArray(this.insightConfig.query)) {
this.insightConfig.query = this.insightConfig.query.join(' ');
} else if (this.insightConfig.queryFile) {

View File

@@ -19,6 +19,8 @@ import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/theme
import * as nls from 'vs/nls';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
declare var Chart: any;
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
@@ -81,8 +83,8 @@ export interface IChartConfig {
}
export const defaultChartConfig: IChartConfig = {
labelFirstColumn: false,
columnsAsLabels: false,
labelFirstColumn: true,
columnsAsLabels: true,
legendPosition: LegendPosition.Top,
dataDirection: DataDirection.Vertical
};
@@ -161,11 +163,17 @@ export abstract class ChartInsight extends Disposable implements IInsightsView {
protected updateTheme(e: IColorTheme): void {
let foregroundColor = e.getColor(colors.editorForeground);
let foreground = foregroundColor ? foregroundColor.toString() : null;
let backgroundColor = e.getColor(colors.editorBackground);
let background = backgroundColor ? backgroundColor.toString() : null;
let options = {
legend: {
labels: {
fontColor: foreground
}
},
viewArea: {
backgroundColor: background
}
};
this.options = mixin({}, mixin(this.options, options));
@@ -348,3 +356,13 @@ function isValidData(data: IInsightData): boolean {
return true;
}
Chart.pluginService.register({
beforeDraw: function (chart) {
if (chart.config.options.viewArea && chart.config.options.viewArea.backgroundColor) {
var ctx = chart.chart.ctx;
ctx.fillStyle = chart.config.options.viewArea.backgroundColor;
ctx.fillRect(0, 0, chart.chart.width, chart.chart.height);
}
}
});

View File

@@ -24,7 +24,7 @@ const properties: IJSONSchema = {
},
yAxisLabel: {
type: 'string',
description: nls.localize('yAxisLabel', "Label for the y axis")
description: nls.localize('barchart.yAxisLabel', "Label for the y axis")
},
xAxisMin: {
type: 'number',
@@ -36,7 +36,7 @@ const properties: IJSONSchema = {
},
xAxisLabel: {
type: 'string',
description: nls.localize('xAxisLabel', "Label for the x axis")
description: nls.localize('barchart.xAxisLabel', "Label for the x axis")
}
}
};

View File

@@ -62,7 +62,7 @@ export default class TableInsight extends Disposable implements IInsightsView, O
private createTable() {
if (!this.table) {
this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true });
this.table = new Table(this._elementRef.nativeElement, { dataProvider: this.dataView, columns: this.columns }, { showRowNumber: true });
this.table.setSelectionModel(new CellSelectionModel());
this._register(attachTableStyler(this.table, this.themeService));
}

View File

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

View File

@@ -236,7 +236,7 @@ export class RestoreDialog extends Modal {
{
strictSelection: false,
ariaLabel: LocalizedStrings.TARGETDATABASE,
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
actionLabel: localize('restoreDialog.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
}
);
this._databaseDropdown.onValueChange(s => {
@@ -272,7 +272,8 @@ export class RestoreDialog extends Modal {
this._restorePlanTableContainer = labelContainer.getHTMLElement();
labelContainer.hide();
this._restorePlanData = new TableDataView<Slick.SlickData>();
this._restorePlanTable = new Table<Slick.SlickData>(labelContainer.getHTMLElement(), this._restorePlanData, this._restorePlanColumn, { enableColumnReorder: false });
this._restorePlanTable = new Table<Slick.SlickData>(labelContainer.getHTMLElement(),
{ dataProvider: this._restorePlanData, columns: this._restorePlanColumn }, { enableColumnReorder: false });
this._restorePlanTable.setSelectionModel(new RowSelectionModel({ selectActiveRow: false }));
this._restorePlanTable.onSelectedRowsChanged((e, data) => this.backupFileCheckboxChanged(e, data));
});
@@ -328,7 +329,8 @@ export class RestoreDialog extends Modal {
field: 'restoreAs'
}];
this._fileListData = new TableDataView<FileListElement>();
this._fileListTable = new Table<FileListElement>(fileNameContainer.getHTMLElement(), this._fileListData, columns, { enableColumnReorder: false });
this._fileListTable = new Table<FileListElement>(fileNameContainer.getHTMLElement(),
{ dataProvider : this._fileListData, columns } , { enableColumnReorder: false });
this._fileListTable.setSelectionModel(new RowSelectionModel());
});
});

View File

@@ -15,7 +15,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export class FileBrowserController extends treedefaults.DefaultController {
constructor() {
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN });
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN, openMode: treedefaults.OpenMode.SINGLE_CLICK });
}
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean {

View File

@@ -4,14 +4,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISlickColumn, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
export interface ISlickRange {
fromCell: number;
fromRow: number;
toCell: number;
toRow: number;
}
import { ISlickColumn, VirtualizedCollection } from 'angular2-slickgrid';
export interface IGridIcon {
showCondition: () => boolean;
@@ -41,7 +34,7 @@ export interface IGridIcon {
}
export interface IGridDataSet {
dataRows: IObservableCollection<IGridDataRow>;
dataRows: VirtualizedCollection<{}>;
columnDefinitions: ISlickColumn<any>[];
resized: any; // EventEmitter<any>;
totalRows: number;
@@ -61,7 +54,7 @@ export enum SaveFormat {
export interface IGridInfo {
batchIndex: number;
resultSetNumber: number;
selection: ISlickRange[];
selection: Slick.Range[];
gridIndex: number;
rowIndex?: number;
}
@@ -69,5 +62,5 @@ export interface ISaveRequest {
format: SaveFormat;
batchIndex: number;
resultSetNumber: number;
selection: ISlickRange[];
selection: Slick.Range[];
}

View File

@@ -9,170 +9,168 @@
*/
.errorMessage {
color: var(--color-error);
color: var(--color-error);
}
.batchMessage {
padding-left: 20px;
padding-left: 20px;
}
.slick-cell a, a:link {
color: var(--color-grid-link);
text-decoration: underline;
color: var(--color-grid-link);
text-decoration: underline;
}
.slick-cell a:hover {
color: var(--color-grid-link-hover);
color: var(--color-grid-link-hover);
}
.resultsMessageValue a, a:link {
color: var(--color-grid-link);
text-decoration: underline;
color: var(--color-grid-link);
text-decoration: underline;
}
.resultsMessageValue a:hover {
color: var(--color-grid-link-hover);
color: var(--color-grid-link-hover);
}
.grid .slick-cell.dirtyCell {
color: var(--color-grid-dirty-text);
background-color: var(--color-grid-dirty-background);
color: var(--color-grid-dirty-text);
background-color: var(--color-grid-dirty-background);
}
.grid .slick-cell.dirtyRowHeader {
background-color: var(--color-grid-dirty-background);
background-color: var(--color-grid-dirty-background);
}
.slick-cell.dirtyRowHeader > .row-number {
color: var(--color-grid-dirty-text);
font-weight: 500;
color: var(--color-grid-dirty-text);
font-weight: 500;
}
/*
* vs theme
*
*/
.vs .slickgridContainer {
--color-content: #101010;
--color-content-disabled: #a9a9a9;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2);
--color-resize-handle: grey;
--color-bg-content-header: #F5F5F5; /* used for color of grid headers */
--color-cell-border-active: grey;
--color-cell-bg-grid-selected: rgb(173, 214, 255);
--color-grid-link: #0078D7;
--color-grid-link-hover: #0b93ff;
--color-grid-dirty-background: #CCC;
--color-grid-dirty-text: #101010;
--color-content: #101010;
--color-content-disabled: #a9a9a9;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2);
--color-resize-handle: grey;
--color-bg-content-header: #F5F5F5; /* used for color of grid headers */
--color-cell-border-active: grey;
--color-cell-bg-grid-selected: rgb(173, 214, 255);
--color-grid-link: #0078D7;
--color-grid-link-hover: #0b93ff;
--color-grid-dirty-background: #CCC;
--color-grid-dirty-text: #101010;
}
/* grid styling */
.vs slick-grid.active .grid .slick-cell.active {
border: dotted 1px var(--color-cell-border-active);
border-color: var(--color-cell-border-active);
}
.vs slick-grid.active .grid .slick-cell.selected {
background-color: var(--color-cell-bg-grid-selected);
background-color: var(--color-cell-bg-grid-selected);
}
.vs .grid .slick-cell.selected .grid-cell-value-container.missing-value {
color: var(--color-content) !important;
color: var(--color-content) !important;
}
.vs .boxRow.content.horzBox.slickgrid {
border: solid 1px #EEEEF2;
border: solid 1px #EEEEF2;
}
/* icons */
.vs .gridIcon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode */
background-image: url("extendFullScreen.svg");
.vs .icon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode */
background-image: url("extendFullScreen.svg");
}
.vs .gridIcon.exitFullScreen {
/* ExitFullScreen_16x_vscode */
background-image: url("exitFullScreen.svg");
.vs .icon.exitFullScreen {
/* ExitFullScreen_16x_vscode */
background-image: url("exitFullScreen.svg");
}
.vs .gridIcon.saveJson {
/* ResultToJSON_16x_vscode */
background-image: url("saveJson.svg");
.vs .icon.saveJson {
/* ResultToJSON_16x_vscode */
background-image: url("saveJson.svg");
}
.vs .gridIcon.saveCsv {
/* ResultToCSV_16x_vscode */
background-image: url("saveCsv.svg");
.vs .icon.saveCsv {
/* ResultToCSV_16x_vscode */
background-image: url("saveCsv.svg");
}
.vs .gridIcon.saveExcel {
/* ResultToXlsx_16x_vscode */
background-image: url("saveExcel.svg");
.vs .icon.saveExcel {
/* ResultToXlsx_16x_vscode */
background-image: url("saveExcel.svg");
}
.vs .gridIcon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart.svg");
.vs .icon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart.svg");
}
/* headers */
.vs .resultsMessageHeader {
background: var(--color-bg-header);
color: var(--color-content);
background: var(--color-bg-header);
color: var(--color-content);
}
.vs .resultsViewCollapsible:not(.collapsed) {
background-image: url("uncollapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
background-image: url("uncollapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
}
.vs .resultsViewCollapsible {
background-image: url("collapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
background-image: url("collapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
}
.vs .queryResultsShortCut {
color: grey;
color: grey;
}
/* scroll bar */
.vs ::-webkit-scrollbar {
width: 14px;
height: 10px;
width: 14px;
height: 10px;
}
.vs ::-webkit-scrollbar-thumb {
background: hsla(0,0%,47%,.4);
background: hsla(0,0%,47%,.4);
}
.vs ::-webkit-scrollbar-thumb:hover {
background: hsla(0,0%,39%,.7);
background: hsla(0,0%,39%,.7);
}
.vs ::-webkit-scrollbar-thumb:active {
background: rgba(85,85,85,0.8);
background: rgba(85,85,85,0.8);
}
.vs ::-webkit-scrollbar-track {
background: var(--background-color);
background: var(--background-color);
}
.vs ::-webkit-scrollbar-corner {
background: transparent;
background: transparent;
}
.vs .monaco-workbench input {
color: var(--color-content);
color: var(--color-content);
}
.vs .monaco-workbench .input {
background-color: white;
background-color: white;
}
/*
@@ -181,131 +179,139 @@
*/
.vs-dark .slickgridContainer {
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: white;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #4d4d4d;
--color-grid-dirty-text: #E5E5E5;
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: white;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #4d4d4d;
--color-grid-dirty-text: #E5E5E5;
}
/* grid styling */
.vs-dark slick-grid.active .grid .slick-cell.active {
border: dotted 1px var(--color-cell-border-active);
border-color: var(--color-cell-border-active);
}
.vs-dark slick-grid.active .grid .slick-cell.selected {
background-color: var(--color-cell-bg-grid-selected);
background-color: var(--color-cell-bg-grid-selected);
}
.vs-dark .grid .slick-cell.selected .grid-cell-value-container.missing-value {
color: var(--color-content) !important;
color: var(--color-content) !important;
}
.vs-dark .boxRow.content.horzBox.slickgrid {
border: solid 1px #2D2D30;
border: solid 1px #2D2D30;
}
/* icons */
.vs-dark .gridIcon.extendFullScreen,
.hc-black .gridIcon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode_inverse.svg */
background-image: url("extendFullScreen_inverse.svg");
.vs-dark .icon.extendFullScreen,
.hc-black .icon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode_inverse.svg */
background-image: url("extendFullScreen_inverse.svg");
}
.vs-dark .gridIcon.exitFullScreen,
.hc-black .gridIcon.exitFullScreen {
/* ExitFullScreen_16x_vscode_inverse.svg */
background-image: url("exitFullScreen_inverse.svg");
.vs-dark .icon.exitFullScreen,
.hc-black .icon.exitFullScreen {
/* ExitFullScreen_16x_vscode_inverse.svg */
background-image: url("exitFullScreen_inverse.svg");
}
.vs-dark .gridIcon.saveJson,
.hc-black .gridIcon.saveJson {
/* ResultToJSON_16x_vscode_inverse.svg */
background-image: url("saveJson_inverse.svg");
.vs-dark .icon.saveJson,
.hc-black .icon.saveJson {
/* ResultToJSON_16x_vscode_inverse.svg */
background-image: url("saveJson_inverse.svg");
}
.vs-dark .gridIcon.saveCsv,
.hc-black .gridIcon.saveCsv {
/* ResultToCSV_16x_vscode_inverse.svg */
background-image: url("saveCsv_inverse.svg");
.vs-dark .icon.saveCsv,
.hc-black .icon.saveCsv {
/* ResultToCSV_16x_vscode_inverse.svg */
background-image: url("saveCsv_inverse.svg");
}
.vs-dark .gridIcon.saveExcel,
.hc-black .gridIcon.saveExcel {
/* ResultToXlsx_16x_vscode_inverse.svg */
background-image: url("saveExcel_inverse.svg");
.vs-dark .icon.saveExcel,
.hc-black .icon.saveExcel {
/* ResultToXlsx_16x_vscode_inverse.svg */
background-image: url("saveExcel_inverse.svg");
}
.vs-dark .gridIcon.viewChart,
.hc-black .gridIcon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart_inverse.svg");
.vs-dark .icon.viewChart,
.hc-black .icon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart_inverse.svg");
}
.grid-panel .action-label.icon {
height: 35px;
line-height: 35px;
min-width: 28px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
}
/* headers */
.vs-dark .resultsMessageHeader {
background: var(--color-bg-header);
color: var(--color-content);
background: var(--color-bg-header);
color: var(--color-content);
}
.vs-dark .resultsViewCollapsible:not(.collapsed),
.hc-black .resultsViewCollapsible:not(.collapsed) {
background-image:url("uncollapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
background-image:url("uncollapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
}
.vs-dark .resultsViewCollapsible,
.hc-black .resultsViewCollapsible {
background-image: url("collapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
background-image: url("collapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
}
.vs-dark .queryResultsShortCut {
color: grey;
color: grey;
}
/* scroll bar */
.vs-dark ::-webkit-scrollbar {
width: 14px;
height: 10px;
width: 14px;
height: 10px;
}
.vs-dark ::-webkit-scrollbar-thumb {
background: hsla(0,0%,47%,.4);
background: hsla(0,0%,47%,.4);
}
.vs-dark ::-webkit-scrollbar-thumb:hover {
background: hsla(0,0%,39%,.7);
background: hsla(0,0%,39%,.7);
}
.vs-dark ::-webkit-scrollbar-thumb:active {
background: rgba(85,85,85,0.8);
background: rgba(85,85,85,0.8);
}
.vs-dark ::-webkit-scrollbar-track {
background: var(--background-color);
background: var(--background-color);
}
.vs-dark ::-webkit-scrollbar-corner {
background: transparent;
background: transparent;
}
.vs-dark .monaco-workbench input, .vs-dark .monaco-workbench .input {
color: var(--color-content);
background-color: #3C3C3C;
color: var(--color-content);
background-color: #3C3C3C;
}
/*
@@ -313,55 +319,55 @@
*
*/
.hc-black .slickgridContainer {
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: orange;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #FFF;
--color-grid-dirty-text: #000;
.hc-black .slickgridContainer {
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: orange;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #FFF;
--color-grid-dirty-text: #000;
}
/* grid styling */
.hc-black slick-grid.active .grid .slick-cell.active {
border: solid 1px var(--color-cell-border-active);
border-color: var(--color-cell-border-active);
}
.hc-black slick-grid.active .grid .slick-cell.selected {
background-color: var(--color-cell-bg-grid-selected);
background-color: var(--color-cell-bg-grid-selected);
}
.hc-black .grid .slick-cell.selected .grid-cell-value-container.missing-value {
color: var(--color-content) !important;
color: var(--color-content) !important;
}
.hc-black .boxRow.content.horzBox.slickgrid {
border: solid 1px #2D2D30;
border: solid 1px #2D2D30;
}
/* headers */
.hc-black .resultsMessageHeader {
background: var(--color-bg-header);
color: var(--color-content);
background: var(--color-bg-header);
color: var(--color-content);
}
.hc-black .queryResultsShortCut {
color: grey;
color: grey;
}
/* scroll bar */
.hc-black ::-webkit-scrollbar {
width: 14px;
height: 10px;
width: 14px;
height: 10px;
}
.hc-black ::-webkit-scrollbar-thumb {
@@ -377,14 +383,14 @@
}
.hc-black ::-webkit-scrollbar-track {
background: var(--background-color);
background: var(--background-color);
}
.hc-black ::-webkit-scrollbar-corner {
background: transparent;
background: transparent;
}
.hc-black .monaco-workbench input {
color: #000;
background-color: #FFF;
color: #000;
background-color: #FFF;
}

View File

@@ -14,7 +14,6 @@ import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
import { ISaveRequest } from 'sql/parts/grid/common/interfaces';
import { ISlickRange } from 'angular2-slickgrid';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
@@ -184,7 +183,7 @@ export class DataService {
* @param resultId The result id of the result to copy from
* @param includeHeaders [Optional]: Should column headers be included in the copy selection
*/
copyResults(selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void {
copyResults(selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void {
this._queryModel.copyResults(this._uri, selection, batchId, resultId, includeHeaders);
}

View File

@@ -18,12 +18,9 @@
showHeader="true"
[resized]="dataSet.resized"
[plugins]="plugins[i]"
(activeCellChanged)="onActiveCellChanged($event)"
(cellEditBegin)="onCellEditBegin($event)"
(cellEditExit)="onCellEditEnd($event)"
(rowEditBegin)="onRowEditBegin($event)"
(rowEditExit)="onRowEditEnd($event)"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
(onActiveCellChanged)="onActiveCellChanged($event)"
(onCellChange)="onCellEditEnd($event)"
(onContextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
[isCellEditValid]="onIsCellEditValid"
[overrideCellFn]="overrideCellFn"
enableEditing="true"

View File

@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import 'vs/css!./media/editData';
import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core';
import { IGridDataRow, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import { VirtualizedCollection } from 'angular2-slickgrid';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -71,18 +71,15 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// Edit Data functions
public onActiveCellChanged: (event: { row: number, column: number }) => void;
public onCellEditEnd: (event: { row: number, column: number, newValue: any }) => void;
public onCellEditBegin: (event: { row: number, column: number }) => void;
public onRowEditBegin: (event: { row: number }) => void;
public onRowEditEnd: (event: { row: number }) => void;
public onActiveCellChanged: (event: Slick.OnActiveCellChangedEventArgs<any>) => void;
public onCellEditEnd: (event: Slick.OnCellChangeEventArgs<any>) => void;
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public onIsColumnEditable: (column: number) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
public loadDataFunction: (offset: number, count: number) => Promise<{}[]>;
private savedViewState: {
gridSelections: ISlickRange[];
gridSelections: Slick.Range[];
scrollTop;
scrollLeft;
};
@@ -156,6 +153,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.dataSet = undefined;
self.placeHolderDataSets = [];
self.renderedDataSets = self.placeHolderDataSets;
this._cd.detectChanges();
self.totalElapsedTimeSpan = undefined;
self.complete = false;
@@ -167,17 +165,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onActiveCellChanged = this.onCellSelect;
this.onCellEditEnd = (event: { row: number, column: number, newValue: any }): void => {
this.onCellEditEnd = (event: Slick.OnCellChangeEventArgs<any>): void => {
// Store the value that was set
self.currentEditCellValue = event.newValue;
self.currentEditCellValue = event.item[event.cell - 1];
};
this.onCellEditBegin = (event: { row: number, column: number }): void => { };
this.onRowEditBegin = (event: { row: number }): void => { };
this.onRowEditEnd = (event: { row: number }): void => { };
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
let returnVal = '';
if (Services.DBCellValue.isDBCellValue(value)) {
@@ -189,23 +181,31 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
};
// Setup a function for generating a promise to lookup result subsets
this.loadDataFunction = (offset: number, count: number): Promise<IGridDataRow[]> => {
return new Promise<IGridDataRow[]>((resolve, reject) => {
this.loadDataFunction = (offset: number, count: number): Promise<{}[]> => {
return new Promise<{}[]>((resolve, reject) => {
self.dataService.getEditRows(offset, count).subscribe(result => {
let rowIndex = offset;
let gridData: IGridDataRow[] = result.subset.map(row => {
self.idMapping[rowIndex] = row.id;
rowIndex++;
return {
values: [{}].concat(row.cells.map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})), row: row.id
};
let gridData = result.subset.map(r => {
let dataWithSchema = {};
// skip the first column since its a number column
for (let i = 1; i < this.dataSet.columnDefinitions.length; i++) {
dataWithSchema[this.dataSet.columnDefinitions[i].field] = r.cells[i - 1].displayValue;
}
return dataWithSchema;
});
// let rowIndex = offset;
// let gridData: IGridDataRow[] = result.subset.map(row => {
// self.idMapping[rowIndex] = row.id;
// rowIndex++;
// return {
// values: [{}].concat(row.cells.map(c => {
// return mixin({ ariaLabel: escape(c.displayValue) }, c);
// })), row: row.id
// };
// });
// Append a NULL row to the end of gridData
let newLastRow = gridData.length === 0 ? 0 : (gridData[gridData.length - 1].row + 1);
gridData.push({ values: self.dataSet.columnDefinitions.map(cell => { return { displayValue: 'NULL', isNull: false }; }), row: newLastRow });
// let newLastRow = gridData.length === 0 ? 0 : (gridData[gridData.length - 1].row + 1);
// gridData.push({ values: self.dataSet.columnDefinitions.map(cell => { return { displayValue: 'NULL', isNull: false }; }), row: newLastRow });
resolve(gridData);
});
});
@@ -233,10 +233,10 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
};
}
onCellSelect(event: { row: number, column: number }): void {
onCellSelect(event: Slick.OnActiveCellChangedEventArgs<any>): void {
let self = this;
let row = event.row;
let column = event.column;
let column = event.cell;
// Skip processing if the newly selected cell is undefined or we don't have column
// definition for the column (ie, the selection was reset)
@@ -313,8 +313,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.currentCell = {
row: row,
column: column,
isEditable: self.dataSet.columnDefinitions[column - 1]
? self.dataSet.columnDefinitions[column - 1].isEditable
isEditable: self.dataSet.columnDefinitions[column]
? self.dataSet.columnDefinitions[column].isEditable
: false
};
});
@@ -365,7 +365,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.windowSize,
resultSet.rowCount,
this.loadDataFunction,
index => { return { values: [] }; }
index => { return {}; }
),
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
@@ -466,9 +466,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
}
} finally {
// The operation may fail if there were no changes sent to the service to revert,
// so clear any existing client-side edit and refresh the table regardless
// so clear any existing client-side edit and refresh on-screen data
// do not refresh the whole dataset as it will move the focus away to the first row.
//
this.currentEditCellValue = null;
this.refreshResultsets();
this.dataSet.dataRows.resetWindowsAroundIndex(this.currentCell.row);
}
}
}
@@ -588,14 +590,17 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
}
private saveViewState(): void {
let gridSelections = this.slickgrids.toArray()[0].getSelectedRanges();
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
let grid = this.slickgrids.toArray()[0]
if (grid) {
let gridSelections = grid.getSelectedRanges();
let viewport = ((grid as any)._grid.getCanvasNode() as HTMLElement).parentElement;
this.savedViewState = {
gridSelections,
scrollTop: viewport.scrollTop,
scrollLeft: viewport.scrollLeft
};
this.savedViewState = {
gridSelections,
scrollTop: viewport.scrollTop,
scrollLeft: viewport.scrollLeft
};
}
}
private restoreViewState(): void {

View File

@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import { Subscription, Subject } from 'rxjs/Rx';
import { ElementRef, QueryList, ChangeDetectorRef, ViewChildren } from '@angular/core';
import { IGridDataRow, ISlickRange, SlickGrid, FieldType } from 'angular2-slickgrid';
import { SlickGrid } from 'angular2-slickgrid';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import * as Constants from 'sql/parts/query/common/constants';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
@@ -80,16 +80,6 @@ export abstract class GridParentComponent {
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
// Edit Data functions
public onActiveCellChanged: (event: { row: number, column: number }) => void;
public onCellEditEnd: (event: { row: number, column: number, newValue: any }) => void;
public onCellEditBegin: (event: { row: number, column: number }) => void;
public onRowEditBegin: (event: { row: number }) => void;
public onRowEditEnd: (event: { row: number }) => void;
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
set messageActive(input: boolean) {
this._messageActive = input;
if (this.resultActive) {
@@ -236,10 +226,14 @@ export abstract class GridParentComponent {
this.messagesFocussedContextKey.set(false);
}
protected getSelection(index?: number): ISlickRange[] {
protected getSelection(index?: number): Slick.Range[] {
let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges();
selection = selection.map(c => { return <ISlickRange>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
if (selection) {
selection = selection.map(c => { return <Slick.Range>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
} else {
return undefined;
}
}
private copySelection(): void {
@@ -338,7 +332,7 @@ export abstract class GridParentComponent {
/**
* Send save result set request to service
*/
handleContextClick(event: { type: string, batchId: number, resultId: number, index: number, selection: ISlickRange[] }): void {
handleContextClick(event: { type: string, batchId: number, resultId: number, index: number, selection: Slick.Range[] }): void {
switch (event.type) {
case 'savecsv':
this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.CSV, selection: event.selection });

View File

@@ -7,9 +7,9 @@
</div>
<div class="angular-modal-body-content chart-viewer" style="flex:1 1 auto; border-left: 1px solid; margin: 5px;">
<div style="position: relative; width: 100%">
<div class="dialog-label" id="chartTypeLabel">{{localizedStrings.CHART_TYPE}}</div>
<div class="dialog-label">{{localizedStrings.CHART_TYPE}}</div>
<select-box #chartTypeSelect class="input-divider"
aria-labelledby="chartTypeLabel"
[aria-label]="localizedStrings.CHART_TYPE"
[options]="insightRegistry.getAllIds()"
[selectedOption]="getDefaultChartType()"
[onlyEmitOnChange]="true"
@@ -150,7 +150,8 @@
-->
<ng-template #legendInput>
<div class="dialog-label" id="legendLabel">{{localizedStrings.LEGEND}}</div>
<select-box class="input-divider" aria-labelledby="legendLabel"
<select-box class="input-divider"
[aria-label]="localizedStrings.LEGEND"
[options]="legendOptions"
[selectedOption]="_chartConfig.legendPosition"
[onlyEmitOnChange]="true"
@@ -214,18 +215,23 @@
<ng-template #yAxisLabelInput>
<span>
{{localizedStrings.Y_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('yAxisLabel', $event)"></input-box>
<input-box (onDidChange)="setConfigValue('yAxisLabel', $event)"
[aria-label]="localizedStrings.Y_AXIS_LABEL"></input-box>
</span>
</ng-template>
<ng-template #yAxisMinMaxInput>
<span>
{{localizedStrings.Y_AXIS_MAX_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('yAxisMax', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('yAxisMax', $event)"
[aria-label]="localizedStrings.Y_AXIS_MAX_VAL"></input-box>
</span>
<span>
{{localizedStrings.Y_AXIS_MIN_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('yAxisMin', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('yAxisMin', $event)"
[aria-label]="localizedStrings.Y_AXIS_MIN_VAL"></input-box>
</span>
</ng-template>
@@ -237,17 +243,22 @@
<ng-template #xAxisLabelInput>
<span>
{{localizedStrings.X_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('xAxisLabel', $event)"></input-box>
<input-box (onDidChange)="setConfigValue('xAxisLabel', $event)"
[aria-label]="localizedStrings.X_AXIS_LABEL"></input-box>
</span>
</ng-template>
<ng-template #xAxisMinMaxInput>
<span>
{{localizedStrings.X_AXIS_MAX_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('xAxisMax', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('xAxisMax', $event)"
[aria-label]="localizedStrings.X_AXIS_MAX_VAL"></input-box>
</span>
<span>
{{localizedStrings.X_AXIS_MIN_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('xAxisMin', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('xAxisMin', $event)"
[aria-label]="localizedStrings.X_AXIS_MIN_VAL"></input-box>
</span>
</ng-template>

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