mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 11:01:37 -05:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a30878599 | ||
|
|
c8a8935db0 | ||
|
|
ec196f57bb | ||
|
|
f7809ec3a7 | ||
|
|
71d3ec3616 | ||
|
|
4a7cf8d870 | ||
|
|
4bf8836c0a | ||
|
|
1ca36ee29c | ||
|
|
3446ff88cf | ||
|
|
de5a91a13f | ||
|
|
814cd73019 | ||
|
|
c21611661b | ||
|
|
8f817ce689 | ||
|
|
971b5111e7 | ||
|
|
07069a64ae | ||
|
|
6acea51f12 | ||
|
|
7aa2dab307 | ||
|
|
3091be8f67 | ||
|
|
487531cc52 | ||
|
|
58bfcb4273 | ||
|
|
8d8be27f22 | ||
|
|
27a978cba5 | ||
|
|
71b4e6afa4 | ||
|
|
e1f3b19c0c | ||
|
|
649c2aa5a6 | ||
|
|
cac8cc99e1 | ||
|
|
cb162b16f2 | ||
|
|
86e54ce145 | ||
|
|
efd809971f | ||
|
|
38ae14cc4d | ||
|
|
c7e33a90fe | ||
|
|
5add835750 | ||
|
|
734c614cba | ||
|
|
f6b347fa62 | ||
|
|
08d2f3125e | ||
|
|
385c48dcad | ||
|
|
0926057bfe | ||
|
|
6912e3893e | ||
|
|
d3052657df | ||
|
|
a5ca4d8edf | ||
|
|
afb1ebebd5 | ||
|
|
a04a9eb5ad | ||
|
|
027badd21f | ||
|
|
1affc760e6 | ||
|
|
3ca72b7398 | ||
|
|
702dbddd78 | ||
|
|
8fbecc0227 | ||
|
|
421271acfa | ||
|
|
98af76b3ac | ||
|
|
3952fdbe2d | ||
|
|
bc13beaa85 |
@@ -9,20 +9,21 @@
|
||||
"@types/mime": "0.0.29",
|
||||
"@types/minimatch": "^3.0.3",
|
||||
"@types/node": "8.0.33",
|
||||
"@types/xml2js": "0.0.33",
|
||||
"@types/request": "^2.47.0",
|
||||
"@types/xml2js": "0.0.33",
|
||||
"azure-storage": "^2.1.0",
|
||||
"decompress": "^4.2.0",
|
||||
"del": "^3.0.0",
|
||||
"documentdb": "1.13.0",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
||||
"fs-extra-promise": "^1.0.1",
|
||||
"github-releases": "^0.4.1",
|
||||
"mime": "^1.3.4",
|
||||
"minimist": "^1.2.0",
|
||||
"request": "^2.85.0",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
||||
"typescript": "2.9.2",
|
||||
"vscode": "^1.0.1",
|
||||
"xml2js": "^0.4.17",
|
||||
"github-releases": "^0.4.1",
|
||||
"request": "^2.85.0"
|
||||
"xml2js": "^0.4.17"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsc -p tsconfig.build.json",
|
||||
@@ -30,4 +31,4 @@
|
||||
"postinstall": "npm run compile",
|
||||
"npmCheckJs": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,17 +91,17 @@ Filename: "{app}\{#ExeBasename}.exe"; Description: "{cm:LaunchProgram,{#NameLong
|
||||
#else
|
||||
#define SoftwareClassesRootKey "HKLM"
|
||||
#endif
|
||||
Root: HKCR; Subkey: "{#RegValueName}SourceFile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,{#NameLong}}"; Flags: uninsdeletekey
|
||||
Root: HKCR; Subkey: "{#RegValueName}SourceFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"
|
||||
Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,{#NameLong}}"; Flags: uninsdeletekey
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin'))
|
||||
|
||||
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
|
||||
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.sql"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
|
||||
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles
|
||||
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
|
||||
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"; Tasks: associatewithfiles
|
||||
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.sql"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"; Tasks: associatewithfiles
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
|
||||
; Environment
|
||||
#if "user" == InstallTarget
|
||||
#define EnvironmentRootKey "HKCU"
|
||||
|
||||
@@ -571,6 +571,18 @@ deep-assign@^1.0.0:
|
||||
dependencies:
|
||||
is-obj "^1.0.0"
|
||||
|
||||
del@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
|
||||
integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
|
||||
dependencies:
|
||||
globby "^6.1.0"
|
||||
is-path-cwd "^1.0.0"
|
||||
is-path-in-cwd "^1.0.0"
|
||||
p-map "^1.1.1"
|
||||
pify "^3.0.0"
|
||||
rimraf "^2.2.8"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
@@ -942,6 +954,29 @@ glob@^5.0.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globby@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
|
||||
integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
|
||||
dependencies:
|
||||
array-union "^1.0.1"
|
||||
glob "^7.0.3"
|
||||
object-assign "^4.0.1"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
glogg@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810"
|
||||
@@ -1313,6 +1348,25 @@ is-obj@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
|
||||
integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
|
||||
|
||||
is-path-cwd@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
|
||||
integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
|
||||
|
||||
is-path-in-cwd@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
|
||||
integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
|
||||
dependencies:
|
||||
is-path-inside "^1.0.0"
|
||||
|
||||
is-path-inside@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
|
||||
integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
|
||||
dependencies:
|
||||
path-is-inside "^1.0.1"
|
||||
|
||||
is-posix-bracket@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
|
||||
@@ -1778,6 +1832,11 @@ os-tmpdir@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
p-map@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
|
||||
integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
|
||||
|
||||
parse-glob@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
|
||||
@@ -1798,6 +1857,11 @@ path-is-absolute@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-is-inside@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
|
||||
|
||||
pause-stream@0.0.11:
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
|
||||
@@ -1820,7 +1884,7 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
pify@^2.3.0:
|
||||
pify@^2.0.0, pify@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
|
||||
@@ -2118,7 +2182,7 @@ requires-port@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
rimraf@2:
|
||||
rimraf@2, rimraf@^2.2.8:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
||||
integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "agent",
|
||||
"displayName": "SQL Server Agent",
|
||||
"description": "Manage and troubleshoot SQL Server Agent jobs",
|
||||
"version": "0.35.1",
|
||||
"version": "0.35.2",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
|
||||
@@ -45,6 +45,7 @@ export class JobData implements IAgentDialogData {
|
||||
public jobSchedules: sqlops.AgentJobScheduleInfo[];
|
||||
public alerts: sqlops.AgentAlertInfo[];
|
||||
public jobId: string;
|
||||
public startStepId: number;
|
||||
|
||||
constructor(
|
||||
ownerUri: string,
|
||||
@@ -60,10 +61,11 @@ export class JobData implements IAgentDialogData {
|
||||
this.category = jobInfo.category;
|
||||
this.description = jobInfo.description;
|
||||
this.enabled = jobInfo.enabled;
|
||||
this.jobSteps = jobInfo.JobSteps;
|
||||
this.jobSchedules = jobInfo.JobSchedules;
|
||||
this.alerts = jobInfo.Alerts;
|
||||
this.jobSteps = jobInfo.jobSteps;
|
||||
this.jobSchedules = jobInfo.jobSchedules;
|
||||
this.alerts = jobInfo.alerts;
|
||||
this.jobId = jobInfo.jobId;
|
||||
this.startStepId = jobInfo.startStepId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,17 +143,17 @@ export class JobData implements IAgentDialogData {
|
||||
name: this.name,
|
||||
owner: this.owner,
|
||||
description: this.description,
|
||||
EmailLevel: this.emailLevel,
|
||||
PageLevel: this.pageLevel,
|
||||
EventLogLevel: this.eventLogLevel,
|
||||
DeleteLevel: this.deleteLevel,
|
||||
OperatorToEmail: this.operatorToEmail,
|
||||
OperatorToPage: this.operatorToPage,
|
||||
emailLevel: this.emailLevel,
|
||||
pageLevel: this.pageLevel,
|
||||
eventLogLevel: this.eventLogLevel,
|
||||
deleteLevel: this.deleteLevel,
|
||||
operatorToEmail: this.operatorToEmail,
|
||||
operatorToPage: this.operatorToPage,
|
||||
enabled: this.enabled,
|
||||
category: this.category,
|
||||
Alerts: this.alerts,
|
||||
JobSchedules: this.jobSchedules,
|
||||
JobSteps: this.jobSteps,
|
||||
alerts: this.alerts,
|
||||
jobSchedules: this.jobSchedules,
|
||||
jobSteps: this.jobSteps,
|
||||
// The properties below are not collected from UI
|
||||
// We could consider using a seperate class for create job request
|
||||
//
|
||||
@@ -166,7 +168,8 @@ export class JobData implements IAgentDialogData {
|
||||
categoryType: 1, // LocalJob, hard-coding the value, corresponds to the target tab in SSMS
|
||||
lastRun: '',
|
||||
nextRun: '',
|
||||
jobId: this.jobId
|
||||
jobId: this.jobId,
|
||||
startStepId: this.startStepId
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,7 @@ export class JobStepData implements IAgentDialogData {
|
||||
stepData.retryInterval = jobStepInfo.retryInterval,
|
||||
stepData.proxyName = jobStepInfo.proxyName;
|
||||
stepData.dialogMode = AgentDialogMode.EDIT;
|
||||
stepData.viaJobDialog = true;
|
||||
return stepData;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
|
||||
public readonly onSuccess: vscode.Event<T> = this._onSuccess.event;
|
||||
public dialog: sqlops.window.modelviewdialog.Dialog;
|
||||
|
||||
// Dialog Name for Telemetry
|
||||
public dialogName: string;
|
||||
|
||||
constructor(public ownerUri: string, public model: T, public title: string) {
|
||||
}
|
||||
|
||||
@@ -31,8 +34,9 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
|
||||
|
||||
protected abstract async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog);
|
||||
|
||||
public async openDialog() {
|
||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.title);
|
||||
public async openDialog(dialogName?: string) {
|
||||
let event = dialogName ? dialogName : null;
|
||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.title, event);
|
||||
|
||||
await this.model.initialize();
|
||||
|
||||
|
||||
@@ -116,6 +116,10 @@ export class AlertDialog extends AgentDialog<AlertData> {
|
||||
private static readonly DelayMinutesTextBoxLabel: string = localize('alertDialog.DelayMinutes', 'Delay Minutes');
|
||||
private static readonly DelaySecondsTextBoxLabel: string = localize('alertDialog.DelaySeconds', 'Delay Seconds');
|
||||
|
||||
// Event Name strings
|
||||
private readonly NewAlertDialog = 'NewAlertDialogOpen';
|
||||
private readonly EditAlertDialog = 'EditAlertDialogOpened';
|
||||
|
||||
// UI Components
|
||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
private responseTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
@@ -149,6 +153,7 @@ export class AlertDialog extends AgentDialog<AlertData> {
|
||||
private delayMinutesTextBox: sqlops.InputBoxComponent;
|
||||
private delaySecondsTextBox: sqlops.InputBoxComponent;
|
||||
|
||||
private isEdit: boolean = false;
|
||||
private databases: string[];
|
||||
private jobModel: JobData;
|
||||
public jobId: string;
|
||||
@@ -166,6 +171,8 @@ export class AlertDialog extends AgentDialog<AlertData> {
|
||||
this.jobModel = jobModel;
|
||||
this.jobId = this.jobId ? this.jobId : this.jobModel.jobId;
|
||||
this.jobName = this.jobName ? this.jobName : this.jobModel.name;
|
||||
this.isEdit = alertInfo ? true : false;
|
||||
this.dialogName = this.isEdit ? this.EditAlertDialog : this.NewAlertDialog;
|
||||
}
|
||||
|
||||
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||
|
||||
@@ -42,11 +42,12 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
private readonly StepsTable_TypeColumnString: string = localize('jobDialog.type', 'Type');
|
||||
private readonly StepsTable_SuccessColumnString: string = localize('jobDialog.onSuccess', 'On Success');
|
||||
private readonly StepsTable_FailureColumnString: string = localize('jobDialog.onFailure', 'On Failure');
|
||||
private readonly NewStepButtonString: string = localize('jobDialog.new', 'New...');
|
||||
private readonly EditStepButtonString: string = localize('jobDialog.edit', 'Edit');
|
||||
private readonly DeleteStepButtonString: string = localize('jobDialog.delete', 'Delete');
|
||||
private readonly NewStepButtonString: string = localize('jobDialog.new', 'New Step');
|
||||
private readonly EditStepButtonString: string = localize('jobDialog.edit', 'Edit Step');
|
||||
private readonly DeleteStepButtonString: string = localize('jobDialog.delete', 'Delete Step');
|
||||
private readonly MoveStepUpButtonString: string = localize('jobDialog.moveUp', 'Move Step Up');
|
||||
private readonly MoveStepDownButtonString: string = localize('jobDialog.moveDown', 'Move Step Up');
|
||||
private readonly MoveStepDownButtonString: string = localize('jobDialog.moveDown', 'Move Step Down');
|
||||
private readonly StartStepDropdownString: string = localize('jobDialog.startStepAt', 'Start step');
|
||||
|
||||
// Notifications tab strings
|
||||
private readonly NotificationsTabTopLabelString: string = localize('jobDialog.notificationsTabTop', 'Actions to perform when the job completes');
|
||||
@@ -67,6 +68,10 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
private readonly AlertEnabledLabelString: string = localize('jobDialog.alertEnabledLabel', 'Enabled');
|
||||
private readonly AlertTypeLabelString: string = localize('jobDialog.alertTypeLabel', 'Type');
|
||||
|
||||
// Event Name strings
|
||||
private readonly NewJobDialogEvent: string = 'NewJobDialogOpened';
|
||||
private readonly EditJobDialogEvent: string = 'EditJobDialogOpened';
|
||||
|
||||
// UI Components
|
||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
private stepsTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
@@ -101,6 +106,7 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
private eventLogConditionDropdown: sqlops.DropDownComponent;
|
||||
private deleteJobCheckBox: sqlops.CheckBoxComponent;
|
||||
private deleteJobConditionDropdown: sqlops.DropDownComponent;
|
||||
private startStepDropdown: sqlops.DropDownComponent;
|
||||
|
||||
// Schedule tab controls
|
||||
private schedulesTable: sqlops.TableComponent;
|
||||
@@ -115,6 +121,7 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
private steps: sqlops.AgentJobStepInfo[];
|
||||
private schedules: sqlops.AgentJobScheduleInfo[];
|
||||
private alerts: sqlops.AgentAlertInfo[] = [];
|
||||
private startStepDropdownValues: sqlops.CategoryValue[] = [];
|
||||
|
||||
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
|
||||
super(
|
||||
@@ -125,6 +132,7 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
this.schedules = this.model.jobSchedules ? this.model.jobSchedules : [];
|
||||
this.alerts = this.model.alerts ? this.model.alerts : [];
|
||||
this.isEdit = jobInfo ? true : false;
|
||||
this.dialogName = this.isEdit ? this.EditJobDialogEvent : this.NewJobDialogEvent;
|
||||
}
|
||||
|
||||
protected async initializeDialog() {
|
||||
@@ -218,19 +226,26 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
this.StepsTable_FailureColumnString
|
||||
],
|
||||
data: data,
|
||||
height: 750
|
||||
height: 650
|
||||
}).component();
|
||||
|
||||
this.startStepDropdown = view.modelBuilder.dropDown().withProperties({ width: 180 }).component();
|
||||
this.startStepDropdown.enabled = this.steps.length > 1 ? true : false;
|
||||
this.steps.forEach((step) => {
|
||||
this.startStepDropdownValues.push({ displayName: step.id + ': ' + step.stepName, name: step.id.toString() });
|
||||
});
|
||||
this.startStepDropdown.values = this.startStepDropdownValues;
|
||||
|
||||
this.moveStepUpButton = view.modelBuilder.button()
|
||||
.withProperties({
|
||||
label: this.MoveStepUpButtonString,
|
||||
width: 80
|
||||
width: 120
|
||||
}).component();
|
||||
|
||||
this.moveStepDownButton = view.modelBuilder.button()
|
||||
.withProperties({
|
||||
label: this.MoveStepDownButtonString,
|
||||
width: 80
|
||||
width: 120
|
||||
}).component();
|
||||
|
||||
this.moveStepUpButton.enabled = false;
|
||||
@@ -238,7 +253,7 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
|
||||
this.newStepButton = view.modelBuilder.button().withProperties({
|
||||
label: this.NewStepButtonString,
|
||||
width: 80
|
||||
width: 140
|
||||
}).component();
|
||||
|
||||
let stepDialog = new JobStepDialog(this.model.ownerUri, '' , this.model, null, true);
|
||||
@@ -246,6 +261,11 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
let stepInfo = JobStepData.convertToAgentJobStepInfo(step);
|
||||
this.steps.push(stepInfo);
|
||||
this.stepsTable.data = this.convertStepsToData(this.steps);
|
||||
this.startStepDropdownValues = [];
|
||||
this.steps.forEach((step) => {
|
||||
this.startStepDropdownValues.push({ displayName: step.id + ': ' + step.stepName, name: step.id.toString() });
|
||||
});
|
||||
this.startStepDropdown.values = this.startStepDropdownValues;
|
||||
});
|
||||
this.newStepButton.onDidClick((e)=>{
|
||||
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
|
||||
@@ -258,53 +278,127 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
|
||||
this.editStepButton = view.modelBuilder.button().withProperties({
|
||||
label: this.EditStepButtonString,
|
||||
width: 80
|
||||
width: 140
|
||||
}).component();
|
||||
|
||||
this.deleteStepButton = view.modelBuilder.button().withProperties({
|
||||
label: this.DeleteStepButtonString,
|
||||
width: 80
|
||||
width: 140
|
||||
}).component();
|
||||
|
||||
this.stepsTable.enabled = false;
|
||||
this.editStepButton.enabled = false;
|
||||
this.deleteStepButton.enabled = false;
|
||||
|
||||
this.stepsTable.onRowSelected(() => {
|
||||
// only let edit or delete steps if there's
|
||||
// one step selection
|
||||
this.moveStepUpButton.onDidClick(() => {
|
||||
let rowNumber = this.stepsTable.selectedRows[0];
|
||||
let previousRow = rowNumber - 1;
|
||||
let previousStep = this.steps[previousRow];
|
||||
let previousStepId = this.steps[previousRow].id;
|
||||
let currentStep = this.steps[rowNumber];
|
||||
let currentStepId = this.steps[rowNumber].id;
|
||||
this.steps[previousRow] = currentStep;
|
||||
this.steps[rowNumber] = previousStep;
|
||||
this.stepsTable.data = this.convertStepsToData(this.steps);
|
||||
this.steps[previousRow].id = previousStepId;
|
||||
this.steps[rowNumber].id = currentStepId;
|
||||
});
|
||||
|
||||
this.moveStepDownButton.onDidClick(() => {
|
||||
let rowNumber = this.stepsTable.selectedRows[0];
|
||||
let nextRow = rowNumber + 1;
|
||||
let nextStep = this.steps[nextRow];
|
||||
let nextStepId = this.steps[nextRow].id;
|
||||
let currentStep = this.steps[rowNumber];
|
||||
let currentStepId = this.steps[rowNumber].id;
|
||||
this.steps[nextRow] = currentStep;
|
||||
this.steps[rowNumber] = nextStep;
|
||||
this.stepsTable.data = this.convertStepsToData(this.steps);
|
||||
this.steps[nextRow].id = nextStepId;
|
||||
this.steps[rowNumber].id = currentStepId;
|
||||
});
|
||||
|
||||
this.editStepButton.onDidClick(() => {
|
||||
if (this.stepsTable.selectedRows.length === 1) {
|
||||
let rowNumber = this.stepsTable.selectedRows[0];
|
||||
let stepData = this.model.jobSteps[rowNumber];
|
||||
this.deleteStepButton.enabled = true;
|
||||
this.editStepButton.enabled = true;
|
||||
this.editStepButton.onDidClick(() => {
|
||||
let stepDialog = new JobStepDialog(this.model.ownerUri, '' , this.model, stepData, true);
|
||||
stepDialog.openDialog();
|
||||
});
|
||||
let editStepDialog = new JobStepDialog(this.model.ownerUri, '' , this.model, stepData, true);
|
||||
editStepDialog.onSuccess((step) => {
|
||||
let stepInfo = JobStepData.convertToAgentJobStepInfo(step);
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
if (this.steps[i].id === stepInfo.id) {
|
||||
this.steps[i] = stepInfo;
|
||||
}
|
||||
}
|
||||
this.stepsTable.data = this.convertStepsToData(this.steps);
|
||||
this.startStepDropdownValues = [];
|
||||
this.steps.forEach((step) => {
|
||||
this.startStepDropdownValues.push({ displayName: step.id + ': ' + step.stepName, name: step.id.toString() });
|
||||
});
|
||||
this.startStepDropdown.values = this.startStepDropdownValues;
|
||||
|
||||
this.deleteStepButton.onDidClick(() => {
|
||||
AgentUtils.getAgentService().then((agentService) => {
|
||||
let steps = this.model.jobSteps ? this.model.jobSteps : [];
|
||||
agentService.deleteJobStep(this.ownerUri, stepData).then((result) => {
|
||||
if (result && result.success) {
|
||||
delete steps[rowNumber];
|
||||
let data = this.convertStepsToData(steps);
|
||||
this.stepsTable.data = data;
|
||||
}
|
||||
});
|
||||
});
|
||||
editStepDialog.openDialog();
|
||||
}
|
||||
});
|
||||
|
||||
this.deleteStepButton.onDidClick(() => {
|
||||
if (this.stepsTable.selectedRows.length === 1) {
|
||||
let rowNumber = this.stepsTable.selectedRows[0];
|
||||
AgentUtils.getAgentService().then((agentService) => {
|
||||
let steps = this.model.jobSteps ? this.model.jobSteps : [];
|
||||
let stepData = this.model.jobSteps[rowNumber];
|
||||
agentService.deleteJobStep(this.ownerUri, stepData).then((result) => {
|
||||
if (result && result.success) {
|
||||
delete steps[rowNumber];
|
||||
let data = this.convertStepsToData(steps);
|
||||
this.stepsTable.data = data;
|
||||
this.startStepDropdownValues = [];
|
||||
this.steps.forEach((step) => {
|
||||
this.startStepDropdownValues.push({ displayName: step.id + ': ' + step.stepName, name: step.id.toString() });
|
||||
});
|
||||
this.startStepDropdown.values = this.startStepDropdownValues;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let formModel = view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
this.stepsTable.onRowSelected((row) => {
|
||||
// only let edit or delete steps if there's
|
||||
// one step selection
|
||||
if (this.stepsTable.selectedRows.length === 1) {
|
||||
let rowNumber = this.stepsTable.selectedRows[0];
|
||||
// if it's not the last step
|
||||
if (this.steps.length !== rowNumber + 1) {
|
||||
this.moveStepDownButton.enabled = true;
|
||||
}
|
||||
// if it's not the first step
|
||||
if (rowNumber !== 0) {
|
||||
this.moveStepUpButton.enabled = true;
|
||||
}
|
||||
this.deleteStepButton.enabled = true;
|
||||
this.editStepButton.enabled = true;
|
||||
}
|
||||
});
|
||||
let stepMoveContainer = this.createRowContainer(view).withItems([this.startStepDropdown, this.moveStepUpButton, this.moveStepDownButton]).component();
|
||||
let stepsDialogContainer = this.createRowContainer(view).withItems([this.newStepButton, this.editStepButton, this.deleteStepButton]).component();
|
||||
let formModel = view.modelBuilder.formContainer().withFormItems([
|
||||
{
|
||||
component: this.stepsTable,
|
||||
title: this.JobStepsTopLabelString,
|
||||
actions: [this.moveStepUpButton, this.moveStepDownButton, this.newStepButton, this.editStepButton, this.deleteStepButton]
|
||||
}]).withLayout({ width: '100%' }).component();
|
||||
title: this.JobStepsTopLabelString
|
||||
},
|
||||
{
|
||||
component: stepMoveContainer,
|
||||
title: this.StartStepDropdownString
|
||||
},
|
||||
{
|
||||
component: stepsDialogContainer,
|
||||
title: ''
|
||||
}
|
||||
]).withLayout({ width: '100%' }).component();
|
||||
await view.initializeModel(formModel);
|
||||
this.setConditionDropdownSelectedValue(this.startStepDropdown, this.model.startStepId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -567,6 +661,7 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
this.model.pageLevel = this.getActualConditionValue(this.pagerCheckBox, this.pagerConditionDropdown);
|
||||
this.model.eventLogLevel = this.getActualConditionValue(this.eventLogCheckBox, this.eventLogConditionDropdown);
|
||||
this.model.deleteLevel = this.getActualConditionValue(this.deleteJobCheckBox, this.deleteJobConditionDropdown);
|
||||
this.model.startStepId = +this.getDropdownValue(this.startStepDropdown);
|
||||
if (!this.model.jobSteps) {
|
||||
this.model.jobSteps = [];
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
||||
private readonly QuitJobReportingSuccess: string = localize('jobStepDialog.quitJobSuccess', 'Quit the job reporting success');
|
||||
private readonly QuitJobReportingFailure: string = localize('jobStepDialog.quitJobFailure', 'Quit the job reporting failure');
|
||||
|
||||
// Event Name strings
|
||||
private readonly NewStepDialog = 'NewStepDialogOpened';
|
||||
private readonly EditStepDialog = 'EditStepDialogOpened';
|
||||
// UI Components
|
||||
|
||||
// Dialogs
|
||||
@@ -131,6 +134,7 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
||||
this.jobModel = jobModel;
|
||||
this.jobName = this.jobName ? this.jobName : this.jobModel.name;
|
||||
this.server = server;
|
||||
this.dialogName = this.isEdit ? this.EditStepDialog : this.NewStepDialog;
|
||||
}
|
||||
|
||||
private initializeUIComponents() {
|
||||
@@ -519,6 +523,7 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
||||
this.model.failureAction = this.failureActionDropdown.value as string;
|
||||
this.model.outputFileName = this.outputFileNameBox.value;
|
||||
this.model.appendToLogFile = this.appendToExistingFileCheckbox.checked;
|
||||
this.model.command = this.commandTextBox.value ? this.commandTextBox.value : '';
|
||||
}
|
||||
|
||||
public async initializeDialog() {
|
||||
|
||||
@@ -43,6 +43,10 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
|
||||
private static readonly AlertEmailColumnLabel: string = localize('createOperator.AlertEmailColumnLabel', 'E-mail');
|
||||
private static readonly AlertPagerColumnLabel: string = localize('createOperator.AlertPagerColumnLabel', 'Pager');
|
||||
|
||||
// Event strings
|
||||
private readonly NewOperatorDialog = 'NewOperatorDialogOpened';
|
||||
private readonly EditOperatorDialog = 'EditOperatorDialogOpened';
|
||||
|
||||
// UI Components
|
||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
private notificationsTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
@@ -68,12 +72,15 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
|
||||
|
||||
// Notification tab controls
|
||||
private alertsTable: sqlops.TableComponent;
|
||||
private isEdit: boolean = false;
|
||||
|
||||
constructor(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo = undefined) {
|
||||
super(
|
||||
ownerUri,
|
||||
new OperatorData(ownerUri, operatorInfo),
|
||||
operatorInfo ? OperatorDialog.EditDialogTitle : OperatorDialog.CreateDialogTitle);
|
||||
this.isEdit = operatorInfo ? true : false;
|
||||
this.dialogName = this.isEdit ? this.EditOperatorDialog : this.NewOperatorDialog;
|
||||
}
|
||||
|
||||
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||
|
||||
@@ -36,6 +36,9 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
|
||||
private static readonly PowerShellLabel: string = localize('createProxy.PowerShell', 'PowerShell');
|
||||
private static readonly SubSystemHeadingLabel: string = localize('createProxy.subSystemHeading', 'Active to the following subsytems');
|
||||
|
||||
private readonly NewProxyDialog = 'NewProxyDialogOpened';
|
||||
private readonly EditProxyDialog = 'EditProxyDialogOpened';
|
||||
|
||||
// UI Components
|
||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
||||
|
||||
@@ -56,6 +59,7 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
|
||||
private powershellCheckBox: sqlops.CheckBoxComponent;
|
||||
|
||||
private credentials: sqlops.CredentialInfo[];
|
||||
private isEdit: boolean = false;
|
||||
|
||||
constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined, credentials: sqlops.CredentialInfo[]) {
|
||||
super(
|
||||
@@ -63,6 +67,8 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
|
||||
new ProxyData(ownerUri, proxyInfo),
|
||||
proxyInfo ? ProxyDialog.EditDialogTitle : ProxyDialog.CreateDialogTitle);
|
||||
this.credentials = credentials;
|
||||
this.isEdit = proxyInfo ? true : false;
|
||||
this.dialogName = this.isEdit ? this.EditProxyDialog : this.NewProxyDialog;
|
||||
}
|
||||
|
||||
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||
|
||||
@@ -40,13 +40,13 @@ export class MainController {
|
||||
public activate(): void {
|
||||
vscode.commands.registerCommand('agent.openJobDialog', (ownerUri: string, jobInfo: sqlops.AgentJobInfo) => {
|
||||
let dialog = new JobDialog(ownerUri, jobInfo);
|
||||
dialog.openDialog();
|
||||
dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog();
|
||||
});
|
||||
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, jobInfo: sqlops.AgentJobInfo, jobStepInfo: sqlops.AgentJobStepInfo) => {
|
||||
AgentUtils.getAgentService().then((agentService) => {
|
||||
let jobData: JobData = new JobData(ownerUri, jobInfo, agentService);
|
||||
let dialog = new JobStepDialog(ownerUri, server, jobData, jobStepInfo, false);
|
||||
dialog.openDialog();
|
||||
dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog();
|
||||
});
|
||||
});
|
||||
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string, jobName: string) => {
|
||||
@@ -57,17 +57,16 @@ export class MainController {
|
||||
AgentUtils.getAgentService().then((agentService) => {
|
||||
let jobData: JobData = new JobData(ownerUri, jobInfo, agentService);
|
||||
let dialog = new AlertDialog(ownerUri, jobData, alertInfo, false);
|
||||
dialog.openDialog();
|
||||
dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog();
|
||||
});
|
||||
});
|
||||
vscode.commands.registerCommand('agent.openOperatorDialog', (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo) => {
|
||||
let dialog = new OperatorDialog(ownerUri, operatorInfo);
|
||||
dialog.openDialog();
|
||||
dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog();
|
||||
});
|
||||
vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo, credentials: sqlops.CredentialInfo[]) => {
|
||||
let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials);
|
||||
dialog.openDialog();
|
||||
MainController.showNotYetImplemented();
|
||||
dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -223,22 +223,16 @@ export default class TokenCache implements adal.TokenCache {
|
||||
return this.getOrCreateEncryptionParams()
|
||||
.then(encryptionParams => {
|
||||
try {
|
||||
let cacheCipher = fs.readFileSync(self._cacheSerializationPath, TokenCache.FsOptions);
|
||||
|
||||
let decipher = crypto.createDecipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
|
||||
let cacheJson = decipher.update(cacheCipher, 'hex', 'binary');
|
||||
cacheJson += decipher.final('binary');
|
||||
|
||||
// Deserialize the JSON into the array of tokens
|
||||
let cacheObj = <adal.TokenResponse[]>JSON.parse(cacheJson);
|
||||
for (let objIndex in cacheObj) {
|
||||
// Rehydrate Date objects since they will always serialize as a string
|
||||
cacheObj[objIndex].expiresOn = new Date(<string>cacheObj[objIndex].expiresOn);
|
||||
}
|
||||
|
||||
return cacheObj;
|
||||
return self.decryptCache('utf8', encryptionParams);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
try {
|
||||
// try to parse using 'binary' encoding and rewrite cache as UTF8
|
||||
let response = self.decryptCache('binary', encryptionParams);
|
||||
self.writeCache(response);
|
||||
return response;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(null, err => {
|
||||
@@ -248,6 +242,22 @@ export default class TokenCache implements adal.TokenCache {
|
||||
});
|
||||
}
|
||||
|
||||
private decryptCache(encoding: crypto.Utf8AsciiBinaryEncoding, encryptionParams: EncryptionParams): adal.TokenResponse[] {
|
||||
let cacheCipher = fs.readFileSync(this._cacheSerializationPath, TokenCache.FsOptions);
|
||||
let decipher = crypto.createDecipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
|
||||
let cacheJson = decipher.update(cacheCipher, 'hex', encoding);
|
||||
cacheJson += decipher.final(encoding);
|
||||
|
||||
// Deserialize the JSON into the array of tokens
|
||||
let cacheObj = <adal.TokenResponse[]>JSON.parse(cacheJson);
|
||||
for (let objIndex in cacheObj) {
|
||||
// Rehydrate Date objects since they will always serialize as a string
|
||||
cacheObj[objIndex].expiresOn = new Date(<string>cacheObj[objIndex].expiresOn);
|
||||
}
|
||||
|
||||
return cacheObj;
|
||||
}
|
||||
|
||||
private removeFromCache(cache: adal.TokenResponse[], entries: adal.TokenResponse[]): adal.TokenResponse[] {
|
||||
entries.forEach((entry: adal.TokenResponse) => {
|
||||
// Check to see if the entry exists
|
||||
@@ -274,7 +284,7 @@ export default class TokenCache implements adal.TokenCache {
|
||||
let cacheJson = JSON.stringify(cache);
|
||||
|
||||
let cipher = crypto.createCipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
|
||||
let cacheCipher = cipher.update(cacheJson, 'binary', 'hex');
|
||||
let cacheCipher = cipher.update(cacheJson, 'utf8', 'hex');
|
||||
cacheCipher += cipher.final('hex');
|
||||
|
||||
fs.writeFileSync(self._cacheSerializationPath, cacheCipher, TokenCache.FsOptions);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "import",
|
||||
"displayName": "SQL Server Import",
|
||||
"description": "SQL Server Import for Azure Data Studio supports importing CSV or JSON files into SQL Server.",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
@@ -67,7 +67,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.10",
|
||||
"opener": "^1.4.3",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
||||
"vscode-extension-telemetry": "0.0.18",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard, Operation } from '../DataTierApplicationWizard';
|
||||
import { DataTierApplicationWizard, Operation } from '../dataTierApplicationWizard';
|
||||
import { BasePage } from '../api/basePage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -11,7 +11,7 @@ agent-base@4, agent-base@^4.1.0:
|
||||
|
||||
applicationinsights@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.1.tgz#53446b830fe8d5d619eee2a278b31d3d25030927"
|
||||
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.1.tgz#53446b830fe8d5d619eee2a278b31d3d25030927"
|
||||
integrity sha1-U0Rrgw/o1dYZ7uKieLMdPSUDCSc=
|
||||
dependencies:
|
||||
diagnostic-channel "0.2.0"
|
||||
@@ -36,7 +36,7 @@ buffer-alloc-unsafe@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||
|
||||
buffer-alloc@^1.1.0:
|
||||
buffer-alloc@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||
@@ -75,19 +75,26 @@ core-util-is@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.7":
|
||||
version "0.2.6"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/85653d8b305af8aef334728d71f07bdc240dfcb7"
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.10":
|
||||
version "0.2.10"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/4de3f7caf0eba54159911b977ddb4f5d7c0a9ca8"
|
||||
dependencies:
|
||||
vscode-languageclient "3.5.1"
|
||||
|
||||
debug@3.1.0, debug@^3.1.0:
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
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"
|
||||
@@ -143,12 +150,12 @@ decompress@^4.2.0:
|
||||
|
||||
diagnostic-channel-publishers@0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
|
||||
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
|
||||
integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM=
|
||||
|
||||
diagnostic-channel@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
|
||||
resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
|
||||
integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=
|
||||
dependencies:
|
||||
semver "^5.3.0"
|
||||
@@ -161,9 +168,9 @@ end-of-stream@^1.0.0:
|
||||
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"
|
||||
integrity sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
|
||||
integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -213,9 +220,9 @@ get-stream@^2.2.0:
|
||||
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"
|
||||
integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
@@ -287,6 +294,11 @@ ms@2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
object-assign@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
@@ -300,9 +312,9 @@ once@^1.4.0:
|
||||
wrappy "1"
|
||||
|
||||
opener@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
|
||||
integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
|
||||
integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
@@ -368,7 +380,7 @@ seek-bzip@^1.0.5:
|
||||
|
||||
semver@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
|
||||
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
|
||||
@@ -397,16 +409,16 @@ strip-dirs@^2.0.0:
|
||||
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"
|
||||
integrity sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
|
||||
integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
|
||||
dependencies:
|
||||
bl "^1.0.0"
|
||||
buffer-alloc "^1.1.0"
|
||||
buffer-alloc "^1.2.0"
|
||||
end-of-stream "^1.0.0"
|
||||
fs-constants "^1.0.0"
|
||||
readable-stream "^2.3.0"
|
||||
to-buffer "^1.1.0"
|
||||
to-buffer "^1.1.1"
|
||||
xtend "^4.0.0"
|
||||
|
||||
through@^2.3.6:
|
||||
@@ -421,15 +433,15 @@ tmp@^0.0.33:
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
to-buffer@^1.1.0:
|
||||
to-buffer@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
|
||||
|
||||
unbzip2-stream@^1.0.9:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
|
||||
integrity sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz#7854da51622a7e63624221196357803b552966a1"
|
||||
integrity sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==
|
||||
dependencies:
|
||||
buffer "^3.0.1"
|
||||
through "^2.3.6"
|
||||
@@ -441,7 +453,7 @@ util-deprecate@~1.0.1:
|
||||
|
||||
vscode-extension-telemetry@0.0.18:
|
||||
version "0.0.18"
|
||||
resolved "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327"
|
||||
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327"
|
||||
integrity sha512-Vw3Sr+dZwl+c6PlsUwrTtCOJkgrmvS3OUVDQGcmpXWAgq9xGq6as0K4pUx+aGqTjzLAESmWSrs6HlJm6J6Khcg==
|
||||
dependencies:
|
||||
applicationinsights "1.0.1"
|
||||
@@ -472,9 +484,9 @@ vscode-languageserver-types@3.5.0:
|
||||
integrity sha1-5I15li8LjgLelV4/UkkI4rGcA3Q=
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
integrity sha512-FTjdqa4jDDoBjJqr36O8lmmZf/55kQ2w4ZY/+GL6K92fq765BqO3aYw21atnXUno/P04V5DWagNl4ybDIndJsw==
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.5.tgz#25520c1955108036dec607c85e00a522f247f1a4"
|
||||
integrity sha512-ITtoh3V4AkWXMmp3TB97vsMaHRgHhsSFPsUdzlueSL+dRZbSNTZeOmdQv60kjCV306ghPxhDeoNUEm3+EZMuyw==
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
@@ -496,5 +508,5 @@ yauzl@^2.4.2:
|
||||
|
||||
zone.js@0.7.6:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
|
||||
integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"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.9",
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.10",
|
||||
"opener": "^1.4.3",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
||||
"vscode-extension-telemetry": "^0.0.15"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "1.5.0-alpha.59",
|
||||
"version": "1.5.0-alpha.63",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
||||
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
||||
|
||||
@@ -36,7 +36,7 @@ buffer-alloc-unsafe@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||
|
||||
buffer-alloc@^1.1.0:
|
||||
buffer-alloc@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||
@@ -75,19 +75,26 @@ core-util-is@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.9":
|
||||
version "0.2.9"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0a3c0f22940d1c67bb567171508ccb1169c6313a"
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.10":
|
||||
version "0.2.10"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/4de3f7caf0eba54159911b977ddb4f5d7c0a9ca8"
|
||||
dependencies:
|
||||
vscode-languageclient "3.5.1"
|
||||
|
||||
debug@3.1.0, debug@^3.1.0:
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
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"
|
||||
@@ -161,9 +168,9 @@ end-of-stream@^1.0.0:
|
||||
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"
|
||||
integrity sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
|
||||
integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -213,9 +220,9 @@ get-stream@^2.2.0:
|
||||
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"
|
||||
integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
@@ -287,6 +294,11 @@ ms@2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
object-assign@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
@@ -300,9 +312,9 @@ once@^1.4.0:
|
||||
wrappy "1"
|
||||
|
||||
opener@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
|
||||
integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
|
||||
integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
@@ -367,9 +379,9 @@ seek-bzip@^1.0.5:
|
||||
commander "~2.8.1"
|
||||
|
||||
semver@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
|
||||
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
|
||||
version "0.1.5"
|
||||
@@ -397,16 +409,16 @@ strip-dirs@^2.0.0:
|
||||
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"
|
||||
integrity sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
|
||||
integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
|
||||
dependencies:
|
||||
bl "^1.0.0"
|
||||
buffer-alloc "^1.1.0"
|
||||
buffer-alloc "^1.2.0"
|
||||
end-of-stream "^1.0.0"
|
||||
fs-constants "^1.0.0"
|
||||
readable-stream "^2.3.0"
|
||||
to-buffer "^1.1.0"
|
||||
to-buffer "^1.1.1"
|
||||
xtend "^4.0.0"
|
||||
|
||||
through@^2.3.6:
|
||||
@@ -421,15 +433,15 @@ tmp@^0.0.33:
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
to-buffer@^1.1.0:
|
||||
to-buffer@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
|
||||
|
||||
unbzip2-stream@^1.0.9:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
|
||||
integrity sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz#7854da51622a7e63624221196357803b552966a1"
|
||||
integrity sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==
|
||||
dependencies:
|
||||
buffer "^3.0.1"
|
||||
through "^2.3.6"
|
||||
|
||||
17
extensions/notebook/README.md
Normal file
17
extensions/notebook/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Notebook extension for Azure Data Studio
|
||||
|
||||
Welcome to the Notebook extension for Azure Data Studio! This extension supports core notebook functionality including configuration settings, actions such as New / Open Notebook, and more.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt).
|
||||
71
extensions/notebook/package.json
Normal file
71
extensions/notebook/package.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "notebook",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "0.1.0",
|
||||
"publisher": "Microsoft",
|
||||
"engines": {
|
||||
"vscode": "*",
|
||||
"sqlops": "*"
|
||||
},
|
||||
"main": "./out/extension",
|
||||
"activationEvents": [
|
||||
"*"
|
||||
],
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "%notebook.configuration.title%",
|
||||
"properties": {
|
||||
"notebook.enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%notebook.enabled.description%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "notebook.command.new",
|
||||
"title": "%notebook.command.new%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/new_notebook_inverse.svg",
|
||||
"light": "resources/light/new_notebook.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.open",
|
||||
"title": "%notebook.command.open%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/open_notebook_inverse.svg",
|
||||
"light": "resources/light/open_notebook.svg"
|
||||
}
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "notebook.command.new",
|
||||
"when": "config.notebook.enabled"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.open",
|
||||
"when": "config.notebook.enabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "notebook.command.new",
|
||||
"key": "Ctrl+Shift+N",
|
||||
"when": "config.notebook.enabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "8.0.33"
|
||||
}
|
||||
}
|
||||
8
extensions/notebook/package.nls.json
Normal file
8
extensions/notebook/package.nls.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"displayName": "Notebook Core Extensions",
|
||||
"description": "Defines the Data-procotol based Notebook contribution and many Notebook commands and contributions.",
|
||||
"notebook.configuration.title": "Notebook configuration",
|
||||
"notebook.enabled.description": "Enable viewing notebook files using built-in notebook editor.",
|
||||
"notebook.command.new": "New Notebook",
|
||||
"notebook.command.open": "Open Notebook"
|
||||
}
|
||||
1
extensions/notebook/resources/dark/new_notebook_inverse.svg
Executable file
1
extensions/notebook/resources/dark/new_notebook_inverse.svg
Executable file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#388a34;}</style></defs><title>new_notebook_inverse</title><path class="cls-1" d="M11.87,1.24V.33H9.13A3.78,3.78,0,0,0,7.92.52a3.48,3.48,0,0,0-1.07.58A3.6,3.6,0,0,0,5.78.52,3.78,3.78,0,0,0,4.57.33H1.83v.91H0V13.1H9.67v-.91H7a4,4,0,0,1,.47-.39A2.39,2.39,0,0,1,8,11.52a2.2,2.2,0,0,1,.53-.18,2.93,2.93,0,0,1,.61-.06h2.74V2.15h.91V9h.91V1.24Zm-9.13,0H4.57a3,3,0,0,1,1,.17,2.58,2.58,0,0,1,.85.49v8.93a3.94,3.94,0,0,0-.88-.35,3.73,3.73,0,0,0-.94-.12H2.74Zm-1.82,11v-10h.91v9.13H4.57a2.93,2.93,0,0,1,.61.06,2.55,2.55,0,0,1,.53.18,2.68,2.68,0,0,1,.49.28,3.29,3.29,0,0,1,.46.39Zm8.21-1.83a3.73,3.73,0,0,0-.94.12,4.22,4.22,0,0,0-.89.35V1.9a2.74,2.74,0,0,1,.86-.49,2.91,2.91,0,0,1,1-.17H11v9.12ZM12.87,10v2.2h-2.2v.91h3V10Z"/><polygon class="cls-2" points="16 12.19 16 13.13 13.8 13.13 13.8 15.33 12.87 15.33 12.87 13.13 10.67 13.13 10.67 12.19 12.87 12.19 12.87 9.99 13.8 9.99 13.8 12.19 16 12.19"/><path class="cls-2" d="M13.8,12.19V10h-.93v2.2h-2.2v.94h2.2v2.2h.93v-2.2H16v-.94Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
extensions/notebook/resources/dark/open_notebook_inverse.svg
Executable file
1
extensions/notebook/resources/dark/open_notebook_inverse.svg
Executable file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#0095d7;}</style></defs><title>open_notebook_inverse</title><path class="cls-1" d="M12.55,4.21l-.08-.11h-.56l-.69.06a1.54,1.54,0,0,0-.23.29v8.69H9.18a3.32,3.32,0,0,0-.93.13,3.34,3.34,0,0,0-.87.34V4.76a2.88,2.88,0,0,1,.43-.31A5.58,5.58,0,0,1,8.29,3.3a2.63,2.63,0,0,0-.3.09A3.62,3.62,0,0,0,6.93,4a3.68,3.68,0,0,0-1.07-.57A3.58,3.58,0,0,0,4.67,3.2H2v.9H.15V15.85H13.72V5.48ZM2.86,4.1H4.67a2.61,2.61,0,0,1,1,.17,2.32,2.32,0,0,1,.86.49v8.85a3.27,3.27,0,0,0-.88-.34,3.22,3.22,0,0,0-.93-.13H2.86ZM1,15V5H2v9H4.67a3.94,3.94,0,0,1,.61.06,3.2,3.2,0,0,1,.52.18,4.19,4.19,0,0,1,.49.29,2.28,2.28,0,0,1,.45.39ZM12.8,15H7.11a2.7,2.7,0,0,1,.47-.39A2.83,2.83,0,0,1,8,14.28a3.42,3.42,0,0,1,.54-.18A3.81,3.81,0,0,1,9.18,14h2.73V5h.89Z"/><polygon class="cls-2" points="13.2 3.56 13.2 3.58 13.19 3.57 13.2 3.56"/><path class="cls-2" d="M13.19,3.57h0v0Z"/><polygon class="cls-2" points="13.2 3.56 13.2 3.58 13.19 3.57 13.2 3.56"/><polygon class="cls-2" points="14.21 1.65 14.19 1.65 14.19 1.63 14.21 1.65"/><path class="cls-2" d="M15.91,2.1,14.2,3.81l-.38.38-.62-.61v0l1-1H12.79a3.35,3.35,0,0,0-1.09.26h0a3.94,3.94,0,0,0-.86.52l-.24.21s0,0,0,0a3.3,3.3,0,0,0-.51.67,3.1,3.1,0,0,0-.26.47A3.41,3.41,0,0,0,9.5,6.11H8.6a4.68,4.68,0,0,1,.16-1.19A4.74,4.74,0,0,1,9,4.26a2.21,2.21,0,0,1,.2-.41,4.66,4.66,0,0,1,.36-.51c.1-.13.22-.26.34-.39a4.14,4.14,0,0,1,.66-.53,1.19,1.19,0,0,1,.23-.16,2.79,2.79,0,0,1,.34-.18l.31-.13.42-.14a4.32,4.32,0,0,1,1.19-.16h1.15l-1-1L13.82,0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
extensions/notebook/resources/light/new_notebook.svg
Executable file
1
extensions/notebook/resources/light/new_notebook.svg
Executable file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#388a34;}</style></defs><title>new_notebook</title><path d="M11.86,1.24V.33H9.13A3.78,3.78,0,0,0,7.91.52a3.48,3.48,0,0,0-1.07.58A3.6,3.6,0,0,0,5.78.52,3.78,3.78,0,0,0,4.57.33H1.83v.91H0V13.1H9.66v-.91H7a4,4,0,0,1,.47-.39A2.39,2.39,0,0,1,8,11.52a2.2,2.2,0,0,1,.53-.18,2.93,2.93,0,0,1,.61-.06h2.74V2.15h.91V9h.91V1.24Zm-9.13,0H4.57a3,3,0,0,1,1,.17,2.58,2.58,0,0,1,.85.49v8.93a3.94,3.94,0,0,0-.88-.35,3.73,3.73,0,0,0-.94-.12H2.73Zm-1.82,11v-10h.91v9.13H4.57a2.93,2.93,0,0,1,.61.06,2.55,2.55,0,0,1,.53.18,2.68,2.68,0,0,1,.49.28,3.29,3.29,0,0,1,.46.39Zm8.21-1.83a3.73,3.73,0,0,0-.94.12,4.22,4.22,0,0,0-.89.35V1.9a2.74,2.74,0,0,1,.86-.49,2.91,2.91,0,0,1,1-.17h1.82v9.12ZM12.86,10v2.2h-2.2v.91h3V10Z"/><polygon class="cls-1" points="15.99 12.19 15.99 13.13 13.79 13.13 13.79 15.33 12.87 15.33 12.87 13.13 10.66 13.13 10.66 12.19 12.87 12.19 12.87 9.99 13.79 9.99 13.79 12.19 15.99 12.19"/><path class="cls-1" d="M13.79,12.19V10h-.93v2.2h-2.2v.94h2.2v2.2h.93v-2.2H16v-.94Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
extensions/notebook/resources/light/open_notebook.svg
Executable file
1
extensions/notebook/resources/light/open_notebook.svg
Executable file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#00539c;}</style></defs><title>open_notebook</title><path d="M12.4,4.21l-.08-.11h-.56l-.69.06a1.54,1.54,0,0,0-.23.29v8.69H9a3.32,3.32,0,0,0-.93.13,3.34,3.34,0,0,0-.87.34V4.76a2.88,2.88,0,0,1,.43-.31A5.58,5.58,0,0,1,8.14,3.3a2.63,2.63,0,0,0-.3.09A3.62,3.62,0,0,0,6.78,4a3.68,3.68,0,0,0-1.07-.57A3.58,3.58,0,0,0,4.52,3.2H1.81v.9H0V15.85H13.57V5.48ZM2.71,4.1H4.52a2.61,2.61,0,0,1,1,.17,2.32,2.32,0,0,1,.86.49v8.85a3.27,3.27,0,0,0-.88-.34,3.22,3.22,0,0,0-.93-.13H2.71ZM.9,15V5h.91v9H4.52a3.94,3.94,0,0,1,.61.06,3.2,3.2,0,0,1,.52.18,4.19,4.19,0,0,1,.49.29,2.28,2.28,0,0,1,.45.39Zm11.75,0H7a2.7,2.7,0,0,1,.47-.39,2.83,2.83,0,0,1,.47-.29,3.42,3.42,0,0,1,.54-.18A3.81,3.81,0,0,1,9,14h2.73V5h.89Z"/><polygon class="cls-1" points="13.05 3.56 13.05 3.58 13.04 3.57 13.05 3.56"/><path class="cls-1" d="M13,3.57h0v0Z"/><polygon class="cls-1" points="13.05 3.56 13.05 3.58 13.04 3.57 13.05 3.56"/><polygon class="cls-1" points="14.06 1.65 14.04 1.65 14.04 1.63 14.06 1.65"/><path class="cls-1" d="M15.76,2.1,14,3.81l-.38.38L13,3.58v0l1-1H12.64a3.35,3.35,0,0,0-1.09.26h0a3.94,3.94,0,0,0-.86.52l-.24.21s0,0,0,0a3.3,3.3,0,0,0-.51.67,3.1,3.1,0,0,0-.26.47,3.41,3.41,0,0,0-.27,1.39h-.9a4.68,4.68,0,0,1,.16-1.19,4.74,4.74,0,0,1,.25-.66,2.21,2.21,0,0,1,.2-.41,4.66,4.66,0,0,1,.36-.51c.1-.13.22-.26.34-.39a4.14,4.14,0,0,1,.66-.53,1.19,1.19,0,0,1,.23-.16A2.79,2.79,0,0,1,11,2.08l.31-.13.42-.14a4.32,4.32,0,0,1,1.19-.16h1.15l-1-1L13.67,0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
50
extensions/notebook/src/extension.ts
Normal file
50
extensions/notebook/src/extension.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 sqlops from 'sqlops';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export function activate(extensionContext: vscode.ExtensionContext) {
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.new', () => {
|
||||
let title = `Untitled-${counter++}`;
|
||||
let untitledUri = vscode.Uri.parse(`untitled:${title}`);
|
||||
sqlops.nb.showNotebookDocument(untitledUri).then(success => {
|
||||
|
||||
}, (err: Error) => {
|
||||
vscode.window.showErrorMessage(err.message);
|
||||
});
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.open', () => {
|
||||
openNotebook();
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
async function openNotebook(): Promise<void> {
|
||||
try {
|
||||
let filter = {};
|
||||
// TODO support querying valid notebook file types
|
||||
filter[localize('notebookFiles', 'Notebooks')] = ['ipynb'];
|
||||
let file = await vscode.window.showOpenDialog({
|
||||
filters: filter
|
||||
});
|
||||
if (file) {
|
||||
let doc = await vscode.workspace.openTextDocument(file[0]);
|
||||
vscode.window.showTextDocument(doc);
|
||||
}
|
||||
} catch (err) {
|
||||
vscode.window.showErrorMessage(err);
|
||||
}
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {
|
||||
}
|
||||
9
extensions/notebook/src/typings/refs.d.ts
vendored
Normal file
9
extensions/notebook/src/typings/refs.d.ts
vendored
Normal 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/sql/sqlops.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
22
extensions/notebook/tsconfig.json
Normal file
22
extensions/notebook/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "./out",
|
||||
"lib": [
|
||||
"es6", "es2015.promise"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
13
extensions/notebook/yarn.lock
Normal file
13
extensions/notebook/yarn.lock
Normal file
@@ -0,0 +1,13 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@8.0.33":
|
||||
version "8.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
|
||||
integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==
|
||||
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
|
||||
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "profiler",
|
||||
"displayName": "SQL Server Profiler",
|
||||
"description": "SQL Server Profiler for Azure Data Studio",
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
@@ -26,8 +26,7 @@
|
||||
"Microsoft.mssql"
|
||||
],
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"commands": [{
|
||||
"command": "profiler.newProfiler",
|
||||
"title": "Launch Profiler",
|
||||
"category": "Profiler"
|
||||
@@ -49,13 +48,23 @@
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"objectExplorer/item/context": [
|
||||
"commandPalette": [{
|
||||
"command": "profiler.start",
|
||||
"when": "False"
|
||||
},
|
||||
{
|
||||
"command": "profiler.newProfiler",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Server",
|
||||
"group": "profiler"
|
||||
"command": "profiler.stop",
|
||||
"when": "False"
|
||||
}, {
|
||||
"command": "profiler.openCreateSessionDialog",
|
||||
"when": "False"
|
||||
}
|
||||
]
|
||||
],
|
||||
"objectExplorer/item/context": [{
|
||||
"command": "profiler.newProfiler",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Server",
|
||||
"group": "profiler"
|
||||
}]
|
||||
},
|
||||
"outputChannels": [
|
||||
"sqlprofiler"
|
||||
|
||||
File diff suppressed because one or more lines are too long
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "azuredatastudio",
|
||||
"version": "1.3.4",
|
||||
"version": "1.3.7",
|
||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
@@ -68,7 +68,7 @@
|
||||
"rxjs": "5.4.0",
|
||||
"sanitize-html": "^1.19.1",
|
||||
"semver": "^5.5.0",
|
||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.28",
|
||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.29",
|
||||
"spdlog": "0.7.1",
|
||||
"sudo-prompt": "8.2.0",
|
||||
"svg.js": "^2.2.5",
|
||||
@@ -90,6 +90,7 @@
|
||||
"@types/mocha": "2.2.39",
|
||||
"@types/sanitize-html": "^1.18.2",
|
||||
"@types/semver": "5.3.30",
|
||||
"@types/should": "^13.0.0",
|
||||
"@types/sinon": "1.16.34",
|
||||
"@types/winreg": "^1.2.30",
|
||||
"asar": "^0.14.0",
|
||||
@@ -148,8 +149,10 @@
|
||||
"queue": "3.0.6",
|
||||
"remap-istanbul": "^0.6.4",
|
||||
"rimraf": "^2.2.8",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^1.17.2",
|
||||
"source-map": "^0.4.4",
|
||||
"temp-write": "^3.4.0",
|
||||
"tslint": "^5.9.1",
|
||||
"typemoq": "^0.3.2",
|
||||
"typescript": "2.9.2",
|
||||
@@ -174,6 +177,7 @@
|
||||
"windows-process-tree": "0.2.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"rc": "1.2.8"
|
||||
"rc": "1.2.8",
|
||||
"event-stream": "3.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,36 +9,27 @@ 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;
|
||||
setCollectionChangedCallback(callback: (startIndex: number, count: number) => void): void;
|
||||
setLength(number): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface IGridDataRow {
|
||||
row?: number;
|
||||
values: any[];
|
||||
}
|
||||
|
||||
export enum CollectionChange {
|
||||
ItemsReplaced
|
||||
}
|
||||
|
||||
class LoadCancellationToken {
|
||||
isCancelled: boolean;
|
||||
}
|
||||
|
||||
class DataWindow<TData> {
|
||||
private _data: TData[];
|
||||
class DataWindow<T> {
|
||||
private _data: T[];
|
||||
private _length: number = 0;
|
||||
private _offsetFromDataSource: number = -1;
|
||||
|
||||
private lastLoadCancellationToken: LoadCancellationToken;
|
||||
|
||||
constructor(
|
||||
private loadFunction: (offset: number, count: number) => Thenable<TData[]>,
|
||||
private placeholderItemGenerator: (index: number) => TData,
|
||||
private loadFunction: (offset: number, count: number) => Thenable<T[]>,
|
||||
private placeholderItemGenerator: (index: number) => T,
|
||||
private loadCompleteCallback: (start: number, end: number) => void
|
||||
) {
|
||||
}
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this._data = undefined;
|
||||
@@ -50,26 +41,26 @@ class DataWindow<TData> {
|
||||
}
|
||||
}
|
||||
|
||||
getStartIndex(): number {
|
||||
public getStartIndex(): number {
|
||||
return this._offsetFromDataSource;
|
||||
}
|
||||
|
||||
getEndIndex(): number {
|
||||
public getEndIndex(): number {
|
||||
return this._offsetFromDataSource + this._length;
|
||||
}
|
||||
|
||||
contains(dataSourceIndex: number): boolean {
|
||||
public contains(dataSourceIndex: number): boolean {
|
||||
return dataSourceIndex >= this.getStartIndex() && dataSourceIndex < this.getEndIndex();
|
||||
}
|
||||
|
||||
getItem(index: number): TData {
|
||||
public getItem(index: number): T {
|
||||
if (!this._data) {
|
||||
return this.placeholderItemGenerator(index);
|
||||
}
|
||||
return this._data[index - this._offsetFromDataSource];
|
||||
}
|
||||
|
||||
positionWindow(offset: number, length: number): void {
|
||||
public positionWindow(offset: number, length: number): void {
|
||||
this._offsetFromDataSource = offset;
|
||||
this._length = length;
|
||||
this._data = undefined;
|
||||
@@ -92,34 +83,28 @@ class DataWindow<TData> {
|
||||
}
|
||||
}
|
||||
|
||||
export class VirtualizedCollection<TData> implements IObservableCollection<TData> {
|
||||
export class VirtualizedCollection<T extends Slick.SlickData> implements IObservableCollection<T> {
|
||||
private _bufferWindowBefore: DataWindow<T>;
|
||||
private _window: DataWindow<T>;
|
||||
private _bufferWindowAfter: DataWindow<T>;
|
||||
|
||||
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;
|
||||
private collectionChangedCallback: (startIndex: number, count: number) => void;
|
||||
|
||||
constructor(
|
||||
windowSize: number,
|
||||
length: number,
|
||||
loadFn: (offset: number, count: number) => Thenable<TData[]>,
|
||||
private _placeHolderGenerator: (index: number) => TData
|
||||
private readonly windowSize: number,
|
||||
private placeHolderGenerator: (index: number) => T,
|
||||
private length: number,
|
||||
loadFn: (offset: number, count: number) => Thenable<T[]>
|
||||
) {
|
||||
this._windowSize = windowSize;
|
||||
this._length = length;
|
||||
|
||||
let loadCompleteCallback = (start: number, end: number) => {
|
||||
if (this.collectionChangedCallback) {
|
||||
this.collectionChangedCallback(CollectionChange.ItemsReplaced, start, end - start);
|
||||
this.collectionChangedCallback(start, end - start);
|
||||
}
|
||||
};
|
||||
|
||||
this._bufferWindowBefore = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
|
||||
this._window = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
|
||||
this._bufferWindowAfter = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
|
||||
this._bufferWindowBefore = new DataWindow(loadFn, placeHolderGenerator, loadCompleteCallback);
|
||||
this._window = new DataWindow(loadFn, placeHolderGenerator, loadCompleteCallback);
|
||||
this._bufferWindowAfter = new DataWindow(loadFn, placeHolderGenerator, loadCompleteCallback);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -128,19 +113,23 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
||||
this._window.dispose();
|
||||
}
|
||||
|
||||
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void {
|
||||
public setCollectionChangedCallback(callback: (startIndex: number, count: number) => void): void {
|
||||
this.collectionChangedCallback = callback;
|
||||
}
|
||||
|
||||
getLength(): number {
|
||||
return this._length;
|
||||
public getLength(): number {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
at(index: number): TData {
|
||||
setLength(number: any): void {
|
||||
this.length = number;
|
||||
}
|
||||
|
||||
public at(index: number): T {
|
||||
return this.getRange(index, index + 1)[0];
|
||||
}
|
||||
|
||||
getRange(start: number, end: number): TData[] {
|
||||
public getRange(start: number, end: number): T[] {
|
||||
|
||||
// current data may contain placeholders
|
||||
let currentData = this.getRangeFromCurrent(start, end);
|
||||
@@ -155,7 +144,7 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
||||
this._bufferWindowAfter = this._window;
|
||||
this._window = this._bufferWindowBefore;
|
||||
this._bufferWindowBefore = windowToRecycle;
|
||||
let newWindowOffset = Math.max(0, this._window.getStartIndex() - this._windowSize);
|
||||
let newWindowOffset = Math.max(0, this._window.getStartIndex() - this.windowSize);
|
||||
|
||||
this._bufferWindowBefore.positionWindow(newWindowOffset, this._window.getStartIndex() - newWindowOffset);
|
||||
} else if (start >= this._bufferWindowAfter.getStartIndex()) {
|
||||
@@ -164,8 +153,8 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
||||
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);
|
||||
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);
|
||||
}
|
||||
@@ -173,7 +162,7 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
||||
return currentData;
|
||||
}
|
||||
|
||||
private getRangeFromCurrent(start: number, end: number): TData[] {
|
||||
private getRangeFromCurrent(start: number, end: number): T[] {
|
||||
let currentData = [];
|
||||
for (let i = 0; i < end - start; i++) {
|
||||
currentData.push(this.getDataFromCurrent(start + i));
|
||||
@@ -182,7 +171,7 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
||||
return currentData;
|
||||
}
|
||||
|
||||
private getDataFromCurrent(index: number): TData {
|
||||
private getDataFromCurrent(index: number): T {
|
||||
if (this._bufferWindowBefore.contains(index)) {
|
||||
return this._bufferWindowBefore.getItem(index);
|
||||
} else if (this._bufferWindowAfter.contains(index)) {
|
||||
@@ -191,39 +180,47 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
|
||||
return this._window.getItem(index);
|
||||
}
|
||||
|
||||
return this._placeHolderGenerator(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);
|
||||
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);
|
||||
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);
|
||||
let bufferWindowAfterEnd = Math.min(bufferWindowAfterStart + this.windowSize, this.length);
|
||||
this._bufferWindowAfter.positionWindow(bufferWindowAfterStart, bufferWindowAfterEnd - bufferWindowAfterStart);
|
||||
}
|
||||
}
|
||||
|
||||
export class AsyncDataProvider<TData extends IGridDataRow> implements IDisposableDataProvider<TData> {
|
||||
export class AsyncDataProvider<T extends Slick.SlickData> implements IDisposableDataProvider<T> {
|
||||
|
||||
constructor(private dataRows: IObservableCollection<TData>) { }
|
||||
constructor(public dataRows: IObservableCollection<T>) { }
|
||||
|
||||
public getLength(): number {
|
||||
return this.dataRows ? this.dataRows.getLength() : 0;
|
||||
return this.dataRows.getLength();
|
||||
}
|
||||
|
||||
public getItem(index: number): TData {
|
||||
return !this.dataRows ? undefined : this.dataRows.at(index);
|
||||
public getItem(index: number): T {
|
||||
return this.dataRows.at(index);
|
||||
}
|
||||
|
||||
public getRange(start: number, end: number): TData[] {
|
||||
return !this.dataRows ? undefined : this.dataRows.getRange(start, end);
|
||||
public getRange(start: number, end: number): T[] {
|
||||
return this.dataRows.getRange(start, end);
|
||||
}
|
||||
|
||||
public set length(length: number) {
|
||||
this.dataRows.setLength(length);
|
||||
}
|
||||
|
||||
public get length(): number {
|
||||
return this.dataRows.getLength();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
|
||||
export interface IRowNumberColumnOptions {
|
||||
numberOfRows: number;
|
||||
cssClass?: string;
|
||||
@@ -17,7 +15,7 @@ const sizePerDigit = 15;
|
||||
export class RowNumberColumn<T> implements Slick.Plugin<T> {
|
||||
private handler = new Slick.EventHandler();
|
||||
private grid: Slick.Grid<T>;
|
||||
|
||||
private currentColumnWidth: number;
|
||||
|
||||
constructor(private options: IRowNumberColumnOptions) {
|
||||
}
|
||||
@@ -52,16 +50,22 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public updateRowCount(rowNum: number) {
|
||||
this.options.numberOfRows = rowNum;
|
||||
let columnWidth = Math.max(this.options.numberOfRows.toString().length * sizePerDigit, 22);
|
||||
if (columnWidth !== this.currentColumnWidth) {
|
||||
this.grid.setColumnWidths([this.getColumnDefinition()]);
|
||||
}
|
||||
}
|
||||
|
||||
public getColumnDefinition(): Slick.Column<T> {
|
||||
// that smallest we can make it is 22 due to padding and margins in the cells
|
||||
let columnWidth = Math.max(this.options.numberOfRows.toString().length * sizePerDigit, 22);
|
||||
this.currentColumnWidth = Math.max(this.options.numberOfRows.toString().length * sizePerDigit, 22);
|
||||
return {
|
||||
id: 'rowNumber',
|
||||
name: '',
|
||||
field: 'rowNumber',
|
||||
width: columnWidth,
|
||||
minWidth: columnWidth,
|
||||
maxWidth: columnWidth,
|
||||
width: this.currentColumnWidth,
|
||||
resizable: false,
|
||||
cssClass: this.options.cssClass,
|
||||
focusable: false,
|
||||
|
||||
@@ -25,7 +25,6 @@ export const NewQuery = 'NewQuery';
|
||||
export const FirewallRuleRequested = 'FirewallRuleCreated';
|
||||
export const DashboardNavigated = 'DashboardNavigated';
|
||||
|
||||
|
||||
// Telemetry Properties
|
||||
|
||||
// Modal Dialogs:
|
||||
@@ -42,3 +41,21 @@ export const Accounts = 'Accounts';
|
||||
export const FireWallRule = 'FirewallRule';
|
||||
export const AutoOAuth = 'AutoOAuth';
|
||||
export const AddNewDashboardTab = 'AddNewDashboardTab';
|
||||
|
||||
// SQL Agent Events:
|
||||
|
||||
// Views
|
||||
export const JobsView = 'JobsViewOpened';
|
||||
export const JobHistoryView = 'JobHistoryViewOpened';
|
||||
export const JobStepsView = 'JobStepsViewOpened';
|
||||
|
||||
// Actions
|
||||
export const RunAgentJob = 'RunAgentJob';
|
||||
export const StopAgentJob = 'StopAgentJob';
|
||||
export const DeleteAgentJob = 'DeleteAgentJob';
|
||||
export const DeleteAgentJobStep = 'DeleteAgentJobStep';
|
||||
export const DeleteAgentAlert = 'DeleteAgentAlert';
|
||||
export const DeleteAgentOperator = 'DeleteAgentOperator';
|
||||
export const DeleteAgentProxy = 'DeleteAgentProxy';
|
||||
|
||||
|
||||
|
||||
@@ -4,31 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import * as crypto from 'crypto';
|
||||
import * as os from 'os';
|
||||
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { warn } from 'sql/base/common/log';
|
||||
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
|
||||
// Generate a unique, deterministic ID for the current user of the extension
|
||||
export function generateUserId(): Promise<string> {
|
||||
return new Promise<string>(resolve => {
|
||||
try {
|
||||
getmac.getMac((error, macAddress) => {
|
||||
if (!error) {
|
||||
resolve(crypto.createHash('sha256').update(macAddress + os.homedir(), 'utf8').digest('hex'));
|
||||
} else {
|
||||
resolve(generateUuid()); // fallback
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
resolve(generateUuid()); // fallback
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface IConnectionTelemetryData extends ITelemetryData {
|
||||
provider?: string;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export class CommandLineService implements ICommandLineProcessing {
|
||||
// prompt the user for a new connection on startup if no profiles are registered
|
||||
this._connectionManagementService.showConnectionDialog();
|
||||
} else if (this._connectionProfile) {
|
||||
this._connectionManagementService.connectIfNotConnected(this._connectionProfile, 'connection')
|
||||
this._connectionManagementService.connectIfNotConnected(this._connectionProfile, 'connection', true)
|
||||
.then(result => TaskUtilities.newQuery(this._connectionProfile,
|
||||
this._connectionManagementService,
|
||||
this._queryEditorService,
|
||||
|
||||
@@ -19,6 +19,7 @@ import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput';
|
||||
import { NotebookInput, NotebookInputModel, NotebookInputValidator } from 'sql/parts/notebook/notebookInput';
|
||||
import { Extensions, INotebookProviderRegistry } from 'sql/services/notebook/notebookRegistry';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER } from 'sql/services/notebook/notebookService';
|
||||
import { getProviderForFileName } from 'sql/parts/notebook/notebookUtils';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -183,17 +184,6 @@ function getNotebookFileExtensions() {
|
||||
return notebookRegistry.getSupportedFileExtensions();
|
||||
}
|
||||
|
||||
function getProviderForFileName(fileName: string) {
|
||||
let fileExt = path.extname(fileName);
|
||||
if (fileExt && fileExt.startsWith('.')) {
|
||||
fileExt = fileExt.slice(1,fileExt.length);
|
||||
let notebookRegistry = Registry.as<INotebookProviderRegistry>(Extensions.NotebookProviderContribution);
|
||||
return notebookRegistry.getProviderForFileType(fileExt);
|
||||
}
|
||||
return DEFAULT_NOTEBOOK_PROVIDER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the given EditorInput is set to either undefined or sql mode
|
||||
* @param input The EditorInput to check the mode of
|
||||
|
||||
@@ -121,7 +121,7 @@ export interface IConnectionManagementService {
|
||||
* otherwise tries to make a connection and returns the owner uri when connection is complete
|
||||
* The purpose is connection by default
|
||||
*/
|
||||
connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): Promise<string>;
|
||||
connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection', saveConnection?: boolean): Promise<string>;
|
||||
|
||||
/**
|
||||
* Adds the successful connection to MRU and send the connection error back to the connection handler for failed connections
|
||||
|
||||
@@ -380,14 +380,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
* otherwise tries to make a connection and returns the owner uri when connection is complete
|
||||
* The purpose is connection by default
|
||||
*/
|
||||
public connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): Promise<string> {
|
||||
public connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection', saveConnection: boolean = false): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
let ownerUri: string = Utils.generateUri(connection, purpose);
|
||||
if (this._connectionStatusManager.isConnected(ownerUri)) {
|
||||
resolve(this._connectionStatusManager.getOriginalOwnerUri(ownerUri));
|
||||
} else {
|
||||
const options: IConnectionCompletionOptions = {
|
||||
saveTheConnection: false,
|
||||
saveTheConnection: saveConnection,
|
||||
showConnectionDialogOnError: true,
|
||||
showDashboard: purpose === 'dashboard',
|
||||
params: undefined,
|
||||
@@ -769,8 +769,19 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let tokens = await this._accountManagementService.getSecurityToken(account, AzureResource.Sql);
|
||||
connection.options['azureAccountToken'] = Object.values(tokens)[0].token;
|
||||
let tokensByTenant = await this._accountManagementService.getSecurityToken(account, AzureResource.Sql);
|
||||
let token: string;
|
||||
let tenantId = connection.azureTenantId;
|
||||
if (tenantId && tokensByTenant[tenantId]) {
|
||||
token = tokensByTenant[tenantId].token;
|
||||
} else {
|
||||
let tokens = Object.values(tokensByTenant);
|
||||
if (tokens.length === 0) {
|
||||
return false;
|
||||
}
|
||||
token = Object.values(tokensByTenant)[0].token;
|
||||
}
|
||||
connection.options['azureAccountToken'] = token;
|
||||
connection.options['password'] = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
|
||||
this.savePassword = model.savePassword;
|
||||
this.saveProfile = model.saveProfile;
|
||||
this._id = model.id;
|
||||
this.azureTenantId = model.azureTenantId;
|
||||
} else {
|
||||
//Default for a new connection
|
||||
this.savePassword = false;
|
||||
@@ -84,6 +85,14 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
|
||||
this._id = value;
|
||||
}
|
||||
|
||||
public get azureTenantId(): string {
|
||||
return this.options['azureTenantId'];
|
||||
}
|
||||
|
||||
public set azureTenantId(value: string) {
|
||||
this.options['azureTenantId'] = value;
|
||||
}
|
||||
|
||||
public get groupFullName(): string {
|
||||
return this._groupName;
|
||||
}
|
||||
@@ -159,7 +168,8 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
|
||||
userName: this.userName,
|
||||
options: this.options,
|
||||
saveProfile: this.saveProfile,
|
||||
id: this.id
|
||||
id: this.id,
|
||||
azureTenantId: this.azureTenantId
|
||||
};
|
||||
|
||||
return result;
|
||||
|
||||
@@ -52,9 +52,11 @@ export class ConnectionWidget {
|
||||
private _password: string;
|
||||
private _rememberPasswordCheckBox: Checkbox;
|
||||
private _azureAccountDropdown: SelectBox;
|
||||
private _azureTenantDropdown: SelectBox;
|
||||
private _refreshCredentialsLinkBuilder: Builder;
|
||||
private _addAzureAccountMessage: string = localize('connectionWidget.AddAzureAccount', 'Add an account...');
|
||||
private readonly _azureProviderId = 'azurePublicCloud';
|
||||
private _azureTenantId: string;
|
||||
private _azureAccountList: sqlops.Account[];
|
||||
private _advancedButton: Button;
|
||||
private _callbacks: IConnectionComponentCallbacks;
|
||||
@@ -215,6 +217,12 @@ export class ConnectionWidget {
|
||||
let refreshCredentialsBuilder = DialogHelper.appendRow(this._tableContainer, '', 'connection-label', 'connection-input', 'azure-account-row refresh-credentials-link');
|
||||
this._refreshCredentialsLinkBuilder = refreshCredentialsBuilder.a({ href: '#' }).text(localize('connectionWidget.refreshAzureCredentials', 'Refresh account credentials'));
|
||||
|
||||
// Azure tenant picker
|
||||
let tenantLabel = localize('connection.azureTenantDropdownLabel', 'Azure AD tenant');
|
||||
let tenantDropdownBuilder = DialogHelper.appendRow(this._tableContainer, tenantLabel, 'connection-label', 'connection-input', 'azure-account-row azure-tenant-row');
|
||||
this._azureTenantDropdown = new SelectBox([], undefined, this._contextViewService, tenantDropdownBuilder.getContainer(), { ariaLabel: tenantLabel });
|
||||
DialogHelper.appendInputSelectBox(tenantDropdownBuilder, this._azureTenantDropdown);
|
||||
|
||||
// Database
|
||||
let databaseOption = this._optionsMaps[ConnectionOptionSpecialType.databaseName];
|
||||
let databaseNameBuilder = DialogHelper.appendRow(this._tableContainer, databaseOption.displayName, 'connection-label', 'connection-input');
|
||||
@@ -308,6 +316,13 @@ export class ConnectionWidget {
|
||||
}));
|
||||
}
|
||||
|
||||
if (this._azureTenantDropdown) {
|
||||
this._toDispose.push(styler.attachSelectBoxStyler(this._azureTenantDropdown, this._themeService));
|
||||
this._toDispose.push(this._azureTenantDropdown.onDidSelect((selectInfo) => {
|
||||
this.onAzureTenantSelected(selectInfo.index);
|
||||
}));
|
||||
}
|
||||
|
||||
if (this._refreshCredentialsLinkBuilder) {
|
||||
this._toDispose.push(this._refreshCredentialsLinkBuilder.on(DOM.EventType.CLICK, async () => {
|
||||
let account = this._azureAccountList.find(account => account.key.accountId === this._azureAccountDropdown.value);
|
||||
@@ -426,7 +441,7 @@ export class ConnectionWidget {
|
||||
accountDropdownOptions.push(this._addAzureAccountMessage);
|
||||
this._azureAccountDropdown.setOptions(accountDropdownOptions);
|
||||
this._azureAccountDropdown.selectWithOptionName(oldSelection);
|
||||
this.updateRefreshCredentialsLink();
|
||||
await this.onAzureAccountSelected();
|
||||
}
|
||||
|
||||
private async updateRefreshCredentialsLink(): Promise<void> {
|
||||
@@ -441,7 +456,6 @@ export class ConnectionWidget {
|
||||
private async onAzureAccountSelected(): Promise<void> {
|
||||
// Reset the dropdown's validation message if the old selection was not valid but the new one is
|
||||
this.validateAzureAccountSelection(false);
|
||||
this._refreshCredentialsLinkBuilder.display('none');
|
||||
|
||||
// Open the add account dialog if needed, then select the added account
|
||||
if (this._azureAccountDropdown.value === this._addAzureAccountMessage) {
|
||||
@@ -461,6 +475,35 @@ export class ConnectionWidget {
|
||||
}
|
||||
|
||||
this.updateRefreshCredentialsLink();
|
||||
|
||||
// Display the tenant select box if needed
|
||||
const hideTenantsClassName = 'hide-azure-tenants';
|
||||
let selectedAccount = this._azureAccountList.find(account => account.key.accountId === this._azureAccountDropdown.value);
|
||||
if (selectedAccount && selectedAccount.properties.tenants && selectedAccount.properties.tenants.length > 1) {
|
||||
// There are multiple tenants available so let the user select one
|
||||
let options = selectedAccount.properties.tenants.map(tenant => tenant.displayName);
|
||||
this._azureTenantDropdown.setOptions(options);
|
||||
this._tableContainer.getContainer().classList.remove(hideTenantsClassName);
|
||||
this.onAzureTenantSelected(0);
|
||||
} else {
|
||||
if (selectedAccount && selectedAccount.properties.tenants && selectedAccount.properties.tenants.length === 1) {
|
||||
this._azureTenantId = selectedAccount.properties.tenants[0].id;
|
||||
} else {
|
||||
this._azureTenantId = undefined;
|
||||
}
|
||||
this._tableContainer.getContainer().classList.add(hideTenantsClassName);
|
||||
}
|
||||
}
|
||||
|
||||
private onAzureTenantSelected(tenantIndex: number): void {
|
||||
this._azureTenantId = undefined;
|
||||
let account = this._azureAccountList.find(account => account.key.accountId === this._azureAccountDropdown.value);
|
||||
if (account && account.properties.tenants) {
|
||||
let tenant = account.properties.tenants[tenantIndex];
|
||||
if (tenant) {
|
||||
this._azureTenantId = tenant.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private serverNameChanged(serverName: string) {
|
||||
@@ -518,6 +561,7 @@ export class ConnectionWidget {
|
||||
this._passwordInputBox.value = connectionInfo.password ? Constants.passwordChars : '';
|
||||
this._password = this.getModelValue(connectionInfo.password);
|
||||
this._saveProfile = connectionInfo.saveProfile;
|
||||
this._azureTenantId = connectionInfo.azureTenantId;
|
||||
let groupName: string;
|
||||
if (this._saveProfile) {
|
||||
if (!connectionInfo.groupFullName) {
|
||||
@@ -545,6 +589,26 @@ export class ConnectionWidget {
|
||||
|
||||
if (this._authTypeSelectBox) {
|
||||
this.onAuthTypeSelected(this._authTypeSelectBox.value);
|
||||
} else {
|
||||
let tableContainerElement = this._tableContainer.getContainer();
|
||||
tableContainerElement.classList.remove('hide-username-password');
|
||||
tableContainerElement.classList.add('hide-azure-accounts');
|
||||
}
|
||||
|
||||
if (this.authType === AuthenticationType.AzureMFA) {
|
||||
this.fillInAzureAccountOptions().then(async () => {
|
||||
this._azureAccountDropdown.selectWithOptionName(this.getModelValue(connectionInfo.userName));
|
||||
await this.onAzureAccountSelected();
|
||||
let tenantId = connectionInfo.azureTenantId;
|
||||
let account = this._azureAccountList.find(account => account.key.accountId === this._azureAccountDropdown.value);
|
||||
if (account && account.properties.tenants.length > 1) {
|
||||
let tenant = account.properties.tenants.find(tenant => tenant.id === tenantId);
|
||||
if (tenant) {
|
||||
this._azureTenantDropdown.selectWithOptionName(tenant.displayName);
|
||||
}
|
||||
this.onAzureTenantSelected(this._azureTenantDropdown.values.indexOf(this._azureTenantDropdown.value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Disable connect button if -
|
||||
@@ -712,6 +776,9 @@ export class ConnectionWidget {
|
||||
model.saveProfile = true;
|
||||
model.groupId = this.findGroupId(model.groupFullName);
|
||||
}
|
||||
if (this.authType === AuthenticationType.AzureMFA) {
|
||||
model.azureTenantId = this._azureTenantId;
|
||||
}
|
||||
}
|
||||
return validInputs;
|
||||
}
|
||||
|
||||
@@ -128,3 +128,7 @@
|
||||
.hide-refresh-link .azure-account-row.refresh-credentials-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hide-azure-tenants .azure-tenant-row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
|
||||
export class DBCellValue {
|
||||
@@ -41,20 +40,24 @@ export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef
|
||||
*/
|
||||
export function textFormatter(row: number, cell: any, value: any, columnDef: any, dataContext: any): string {
|
||||
let cellClasses = 'grid-cell-value-container';
|
||||
let valueToDisplay: string = '';
|
||||
let valueToDisplay = '';
|
||||
let titleValue = '';
|
||||
|
||||
if (DBCellValue.isDBCellValue(value)) {
|
||||
valueToDisplay = 'NULL';
|
||||
if (!value.isNull) {
|
||||
valueToDisplay = escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
|
||||
valueToDisplay = value.displayValue.replace(/(\r\n|\n|\r)/g, ' ');
|
||||
valueToDisplay = escape(valueToDisplay.length > 250 ? valueToDisplay.slice(0, 250) + '...' : valueToDisplay);
|
||||
titleValue = value.displayValue;
|
||||
} else {
|
||||
cellClasses += ' missing-value';
|
||||
}
|
||||
} else if (typeof value === 'string') {
|
||||
valueToDisplay = escape(value);
|
||||
valueToDisplay = escape(value.length > 250 ? value.slice(0, 250) + '...' : value);
|
||||
titleValue = value;
|
||||
}
|
||||
|
||||
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;
|
||||
return `<span title="${titleValue}" class="${cellClasses}">${valueToDisplay}</span>`;
|
||||
}
|
||||
|
||||
/** The following code is a rewrite over the both formatter function using dom builder
|
||||
|
||||
@@ -17,6 +17,9 @@ import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.co
|
||||
import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component';
|
||||
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
|
||||
export enum JobActions {
|
||||
Run = 'run',
|
||||
@@ -80,7 +83,8 @@ export class RunJobAction extends Action {
|
||||
constructor(
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IJobManagementService private jobManagementService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
) {
|
||||
super(RunJobAction.ID, RunJobAction.LABEL, 'runJobIcon');
|
||||
}
|
||||
@@ -89,6 +93,7 @@ export class RunJobAction extends Action {
|
||||
let jobName = context.agentJobInfo.name;
|
||||
let ownerUri = context.ownerUri;
|
||||
let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
|
||||
this.telemetryService.publicLog(TelemetryKeys.RunAgentJob);
|
||||
return new TPromise<boolean>((resolve, reject) => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => {
|
||||
if (result.success) {
|
||||
@@ -118,7 +123,8 @@ export class StopJobAction extends Action {
|
||||
constructor(
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IJobManagementService private jobManagementService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
) {
|
||||
super(StopJobAction.ID, StopJobAction.LABEL, 'stopJobIcon');
|
||||
}
|
||||
@@ -127,6 +133,7 @@ export class StopJobAction extends Action {
|
||||
let jobName = context.agentJobInfo.name;
|
||||
let ownerUri = context.ownerUri;
|
||||
let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
|
||||
this.telemetryService.publicLog(TelemetryKeys.StopAgentJob);
|
||||
return new TPromise<boolean>((resolve, reject) => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => {
|
||||
if (result.success) {
|
||||
@@ -174,7 +181,8 @@ export class DeleteJobAction extends Action {
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IJobManagementService private _jobService: IJobManagementService
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteJobAction.ID, DeleteJobAction.LABEL);
|
||||
}
|
||||
@@ -188,6 +196,7 @@ export class DeleteJobAction extends Action {
|
||||
[{
|
||||
label: DeleteJobAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJob);
|
||||
self._jobService.deleteJob(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteJob", "Could not delete job '{0}'.\nError: {1}",
|
||||
@@ -234,7 +243,8 @@ export class DeleteStepAction extends Action {
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteStepAction.ID, DeleteStepAction.LABEL);
|
||||
}
|
||||
@@ -249,6 +259,7 @@ export class DeleteStepAction extends Action {
|
||||
[{
|
||||
label: DeleteStepAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJobStep);
|
||||
self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteStep", "Could not delete step '{0}'.\nError: {1}",
|
||||
@@ -318,7 +329,8 @@ export class DeleteAlertAction extends Action {
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IJobManagementService private _jobService: IJobManagementService
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteAlertAction.ID, DeleteAlertAction.LABEL);
|
||||
}
|
||||
@@ -332,6 +344,7 @@ export class DeleteAlertAction extends Action {
|
||||
[{
|
||||
label: DeleteAlertAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentAlert);
|
||||
self._jobService.deleteAlert(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteAlert", "Could not delete alert '{0}'.\nError: {1}",
|
||||
@@ -397,7 +410,8 @@ export class DeleteOperatorAction extends Action {
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IJobManagementService private _jobService: IJobManagementService
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL);
|
||||
}
|
||||
@@ -411,6 +425,7 @@ export class DeleteOperatorAction extends Action {
|
||||
[{
|
||||
label: DeleteOperatorAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentOperator);
|
||||
self._jobService.deleteOperator(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteOperator", "Could not delete operator '{0}'.\nError: {1}",
|
||||
@@ -477,7 +492,8 @@ export class DeleteProxyAction extends Action {
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IJobManagementService private _jobService: IJobManagementService
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteProxyAction.ID, DeleteProxyAction.LABEL);
|
||||
}
|
||||
@@ -491,6 +507,7 @@ export class DeleteProxyAction extends Action {
|
||||
[{
|
||||
label: DeleteProxyAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentProxy);
|
||||
self._jobService.deleteProxy(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteProxy", "Could not delete proxy '{0}'.\nError: {1}",
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<!-- Job History details -->
|
||||
<div class='history-details'>
|
||||
<!-- Previous run list -->
|
||||
<div class="prev-run-list-container" style="min-width: 275px; height: 75vh">
|
||||
<div class="prev-run-list-container" style="min-width: 250px">
|
||||
<table *ngIf="_showPreviousRuns === true">
|
||||
<tr>
|
||||
<td class="date-column">
|
||||
@@ -89,7 +89,9 @@
|
||||
</tr>
|
||||
</table>
|
||||
<h3 *ngIf="_showPreviousRuns === false" style="text-align: center">No Previous Runs Available</h3>
|
||||
<div #table class="step-table prev-run-list" style="position: relative; height: 100%; width: 100%"></div>
|
||||
<div class="step-table prev-run-list" style="position: relative; width: 100%">
|
||||
<div #table style="position: absolute; width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Job Steps -->
|
||||
<div class="job-steps" id="job-steps">
|
||||
@@ -154,8 +156,8 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div #jobsteps style="height: 100%">
|
||||
<jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component>
|
||||
<div #jobsteps style="flex: 1 1 auto; position: relative">
|
||||
<jobstepsview-component *ngIf="showSteps === true" style="position: absolute; height: 100%; width: 100%"></jobstepsview-component>
|
||||
</div>
|
||||
<h3 *ngIf="showSteps === false">No Steps Available</h3>
|
||||
</div>
|
||||
|
||||
@@ -23,13 +23,14 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
|
||||
export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
|
||||
|
||||
@@ -77,7 +78,8 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
|
||||
this._treeController = new JobHistoryController();
|
||||
@@ -141,9 +143,9 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||
renderer: this._treeRenderer
|
||||
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||
this._register(attachListStyler(this._tree, this.themeService));
|
||||
this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT);
|
||||
this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement));
|
||||
this.initActionBar();
|
||||
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobHistoryView);
|
||||
}
|
||||
|
||||
private loadHistory() {
|
||||
@@ -293,6 +295,7 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||
if (historyDetails && statusBar) {
|
||||
let historyBottom = historyDetails.getBoundingClientRect().bottom;
|
||||
let statusTop = statusBar.getBoundingClientRect().top;
|
||||
|
||||
let height: number = statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT;
|
||||
|
||||
if (this._table) {
|
||||
@@ -302,14 +305,7 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||
}
|
||||
|
||||
if (this._tree) {
|
||||
this._tree.layout(height);
|
||||
}
|
||||
|
||||
if (this._jobStepsView) {
|
||||
let element = this._jobStepsView.nativeElement as HTMLElement;
|
||||
if (element) {
|
||||
element.style.height = height + 'px';
|
||||
}
|
||||
this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,17 +177,17 @@ table.step-list tr.step-row td {
|
||||
}
|
||||
|
||||
.history-details {
|
||||
height: 100%;
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.history-details > .job-steps {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
border-left: 3px solid #f4f4f4;
|
||||
padding-left: 10px;
|
||||
height: 100%;
|
||||
width: 90%;
|
||||
overflow-y: scroll;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vs-dark .history-details > .job-steps {
|
||||
@@ -241,13 +241,22 @@ table.step-list tr.step-row td {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.steps-tree .monaco-tree .monaco-tree-row {
|
||||
white-space: normal;
|
||||
min-height: 40px !important;
|
||||
.step-table {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
jobhistory-component .jobhistory-heading-container {
|
||||
display: -webkit-box;
|
||||
.prev-run-list-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
jobhistory-component {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
jobhistory-component > .jobhistory-heading-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
jobhistory-component > .jobhistory-heading-container > .icon.in-progress {
|
||||
@@ -267,4 +276,4 @@ jobhistory-component > .agent-actionbar-container .monaco-action-bar > ul.action
|
||||
|
||||
.hc-black jobhistory-component > .agent-actionbar-container .monaco-action-bar > ul.actions-container {
|
||||
border-top: 3px solid #2b56f2;
|
||||
}
|
||||
}
|
||||
@@ -22,4 +22,6 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class='steps-tree' #table style="height: 100%; width: 100%"></div>
|
||||
<div class='steps-tree' style="flex: 1 1 auto; position: relative">
|
||||
<div #table style="position: absolute; height: 100%; width: 100%" ></div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import 'vs/css!./jobStepsView';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
@@ -20,7 +21,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
|
||||
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
||||
|
||||
@@ -36,7 +38,6 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
|
||||
private _treeDataSource = new JobStepsViewDataSource();
|
||||
private _treeRenderer = new JobStepsViewRenderer();
|
||||
private _treeFilter = new JobStepsViewFilter();
|
||||
private _pageSize = 1024;
|
||||
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
|
||||
@@ -49,7 +50,8 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
|
||||
}
|
||||
@@ -57,17 +59,8 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
|
||||
ngAfterContentChecked() {
|
||||
if (this._jobHistoryComponent.stepRows.length > 0) {
|
||||
this._treeDataSource.data = this._jobHistoryComponent.stepRows;
|
||||
if (!this._tree) {
|
||||
this._tree = new Tree(this._tableContainer.nativeElement, {
|
||||
controller: this._treeController,
|
||||
dataSource: this._treeDataSource,
|
||||
filter: this._treeFilter,
|
||||
renderer: this._treeRenderer
|
||||
}, { verticalScrollMode: ScrollbarVisibility.Visible });
|
||||
this._register(attachListStyler(this._tree, this.themeService));
|
||||
}
|
||||
this._tree.layout(this._pageSize);
|
||||
this._tree.setInput(new JobStepsViewModel());
|
||||
this.layout();
|
||||
$('jobstepsview-component .steps-tree .monaco-tree').attr('tabIndex', '-1');
|
||||
$('jobstepsview-component .steps-tree .monaco-tree-row').attr('tabIndex', '0');
|
||||
}
|
||||
@@ -79,14 +72,20 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
|
||||
dataSource: this._treeDataSource,
|
||||
filter: this._treeFilter,
|
||||
renderer: this._treeRenderer
|
||||
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||
}, {verticalScrollMode: ScrollbarVisibility.Visible, horizontalScrollMode: ScrollbarVisibility.Visible });
|
||||
this.layout();
|
||||
this._register(attachListStyler(this._tree, this.themeService));
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobStepsView);
|
||||
}
|
||||
|
||||
public onFirstVisible() {
|
||||
}
|
||||
|
||||
public layout() {
|
||||
if (this._tree) {
|
||||
let treeheight = dom.getContentHeight(this._tableContainer.nativeElement);
|
||||
this._tree.layout(treeheight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
}
|
||||
|
||||
jobstepsview-component {
|
||||
padding-top: 10px;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import * as tree from 'vs/base/parts/tree/browser/tree';
|
||||
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -86,7 +87,7 @@ export class JobStepsViewRenderer implements tree.IRenderer {
|
||||
private _statusIcon: HTMLElement;
|
||||
|
||||
public getHeight(tree: tree.ITree, element: JobStepsViewRow): number {
|
||||
return 22 * Math.ceil(element.message.length/JobManagementUtilities.jobMessageLength);
|
||||
return 40;
|
||||
}
|
||||
|
||||
public getTemplateId(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): string {
|
||||
@@ -118,6 +119,7 @@ export class JobStepsViewRenderer implements tree.IRenderer {
|
||||
let stepMessageCol: HTMLElement = DOM.$('div');
|
||||
stepMessageCol.className = 'tree-message-col';
|
||||
stepMessageCol.innerText = element.message;
|
||||
$(templateData.label).empty();
|
||||
templateData.label.appendChild(stepIdCol);
|
||||
templateData.label.appendChild(stepNameCol);
|
||||
templateData.label.appendChild(stepMessageCol);
|
||||
|
||||
@@ -37,6 +37,8 @@ import { IDashboardService } from 'sql/services/dashboard/common/dashboardServic
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { tableBackground, cellBackground, cellBorderColor } from 'sql/common/theme/colors';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
|
||||
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
@@ -106,7 +108,8 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
|
||||
this._didTabChange = false;
|
||||
@@ -127,6 +130,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
|
||||
this._visibilityElement = this._gridEl;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e)));
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobsView);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -933,19 +937,19 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
|
||||
// add steps
|
||||
if (this.jobSteps && this.jobSteps[jobId]) {
|
||||
let steps = this.jobSteps[jobId];
|
||||
job[0].JobSteps = steps;
|
||||
job[0].jobSteps = steps;
|
||||
}
|
||||
|
||||
// add schedules
|
||||
if (this.jobSchedules && this.jobSchedules[jobId]) {
|
||||
let schedules = this.jobSchedules[jobId];
|
||||
job[0].JobSchedules = schedules;
|
||||
job[0].jobSchedules = schedules;
|
||||
}
|
||||
|
||||
// add alerts
|
||||
if (this.jobAlerts && this.jobAlerts[jobId]) {
|
||||
let alerts = this.jobAlerts[jobId];
|
||||
job[0].Alerts = alerts;
|
||||
job[0].alerts = alerts;
|
||||
}
|
||||
return job && job.length > 0 ? job[0] : undefined;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,20 @@ export class QueryTextEditor extends BaseTextEditor {
|
||||
if (!this._config) {
|
||||
this._config = new Configuration(undefined, editorWidget.getDomNode());
|
||||
}
|
||||
let editorHeightUsingLines = this._config.editor.lineHeight * editorWidget.getModel().getLineCount();
|
||||
let editorWidgetModel = editorWidget.getModel();
|
||||
let lineCount = editorWidgetModel.getLineCount();
|
||||
// Need to also keep track of lines that wrap; if we just keep into account line count, then the editor's height would not be
|
||||
// tall enough and we would need to show a scrollbar. Unfortunately, it looks like there isn't any metadata saved in a ICodeEditor
|
||||
// around max column length for an editor (which we could leverage to see if we need to loop through every line to determine
|
||||
// number of lines that wrap). Finally, viewportColumn is calculated on editor resizing automatically; we can use it to ensure
|
||||
// that the viewportColumn will always be greater than any character's column in an editor.
|
||||
let numberWrappedLines = 0;
|
||||
for (let line = 1; line <= lineCount; line++) {
|
||||
if (editorWidgetModel.getLineMaxColumn(line) >= this._config.editor.layoutInfo.viewportColumn - 1) {
|
||||
numberWrappedLines += Math.ceil(editorWidgetModel.getLineMaxColumn(line) / this._config.editor.layoutInfo.viewportColumn);
|
||||
}
|
||||
}
|
||||
let editorHeightUsingLines = this._config.editor.lineHeight * (lineCount + numberWrappedLines);
|
||||
let editorHeightUsingMinHeight = Math.max(editorHeightUsingLines, this._minHeight);
|
||||
this.setHeight(editorHeightUsingMinHeight);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
</div>
|
||||
<div #editor class="editor" style="flex: 1 1 auto; overflow: hidden;">
|
||||
</div>
|
||||
<div #moreactions class="toolbar" style="flex: 0 0 auto; display: flex; flex-flow:column; width: 20px; min-height: 20px; max-height: 20px; padding-top: 10px; orientation: portrait">
|
||||
<div #moreactions class="toolbar" style="flex: 0 0 auto; display: flex; flex-flow:column; width: 20px; min-height: 20px; max-height: 20px; padding-top: 0px; orientation: portrait">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { RunCellAction, CellContext, NotebookCellToggleMoreActon } from 'sql/parts/notebook/cellViews/codeActions';
|
||||
import { RunCellAction, DeleteCellAction, AddCellAction, CellContext } from 'sql/parts/notebook/cellViews/codeActions';
|
||||
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { ToggleMoreWidgetAction } from 'sql/parts/dashboard/common/actions';
|
||||
import { CellTypes } from 'sql/parts/notebook/models/contracts';
|
||||
@@ -59,14 +59,15 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
}
|
||||
|
||||
protected _actionBar: Taskbar;
|
||||
protected _moreActions: ActionBar;
|
||||
private readonly _minimumHeight = 30;
|
||||
private _editor: QueryTextEditor;
|
||||
private _editorInput: UntitledEditorInput;
|
||||
private _editorModel: ITextModel;
|
||||
private _uri: string;
|
||||
private _model: NotebookModel;
|
||||
private _actions: Action[] = [];
|
||||
private _activeCellId: string;
|
||||
private _toggleMoreActions: NotebookCellToggleMoreActon;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface,
|
||||
@@ -80,6 +81,13 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
@Inject(INotificationService) private notificationService: INotificationService,
|
||||
) {
|
||||
super();
|
||||
this._actions.push(
|
||||
this._instantiationService.createInstance(AddCellAction, 'codeBefore', localize('codeBefore', 'Insert Code before'), CellTypes.Code, false),
|
||||
this._instantiationService.createInstance(AddCellAction, 'codeAfter', localize('codeAfter', 'Insert Code after'), CellTypes.Code, true),
|
||||
this._instantiationService.createInstance(AddCellAction, 'markdownBefore', localize('markdownBefore', 'Insert Markdown before'), CellTypes.Markdown, false),
|
||||
this._instantiationService.createInstance(AddCellAction, 'markdownAfter', localize('markdownAfter', 'Insert Markdown after'), CellTypes.Markdown, true),
|
||||
this._instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', 'Delete'))
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -90,20 +98,20 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this._toggleMoreActions = new NotebookCellToggleMoreActon(
|
||||
this._instantiationService,
|
||||
this.contextMenuService,
|
||||
this.notificationService,
|
||||
this.moreActionsElementRef,
|
||||
this.model);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
this.updateLanguageMode();
|
||||
this.updateModel();
|
||||
if (this._toggleMoreActions) {
|
||||
this._toggleMoreActions.onChange(this.cellModel, changes);
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
if (this.cellModel.id === changedProp.currentValue) {
|
||||
this.toggleMoreActions(true);
|
||||
}
|
||||
else {
|
||||
this.toggleMoreActions(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +173,21 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
]);
|
||||
}
|
||||
|
||||
private toggleMoreActions(showIcon: boolean) {
|
||||
let context = new CellContext(this.model, this.cellModel);
|
||||
let moreActionsElement = <HTMLElement>this.moreActionsElementRef.nativeElement;
|
||||
if (showIcon) {
|
||||
if (moreActionsElement.childNodes.length > 0) {
|
||||
moreActionsElement.removeChild(moreActionsElement.childNodes[0]);
|
||||
}
|
||||
this._moreActions = new ActionBar(moreActionsElement, { orientation: ActionsOrientation.VERTICAL });
|
||||
this._moreActions.context = { target: moreActionsElement };
|
||||
this._moreActions.push(this._instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, context), { icon: showIcon, label: false });
|
||||
}
|
||||
else if (moreActionsElement.childNodes.length > 0) {
|
||||
moreActionsElement.removeChild(moreActionsElement.childNodes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private createUri(): URI {
|
||||
let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.cellModel.id}` });
|
||||
@@ -192,8 +215,8 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
let toolbarEl = <HTMLElement>this.toolbarElement.nativeElement;
|
||||
toolbarEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
|
||||
let moreactionsEl = <HTMLElement>this.moreActionsElementRef.nativeElement;
|
||||
moreactionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
let moreActionsEl = <HTMLElement>this.moreActionsElementRef.nativeElement;
|
||||
moreActionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,20 +5,13 @@
|
||||
|
||||
import { nb } from 'sqlops';
|
||||
|
||||
import { ElementRef, SimpleChange } from '@angular/core';
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ToggleMoreWidgetAction } from 'sql/parts/dashboard/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
import { CellType, CellTypes } from 'sql/parts/notebook/models/contracts';
|
||||
import { CellType } from 'sql/parts/notebook/models/contracts';
|
||||
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { getErrorMessage } from 'sql/parts/notebook/notebookUtils';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ICellModel, FutureInternal } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { ToggleableAction } from 'sql/parts/notebook/notebookActions';
|
||||
|
||||
@@ -191,51 +184,4 @@ export class DeleteCellAction extends CellActionBase {
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellToggleMoreActon {
|
||||
private _actions: Action[] = [];
|
||||
private _moreActions: ActionBar;
|
||||
|
||||
constructor (
|
||||
private _instantiationService: IInstantiationService,
|
||||
private contextMenuService: IContextMenuService,
|
||||
private notificationService: INotificationService,
|
||||
private moreActionElementRef: ElementRef,
|
||||
private model: NotebookModel
|
||||
) {
|
||||
this._actions.push(
|
||||
this._instantiationService.createInstance(AddCellAction, 'codeBefore', localize('codeBefore', 'Insert Code before'), CellTypes.Code, false, this.notificationService),
|
||||
this._instantiationService.createInstance(AddCellAction, 'codeBefore', localize('codeAfter', 'Insert Code after'), CellTypes.Code, true, this.notificationService),
|
||||
this._instantiationService.createInstance(AddCellAction, 'markdownBefore', localize('markdownBefore', 'Insert Markdown before'), CellTypes.Markdown, false, this.notificationService),
|
||||
this._instantiationService.createInstance(AddCellAction, 'markdownAfter', localize('markdownAfter', 'Insert Markdown after'), CellTypes.Markdown, true, this.notificationService),
|
||||
this._instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', 'Delete'), this.notificationService)
|
||||
);
|
||||
let moreActionsElement = <HTMLElement>this.moreActionElementRef.nativeElement;
|
||||
this._moreActions = new ActionBar(moreActionsElement, { orientation: ActionsOrientation.VERTICAL });
|
||||
this._moreActions.context = { target: moreActionsElement };
|
||||
}
|
||||
|
||||
toggle(showIcon: boolean): void {
|
||||
if (showIcon) {
|
||||
this._moreActions.push(this._instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, this.model, this.contextMenuService), { icon: showIcon, label: false });
|
||||
} else if (this._moreActions) {
|
||||
this._moreActions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public onChange(cellModel: ICellModel, changes: { [propKey: string]: SimpleChange }): void {
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
if (cellModel.id === changedProp.currentValue) {
|
||||
this.toggle(true);
|
||||
}
|
||||
else {
|
||||
this.toggle(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="notebook-code" style="flex: 0 0 auto;">
|
||||
<code-component [cellModel]="cellModel" [model]="model" [activeCellId]="activeCellId"></code-component>
|
||||
</div>
|
||||
<div #codeCellOutput class="notebook-output" style="flex: 0 0 auto;">
|
||||
<div style="flex: 0 0 auto; width: 100%; height: 100%; display: block">
|
||||
<output-area-component *ngIf="cellModel.outputs && cellModel.outputs.length > 0" [cellModel]="cellModel">
|
||||
</output-area-component>
|
||||
</div>
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./codeCell';
|
||||
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, SimpleChange, OnChanges } from '@angular/core';
|
||||
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
|
||||
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||
|
||||
@@ -21,9 +16,9 @@ export const CODE_SELECTOR: string = 'code-cell-component';
|
||||
selector: CODE_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./codeCell.component.html'))
|
||||
})
|
||||
|
||||
export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
@ViewChild('codeCellOutput', { read: ElementRef }) private outputPreview: ElementRef;
|
||||
|
||||
@Input() cellModel: ICellModel;
|
||||
@Input() set model(value: NotebookModel) {
|
||||
this._model = value;
|
||||
@@ -37,14 +32,11 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
if (this.cellModel) {
|
||||
this.cellModel.onOutputsChanged(() => {
|
||||
this._changeRef.detectChanges();
|
||||
@@ -74,10 +66,4 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
public layout() {
|
||||
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
let outputElement = <HTMLElement>this.outputPreview.nativeElement;
|
||||
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div style="flex: 0 0 auto;">
|
||||
<div #outputarea class="notebook-output" style="flex: 0 0 auto;">
|
||||
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" >
|
||||
</output-component>
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./code';
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef } from '@angular/core';
|
||||
import 'vs/css!./outputArea';
|
||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, forwardRef, ChangeDetectorRef } from '@angular/core';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
|
||||
export const OUTPUT_AREA_SELECTOR: string = 'output-area-component';
|
||||
|
||||
@@ -14,20 +17,30 @@ export const OUTPUT_AREA_SELECTOR: string = 'output-area-component';
|
||||
templateUrl: decodeURI(require.toUrl('./outputArea.component.html'))
|
||||
})
|
||||
export class OutputAreaComponent extends AngularDisposable implements OnInit {
|
||||
@ViewChild('outputarea', { read: ElementRef }) private outputArea: ElementRef;
|
||||
@Input() cellModel: ICellModel;
|
||||
|
||||
private readonly _minimumHeight = 30;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
ngOnInit(): void {
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
if (this.cellModel) {
|
||||
this.cellModel.onOutputsChanged(() => {
|
||||
this._changeRef.detectChanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
let outputElement = <HTMLElement>this.outputArea.nativeElement;
|
||||
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
code-cell-component {
|
||||
output-area-component {
|
||||
display: block;
|
||||
}
|
||||
|
||||
code-cell-component .notebook-output {
|
||||
output-area-component .notebook-output {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
user-select: initial;
|
||||
padding: 5px 20px 0px;
|
||||
box-shadow: rgba(120, 120, 120, 0.75) 0px -2px 1px -2px;
|
||||
}
|
||||
@@ -6,8 +6,9 @@
|
||||
-->
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div class="notebook-text" style="flex: 0 0 auto;">
|
||||
<code-component *ngIf="isEditMode" [cellModel]="cellModel" (onContentChanged)="handleContentChanged()" [activeCellId]="activeCellId" [hideVerticalToolbar]=1></code-component>
|
||||
<code-component *ngIf="isEditMode" [cellModel]="cellModel" (onContentChanged)="handleContentChanged()" [model]="model" [activeCellId]="activeCellId" [hideVerticalToolbar]=true>
|
||||
</code-component>
|
||||
</div>
|
||||
<div #preview class="notebook-preview" style="flex: 0 0 auto;" (dblclick)="toggleEditMode()">
|
||||
<div #preview style="flex: 0 0 auto;" (dblclick)="toggleEditMode()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { ISanitizer, defaultSanitizer } from 'sql/parts/notebook/outputs/sanitizer';
|
||||
import { localize } from 'vs/nls';
|
||||
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||
|
||||
export const TEXT_SELECTOR: string = 'text-cell-component';
|
||||
|
||||
@@ -25,12 +26,20 @@ export const TEXT_SELECTOR: string = 'text-cell-component';
|
||||
export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
@ViewChild('preview', { read: ElementRef }) private output: ElementRef;
|
||||
@Input() cellModel: ICellModel;
|
||||
|
||||
@Input() set model(value: NotebookModel) {
|
||||
this._model = value;
|
||||
}
|
||||
|
||||
@Input() set activeCellId(value: string) {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
private _content: string;
|
||||
private isEditMode: boolean;
|
||||
private _sanitizer: ISanitizer;
|
||||
private _previewCssApplied: boolean = false;
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
|
||||
constructor(
|
||||
@@ -43,17 +52,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
this.isEditMode = false;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
this.updatePreview();
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
this._activeCellId = changedProp.currentValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Gets sanitizer from ISanitizer interface
|
||||
private get sanitizer(): ISanitizer {
|
||||
if (this._sanitizer) {
|
||||
@@ -62,17 +60,41 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
return this._sanitizer = defaultSanitizer;
|
||||
}
|
||||
|
||||
get model(): NotebookModel {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.updatePreview();
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
this.cellModel.onOutputsChanged(e => {
|
||||
this.updatePreview();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
this._activeCellId = changedProp.currentValue;
|
||||
this.toggleEditMode(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the preview of markdown component with latest changes
|
||||
* If content is empty and in non-edit mode, default it to 'Double-click to edit'
|
||||
* Sanitizes the data to be shown in markdown cell
|
||||
*/
|
||||
private updatePreview() {
|
||||
if (this._content !== this.cellModel.source) {
|
||||
if (this._content !== this.cellModel.source || this.cellModel.source.length === 0) {
|
||||
if (!this.cellModel.source && !this.isEditMode) {
|
||||
(<HTMLElement>this.output.nativeElement).innerHTML = localize('doubleClickEdit', 'Double-click to edit');
|
||||
} else {
|
||||
@@ -94,14 +116,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
return content;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.updatePreview();
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
this.cellModel.onOutputsChanged(e => {
|
||||
this.updatePreview();
|
||||
});
|
||||
}
|
||||
|
||||
// Todo: implement layout
|
||||
public layout() {
|
||||
@@ -113,12 +127,29 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
public handleContentChanged(): void {
|
||||
if (!this._previewCssApplied) {
|
||||
this.updatePreviewCssClass();
|
||||
}
|
||||
this.updatePreview();
|
||||
}
|
||||
|
||||
public toggleEditMode(): void {
|
||||
this.isEditMode = !this.isEditMode;
|
||||
public toggleEditMode(editMode?: boolean): void {
|
||||
this.isEditMode = editMode !== undefined? editMode : !this.isEditMode;
|
||||
this.updatePreviewCssClass();
|
||||
this.updatePreview();
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
// Updates the css class to preview 'div' based on edit mode
|
||||
private updatePreviewCssClass() {
|
||||
let outputElement = <HTMLElement>this.output.nativeElement;
|
||||
if (this.isEditMode && this.cellModel.source) {
|
||||
outputElement.className = 'notebook-preview';
|
||||
this._previewCssApplied = true;
|
||||
}
|
||||
else {
|
||||
outputElement.className = '';
|
||||
this._previewCssApplied = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
src/sql/parts/notebook/media/dark/save_inverse.svg
Normal file
1
src/sql/parts/notebook/media/dark/save_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>save_inverse</title><path class="cls-1" d="M14,1a1,1,0,0,1,.39.08,1,1,0,0,1,.53.53A1,1,0,0,1,15,2V15H2.79L1,13.2V2a1,1,0,0,1,.08-.39,1,1,0,0,1,.53-.53A1,1,0,0,1,2,1Zm0,1H13V8H3V2H2V12.79L3.2,14H4V10h7v4h3ZM4,7h8V2H4Zm6,4H5v3H6V12H7v2h3Z"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
src/sql/parts/notebook/media/light/save.svg
Normal file
1
src/sql/parts/notebook/media/light/save.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>save</title><path d="M14,1a1,1,0,0,1,.39.08,1,1,0,0,1,.53.53A1,1,0,0,1,15,2V15H2.79L1,13.2V2a1,1,0,0,1,.08-.39,1,1,0,0,1,.53-.53A1,1,0,0,1,2,1Zm0,1H13V8H3V2H2V12.79L3.2,14H4V10h7v4h3ZM4,7h8V2H4Zm6,4H5v3H6V12H7v2h3Z"/></svg>
|
||||
|
After Width: | Height: | Size: 323 B |
@@ -14,6 +14,7 @@ import { ICellModelOptions, IModelFactory, FutureInternal } from './modelInterfa
|
||||
import * as notebookUtils from '../notebookUtils';
|
||||
import { CellTypes, CellType, NotebookChangeType } from 'sql/parts/notebook/models/contracts';
|
||||
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||
|
||||
let modelId = 0;
|
||||
|
||||
@@ -34,7 +35,7 @@ export class CellModel implements ICellModel {
|
||||
private _active: boolean;
|
||||
private _cellUri: URI;
|
||||
|
||||
constructor(private factory: IModelFactory, cellData?: nb.ICell, private _options?: ICellModelOptions) {
|
||||
constructor(private factory: IModelFactory, cellData?: nb.ICellContents, private _options?: ICellModelOptions) {
|
||||
this.id = `${modelId++}`;
|
||||
CellModel.CreateLanguageMappings();
|
||||
// Do nothing for now
|
||||
@@ -222,18 +223,48 @@ export class CellModel implements ICellModel {
|
||||
if (output) {
|
||||
// deletes transient node in the serialized JSON
|
||||
delete output['transient'];
|
||||
this._outputs.push(output);
|
||||
this._outputs.push(this.rewriteOutputUrls(output));
|
||||
this.fireOutputsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private rewriteOutputUrls(output: nb.ICellOutput): nb.ICellOutput {
|
||||
// Only rewrite if this is coming back during execution, not when loading from disk.
|
||||
// A good approximation is that the model has a future (needed for execution)
|
||||
if (this.future) {
|
||||
try {
|
||||
let result = output as nb.IDisplayResult;
|
||||
if (result && result.data && result.data['text/html']) {
|
||||
let nbm = (this as CellModel).options.notebook as NotebookModel;
|
||||
if (nbm.hadoopConnection) {
|
||||
let host = nbm.hadoopConnection.host;
|
||||
let html = result.data['text/html'];
|
||||
html = html.replace(/(https?:\/\/mssql-master.*\/proxy)(.*)/g, function (a, b, c) {
|
||||
let ret = '';
|
||||
if (b !== '') {
|
||||
ret = 'https://' + host + ':30443/gateway/default/yarn/proxy';
|
||||
}
|
||||
if (c !== '') {
|
||||
ret = ret + c;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
(<nb.IDisplayResult>output).data['text/html'] = html;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private getDisplayId(msg: nb.IIOPubMessage): string | undefined {
|
||||
let transient = (msg.content.transient || {});
|
||||
return transient['display_id'] as string;
|
||||
}
|
||||
|
||||
public toJSON(): nb.ICell {
|
||||
let cellJson: Partial<nb.ICell> = {
|
||||
public toJSON(): nb.ICellContents {
|
||||
let cellJson: Partial<nb.ICellContents> = {
|
||||
cell_type: this._cellType,
|
||||
source: this._source,
|
||||
metadata: {
|
||||
@@ -244,10 +275,10 @@ export class CellModel implements ICellModel {
|
||||
cellJson.outputs = this._outputs;
|
||||
cellJson.execution_count = 1; // TODO: keep track of actual execution count
|
||||
}
|
||||
return cellJson as nb.ICell;
|
||||
return cellJson as nb.ICellContents;
|
||||
}
|
||||
|
||||
public fromJSON(cell: nb.ICell): void {
|
||||
public fromJSON(cell: nb.ICellContents): void {
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { ClientSession } from './clientSession';
|
||||
|
||||
export class ModelFactory implements IModelFactory {
|
||||
|
||||
public createCell(cell: nb.ICell, options: ICellModelOptions): ICellModel {
|
||||
public createCell(cell: nb.ICellContents, options: ICellModelOptions): ICellModel {
|
||||
return new CellModel(this, cell, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { INotebookManager } from 'sql/services/notebook/notebookService';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { NotebookConnection } from 'sql/parts/notebook/models/notebookConnection';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export interface IClientSessionOptions {
|
||||
notebookUri: URI;
|
||||
@@ -328,6 +329,14 @@ export interface INotebookModel {
|
||||
* Notifies the notebook of a change in the cell
|
||||
*/
|
||||
onCellChange(cell: ICellModel, change: NotebookChangeType): void;
|
||||
|
||||
|
||||
/**
|
||||
* Push edit operations, basically editing the model. This is the preferred way of
|
||||
* editing the model. Long-term, this will ensure edit operations can be added to the undo stack
|
||||
* @param edits The edit operations to perform
|
||||
*/
|
||||
pushEditOperations(edits: ISingleNotebookEditOperation[]): void;
|
||||
}
|
||||
|
||||
export interface ICellModelOptions {
|
||||
@@ -348,7 +357,7 @@ export interface ICellModel {
|
||||
readonly onOutputsChanged: Event<ReadonlyArray<nb.ICellOutput>>;
|
||||
setFuture(future: FutureInternal): void;
|
||||
equals(cellModel: ICellModel): boolean;
|
||||
toJSON(): nb.ICell;
|
||||
toJSON(): nb.ICellContents;
|
||||
}
|
||||
|
||||
export interface FutureInternal extends nb.IFuture {
|
||||
@@ -357,7 +366,7 @@ export interface FutureInternal extends nb.IFuture {
|
||||
|
||||
export interface IModelFactory {
|
||||
|
||||
createCell(cell: nb.ICell, options: ICellModelOptions): ICellModel;
|
||||
createCell(cell: nb.ICellContents, options: ICellModelOptions): ICellModel;
|
||||
createClientSession(options: IClientSessionOptions): IClientSession;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { NotebookConnection } from 'sql/parts/notebook/models/notebookConnection';
|
||||
import { INotification, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
/*
|
||||
* Used to control whether a message in a dialog/wizard is displayed as an error,
|
||||
@@ -237,7 +238,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
|
||||
private createCell(cellType: CellType): ICellModel {
|
||||
let singleCell: nb.ICell = {
|
||||
let singleCell: nb.ICellContents = {
|
||||
cell_type: cellType,
|
||||
source: '',
|
||||
metadata: {},
|
||||
@@ -263,6 +264,25 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
}
|
||||
|
||||
pushEditOperations(edits: ISingleNotebookEditOperation[]): void {
|
||||
if (this.inErrorState || !this._cells) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let edit of edits) {
|
||||
let newCells: ICellModel[] = [];
|
||||
if (edit.cell) {
|
||||
// TODO: should we validate and complete required missing parameters?
|
||||
let contents: nb.ICellContents = edit.cell as nb.ICellContents;
|
||||
newCells.push(this.notebookOptions.factory.createCell(contents, { notebook: this, isTrusted: this._trustedMode }));
|
||||
}
|
||||
this._cells.splice(edit.range.start, edit.range.end - edit.range.start, ...newCells);
|
||||
this._contentChangedEmitter.fire({
|
||||
changeType: NotebookChangeType.CellsAdded
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get activeCell(): ICellModel {
|
||||
return this._activeCell;
|
||||
}
|
||||
@@ -281,9 +301,14 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
notebookManager: this.notebookManager,
|
||||
notificationService: this.notebookOptions.notificationService
|
||||
});
|
||||
let id: string = this.connectionProfile ? this.connectionProfile.id : undefined;
|
||||
let profile = this.connectionProfile as IConnectionProfile;
|
||||
|
||||
if (this.isValidKnoxConnection(profile)) {
|
||||
this._hadoopConnection = new NotebookConnection(this.connectionProfile);
|
||||
} else {
|
||||
this._hadoopConnection = undefined;
|
||||
}
|
||||
|
||||
this._hadoopConnection = this.connectionProfile ? new NotebookConnection(this.connectionProfile) : undefined;
|
||||
this._clientSession.initialize(this._hadoopConnection);
|
||||
this._sessionLoadFinished = this._clientSession.ready.then(async () => {
|
||||
if (this._clientSession.isInErrorState) {
|
||||
@@ -389,7 +414,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
|
||||
// Get default language if saved in notebook file
|
||||
// Otherwise, default to python
|
||||
private getDefaultLanguageInfo(notebook: nb.INotebook): nb.ILanguageInfo {
|
||||
private getDefaultLanguageInfo(notebook: nb.INotebookContents): nb.ILanguageInfo {
|
||||
return notebook!.metadata!.language_info || {
|
||||
name: 'python',
|
||||
version: '',
|
||||
@@ -398,7 +423,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
|
||||
// Get default kernel info if saved in notebook file
|
||||
private getSavedKernelInfo(notebook: nb.INotebook): nb.IKernelInfo {
|
||||
private getSavedKernelInfo(notebook: nb.INotebookContents): nb.IKernelInfo {
|
||||
return notebook!.metadata!.kernelspec;
|
||||
}
|
||||
|
||||
@@ -490,8 +515,8 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
/**
|
||||
* Serialize the model to JSON.
|
||||
*/
|
||||
toJSON(): nb.INotebook {
|
||||
let cells: nb.ICell[] = this.cells.map(c => c.toJSON());
|
||||
toJSON(): nb.INotebookContents {
|
||||
let cells: nb.ICellContents[] = this.cells.map(c => c.toJSON());
|
||||
let metadata = Object.create(null) as nb.INotebookMetadata;
|
||||
// TODO update language and kernel when these change
|
||||
metadata.kernelspec = this._savedKernelInfo;
|
||||
|
||||
@@ -140,9 +140,16 @@ export class SparkMagicContexts {
|
||||
* @param savedKernelInfo kernel info loaded from
|
||||
*/
|
||||
public static getDefaultKernel(specs: nb.IAllKernels, connectionInfo: IConnectionProfile, savedKernelInfo: nb.IKernelInfo, notificationService: INotificationService): nb.IKernelSpec {
|
||||
let defaultKernel = specs.kernels.find((kernel) => kernel.name === specs.defaultKernel);
|
||||
let foundSavedKernelInSpecs;
|
||||
let defaultKernel;
|
||||
if (specs) {
|
||||
defaultKernel = specs.kernels.find((kernel) => kernel.name === specs.defaultKernel);
|
||||
if (savedKernelInfo) {
|
||||
foundSavedKernelInSpecs = specs.kernels.find((kernel) => kernel.name === savedKernelInfo.name);
|
||||
}
|
||||
}
|
||||
let profile = connectionInfo as IConnectionProfile;
|
||||
if (specs && connectionInfo && profile.providerName === notebookConstants.hadoopKnoxProviderName) {
|
||||
if (foundSavedKernelInSpecs && specs && connectionInfo && profile.providerName === notebookConstants.hadoopKnoxProviderName) {
|
||||
// set default kernel to default spark kernel if profile exists
|
||||
// otherwise, set default to kernel info loaded from existing file
|
||||
defaultKernel = !savedKernelInfo ? specs.kernels.find((spec) => spec.name === notebookConstants.defaultSparkKernel) : savedKernelInfo;
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div #toolbar class="editor-toolbar actionbar-container" style="flex: 0 0 auto; display: flex; flex-flow: row; width: 100%; align-items: center; height: 36px">
|
||||
</div>
|
||||
<div class="scrollable" style="flex: 1 1 auto; position: relative">
|
||||
<div class="scrollable" style="flex: 1 1 auto; position: relative" (click)="unselectActiveCell()">
|
||||
<loading-spinner [loading]="isLoading"></loading-spinner>
|
||||
<div class="notebook-cell" *ngFor="let cell of cells" (click)="selectCell(cell)" [class.active]="cell.active" (keydown)="onKeyDown($event)">
|
||||
<div class="notebook-cell" *ngFor="let cell of cells" (click)="selectCell(cell, $event)" [class.active]="cell.active">
|
||||
<code-cell-component *ngIf="cell.cellType === 'code'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
||||
</code-cell-component>
|
||||
<text-cell-component *ngIf="cell.cellType === 'markdown'" [cellModel]="cell" [activeCellId]="activeCellId">
|
||||
<text-cell-component *ngIf="cell.cellType === 'markdown'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
||||
</text-cell-component>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,8 @@ import './notebookStyles';
|
||||
|
||||
import { nb } from 'sqlops';
|
||||
|
||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnDestroy } from '@angular/core';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { INotificationService, INotification } from 'vs/platform/notification/common/notification';
|
||||
@@ -18,12 +17,12 @@ import { localize } from 'vs/nls';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
|
||||
import { CellTypes, CellType, NotebookChangeType } from 'sql/parts/notebook/models/contracts';
|
||||
import { ICellModel, IModelFactory } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
|
||||
import { ICellModel, IModelFactory, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { INotebookService, INotebookParams, INotebookManager } from 'sql/services/notebook/notebookService';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor } from 'sql/services/notebook/notebookService';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { NotebookModel, ErrorInfo, MessageLevel, NotebookContentChange } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { NotebookModel, NotebookContentChange } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
|
||||
import * as notebookUtils from './notebookUtils';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
@@ -31,8 +30,17 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction } from 'sql/parts/notebook/notebookActions';
|
||||
import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction, SaveNotebookAction } from 'sql/parts/notebook/notebookActions';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IAction, Action, IActionItem } from 'vs/base/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { fillInActions, LabeledMenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||
|
||||
@@ -41,7 +49,7 @@ export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||
selector: NOTEBOOK_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./notebook.component.html'))
|
||||
})
|
||||
export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
export class NotebookComponent extends AngularDisposable implements OnInit, OnDestroy, INotebookEditor {
|
||||
@ViewChild('toolbar', { read: ElementRef }) private toolbar: ElementRef;
|
||||
private _model: NotebookModel;
|
||||
private _isInErrorState: boolean = false;
|
||||
@@ -62,26 +70,56 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IConnectionManagementService) private connectionManagementService: IConnectionManagementService,
|
||||
@Inject(IObjectExplorerService) private objectExplorerService: IObjectExplorerService,
|
||||
@Inject(IEditorService) private editorService: IEditorService,
|
||||
@Inject(INotificationService) private notificationService: INotificationService,
|
||||
@Inject(INotebookService) private notebookService: INotebookService,
|
||||
@Inject(IBootstrapParams) private notebookParams: INotebookParams,
|
||||
@Inject(IBootstrapParams) private _notebookParams: INotebookParams,
|
||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
|
||||
@Inject(IContextViewService) private contextViewService: IContextViewService,
|
||||
@Inject(IConnectionDialogService) private connectionDialogService: IConnectionDialogService
|
||||
@Inject(IConnectionDialogService) private connectionDialogService: IConnectionDialogService,
|
||||
@Inject(IContextKeyService) private contextKeyService: IContextKeyService,
|
||||
@Inject(IMenuService) private menuService: IMenuService,
|
||||
@Inject(IKeybindingService) private keybindingService: IKeybindingService
|
||||
) {
|
||||
super();
|
||||
this.profile = this.notebookParams!.profile;
|
||||
this.updateProfile();
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
private updateProfile(): void {
|
||||
this.profile = this.notebookParams!.profile;
|
||||
if (!this.profile) {
|
||||
// use global connection if possible
|
||||
let profile = TaskUtilities.getCurrentGlobalConnection(this.objectExplorerService, this.connectionManagementService, this.editorService);
|
||||
// TODO use generic method to match kernel with valid connection that's compatible. For now, we only have 1
|
||||
if (profile && profile.providerName === notebookConstants.hadoopKnoxProviderName) {
|
||||
this.profile = profile;
|
||||
} else {
|
||||
// if not, try 1st active connection that matches our filter
|
||||
let profiles = this.connectionManagementService.getActiveConnections([notebookConstants.hadoopKnoxProviderName]);
|
||||
if (profiles && profiles.length > 0) {
|
||||
this.profile = profiles[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
this.notebookService.addNotebookEditor(this);
|
||||
this.initActionBar();
|
||||
this.doLoad();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.notebookService) {
|
||||
this.notebookService.removeNotebookEditor(this);
|
||||
}
|
||||
}
|
||||
|
||||
public get model(): NotebookModel {
|
||||
return this._model;
|
||||
}
|
||||
@@ -103,7 +141,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
toolbarEl.style.borderBottomColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
|
||||
public selectCell(cell: ICellModel) {
|
||||
public selectCell(cell: ICellModel, event?: Event) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
if (cell !== this._activeCell) {
|
||||
if (this._activeCell) {
|
||||
this._activeCell.active = false;
|
||||
@@ -116,6 +157,16 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public unselectActiveCell() {
|
||||
if (this._activeCell) {
|
||||
this._activeCell.active = false;
|
||||
}
|
||||
this._activeCell = null;
|
||||
this._model.activeCell = null;
|
||||
this._activeCellId = null;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
// Add cell based on cell type
|
||||
public addCell(cellType: CellType)
|
||||
{
|
||||
@@ -171,16 +222,16 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
}
|
||||
|
||||
private async loadModel(): Promise<void> {
|
||||
this.notebookManager = await this.notebookService.getOrCreateNotebookManager(this.notebookParams.providerId, this.notebookParams.notebookUri);
|
||||
this.notebookManager = await this.notebookService.getOrCreateNotebookManager(this._notebookParams.providerId, this._notebookParams.notebookUri);
|
||||
let model = new NotebookModel({
|
||||
factory: this.modelFactory,
|
||||
notebookUri: this.notebookParams.notebookUri,
|
||||
notebookUri: this._notebookParams.notebookUri,
|
||||
connectionService: this.connectionManagementService,
|
||||
notificationService: this.notificationService,
|
||||
notebookManager: this.notebookManager
|
||||
}, false, this.profile);
|
||||
model.onError((errInfo: INotification) => this.handleModelError(errInfo));
|
||||
await model.requestModelLoad(this.notebookParams.isTrusted);
|
||||
await model.requestModelLoad(this._notebookParams.isTrusted);
|
||||
model.contentChanged((change) => this.handleContentChanged(change));
|
||||
this._model = model;
|
||||
this.updateToolbarComponents(this._model.trustedMode);
|
||||
@@ -201,10 +252,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
}
|
||||
|
||||
private get modelFactory(): IModelFactory {
|
||||
if (!this.notebookParams.modelFactory) {
|
||||
this.notebookParams.modelFactory = new ModelFactory();
|
||||
if (!this._notebookParams.modelFactory) {
|
||||
this._notebookParams.modelFactory = new ModelFactory();
|
||||
}
|
||||
return this.notebookParams.modelFactory;
|
||||
return this._notebookParams.modelFactory;
|
||||
}
|
||||
private handleModelError(notification: INotification): void {
|
||||
this.notificationService.notify(notification);
|
||||
@@ -239,9 +290,6 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
attachTodropdwon.render(attachToContainer);
|
||||
attachSelectBoxStyler(attachTodropdwon, this.themeService);
|
||||
|
||||
let attachToInfoText = document.createElement('div');
|
||||
attachToInfoText.className = 'notebook-info-label';
|
||||
attachToInfoText.innerText = 'Attach To: ';
|
||||
|
||||
let addCodeCellButton = new AddCellAction('notebook.AddCodeCell', localize('code', 'Code'), 'notebook-button icon-add');
|
||||
addCodeCellButton.cellType = CellTypes.Code;
|
||||
@@ -252,21 +300,44 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
this._trustedAction = this.instantiationService.createInstance(TrustedAction, 'notebook.Trusted');
|
||||
this._trustedAction.enabled = false;
|
||||
|
||||
let saveNotebookButton = this.instantiationService.createInstance(SaveNotebookAction, 'notebook.SaveNotebook', localize('save', 'Save'), 'notebook-button icon-save');
|
||||
|
||||
// Get all of the menu contributions that use the ID 'notebook/toolbar'.
|
||||
// Then, find all groups (currently we don't leverage the contributed
|
||||
// groups functionality for the notebook toolbar), and fill in the 'primary'
|
||||
// array with items that don't list a group. Finally, add any actions from
|
||||
// the primary array to the end of the toolbar.
|
||||
const notebookBarMenu = this.menuService.createMenu(MenuId.NotebookToolbar, this.contextKeyService);
|
||||
let groups = notebookBarMenu.getActions({ arg: null, shouldForwardArgs: true });
|
||||
let primary: IAction[] = [];
|
||||
let secondary: IAction[] = [];
|
||||
fillInActions(groups, {primary, secondary}, false, (group: string) => group === undefined);
|
||||
|
||||
let taskbar = <HTMLElement>this.toolbar.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
|
||||
this._actionBar = new Taskbar(taskbar, this.contextMenuService, { actionItemProvider: action => this.actionItemProvider(action as Action)});
|
||||
this._actionBar.context = this;
|
||||
this._actionBar.setContent([
|
||||
{ element: kernelContainer },
|
||||
{ element: attachToContainer },
|
||||
{ action: addCodeCellButton},
|
||||
{ action: addTextCellButton},
|
||||
{ action: this._trustedAction}
|
||||
{ action: addCodeCellButton },
|
||||
{ action: addTextCellButton },
|
||||
{ action: saveNotebookButton },
|
||||
{ action: this._trustedAction }
|
||||
]);
|
||||
|
||||
// Primary actions are categorized as those that are added to the 'horizontal' group.
|
||||
// For the vertical toolbar, we can do the same thing and instead use the 'vertical' group.
|
||||
for (let action of primary) {
|
||||
this._actionBar.addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
public async save(): Promise<boolean> {
|
||||
try {
|
||||
let saved = await this._model.saveModel();
|
||||
if (saved) {
|
||||
this.setDirty(false);
|
||||
}
|
||||
return saved;
|
||||
} catch (err) {
|
||||
this.notificationService.error(localize('saveFailed', 'Failed to save notebook: {0}', notebookUtils.getErrorMessage(err)));
|
||||
@@ -275,11 +346,46 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
|
||||
}
|
||||
|
||||
private setDirty(isDirty: boolean): void {
|
||||
// TODO reenable handling of isDirty
|
||||
// if (this.editor) {
|
||||
// this.editor.isDirty = isDirty;
|
||||
// }
|
||||
if(this._notebookParams.input){
|
||||
this._notebookParams.input.setDirty(isDirty);
|
||||
}
|
||||
}
|
||||
|
||||
private actionItemProvider(action: Action): IActionItem {
|
||||
// Check extensions to create ActionItem; otherwise, return undefined
|
||||
// This is similar behavior that exists in MenuItemActionItem
|
||||
if (action instanceof MenuItemAction) {
|
||||
return new LabeledMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService, 'notebook-button');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get notebookParams(): INotebookParams {
|
||||
return this._notebookParams;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this._notebookParams.notebookUri.toString();
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.editorService.activeEditor === this.notebookParams.input;
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
let notebookEditor = this.notebookParams.input;
|
||||
return this.editorService.visibleEditors.some(e => e === notebookEditor);
|
||||
}
|
||||
|
||||
isDirty(): boolean {
|
||||
return this.notebookParams.input.isDirty();
|
||||
}
|
||||
|
||||
executeEdits(edits: ISingleNotebookEditOperation[]): boolean {
|
||||
if (!edits || edits.length === 0) {
|
||||
return false;
|
||||
}
|
||||
this._model.pushEditOperations(edits);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,50 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import { NotebookInput, NotebookInputModel, notebooksEnabledCondition } from 'sql/parts/notebook/notebookInput';
|
||||
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
|
||||
import { NotebookEditor } from 'sql/parts/notebook/notebookEditor';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
|
||||
|
||||
let counter = 0;
|
||||
|
||||
/**
|
||||
* todo: Will remove this code.
|
||||
* This is the entry point to open the new Notebook
|
||||
*/
|
||||
export class NewNotebookAction extends Action {
|
||||
|
||||
public static ID = 'workbench.action.newnotebook';
|
||||
public static LABEL = localize('workbench.action.newnotebook.description', 'New Notebook');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorService private _editorService: IEditorService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<void> {
|
||||
let title = `Untitled-${counter++}`;
|
||||
let untitledUri = URI.from({ scheme: Schemas.untitled, path: title });
|
||||
let model = new NotebookInputModel(untitledUri, undefined, false, undefined);
|
||||
let input = this._instantiationService.createInstance(NotebookInput, title, model);
|
||||
return this._editorService.openEditor(input, { pinned: true }).then(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
// Model View editor registration
|
||||
const viewModelEditorDescriptor = new EditorDescriptor(
|
||||
@@ -58,31 +18,3 @@ const viewModelEditorDescriptor = new EditorDescriptor(
|
||||
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
|
||||
.registerEditor(viewModelEditorDescriptor, [new SyncDescriptor(NotebookInput)]);
|
||||
|
||||
// Feature flag for built-in Notebooks. Will be removed in the future.
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'notebook',
|
||||
'title': 'Notebook',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'notebook.enabled': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': localize('notebook.enabledDescription', 'Enable viewing notebook files using built-in notebook editor.')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// this is the entry point to open the new Notebook
|
||||
CommandsRegistry.registerCommand(NewNotebookAction.ID, serviceAccessor => {
|
||||
serviceAccessor.get(IInstantiationService).createInstance(NewNotebookAction, NewNotebookAction.ID, NewNotebookAction.LABEL).run();
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: NewNotebookAction.ID,
|
||||
title:NewNotebookAction.LABEL,
|
||||
},
|
||||
when: notebooksEnabledCondition
|
||||
});
|
||||
@@ -31,9 +31,13 @@
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
padding-left: 15px;
|
||||
background-size: 11px;
|
||||
background-size: 13px;
|
||||
margin-right: 0.3em;
|
||||
font-size: 11px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.notebookEditor .monaco-select-box {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.notebookEditor .notebook-button.icon-add{
|
||||
@@ -61,4 +65,13 @@
|
||||
.vs-dark .notebookEditor .notebook-button.icon-notTrusted,
|
||||
.hc-black .notebookEditor .notebook-button.icon-notTrusted{
|
||||
background-image: url("./media/dark/nottrusted_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor .notebook-button.icon-save{
|
||||
background-image: url("./media/light/save.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor .notebook-button.icon-save,
|
||||
.hc-black .notebookEditor .notebook-button.icon-save{
|
||||
background-image: url("./media/dark/save_inverse.svg");
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { NotebookComponent } from 'sql/parts/notebook/notebook.component';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { getErrorMessage } from 'sql/parts/notebook/notebookUtils';
|
||||
import { noKernel } from 'sql/services/notebook/sessionManager';
|
||||
|
||||
const msgLoading = localize('loading', 'Loading kernels...');
|
||||
const kernelLabel: string = localize('Kernel', 'Kernel: ');
|
||||
@@ -26,6 +27,7 @@ const msgLoadingContexts = localize('loadingContexts', 'Loading contexts...');
|
||||
const msgAddNewConnection = localize('addNewConnection', 'Add new connection');
|
||||
const msgSelectConnection = localize('selectConnection', 'Select connection');
|
||||
const msgConnectionNotApplicable = localize('connectionNotSupported', 'n/a');
|
||||
const msgLocalHost = localize('localhost', 'Localhost');
|
||||
|
||||
// Action to add a cell to notebook based on cell type(code/markdown).
|
||||
export class AddCellAction extends Action {
|
||||
@@ -48,6 +50,28 @@ export class AddCellAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class SaveNotebookAction extends Action {
|
||||
private static readonly notebookSavedMsg = localize('notebookSavedMsg', 'Notebook saved successfully.');
|
||||
private static readonly notebookFailedSaveMsg = localize('notebookFailedSaveMsg', 'Failed to save Notebook.');
|
||||
constructor(
|
||||
id: string, label: string, cssClass: string,
|
||||
@INotificationService private _notificationService: INotificationService
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
|
||||
public async run(context: NotebookComponent): TPromise<boolean> {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
let saved = await context.save();
|
||||
if (saved) {
|
||||
this._notificationService.notify({ severity: Severity.Info, message: SaveNotebookAction.notebookSavedMsg, actions });
|
||||
} else {
|
||||
this._notificationService.error(SaveNotebookAction.notebookFailedSaveMsg);
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IToggleableState {
|
||||
baseClass?: string;
|
||||
shouldToggleTooltip?: boolean;
|
||||
@@ -215,9 +239,8 @@ export class AttachToDropdown extends SelectBox {
|
||||
|
||||
// Load "Attach To" dropdown with the values corresponding to Kernel dropdown
|
||||
public async loadAttachToDropdown(model: INotebookModel, currentKernel: string): Promise<void> {
|
||||
if (currentKernel === notebookConstants.python3) {
|
||||
this.setOptions([msgConnectionNotApplicable]);
|
||||
this.disable();
|
||||
if (currentKernel === notebookConstants.python3 || currentKernel === noKernel) {
|
||||
this.setOptions([msgLocalHost]);
|
||||
}
|
||||
else {
|
||||
let hadoopConnections = this.getHadoopConnections(model);
|
||||
@@ -292,6 +315,7 @@ export class AttachToDropdown extends SelectBox {
|
||||
attachToConnections = attachToConnections.filter(val => val !== msgSelectConnection);
|
||||
|
||||
let index = attachToConnections.findIndex((connection => connection === connectedServer));
|
||||
this.setOptions([]);
|
||||
this.setOptions(attachToConnections);
|
||||
if (!index || index < 0 || index >= attachToConnections.length) {
|
||||
index = 0;
|
||||
|
||||
@@ -86,6 +86,7 @@ export class NotebookEditor extends BaseEditor {
|
||||
input.hasBootstrapped = true;
|
||||
let params: INotebookParams = {
|
||||
notebookUri: input.notebookUri,
|
||||
input: input,
|
||||
providerId: input.providerId ? input.providerId : DEFAULT_NOTEBOOK_PROVIDER,
|
||||
isTrusted: input.isTrusted
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/edi
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
|
||||
import { INotebookService } from 'sql/services/notebook/notebookService';
|
||||
|
||||
@@ -88,15 +89,6 @@ export class NotebookInput extends EditorInput {
|
||||
) {
|
||||
super();
|
||||
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
|
||||
this.onDispose(() => {
|
||||
if (this.notebookService) {
|
||||
this.notebookService.handleNotebookClosed(this.notebookUri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
@@ -116,6 +108,10 @@ export class NotebookInput extends EditorInput {
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
if (!this._title) {
|
||||
this._title = resources.basenameOrAuthority(this._model.notebookUri);
|
||||
}
|
||||
|
||||
return this._title;
|
||||
}
|
||||
|
||||
@@ -173,4 +169,12 @@ export class NotebookInput extends EditorInput {
|
||||
save(): TPromise<boolean> {
|
||||
return this._model.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets active editor with dirty value.
|
||||
* @param isDirty boolean value to set editor dirty
|
||||
*/
|
||||
setDirty(isDirty: boolean): void {
|
||||
this._model.setDirty(isDirty);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
collector.addRule(`
|
||||
.notebookEditor .notebook-cell.active {
|
||||
border-color: ${activeBorder};
|
||||
border-width: 2px;
|
||||
border-width: 1px;
|
||||
box-shadow: 0px 4px 6px 0px rgba(0,0,0,0.14);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import { nb } from 'sqlops';
|
||||
import * as os from 'os';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IOutputChannel } from 'vs/workbench/parts/output/common/output';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { INotebookProviderRegistry, Extensions } from 'sql/services/notebook/notebookRegistry';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE } from 'sql/services/notebook/notebookService';
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,3 +40,23 @@ export async function mkDir(dirPath: string, outputChannel?: IOutputChannel): Pr
|
||||
await pfs.mkdirp(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
export function getProviderForFileName(fileName: string): string {
|
||||
let fileExt = path.extname(fileName);
|
||||
let provider: string;
|
||||
let notebookRegistry = Registry.as<INotebookProviderRegistry>(Extensions.NotebookProviderContribution);
|
||||
// First try to get provider for actual file type
|
||||
if (fileExt && fileExt.startsWith('.')) {
|
||||
fileExt = fileExt.slice(1,fileExt.length);
|
||||
provider = notebookRegistry.getProviderForFileType(fileExt);
|
||||
}
|
||||
// Fallback to provider for default file type (assume this is a global handler)
|
||||
if (!provider) {
|
||||
provider = notebookRegistry.getProviderForFileType(DEFAULT_NOTEBOOK_FILETYPE);
|
||||
}
|
||||
// Finally if all else fails, use the built-in handler
|
||||
if (!provider) {
|
||||
provider = DEFAULT_NOTEBOOK_PROVIDER;
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati
|
||||
import * as nls from 'vs/nls';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConnectionManagementService, IConnectionDialogService} from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IObjectExplorerService } from '../../objectExplorer/common/objectExplorerService';
|
||||
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -55,10 +55,10 @@ CommandsRegistry.registerCommand({
|
||||
|
||||
let promise;
|
||||
if (connectionProfile) {
|
||||
promise = connectionService.connectIfNotConnected(connectionProfile);
|
||||
promise = connectionService.connectIfNotConnected(connectionProfile, 'connection', true);
|
||||
} else {
|
||||
// if still no luck, we will open the Connection dialog and let user connect to a server
|
||||
promise = connectionDialogService.openDialogAndWait(connectionService, { connectionType: 1, providers: [mssqlProviderName] }).then((profile) => {
|
||||
promise = connectionDialogService.openDialogAndWait(connectionService, { connectionType: 0, showDashboard: false, providers: [mssqlProviderName] }).then((profile) => {
|
||||
connectionProfile = profile as ConnectionProfile;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -308,8 +308,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
// ----- Public
|
||||
|
||||
public focusFindInput(): void {
|
||||
this._findInput.select();
|
||||
// Edge browser requires focus() in addition to select()
|
||||
this._findInput.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll
|
||||
if (p) {
|
||||
this._profilerTable.setActiveCell(p.row, p.col);
|
||||
this._updateFinderMatchState();
|
||||
this._finder.focusFindInput();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -557,6 +557,7 @@ export class ProfilerEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._profilerEditorContextKey.set(true);
|
||||
super.focus();
|
||||
let savedViewState = this._savedTableViewStates.get(this.input);
|
||||
if (savedViewState) {
|
||||
|
||||
@@ -66,7 +66,8 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
||||
let searchFn = (val: { [x: string]: string }, exp: string): Array<number> => {
|
||||
let ret = new Array<number>();
|
||||
for (let i = 0; i < this._columns.length; i++) {
|
||||
if (val[this._columns[i]].includes(exp)) {
|
||||
let colVal = val[this._columns[i]];
|
||||
if (colVal && colVal.toLocaleLowerCase().includes(exp.toLocaleLowerCase())) {
|
||||
ret.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface IQueryManagementService {
|
||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
||||
parseSyntax(ownerUri:string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||
parseSyntax(ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
||||
disposeQuery(ownerUri: string): Thenable<void>;
|
||||
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
||||
@@ -38,7 +38,8 @@ export interface IQueryManagementService {
|
||||
onQueryComplete(result: sqlops.QueryExecuteCompleteNotificationResult): void;
|
||||
onBatchStart(batchInfo: sqlops.QueryExecuteBatchNotificationParams): void;
|
||||
onBatchComplete(batchInfo: sqlops.QueryExecuteBatchNotificationParams): void;
|
||||
onResultSetComplete(resultSetInfo: sqlops.QueryExecuteResultSetCompleteNotificationParams): void;
|
||||
onResultSetAvailable(resultSetInfo: sqlops.QueryExecuteResultSetNotificationParams): void;
|
||||
onResultSetUpdated(resultSetInfo: sqlops.QueryExecuteResultSetNotificationParams): void;
|
||||
onMessage(message: sqlops.QueryExecuteMessageParams): void;
|
||||
|
||||
// Edit Data Callbacks
|
||||
@@ -65,7 +66,7 @@ export interface IQueryRequestHandler {
|
||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
||||
parseSyntax(ownerUri:string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||
parseSyntax(ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
||||
disposeQuery(ownerUri: string): Thenable<void>;
|
||||
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
||||
@@ -244,9 +245,15 @@ export class QueryManagementService implements IQueryManagementService {
|
||||
});
|
||||
}
|
||||
|
||||
public onResultSetComplete(resultSetInfo: sqlops.QueryExecuteResultSetCompleteNotificationParams): void {
|
||||
public onResultSetAvailable(resultSetInfo: sqlops.QueryExecuteResultSetNotificationParams): void {
|
||||
this._notify(resultSetInfo.ownerUri, (runner: QueryRunner) => {
|
||||
runner.handleResultSetComplete(resultSetInfo);
|
||||
runner.handleResultSetAvailable(resultSetInfo);
|
||||
});
|
||||
}
|
||||
|
||||
public onResultSetUpdated(resultSetInfo: sqlops.QueryExecuteResultSetNotificationParams): void {
|
||||
this._notify(resultSetInfo.ownerUri, (runner: QueryRunner) => {
|
||||
runner.handleResultSetUpdated(resultSetInfo);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as pretty from 'pretty-data';
|
||||
|
||||
import { attachTableStyler } from 'sql/common/theme/styler';
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
|
||||
@@ -12,7 +14,7 @@ import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scr
|
||||
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
|
||||
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
|
||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, RestoreTableAction, ChartDataAction, ShowQueryPlanAction } from 'sql/parts/query/editor/actions';
|
||||
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, RestoreTableAction, ChartDataAction } from 'sql/parts/query/editor/actions';
|
||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
@@ -20,9 +22,9 @@ import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/share
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
||||
import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||
import { warn } from 'sql/base/common/log';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as pretty from 'pretty-data';
|
||||
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -38,7 +40,7 @@ import { $ } from 'vs/base/browser/builder';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Dimension, getContentWidth, isInDOM } from 'vs/base/browser/dom';
|
||||
import { isInDOM } from 'vs/base/browser/dom';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -175,7 +177,8 @@ export class GridPanel extends ViewletPanel {
|
||||
this.reset();
|
||||
this.queryRunnerDisposables = [];
|
||||
this.runner = runner;
|
||||
this.queryRunnerDisposables.push(this.runner.onResultSet(e => this.onResultSet(e)));
|
||||
this.queryRunnerDisposables.push(this.runner.onResultSet(this.onResultSet, this));
|
||||
this.queryRunnerDisposables.push(this.runner.onResultSetUpdate(this.updateResultSet, this));
|
||||
this.queryRunnerDisposables.push(this.runner.onQueryStart(() => {
|
||||
if (this.state) {
|
||||
this.state.tableStates = [];
|
||||
@@ -200,6 +203,33 @@ export class GridPanel extends ViewletPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private updateResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
|
||||
|
||||
let resultsToUpdate: sqlops.ResultSetSummary[];
|
||||
if (!Array.isArray(resultSet)) {
|
||||
resultsToUpdate = [resultSet];
|
||||
} else {
|
||||
resultsToUpdate = resultSet;
|
||||
}
|
||||
|
||||
for (let set of resultsToUpdate) {
|
||||
let table = this.tables.find(t => t.resultSet.batchId === set.batchId && t.resultSet.id === set.id);
|
||||
if (table) {
|
||||
table.updateResult(set);
|
||||
} else {
|
||||
warn('Got result set update request for non-existant table');
|
||||
}
|
||||
}
|
||||
|
||||
this.maximumBodySize = this.tables.reduce((p, c) => {
|
||||
return p + c.maximumSize;
|
||||
}, 0);
|
||||
|
||||
if (this.state && this.state.scrollPosition) {
|
||||
this.splitView.setScrollPosition(this.state.scrollPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
|
||||
let resultsToAdd: sqlops.ResultSetSummary[];
|
||||
if (!Array.isArray(resultSet)) {
|
||||
@@ -323,9 +353,12 @@ class GridTable<T> extends Disposable implements IView {
|
||||
private selectionModel = new CellSelectionModel();
|
||||
private styles: ITableStyles;
|
||||
private currentHeight: number;
|
||||
private dataProvider: AsyncDataProvider<T>;
|
||||
|
||||
private columns: Slick.Column<T>[];
|
||||
|
||||
private rowNumberColumn: RowNumberColumn<T>;
|
||||
|
||||
private _onDidChange = new Emitter<number>();
|
||||
public readonly onDidChange: Event<number> = this._onDidChange.event;
|
||||
|
||||
@@ -335,13 +368,20 @@ class GridTable<T> extends Disposable implements IView {
|
||||
private _state: GridTableState;
|
||||
|
||||
private scrolled = false;
|
||||
private visible = false;
|
||||
|
||||
public get resultSet(): sqlops.ResultSetSummary {
|
||||
return this._resultSet;
|
||||
}
|
||||
|
||||
// this handles if the row count is small, like 4-5 rows
|
||||
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
private get maxSize(): number {
|
||||
return ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private runner: QueryRunner,
|
||||
public readonly resultSet: sqlops.ResultSetSummary,
|
||||
private _resultSet: sqlops.ResultSetSummary,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@@ -367,7 +407,31 @@ class GridTable<T> extends Disposable implements IView {
|
||||
});
|
||||
}
|
||||
|
||||
public onAdd() {
|
||||
this.visible = true;
|
||||
let collection = new VirtualizedCollection(
|
||||
50,
|
||||
index => this.placeholdGenerator(index),
|
||||
this.resultSet.rowCount,
|
||||
(offset, count) => this.loadData(offset, count)
|
||||
);
|
||||
collection.setCollectionChangedCallback((startIndex, count) => {
|
||||
this.renderGridDataRowsRange(startIndex, count);
|
||||
});
|
||||
this.dataProvider.dataRows = collection;
|
||||
this.table.updateRowCount();
|
||||
}
|
||||
|
||||
public onRemove() {
|
||||
this.visible = false;
|
||||
let collection = new VirtualizedCollection(
|
||||
50,
|
||||
index => this.placeholdGenerator(index),
|
||||
0,
|
||||
() => TPromise.as([])
|
||||
);
|
||||
this.dataProvider.dataRows = collection;
|
||||
this.table.updateRowCount();
|
||||
// when we are removed slickgrid acts badly so we need to account for that
|
||||
this.scrolled = false;
|
||||
}
|
||||
@@ -379,34 +443,38 @@ class GridTable<T> extends Disposable implements IView {
|
||||
private build(): void {
|
||||
let tableContainer = document.createElement('div');
|
||||
tableContainer.style.display = 'inline-block';
|
||||
tableContainer.style.width = `calc(100% - ${ACTIONBAR_WIDTH}px)`;
|
||||
|
||||
this.container.appendChild(tableContainer);
|
||||
|
||||
let collection = new VirtualizedCollection(50, this.resultSet.rowCount,
|
||||
(offset, count) => this.loadData(offset, count),
|
||||
index => this.placeholdGenerator(index)
|
||||
let collection = new VirtualizedCollection(
|
||||
50,
|
||||
index => this.placeholdGenerator(index),
|
||||
0,
|
||||
() => TPromise.as([])
|
||||
);
|
||||
collection.setCollectionChangedCallback((change, startIndex, count) => {
|
||||
collection.setCollectionChangedCallback((startIndex, count) => {
|
||||
this.renderGridDataRowsRange(startIndex, count);
|
||||
});
|
||||
let numberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount });
|
||||
this.rowNumberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount });
|
||||
let copyHandler = new CopyKeybind();
|
||||
copyHandler.onCopy(e => {
|
||||
new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run(this.generateContext());
|
||||
});
|
||||
this.columns.unshift(numberColumn.getColumnDefinition());
|
||||
this.columns.unshift(this.rowNumberColumn.getColumnDefinition());
|
||||
let tableOptions: Slick.GridOptions<T> = {
|
||||
rowHeight: ROW_HEIGHT,
|
||||
showRowNumber: true,
|
||||
forceFitColumns: false,
|
||||
defaultColumnWidth: 120
|
||||
};
|
||||
this.table = this._register(new Table(tableContainer, { dataProvider: new AsyncDataProvider(collection), columns: this.columns }, tableOptions));
|
||||
this.dataProvider = new AsyncDataProvider(collection);
|
||||
this.table = this._register(new Table(tableContainer, { dataProvider: this.dataProvider, columns: this.columns }, tableOptions));
|
||||
this.table.setSelectionModel(this.selectionModel);
|
||||
this.table.registerPlugin(new MouseWheelSupport());
|
||||
this.table.registerPlugin(new AutoColumnSize());
|
||||
this.table.registerPlugin(copyHandler);
|
||||
this.table.registerPlugin(numberColumn);
|
||||
this.table.registerPlugin(this.rowNumberColumn);
|
||||
this.table.registerPlugin(new AdditionalKeyBindings());
|
||||
this._register(this.table.onContextMenu(this.contextMenu, this));
|
||||
this._register(this.table.onClick(this.onTableClick, this));
|
||||
@@ -507,7 +575,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
} catch (e) {
|
||||
// If Xml fails to parse, fall back on original Xml content
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
let jsonContent: string = undefined;
|
||||
try {
|
||||
jsonContent = JSON.parse(content);
|
||||
@@ -526,6 +594,15 @@ class GridTable<T> extends Disposable implements IView {
|
||||
}
|
||||
}
|
||||
|
||||
public updateResult(resultSet: sqlops.ResultSetSummary) {
|
||||
this._resultSet = resultSet;
|
||||
if (this.table && this.visible) {
|
||||
this.dataProvider.length = resultSet.rowCount;
|
||||
this.table.updateRowCount();
|
||||
}
|
||||
this.rowNumberColumn.updateRowCount(resultSet.rowCount);
|
||||
}
|
||||
|
||||
private generateContext(cell?: Slick.Cell): IGridActionContext {
|
||||
const selection = this.selectionModel.getSelectedRanges();
|
||||
return <IGridActionContext>{
|
||||
@@ -577,12 +654,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
} else {
|
||||
this.currentHeight = size;
|
||||
}
|
||||
this.table.layout(
|
||||
new Dimension(
|
||||
getContentWidth(this.container) - ACTIONBAR_WIDTH,
|
||||
size
|
||||
)
|
||||
);
|
||||
this.table.layout(size, Orientation.VERTICAL);
|
||||
}
|
||||
|
||||
public get minimumSize(): number {
|
||||
@@ -596,6 +668,9 @@ class GridTable<T> extends Disposable implements IView {
|
||||
|
||||
private loadData(offset: number, count: number): Thenable<T[]> {
|
||||
return this.runner.getQueryRows(offset, count, this.resultSet.batchId, this.resultSet.id).then(response => {
|
||||
if (!response.resultSubset) {
|
||||
return [];
|
||||
}
|
||||
return response.resultSubset.rows.map(r => {
|
||||
let dataWithSchema = {};
|
||||
// skip the first column since its a number column
|
||||
|
||||
@@ -282,6 +282,7 @@ export class QueryResultsView extends Disposable {
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
dispose(this.runnerDisposables);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,18 @@ export default class QueryRunner extends Disposable {
|
||||
private _echoedResultSet = echo(this._debouncedResultSet.event);
|
||||
public readonly onResultSet = this._echoedResultSet.event;
|
||||
|
||||
private _onResultSetUpdate = this._register(new Emitter<sqlops.ResultSetSummary>());
|
||||
private _debouncedResultSetUpdate = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSetUpdate.event, (l, e) => {
|
||||
// on first run
|
||||
if (types.isUndefinedOrNull(l)) {
|
||||
return [e];
|
||||
} else {
|
||||
return l.concat(e);
|
||||
}
|
||||
});
|
||||
private _echoedResultSetUpdate = echo(this._debouncedResultSetUpdate.event);
|
||||
public readonly onResultSetUpdate = this._echoedResultSetUpdate.event;
|
||||
|
||||
private _onQueryStart = this._register(new Emitter<void>());
|
||||
public readonly onQueryStart: Event<void> = this._onQueryStart.event;
|
||||
|
||||
@@ -336,7 +348,7 @@ export default class QueryRunner extends Disposable {
|
||||
/**
|
||||
* Handle a ResultSetComplete from the service layer
|
||||
*/
|
||||
public handleResultSetComplete(result: sqlops.QueryExecuteResultSetCompleteNotificationParams): void {
|
||||
public handleResultSetAvailable(result: sqlops.QueryExecuteResultSetNotificationParams): void {
|
||||
if (result && result.resultSetSummary) {
|
||||
let resultSet = result.resultSetSummary;
|
||||
let batchSet: sqlops.BatchSummary;
|
||||
@@ -365,7 +377,9 @@ export default class QueryRunner extends Disposable {
|
||||
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => this._planXml.resolve(e.resultSubset.rows[0][0].displayValue));
|
||||
}
|
||||
}
|
||||
if (batchSet) {
|
||||
// we will just ignore the set if we already have it
|
||||
// ideally this should never happen
|
||||
if (batchSet && !batchSet.resultSetSummaries[resultSet.id]) {
|
||||
// Store the result set in the batch and emit that a result set has completed
|
||||
batchSet.resultSetSummaries[resultSet.id] = resultSet;
|
||||
this._eventEmitter.emit(EventType.RESULT_SET, resultSet);
|
||||
@@ -374,6 +388,27 @@ export default class QueryRunner extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public handleResultSetUpdated(result: sqlops.QueryExecuteResultSetNotificationParams): void {
|
||||
if (result && result.resultSetSummary) {
|
||||
let resultSet = result.resultSetSummary;
|
||||
let batchSet: sqlops.BatchSummary;
|
||||
batchSet = this.batchSets[resultSet.batchId];
|
||||
// handle getting queryPlanxml if we need too
|
||||
if (this.isQueryPlan) {
|
||||
// check if this result has show plan, this needs work, it won't work for any other provider
|
||||
let hasShowPlan = !!result.resultSetSummary.columnInfo.find(e => e.columnName === 'Microsoft SQL Server 2005 XML Showplan');
|
||||
if (hasShowPlan) {
|
||||
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => this._planXml.resolve(e.resultSubset.rows[0][0].displayValue));
|
||||
}
|
||||
}
|
||||
if (batchSet) {
|
||||
// Store the result set in the batch and emit that a result set has completed
|
||||
batchSet.resultSetSummaries[resultSet.id] = resultSet;
|
||||
this._onResultSetUpdate.fire(resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a Mssage from the service layer
|
||||
*/
|
||||
|
||||
@@ -249,9 +249,9 @@ export class QueryEditorService implements IQueryEditorService {
|
||||
QueryEditorService.editorService.openEditor(newEditorInput, options, group).then((editor) => {
|
||||
resolve(QueryEditorService._onEditorOpened(editor, uri.toString(), undefined, options.pinned));
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
});
|
||||
(error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@ export class CustomDialogService {
|
||||
|
||||
constructor( @IInstantiationService private _instantiationService: IInstantiationService) { }
|
||||
|
||||
public showDialog(dialog: Dialog, options?: IModalOptions): void {
|
||||
let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions);
|
||||
public showDialog(dialog: Dialog, dialogName?: string, options?: IModalOptions): void {
|
||||
let name = dialogName ? dialogName : 'CustomDialog';
|
||||
let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, name, options || defaultOptions);
|
||||
this._dialogModals.set(dialog, dialogModal);
|
||||
dialogModal.render();
|
||||
dialogModal.open();
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
'use strict';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Memento, Scope as MementoScope } from 'vs/workbench/common/memento';
|
||||
@@ -20,8 +20,8 @@ import { AccountDialogController } from 'sql/parts/accountManagement/accountDial
|
||||
import { AutoOAuthDialogController } from 'sql/parts/accountManagement/autoOAuthDialog/autoOAuthDialogController';
|
||||
import { AccountListStatusbarItem } from 'sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
|
||||
import { IAccountManagementService, AzureResource } from 'sql/services/accountManagement/interfaces';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
export class AccountManagementService implements IAccountManagementService {
|
||||
// CONSTANTS ///////////////////////////////////////////////////////////
|
||||
@@ -198,11 +198,6 @@ export class AccountManagementService implements IAccountManagementService {
|
||||
public getAccountsForProvider(providerId: string): Thenable<sqlops.Account[]> {
|
||||
let self = this;
|
||||
|
||||
// Make sure the provider exists before attempting to retrieve accounts
|
||||
if (!this._providers[providerId]) {
|
||||
return Promise.reject(new Error(nls.localize('accountManagementNoProvider', 'Account provider does not exist'))).then();
|
||||
}
|
||||
|
||||
// 1) Get the accounts from the store
|
||||
// 2) Update our local cache of accounts
|
||||
return this.doWithProvider(providerId, provider => {
|
||||
@@ -217,7 +212,7 @@ export class AccountManagementService implements IAccountManagementService {
|
||||
/**
|
||||
* Generates a security token by asking the account's provider
|
||||
* @param {Account} account Account to generate security token for
|
||||
* @param {AzureResource} resource The resource to get the security token for
|
||||
* @param {sqlops.AzureResource} resource The resource to get the security token for
|
||||
* @return {Thenable<{}>} Promise to return the security token
|
||||
*/
|
||||
public getSecurityToken(account: sqlops.Account, resource: sqlops.AzureResource): Thenable<{}> {
|
||||
@@ -389,10 +384,17 @@ export class AccountManagementService implements IAccountManagementService {
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private doWithProvider<T>(providerId: string, op: (provider: AccountProviderWithMetadata) => Thenable<T>): Thenable<T> {
|
||||
// Make sure the provider exists before attempting to retrieve accounts
|
||||
let provider = this._providers[providerId];
|
||||
if (!provider) {
|
||||
return Promise.reject(new Error(nls.localize('accountManagementNoProvider', 'Account provider does not exist'))).then();
|
||||
// If the provider doesn't already exist wait until it gets registered
|
||||
let deferredPromise = new Deferred<T>();
|
||||
let toDispose = this.addAccountProviderEvent(params => {
|
||||
if (params.addedProvider.id === providerId) {
|
||||
toDispose.dispose();
|
||||
deferredPromise.resolve(op(this._providers[providerId]));
|
||||
}
|
||||
});
|
||||
return deferredPromise;
|
||||
}
|
||||
|
||||
return op(provider);
|
||||
|
||||
@@ -12,10 +12,9 @@ import * as pfs from 'vs/base/node/pfs';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
import ContentManager = nb.ContentManager;
|
||||
import INotebook = nb.INotebook;
|
||||
|
||||
export class LocalContentManager implements ContentManager {
|
||||
public async getNotebookContents(notebookUri: URI): Promise<INotebook> {
|
||||
public async getNotebookContents(notebookUri: URI): Promise<nb.INotebookContents> {
|
||||
if (!notebookUri) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -23,10 +22,10 @@ export class LocalContentManager implements ContentManager {
|
||||
let path = notebookUri.fsPath;
|
||||
// Note: intentionally letting caller handle exceptions
|
||||
let notebookFileBuffer = await pfs.readFile(path);
|
||||
return <INotebook>json.parse(notebookFileBuffer.toString());
|
||||
return <nb.INotebookContents>json.parse(notebookFileBuffer.toString());
|
||||
}
|
||||
|
||||
public async save(notebookUri: URI, notebook: INotebook): Promise<INotebook> {
|
||||
public async save(notebookUri: URI, notebook: nb.INotebookContents): Promise<nb.INotebookContents> {
|
||||
// Convert to JSON with pretty-print functionality
|
||||
let contents = JSON.stringify(notebook, undefined, ' ');
|
||||
let path = notebookUri.fsPath;
|
||||
|
||||
@@ -6,21 +6,29 @@
|
||||
'use strict';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
|
||||
import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export const SERVICE_ID = 'notebookService';
|
||||
export const INotebookService = createDecorator<INotebookService>(SERVICE_ID);
|
||||
|
||||
export const DEFAULT_NOTEBOOK_PROVIDER = 'builtin';
|
||||
export const DEFAULT_NOTEBOOK_FILETYPE = 'IPYNB';
|
||||
|
||||
export interface INotebookService {
|
||||
_serviceBrand: any;
|
||||
|
||||
onNotebookEditorAdd: Event<INotebookEditor>;
|
||||
onNotebookEditorRemove: Event<INotebookEditor>;
|
||||
|
||||
/**
|
||||
* Register a metadata provider
|
||||
*/
|
||||
@@ -40,7 +48,11 @@ export interface INotebookService {
|
||||
*/
|
||||
getOrCreateNotebookManager(providerId: string, uri: URI): Thenable<INotebookManager>;
|
||||
|
||||
handleNotebookClosed(uri: URI): void;
|
||||
addNotebookEditor(editor: INotebookEditor): void;
|
||||
|
||||
removeNotebookEditor(editor: INotebookEditor): void;
|
||||
|
||||
listNotebookEditors(): INotebookEditor[];
|
||||
|
||||
shutdown(): void;
|
||||
|
||||
@@ -62,8 +74,19 @@ export interface INotebookManager {
|
||||
|
||||
export interface INotebookParams extends IBootstrapParams {
|
||||
notebookUri: URI;
|
||||
input: NotebookInput;
|
||||
providerId: string;
|
||||
isTrusted: boolean;
|
||||
profile?: IConnectionProfile;
|
||||
modelFactory?: ModelFactory;
|
||||
}
|
||||
|
||||
export interface INotebookEditor {
|
||||
readonly notebookParams: INotebookParams;
|
||||
readonly id: string;
|
||||
isDirty(): boolean;
|
||||
isActive(): boolean;
|
||||
isVisible(): boolean;
|
||||
save(): Promise<boolean>;
|
||||
executeEdits(edits: ISingleNotebookEditOperation[]): boolean;
|
||||
}
|
||||
@@ -10,20 +10,26 @@ import { localize } from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
import { INotebookService, INotebookManager, INotebookProvider, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/services/notebook/notebookService';
|
||||
import {
|
||||
INotebookService, INotebookManager, INotebookProvider, DEFAULT_NOTEBOOK_PROVIDER,
|
||||
DEFAULT_NOTEBOOK_FILETYPE, INotebookEditor
|
||||
} from 'sql/services/notebook/notebookService';
|
||||
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
|
||||
import { standardRendererFactories } from 'sql/parts/notebook/outputs/factories';
|
||||
import { LocalContentManager } from 'sql/services/notebook/localContentManager';
|
||||
import { SessionManager } from 'sql/services/notebook/sessionManager';
|
||||
import { Extensions, INotebookProviderRegistry } from 'sql/services/notebook/notebookRegistry';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
const DEFAULT_NOTEBOOK_FILETYPE = 'IPYNB';
|
||||
|
||||
export class NotebookService implements INotebookService {
|
||||
_serviceBrand: any;
|
||||
private _mimeRegistry: RenderMimeRegistry;
|
||||
private _providers: Map<string, INotebookProvider> = new Map();
|
||||
private _managers: Map<string, INotebookManager> = new Map();
|
||||
private _onNotebookEditorAdd = new Emitter<INotebookEditor>();
|
||||
private _onNotebookEditorRemove = new Emitter<INotebookEditor>();
|
||||
private _editors = new Map<string, INotebookEditor>();
|
||||
|
||||
constructor() {
|
||||
this.registerDefaultProvider();
|
||||
@@ -71,8 +77,34 @@ export class NotebookService implements INotebookService {
|
||||
return manager;
|
||||
}
|
||||
|
||||
handleNotebookClosed(notebookUri: URI): void {
|
||||
get onNotebookEditorAdd(): Event<INotebookEditor> {
|
||||
return this._onNotebookEditorAdd.event;
|
||||
}
|
||||
get onNotebookEditorRemove(): Event<INotebookEditor> {
|
||||
return this._onNotebookEditorRemove.event;
|
||||
}
|
||||
|
||||
addNotebookEditor(editor: INotebookEditor): void {
|
||||
this._editors.set(editor.id, editor);
|
||||
this._onNotebookEditorAdd.fire(editor);
|
||||
}
|
||||
|
||||
removeNotebookEditor(editor: INotebookEditor): void {
|
||||
if (this._editors.delete(editor.id)) {
|
||||
this._onNotebookEditorRemove.fire(editor);
|
||||
}
|
||||
// Remove the manager from the tracked list, and let the notebook provider know that it should update its mappings
|
||||
this.sendNotebookCloseToProvider(editor);
|
||||
}
|
||||
|
||||
listNotebookEditors(): INotebookEditor[] {
|
||||
let editors = [];
|
||||
this._editors.forEach(e => editors.push(e));
|
||||
return editors;
|
||||
}
|
||||
|
||||
private sendNotebookCloseToProvider(editor: INotebookEditor) {
|
||||
let notebookUri = editor.notebookParams.notebookUri;
|
||||
let uriString = notebookUri.toString();
|
||||
let manager = this._managers.get(uriString);
|
||||
if (manager) {
|
||||
@@ -82,15 +114,20 @@ export class NotebookService implements INotebookService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private doWithProvider<T>(providerId: string, op: (provider: INotebookProvider) => Thenable<T>): Thenable<T> {
|
||||
// Make sure the provider exists before attempting to retrieve accounts
|
||||
let provider = this._providers.get(providerId);
|
||||
let provider: INotebookProvider;
|
||||
if (this._providers.has(providerId)) {
|
||||
provider = this._providers.get(providerId);
|
||||
}
|
||||
else {
|
||||
provider = this._providers.get(DEFAULT_NOTEBOOK_PROVIDER);
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
return Promise.reject(new Error(localize('notebookServiceNoProvider', 'Notebook provider does not exist'))).then();
|
||||
}
|
||||
|
||||
return op(provider);
|
||||
}
|
||||
|
||||
@@ -103,8 +140,6 @@ export class NotebookService implements INotebookService {
|
||||
}
|
||||
return this._mimeRegistry;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class BuiltinProvider implements INotebookProvider {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { nb } from 'sqlops';
|
||||
import { localize } from 'vs/nls';
|
||||
import { FutureInternal } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
|
||||
const noKernel: string = localize('noKernel', 'No Kernel');
|
||||
export const noKernel: string = localize('noKernel', 'No Kernel');
|
||||
const runNotebookDisabled = localize('runNotebookDisabled', 'Cannot run cells as no kernel has been configured');
|
||||
|
||||
let noKernelSpec: nb.IKernelSpec = ({
|
||||
@@ -24,9 +24,9 @@ export class SessionManager implements nb.SessionManager {
|
||||
|
||||
public get specs(): nb.IAllKernels {
|
||||
let allKernels: nb.IAllKernels = {
|
||||
defaultKernel: noKernel,
|
||||
defaultKernel: noKernel,
|
||||
kernels: [noKernelSpec]
|
||||
};
|
||||
};
|
||||
return allKernels;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export class SessionManager implements nb.SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
class EmptySession implements nb.ISession {
|
||||
export class EmptySession implements nb.ISession {
|
||||
private _kernel: EmptyKernel;
|
||||
private _defaultKernelLoaded = false;
|
||||
|
||||
@@ -146,7 +146,7 @@ class EmptyKernel implements nb.IKernel {
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyFuture implements FutureInternal {
|
||||
export class EmptyFuture implements FutureInternal {
|
||||
|
||||
|
||||
get inProgress(): boolean {
|
||||
|
||||
26
src/sql/sqlops.d.ts
vendored
26
src/sql/sqlops.d.ts
vendored
@@ -214,6 +214,7 @@ declare module 'sqlops' {
|
||||
providerName: string;
|
||||
saveProfile: boolean;
|
||||
id: string;
|
||||
azureTenantId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -694,7 +695,8 @@ declare module 'sqlops' {
|
||||
registerOnQueryComplete(handler: (result: QueryExecuteCompleteNotificationResult) => any): void;
|
||||
registerOnBatchStart(handler: (batchInfo: QueryExecuteBatchNotificationParams) => any): void;
|
||||
registerOnBatchComplete(handler: (batchInfo: QueryExecuteBatchNotificationParams) => any): void;
|
||||
registerOnResultSetComplete(handler: (resultSetInfo: QueryExecuteResultSetCompleteNotificationParams) => any): void;
|
||||
registerOnResultSetAvailable(handler: (resultSetInfo: QueryExecuteResultSetNotificationParams) => any): void;
|
||||
registerOnResultSetUpdated(handler: (resultSetInfo: QueryExecuteResultSetNotificationParams) => any): void;
|
||||
registerOnMessage(handler: (message: QueryExecuteMessageParams) => any): void;
|
||||
|
||||
// Edit Data Requests
|
||||
@@ -769,6 +771,7 @@ declare module 'sqlops' {
|
||||
batchId: number;
|
||||
rowCount: number;
|
||||
columnInfo: IDbColumn[];
|
||||
complete: boolean;
|
||||
}
|
||||
|
||||
export interface BatchSummary {
|
||||
@@ -837,7 +840,7 @@ declare module 'sqlops' {
|
||||
}
|
||||
|
||||
|
||||
export interface QueryExecuteResultSetCompleteNotificationParams {
|
||||
export interface QueryExecuteResultSetNotificationParams {
|
||||
resultSetSummary: ResultSetSummary;
|
||||
ownerUri: string;
|
||||
}
|
||||
@@ -1297,15 +1300,16 @@ declare module 'sqlops' {
|
||||
lastRun: string;
|
||||
nextRun: string;
|
||||
jobId: string;
|
||||
EmailLevel: JobCompletionActionCondition;
|
||||
PageLevel: JobCompletionActionCondition;
|
||||
EventLogLevel: JobCompletionActionCondition;
|
||||
DeleteLevel: JobCompletionActionCondition;
|
||||
OperatorToEmail: string;
|
||||
OperatorToPage: string;
|
||||
JobSteps: AgentJobStepInfo[];
|
||||
JobSchedules: AgentJobScheduleInfo[];
|
||||
Alerts: AgentAlertInfo[];
|
||||
startStepId: number;
|
||||
emailLevel: JobCompletionActionCondition;
|
||||
pageLevel: JobCompletionActionCondition;
|
||||
eventLogLevel: JobCompletionActionCondition;
|
||||
deleteLevel: JobCompletionActionCondition;
|
||||
operatorToEmail: string;
|
||||
operatorToPage: string;
|
||||
jobSteps: AgentJobStepInfo[];
|
||||
jobSchedules: AgentJobScheduleInfo[];
|
||||
alerts: AgentAlertInfo[];
|
||||
}
|
||||
|
||||
export interface AgentJobScheduleInfo {
|
||||
|
||||
307
src/sql/sqlops.proposed.d.ts
vendored
307
src/sql/sqlops.proposed.d.ts
vendored
@@ -839,7 +839,7 @@ declare module 'sqlops' {
|
||||
* Create a dialog with the given title
|
||||
* @param title The title of the dialog, displayed at the top
|
||||
*/
|
||||
export function createDialog(title: string): Dialog;
|
||||
export function createDialog(title: string, dialogName?: string): Dialog;
|
||||
|
||||
/**
|
||||
* Create a dialog tab which can be included as part of the content of a dialog
|
||||
@@ -951,6 +951,12 @@ declare module 'sqlops' {
|
||||
*/
|
||||
message: DialogMessage;
|
||||
|
||||
/**
|
||||
* Set the dialog name when opening
|
||||
* the dialog for telemetry
|
||||
*/
|
||||
dialogName?: string;
|
||||
|
||||
/**
|
||||
* Register a callback that will be called when the user tries to click done. Only
|
||||
* one callback can be registered at once, so each registration call will clear
|
||||
@@ -1368,6 +1374,282 @@ declare module 'sqlops' {
|
||||
}
|
||||
|
||||
export namespace nb {
|
||||
/**
|
||||
* All notebook documents currently known to the system.
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
export let notebookDocuments: NotebookDocument[];
|
||||
|
||||
/**
|
||||
* The currently active Notebook editor or `undefined`. The active editor is the one
|
||||
* that currently has focus or, when none has focus, the one that has changed
|
||||
* input most recently.
|
||||
*/
|
||||
export let activeNotebookEditor: NotebookEditor | undefined;
|
||||
|
||||
/**
|
||||
* The currently visible editors or an empty array.
|
||||
*/
|
||||
export let visibleNotebookEditors: NotebookEditor[];
|
||||
|
||||
/**
|
||||
* An event that is emitted when a [notebook document](#NotebookDocument) is opened.
|
||||
*
|
||||
* To add an event listener when a visible text document is opened, use the [TextEditor](#TextEditor) events in the
|
||||
* [window](#window) namespace. Note that:
|
||||
*
|
||||
* - The event is emitted before the [document](#NotebookDocument) is updated in the
|
||||
* [active notebook editor](#nb.activeNotebookEditor)
|
||||
* - When a [notebook document](#NotebookDocument) is already open (e.g.: open in another visible notebook editor) this event is not emitted
|
||||
*
|
||||
*/
|
||||
export const onDidOpenNotebookDocument: vscode.Event<NotebookDocument>;
|
||||
|
||||
/**
|
||||
* An event that is emitted when a [notebook's](#NotebookDocument) cell contents are changed.
|
||||
*/
|
||||
export const onDidChangeNotebookCell: vscode.Event<NotebookCellChangeEvent>;
|
||||
|
||||
/**
|
||||
* Show the given document in a notebook editor. A [column](#ViewColumn) can be provided
|
||||
* to control where the editor is being shown. Might change the [active editor](#nb.activeNotebookEditor).
|
||||
*
|
||||
* The document is denoted by an [uri](#Uri). Depending on the [scheme](#Uri.scheme) the
|
||||
* following rules apply:
|
||||
* `file`-scheme: Open a file on disk, will be rejected if the file does not exist or cannot be loaded.
|
||||
* `untitled`-scheme: A new file that should be saved on disk, e.g. `untitled:c:\frodo\new.js`. The language
|
||||
* will be derived from the file name.
|
||||
* For all other schemes the registered notebook providers are consulted.
|
||||
*
|
||||
* @param document A document to be shown.
|
||||
* @param column A view column in which the [editor](#NotebookEditor) should be shown. The default is the [active](#ViewColumn.Active), other values
|
||||
* are adjusted to be `Min(column, columnCount + 1)`, the [active](#ViewColumn.Active)-column is not adjusted. Use [`ViewColumn.Beside`](#ViewColumn.Beside)
|
||||
* to open the editor to the side of the currently active one.
|
||||
* @param preserveFocus When `true` the editor will not take focus.
|
||||
* @return A promise that resolves to a [notebook editor](#NotebookEditor).
|
||||
*/
|
||||
export function showNotebookDocument(uri: vscode.Uri, showOptions?: NotebookShowOptions): Thenable<NotebookEditor>;
|
||||
|
||||
export interface NotebookDocument {
|
||||
/**
|
||||
* The associated uri for this notebook document.
|
||||
*
|
||||
* *Note* that most documents use the `file`-scheme, which means they are files on disk. However, **not** all documents are
|
||||
* saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk.
|
||||
*
|
||||
*/
|
||||
readonly uri: vscode.Uri;
|
||||
|
||||
/**
|
||||
* The file system path of the associated resource. Shorthand
|
||||
* notation for [TextDocument.uri.fsPath](#TextDocument.uri). Independent of the uri scheme.
|
||||
*/
|
||||
readonly fileName: string;
|
||||
|
||||
/**
|
||||
* Is this document representing an untitled file which has never been saved yet. *Note* that
|
||||
* this does not mean the document will be saved to disk, use [`uri.scheme`](#Uri.scheme)
|
||||
* to figure out where a document will be [saved](#FileSystemProvider), e.g. `file`, `ftp` etc.
|
||||
*/
|
||||
readonly isUntitled: boolean;
|
||||
|
||||
/**
|
||||
* The identifier of the Notebook provider associated with this document.
|
||||
*/
|
||||
readonly providerId: string;
|
||||
|
||||
/**
|
||||
* `true` if there are unpersisted changes.
|
||||
*/
|
||||
readonly isDirty: boolean;
|
||||
/**
|
||||
* `true` if the document have been closed. A closed document isn't synchronized anymore
|
||||
* and won't be re-used when the same resource is opened again.
|
||||
*/
|
||||
readonly isClosed: boolean;
|
||||
|
||||
/**
|
||||
* All cells.
|
||||
*/
|
||||
readonly cells: NotebookCell[];
|
||||
|
||||
/**
|
||||
* Save the underlying file.
|
||||
*
|
||||
* @return A promise that will resolve to true when the file
|
||||
* has been saved. If the file was not dirty or the save failed,
|
||||
* will return false.
|
||||
*/
|
||||
save(): Thenable<boolean>;
|
||||
|
||||
/**
|
||||
* Ensure a cell range is completely contained in this document.
|
||||
*
|
||||
* @param range A cell range.
|
||||
* @return The given range or a new, adjusted range.
|
||||
*/
|
||||
validateCellRange(range: CellRange): CellRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* A cell range represents an ordered pair of two positions in a list of cells.
|
||||
* It is guaranteed that [start](#CellRange.start).isBeforeOrEqual([end](#CellRange.end))
|
||||
*
|
||||
* CellRange objects are __immutable__.
|
||||
*/
|
||||
export class CellRange {
|
||||
|
||||
/**
|
||||
* The start index. It is before or equal to [end](#CellRange.end).
|
||||
*/
|
||||
readonly start: number;
|
||||
|
||||
/**
|
||||
* The end index. It is after or equal to [start](#CellRange.start).
|
||||
*/
|
||||
readonly end: number;
|
||||
|
||||
/**
|
||||
* Create a new range from two positions. If `start` is not
|
||||
* before or equal to `end`, the values will be swapped.
|
||||
*
|
||||
* @param start A number.
|
||||
* @param end A number.
|
||||
*/
|
||||
constructor(start: number, end: number);
|
||||
}
|
||||
|
||||
export interface NotebookEditor {
|
||||
/**
|
||||
* The document associated with this editor. The document will be the same for the entire lifetime of this editor.
|
||||
*/
|
||||
readonly document: NotebookDocument;
|
||||
/**
|
||||
* The column in which this editor shows. Will be `undefined` in case this
|
||||
* isn't one of the main editors, e.g an embedded editor, or when the editor
|
||||
* column is larger than three.
|
||||
*/
|
||||
viewColumn?: vscode.ViewColumn;
|
||||
|
||||
/**
|
||||
* Perform an edit on the document associated with this notebook editor.
|
||||
*
|
||||
* The given callback-function is invoked with an [edit-builder](#NotebookEditorEdit) which must
|
||||
* be used to make edits. Note that the edit-builder is only valid while the
|
||||
* callback executes.
|
||||
*
|
||||
* @param callback A function which can create edits using an [edit-builder](#NotebookEditorEdit).
|
||||
* @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit.
|
||||
* @return A promise that resolves with a value indicating if the edits could be applied.
|
||||
*/
|
||||
edit(callback: (editBuilder: NotebookEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface NotebookCell {
|
||||
contents: ICellContents;
|
||||
}
|
||||
|
||||
export interface NotebookShowOptions {
|
||||
/**
|
||||
* An optional view column in which the [editor](#NotebookEditor) should be shown.
|
||||
* The default is the [active](#ViewColumn.Active), other values are adjusted to
|
||||
* be `Min(column, columnCount + 1)`, the [active](#ViewColumn.Active)-column is
|
||||
* not adjusted. Use [`ViewColumn.Beside`](#ViewColumn.Beside) to open the
|
||||
* editor to the side of the currently active one.
|
||||
*/
|
||||
viewColumn?: vscode.ViewColumn;
|
||||
|
||||
/**
|
||||
* An optional flag that when `true` will stop the [editor](#NotebookEditor) from taking focus.
|
||||
*/
|
||||
preserveFocus?: boolean;
|
||||
|
||||
/**
|
||||
* An optional flag that controls if an [editor](#NotebookEditor)-tab will be replaced
|
||||
* with the next editor or if it will be kept.
|
||||
*/
|
||||
preview?: boolean;
|
||||
|
||||
/**
|
||||
* An optional string indicating which notebook provider to initially use
|
||||
*/
|
||||
providerId?: string;
|
||||
|
||||
/**
|
||||
* Optional ID indicating the initial connection to use for this editor
|
||||
*/
|
||||
connectionId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an event describing the change in a [notebook documents's cells](#NotebookDocument.cells).
|
||||
*/
|
||||
export interface NotebookCellChangeEvent {
|
||||
/**
|
||||
* The [notebook document](#NotebookDocument) for which the selections have changed.
|
||||
*/
|
||||
notebook: NotebookDocument;
|
||||
/**
|
||||
* The new value for the [notebook documents's cells](#NotebookDocument.cells).
|
||||
*/
|
||||
cell: NotebookCell[];
|
||||
/**
|
||||
* The [change kind](#TextEditorSelectionChangeKind) which has triggered this
|
||||
* event. Can be `undefined`.
|
||||
*/
|
||||
kind?: vscode.TextEditorSelectionChangeKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* A complex edit that will be applied in one transaction on a TextEditor.
|
||||
* This holds a description of the edits and if the edits are valid (i.e. no overlapping regions, document was not changed in the meantime, etc.)
|
||||
* they can be applied on a [document](#TextDocument) associated with a [text editor](#TextEditor).
|
||||
*
|
||||
*/
|
||||
export interface NotebookEditorEdit {
|
||||
/**
|
||||
* Replace a cell range with a new cell.
|
||||
*
|
||||
* @param location The range this operation should remove.
|
||||
* @param value The new cell this operation should insert after removing `location`.
|
||||
*/
|
||||
replace(location: number | CellRange, value: ICellContents): void;
|
||||
|
||||
/**
|
||||
* Insert a cell (optionally) at a specific index. Any index outside of the length of the cells
|
||||
* will result in the cell being added at the end.
|
||||
*
|
||||
* @param index The position where the new text should be inserted.
|
||||
* @param value The new text this operation should insert.
|
||||
*/
|
||||
insertCell(value: ICellContents, index?: number): void;
|
||||
|
||||
/**
|
||||
* Delete a certain cell.
|
||||
*
|
||||
* @param index The index of the cell to remove.
|
||||
*/
|
||||
deleteCell(index: number): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a notebook provider. The supported file types handled by this
|
||||
* provider are defined in the `package.json:
|
||||
* ```json
|
||||
* {
|
||||
* "contributes": {
|
||||
* "notebook.providers": [{
|
||||
* "provider": "providername",
|
||||
* "fileExtensions": ["FILEEXT"]
|
||||
* }]
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @export
|
||||
* @param {NotebookProvider} provider
|
||||
* @returns {vscode.Disposable}
|
||||
*/
|
||||
export function registerNotebookProvider(provider: NotebookProvider): vscode.Disposable;
|
||||
|
||||
export interface NotebookProvider {
|
||||
@@ -1431,7 +1713,7 @@ declare module 'sqlops' {
|
||||
/* Reads contents from a Uri representing a local or remote notebook and returns a
|
||||
* JSON object containing the cells and metadata about the notebook
|
||||
*/
|
||||
getNotebookContents(notebookUri: vscode.Uri): Thenable<INotebook>;
|
||||
getNotebookContents(notebookUri: vscode.Uri): Thenable<INotebookContents>;
|
||||
|
||||
/**
|
||||
* Save a file.
|
||||
@@ -1443,12 +1725,19 @@ declare module 'sqlops' {
|
||||
* @returns A thenable which resolves with the file content model when the
|
||||
* file is saved.
|
||||
*/
|
||||
save(notebookUri: vscode.Uri, notebook: INotebook): Thenable<INotebook>;
|
||||
save(notebookUri: vscode.Uri, notebook: INotebookContents): Thenable<INotebookContents>;
|
||||
}
|
||||
|
||||
export interface INotebook {
|
||||
|
||||
readonly cells: ICell[];
|
||||
/**
|
||||
* Interface defining the file format contents of a notebook, usually in a serializable
|
||||
* format. This interface does not have any methods for manipulating or interacting
|
||||
* with a notebook object.
|
||||
*
|
||||
*/
|
||||
export interface INotebookContents {
|
||||
|
||||
readonly cells: ICellContents[];
|
||||
readonly metadata: INotebookMetadata;
|
||||
readonly nbformat: number;
|
||||
readonly nbformat_minor: number;
|
||||
@@ -1477,7 +1766,13 @@ declare module 'sqlops' {
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface ICell {
|
||||
/**
|
||||
* Interface defining the file format contents of a notebook cell, usually in a serializable
|
||||
* format. This interface does not have any methods for manipulating or interacting
|
||||
* with a cell object.
|
||||
*
|
||||
*/
|
||||
export interface ICellContents {
|
||||
cell_type: CellType;
|
||||
source: string | string[];
|
||||
metadata: {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user