mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-28 18:47:25 -05:00
Merge branch 'master' into release/1.5
This commit is contained in:
94
.github/classifier.yml
vendored
94
.github/classifier.yml
vendored
@@ -1,49 +1,49 @@
|
|||||||
{
|
{
|
||||||
perform: true,
|
perform: true,
|
||||||
alwaysRequireAssignee: false,
|
alwaysRequireAssignee: false,
|
||||||
labelsRequiringAssignee: [],
|
labelsRequiringAssignee: [],
|
||||||
autoAssignees: {
|
autoAssignees: {
|
||||||
accessibility: [],
|
accessibility: [],
|
||||||
acquisition: [],
|
acquisition: [],
|
||||||
agent: [],
|
agent: [],
|
||||||
azure: [],
|
azure: [],
|
||||||
backup: [],
|
backup: [],
|
||||||
bcdr: [],
|
bcdr: [],
|
||||||
'chart viewer': [],
|
'chart viewer': [],
|
||||||
connection: [],
|
connection: [],
|
||||||
dacfx: [],
|
dacfx: [],
|
||||||
dashboard: [],
|
dashboard: [],
|
||||||
'data explorer': [],
|
'data explorer': [],
|
||||||
documentation: [],
|
documentation: [],
|
||||||
'edit data': [],
|
'edit data': [],
|
||||||
export: [],
|
export: [],
|
||||||
extensibility: [],
|
extensibility: [],
|
||||||
extensionManager: [],
|
extensionManager: [],
|
||||||
globalization: [],
|
globalization: [],
|
||||||
grid: [],
|
grid: [],
|
||||||
import: [],
|
import: [],
|
||||||
insights: [],
|
insights: [],
|
||||||
intellisense: [],
|
intellisense: [],
|
||||||
localization: [],
|
localization: [],
|
||||||
'managed instance': [],
|
'managed instance': [],
|
||||||
notebooks: [],
|
notebooks: [],
|
||||||
'object explorer': [],
|
'object explorer': [],
|
||||||
performance: [],
|
performance: [],
|
||||||
profiler: [],
|
profiler: [],
|
||||||
'query editor': [],
|
'query editor': [],
|
||||||
'query execution': [],
|
'query execution': [],
|
||||||
reliability: [],
|
reliability: [],
|
||||||
restore: [],
|
restore: [],
|
||||||
scripting: [],
|
scripting: [],
|
||||||
'server group': [],
|
'server group': [],
|
||||||
settings: [],
|
settings: [],
|
||||||
setup: [],
|
setup: [],
|
||||||
shell: [],
|
shell: [],
|
||||||
showplan: [],
|
showplan: [],
|
||||||
snippet: [],
|
snippet: [],
|
||||||
sql2019Preview: [],
|
sql2019Preview: [],
|
||||||
sqldw: [],
|
sqldw: [],
|
||||||
supportability: [],
|
supportability: [],
|
||||||
ux: []
|
ux: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
.github/commands.yml
vendored
20
.github/commands.yml
vendored
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
perform: false,
|
perform: false,
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
type: 'label',
|
type: 'label',
|
||||||
name: 'duplicate',
|
name: 'duplicate',
|
||||||
allowTriggerByBot: true,
|
allowTriggerByBot: true,
|
||||||
action: 'close',
|
action: 'close',
|
||||||
comment: "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues [here](https://aka.ms/vscodeissuesearch). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
|
comment: "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues [here](https://aka.ms/vscodeissuesearch). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
8
.github/locker.yml
vendored
8
.github/locker.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
daysAfterClose: 45,
|
daysAfterClose: 45,
|
||||||
daysSinceLastUpdate: 3,
|
daysSinceLastUpdate: 3,
|
||||||
ignoredLabels: [],
|
ignoredLabels: [],
|
||||||
perform: false
|
perform: true
|
||||||
}
|
}
|
||||||
|
|||||||
8
.github/needs_more_info.yml
vendored
8
.github/needs_more_info.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
daysUntilClose: 7,
|
daysUntilClose: 7,
|
||||||
needsMoreInfoLabel: 'more info',
|
needsMoreInfoLabel: 'more info',
|
||||||
perform: false,
|
perform: false,
|
||||||
closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
|
closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
|
||||||
}
|
}
|
||||||
|
|||||||
8
.github/new_release.yml
vendored
8
.github/new_release.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
newReleaseLabel: 'new-release',
|
newReleaseLabel: 'new-release',
|
||||||
newReleaseColor: '006b75',
|
newReleaseColor: '006b75',
|
||||||
daysAfterRelease: 5,
|
daysAfterRelease: 5,
|
||||||
perform: true
|
perform: true
|
||||||
}
|
}
|
||||||
|
|||||||
6
.github/similarity.yml
vendored
6
.github/similarity.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
perform: false,
|
perform: true,
|
||||||
whenCreatedByTeam: true,
|
whenCreatedByTeam: true,
|
||||||
comment: "Thanks for submitting this issue. Please also check if it is already covered by an existing one, like:\n${potentialDuplicates}"
|
comment: "Thanks for submitting this issue. Please also check if it is already covered by an existing one, like:\n${potentialDuplicates}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import * as azdata from 'azdata';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
|
|
||||||
import { AppContext } from '../common/appContext';
|
|
||||||
import JupyterServerInstallation from '../jupyter/jupyterServerInstallation';
|
import JupyterServerInstallation from '../jupyter/jupyterServerInstallation';
|
||||||
|
import { ApiWrapper } from '../common/apiWrapper';
|
||||||
|
import { Deferred } from '../common/promise';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -31,27 +32,44 @@ export class ConfigurePythonDialog {
|
|||||||
private pythonLocationTextBox: azdata.InputBoxComponent;
|
private pythonLocationTextBox: azdata.InputBoxComponent;
|
||||||
private browseButton: azdata.ButtonComponent;
|
private browseButton: azdata.ButtonComponent;
|
||||||
|
|
||||||
constructor(private appContext: AppContext, private outputChannel: vscode.OutputChannel, private jupyterInstallation: JupyterServerInstallation) {
|
private _setupComplete: Deferred<void>;
|
||||||
|
|
||||||
|
constructor(private apiWrapper: ApiWrapper, private outputChannel: vscode.OutputChannel, private jupyterInstallation: JupyterServerInstallation) {
|
||||||
|
this._setupComplete = new Deferred<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog() {
|
/**
|
||||||
|
* Opens a dialog to configure python installation for notebooks.
|
||||||
|
* @param rejectOnCancel Specifies whether an error should be thrown after clicking Cancel.
|
||||||
|
* @returns A promise that is resolved when the python installation completes.
|
||||||
|
*/
|
||||||
|
public showDialog(rejectOnCancel: boolean = false): Promise<void> {
|
||||||
this.dialog = azdata.window.createModelViewDialog(this.DialogTitle);
|
this.dialog = azdata.window.createModelViewDialog(this.DialogTitle);
|
||||||
|
|
||||||
this.initializeContent();
|
this.initializeContent();
|
||||||
|
|
||||||
this.dialog.okButton.label = this.OkButtonText;
|
this.dialog.okButton.label = this.OkButtonText;
|
||||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
this.dialog.cancelButton.label = this.CancelButtonText;
|
||||||
|
this.dialog.cancelButton.onClick(() => {
|
||||||
|
if (rejectOnCancel) {
|
||||||
|
this._setupComplete.reject(localize('pythonInstallDeclined', 'Python installation was declined.'));
|
||||||
|
} else {
|
||||||
|
this._setupComplete.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.dialog.registerCloseValidator(() => this.handleInstall());
|
this.dialog.registerCloseValidator(() => this.handleInstall());
|
||||||
|
|
||||||
azdata.window.openDialog(this.dialog);
|
azdata.window.openDialog(this.dialog);
|
||||||
|
|
||||||
|
return this._setupComplete.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeContent() {
|
private initializeContent(): void {
|
||||||
this.dialog.registerContent(async view => {
|
this.dialog.registerContent(async view => {
|
||||||
this.pythonLocationTextBox = view.modelBuilder.inputBox()
|
this.pythonLocationTextBox = view.modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
value: JupyterServerInstallation.getPythonInstallPath(this.appContext.apiWrapper),
|
value: JupyterServerInstallation.getPythonInstallPath(this.apiWrapper),
|
||||||
width: '100%'
|
width: '100%'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -106,14 +124,18 @@ export class ConfigurePythonDialog {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.appContext.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't wait on installation, since there's currently no Cancel functionality
|
// Don't wait on installation, since there's currently no Cancel functionality
|
||||||
this.jupyterInstallation.startInstallProcess(pythonLocation).catch(err => {
|
this.jupyterInstallation.startInstallProcess(pythonLocation)
|
||||||
this.appContext.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
.then(() => {
|
||||||
});
|
this._setupComplete.resolve();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this._setupComplete.reject(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,20 +171,13 @@ export class ConfigurePythonDialog {
|
|||||||
openLabel: this.SelectFileLabel
|
openLabel: this.SelectFileLabel
|
||||||
};
|
};
|
||||||
|
|
||||||
let fileUris: vscode.Uri[] = await this.appContext.apiWrapper.showOpenDialog(options);
|
let fileUris: vscode.Uri[] = await this.apiWrapper.showOpenDialog(options);
|
||||||
if (fileUris && fileUris[0]) {
|
if (fileUris && fileUris[0]) {
|
||||||
this.pythonLocationTextBox.value = fileUris[0].fsPath;
|
this.pythonLocationTextBox.value = fileUris[0].fsPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private showInfoMessage(message: string) {
|
private showErrorMessage(message: string): void {
|
||||||
this.dialog.message = {
|
|
||||||
text: message,
|
|
||||||
level: azdata.window.MessageLevel.Information
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private showErrorMessage(message: string) {
|
|
||||||
this.dialog.message = {
|
this.dialog.message = {
|
||||||
text: message,
|
text: message,
|
||||||
level: azdata.window.MessageLevel.Error
|
level: azdata.window.MessageLevel.Error
|
||||||
|
|||||||
@@ -127,6 +127,6 @@ function writeNotebookToFile(pythonNotebook: INotebook): vscode.Uri {
|
|||||||
async function ensureJupyterInstalled(): Promise<void> {
|
async function ensureJupyterInstalled(): Promise<void> {
|
||||||
let jupterControllerExports = vscode.extensions.getExtension('Microsoft.sql-vnext').exports;
|
let jupterControllerExports = vscode.extensions.getExtension('Microsoft.sql-vnext').exports;
|
||||||
let jupyterController = jupterControllerExports.getJupterController() as JupyterController;
|
let jupyterController = jupterControllerExports.getJupterController() as JupyterController;
|
||||||
await jupyterController.jupyterInstallation;
|
await jupyterController.jupyterInstallation.installReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import CodeAdapter from '../prompts/adapter';
|
|||||||
let untitledCounter = 0;
|
let untitledCounter = 0;
|
||||||
|
|
||||||
export class JupyterController implements vscode.Disposable {
|
export class JupyterController implements vscode.Disposable {
|
||||||
private _jupyterInstallation: Promise<JupyterServerInstallation>;
|
private _jupyterInstallation: JupyterServerInstallation;
|
||||||
private _notebookInstances: IServerInstance[] = [];
|
private _notebookInstances: IServerInstance[] = [];
|
||||||
|
|
||||||
private outputChannel: vscode.OutputChannel;
|
private outputChannel: vscode.OutputChannel;
|
||||||
@@ -55,31 +55,11 @@ export class JupyterController implements vscode.Disposable {
|
|||||||
|
|
||||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||||
public async activate(): Promise<boolean> {
|
public async activate(): Promise<boolean> {
|
||||||
// Prompt for install if the python installation path is not defined
|
this._jupyterInstallation = new JupyterServerInstallation(
|
||||||
let jupyterInstaller = new JupyterServerInstallation(
|
|
||||||
this.extensionContext.extensionPath,
|
this.extensionContext.extensionPath,
|
||||||
this.outputChannel,
|
this.outputChannel,
|
||||||
this.apiWrapper);
|
this.apiWrapper);
|
||||||
if (JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
|
||||||
this._jupyterInstallation = Promise.resolve(jupyterInstaller);
|
|
||||||
} else {
|
|
||||||
this._jupyterInstallation = new Promise(resolve => {
|
|
||||||
jupyterInstaller.onInstallComplete(err => {
|
|
||||||
if (!err) {
|
|
||||||
resolve(jupyterInstaller);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let notebookProvider = undefined;
|
|
||||||
|
|
||||||
notebookProvider = this.registerNotebookProvider();
|
|
||||||
azdata.nb.onDidOpenNotebookDocument(notebook => {
|
|
||||||
if (!JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
|
||||||
this.doConfigurePython(jupyterInstaller);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Add command/task handlers
|
// Add command/task handlers
|
||||||
this.apiWrapper.registerTaskHandler(constants.jupyterOpenNotebookTask, (profile: azdata.IConnectionProfile) => {
|
this.apiWrapper.registerTaskHandler(constants.jupyterOpenNotebookTask, (profile: azdata.IConnectionProfile) => {
|
||||||
return this.handleOpenNotebookTask(profile);
|
return this.handleOpenNotebookTask(profile);
|
||||||
@@ -96,11 +76,12 @@ export class JupyterController implements vscode.Disposable {
|
|||||||
|
|
||||||
this.apiWrapper.registerCommand(constants.jupyterReinstallDependenciesCommand, () => { return this.handleDependenciesReinstallation(); });
|
this.apiWrapper.registerCommand(constants.jupyterReinstallDependenciesCommand, () => { return this.handleDependenciesReinstallation(); });
|
||||||
this.apiWrapper.registerCommand(constants.jupyterInstallPackages, () => { return this.doManagePackages(); });
|
this.apiWrapper.registerCommand(constants.jupyterInstallPackages, () => { return this.doManagePackages(); });
|
||||||
this.apiWrapper.registerCommand(constants.jupyterConfigurePython, () => { return this.doConfigurePython(jupyterInstaller); });
|
this.apiWrapper.registerCommand(constants.jupyterConfigurePython, () => { return this.doConfigurePython(this._jupyterInstallation); });
|
||||||
|
|
||||||
let supportedFileFilter: vscode.DocumentFilter[] = [
|
let supportedFileFilter: vscode.DocumentFilter[] = [
|
||||||
{ scheme: 'untitled', language: '*' }
|
{ scheme: 'untitled', language: '*' }
|
||||||
];
|
];
|
||||||
|
let notebookProvider = this.registerNotebookProvider();
|
||||||
this.extensionContext.subscriptions.push(this.apiWrapper.registerCompletionItemProvider(supportedFileFilter, new NotebookCompletionItemProvider(notebookProvider)));
|
this.extensionContext.subscriptions.push(this.apiWrapper.registerCompletionItemProvider(supportedFileFilter, new NotebookCompletionItemProvider(notebookProvider)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -195,7 +176,7 @@ export class JupyterController implements vscode.Disposable {
|
|||||||
|
|
||||||
private async handleDependenciesReinstallation(): Promise<void> {
|
private async handleDependenciesReinstallation(): Promise<void> {
|
||||||
if (await this.confirmReinstall()) {
|
if (await this.confirmReinstall()) {
|
||||||
this._jupyterInstallation = JupyterServerInstallation.getInstallation(
|
this._jupyterInstallation = await JupyterServerInstallation.getInstallation(
|
||||||
this.extensionContext.extensionPath,
|
this.extensionContext.extensionPath,
|
||||||
this.outputChannel,
|
this.outputChannel,
|
||||||
this.apiWrapper,
|
this.apiWrapper,
|
||||||
@@ -225,14 +206,11 @@ export class JupyterController implements vscode.Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async doConfigurePython(jupyterInstaller: JupyterServerInstallation): Promise<void> {
|
public doConfigurePython(jupyterInstaller: JupyterServerInstallation): void {
|
||||||
try {
|
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, this.outputChannel, jupyterInstaller);
|
||||||
let pythonDialog = new ConfigurePythonDialog(this.appContext, this.outputChannel, jupyterInstaller);
|
pythonDialog.showDialog().catch(err => {
|
||||||
await pythonDialog.showDialog();
|
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
||||||
} catch (error) {
|
});
|
||||||
let message = utils.getErrorMessage(error);
|
|
||||||
this.apiWrapper.showErrorMessage(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTextToSendToTerminal(shellType: any): string {
|
public getTextToSendToTerminal(shellType: any): string {
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ import * as request from 'request';
|
|||||||
import { ApiWrapper } from '../common/apiWrapper';
|
import { ApiWrapper } from '../common/apiWrapper';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
import { OutputChannel, ConfigurationTarget, Event, EventEmitter, window } from 'vscode';
|
import { OutputChannel, ConfigurationTarget, window } from 'vscode';
|
||||||
|
import { Deferred } from '../common/promise';
|
||||||
|
import { ConfigurePythonDialog } from '../dialog/configurePythonDialog';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const msgPythonInstallationProgress = localize('msgPythonInstallationProgress', 'Python installation is in progress');
|
const msgPythonInstallationProgress = localize('msgPythonInstallationProgress', 'Python installation is in progress');
|
||||||
@@ -53,7 +55,7 @@ export default class JupyterServerInstallation {
|
|||||||
|
|
||||||
private static readonly DefaultPythonLocation = path.join(utils.getUserHome(), 'azuredatastudio-python');
|
private static readonly DefaultPythonLocation = path.join(utils.getUserHome(), 'azuredatastudio-python');
|
||||||
|
|
||||||
private _installCompleteEmitter = new EventEmitter<string>();
|
private _installReady: Deferred<void>;
|
||||||
|
|
||||||
constructor(extensionPath: string, outputChannel: OutputChannel, apiWrapper: ApiWrapper, pythonInstallationPath?: string, forceInstall?: boolean) {
|
constructor(extensionPath: string, outputChannel: OutputChannel, apiWrapper: ApiWrapper, pythonInstallationPath?: string, forceInstall?: boolean) {
|
||||||
this.extensionPath = extensionPath;
|
this.extensionPath = extensionPath;
|
||||||
@@ -63,10 +65,15 @@ export default class JupyterServerInstallation {
|
|||||||
this._forceInstall = !!forceInstall;
|
this._forceInstall = !!forceInstall;
|
||||||
|
|
||||||
this.configurePackagePaths();
|
this.configurePackagePaths();
|
||||||
|
|
||||||
|
this._installReady = new Deferred<void>();
|
||||||
|
if (JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
||||||
|
this._installReady.resolve();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get onInstallComplete(): Event<string> {
|
public get installReady(): Promise<void> {
|
||||||
return this._installCompleteEmitter.event;
|
return this._installReady.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getInstallation(
|
public static async getInstallation(
|
||||||
@@ -232,7 +239,7 @@ export default class JupyterServerInstallation {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async startInstallProcess(pythonInstallationPath?: string): Promise<void> {
|
public startInstallProcess(pythonInstallationPath?: string): Promise<void> {
|
||||||
if (pythonInstallationPath) {
|
if (pythonInstallationPath) {
|
||||||
this._pythonInstallationPath = pythonInstallationPath;
|
this._pythonInstallationPath = pythonInstallationPath;
|
||||||
this.configurePackagePaths();
|
this.configurePackagePaths();
|
||||||
@@ -249,23 +256,31 @@ export default class JupyterServerInstallation {
|
|||||||
operation: op => {
|
operation: op => {
|
||||||
this.installDependencies(op)
|
this.installDependencies(op)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._installCompleteEmitter.fire();
|
this._installReady.resolve();
|
||||||
updateConfig();
|
updateConfig();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
let errorMsg = msgDependenciesInstallationFailed(err);
|
let errorMsg = msgDependenciesInstallationFailed(err);
|
||||||
op.updateStatus(azdata.TaskStatus.Failed, errorMsg);
|
op.updateStatus(azdata.TaskStatus.Failed, errorMsg);
|
||||||
this.apiWrapper.showErrorMessage(errorMsg);
|
this.apiWrapper.showErrorMessage(errorMsg);
|
||||||
this._installCompleteEmitter.fire(errorMsg);
|
this._installReady.reject(errorMsg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Python executable already exists, but the path setting wasn't defined,
|
// Python executable already exists, but the path setting wasn't defined,
|
||||||
// so update it here
|
// so update it here
|
||||||
this._installCompleteEmitter.fire();
|
this._installReady.resolve();
|
||||||
updateConfig();
|
updateConfig();
|
||||||
}
|
}
|
||||||
|
return this._installReady.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async promptForPythonInstall(): Promise<void> {
|
||||||
|
if (!JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
||||||
|
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, this.outputChannel, this);
|
||||||
|
return pythonDialog.showDialog(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async installJupyterProsePackage(): Promise<void> {
|
private async installJupyterProsePackage(): Promise<void> {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { PerNotebookServerInstance, IInstanceOptions } from './serverInstance';
|
|||||||
|
|
||||||
export interface IServerManagerOptions {
|
export interface IServerManagerOptions {
|
||||||
documentPath: string;
|
documentPath: string;
|
||||||
jupyterInstallation: Promise<JupyterServerInstallation>;
|
jupyterInstallation: JupyterServerInstallation;
|
||||||
extensionContext: vscode.ExtensionContext;
|
extensionContext: vscode.ExtensionContext;
|
||||||
apiWrapper?: ApiWrapper;
|
apiWrapper?: ApiWrapper;
|
||||||
factory?: ServerInstanceFactory;
|
factory?: ServerInstanceFactory;
|
||||||
@@ -62,7 +62,7 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
|||||||
this._onServerStarted.fire();
|
this._onServerStarted.fire();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.apiWrapper.showErrorMessage(localize('startServerFailed', 'Starting local Notebook server failed with error {0}', utils.getErrorMessage(error)));
|
this.apiWrapper.showErrorMessage(localize('startServerFailed', 'Starting local Notebook server failed with error: {0}', utils.getErrorMessage(error)));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,8 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async doStartServer(): Promise<IServerInstance> { // We can't find or create servers until the installation is complete
|
private async doStartServer(): Promise<IServerInstance> { // We can't find or create servers until the installation is complete
|
||||||
let installation = await this.options.jupyterInstallation;
|
let installation = this.options.jupyterInstallation;
|
||||||
|
await installation.promptForPythonInstall();
|
||||||
|
|
||||||
// Calculate the path to use as the notebook-dir for Jupyter based on the path of the uri of the
|
// Calculate the path to use as the notebook-dir for Jupyter based on the path of the uri of the
|
||||||
// notebook to open. This will be the workspace folder if the notebook uri is inside a workspace
|
// notebook to open. This will be the workspace folder if the notebook uri is inside a workspace
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { MockExtensionContext } from '../common/stubs';
|
|||||||
describe('Local Jupyter Server Manager', function (): void {
|
describe('Local Jupyter Server Manager', function (): void {
|
||||||
let expectedPath = 'my/notebook.ipynb';
|
let expectedPath = 'my/notebook.ipynb';
|
||||||
let serverManager: LocalJupyterServerManager;
|
let serverManager: LocalJupyterServerManager;
|
||||||
let deferredInstall: Deferred<JupyterServerInstallation>;
|
let deferredInstall: Deferred<void>;
|
||||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||||
let mockExtensionContext: MockExtensionContext;
|
let mockExtensionContext: MockExtensionContext;
|
||||||
let mockFactory: TypeMoq.IMock<ServerInstanceFactory>;
|
let mockFactory: TypeMoq.IMock<ServerInstanceFactory>;
|
||||||
@@ -33,10 +33,14 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
mockApiWrapper.setup(a => a.showErrorMessage(TypeMoq.It.isAny()));
|
mockApiWrapper.setup(a => a.showErrorMessage(TypeMoq.It.isAny()));
|
||||||
mockApiWrapper.setup(a => a.getWorkspacePathFromUri(TypeMoq.It.isAny())).returns(() => undefined);
|
mockApiWrapper.setup(a => a.getWorkspacePathFromUri(TypeMoq.It.isAny())).returns(() => undefined);
|
||||||
mockFactory = TypeMoq.Mock.ofType(ServerInstanceFactory);
|
mockFactory = TypeMoq.Mock.ofType(ServerInstanceFactory);
|
||||||
deferredInstall = new Deferred<JupyterServerInstallation>();
|
|
||||||
|
deferredInstall = new Deferred<void>();
|
||||||
|
let mockInstall = TypeMoq.Mock.ofType(JupyterServerInstallation, undefined, undefined, '/root');
|
||||||
|
mockInstall.setup(j => j.promptForPythonInstall()).returns(() => deferredInstall.promise);
|
||||||
|
|
||||||
serverManager = new LocalJupyterServerManager({
|
serverManager = new LocalJupyterServerManager({
|
||||||
documentPath: expectedPath,
|
documentPath: expectedPath,
|
||||||
jupyterInstallation: deferredInstall.promise,
|
jupyterInstallation: mockInstall.object,
|
||||||
extensionContext: mockExtensionContext,
|
extensionContext: mockExtensionContext,
|
||||||
apiWrapper: mockApiWrapper.object,
|
apiWrapper: mockApiWrapper.object,
|
||||||
factory: mockFactory.object
|
factory: mockFactory.object
|
||||||
@@ -58,8 +62,8 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
it('Should configure and start install', async function (): Promise<void> {
|
it('Should configure and start install', async function (): Promise<void> {
|
||||||
// Given an install and instance that start with no issues
|
// Given an install and instance that start with no issues
|
||||||
let expectedUri = vscode.Uri.parse('http://localhost:1234?token=abcdefghijk');
|
let expectedUri = vscode.Uri.parse('http://localhost:1234?token=abcdefghijk');
|
||||||
let [mockInstall, mockServerInstance] = initInstallAndInstance(expectedUri);
|
let mockServerInstance = initInstallAndInstance(expectedUri);
|
||||||
deferredInstall.resolve(mockInstall.object);
|
deferredInstall.resolve();
|
||||||
|
|
||||||
// When I start the server
|
// When I start the server
|
||||||
let notified = false;
|
let notified = false;
|
||||||
@@ -83,9 +87,9 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
it('Should call stop on server instance', async function (): Promise<void> {
|
it('Should call stop on server instance', async function (): Promise<void> {
|
||||||
// Given an install and instance that start with no issues
|
// Given an install and instance that start with no issues
|
||||||
let expectedUri = vscode.Uri.parse('http://localhost:1234?token=abcdefghijk');
|
let expectedUri = vscode.Uri.parse('http://localhost:1234?token=abcdefghijk');
|
||||||
let [mockInstall, mockServerInstance] = initInstallAndInstance(expectedUri);
|
let mockServerInstance = initInstallAndInstance(expectedUri);
|
||||||
mockServerInstance.setup(s => s.stop()).returns(() => Promise.resolve());
|
mockServerInstance.setup(s => s.stop()).returns(() => Promise.resolve());
|
||||||
deferredInstall.resolve(mockInstall.object);
|
deferredInstall.resolve();
|
||||||
|
|
||||||
// When I start and then the server
|
// When I start and then the server
|
||||||
await serverManager.startServer();
|
await serverManager.startServer();
|
||||||
@@ -98,9 +102,9 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
it('Should call stop when extension is disposed', async function (): Promise<void> {
|
it('Should call stop when extension is disposed', async function (): Promise<void> {
|
||||||
// Given an install and instance that start with no issues
|
// Given an install and instance that start with no issues
|
||||||
let expectedUri = vscode.Uri.parse('http://localhost:1234?token=abcdefghijk');
|
let expectedUri = vscode.Uri.parse('http://localhost:1234?token=abcdefghijk');
|
||||||
let [mockInstall, mockServerInstance] = initInstallAndInstance(expectedUri);
|
let mockServerInstance = initInstallAndInstance(expectedUri);
|
||||||
mockServerInstance.setup(s => s.stop()).returns(() => Promise.resolve());
|
mockServerInstance.setup(s => s.stop()).returns(() => Promise.resolve());
|
||||||
deferredInstall.resolve(mockInstall.object);
|
deferredInstall.resolve();
|
||||||
|
|
||||||
// When I start and then dispose the extension
|
// When I start and then dispose the extension
|
||||||
await serverManager.startServer();
|
await serverManager.startServer();
|
||||||
@@ -111,13 +115,12 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
mockServerInstance.verify(s => s.stop(), TypeMoq.Times.once());
|
mockServerInstance.verify(s => s.stop(), TypeMoq.Times.once());
|
||||||
});
|
});
|
||||||
|
|
||||||
function initInstallAndInstance(uri: vscode.Uri): [TypeMoq.IMock<JupyterServerInstallation>, TypeMoq.IMock<IServerInstance>] {
|
function initInstallAndInstance(uri: vscode.Uri): TypeMoq.IMock<IServerInstance> {
|
||||||
let mockInstall = TypeMoq.Mock.ofType(JupyterServerInstallation, undefined, undefined, '/root');
|
|
||||||
let mockServerInstance = TypeMoq.Mock.ofType(JupyterServerInstanceStub);
|
let mockServerInstance = TypeMoq.Mock.ofType(JupyterServerInstanceStub);
|
||||||
mockFactory.setup(f => f.createInstance(TypeMoq.It.isAny())).returns(() => mockServerInstance.object);
|
mockFactory.setup(f => f.createInstance(TypeMoq.It.isAny())).returns(() => mockServerInstance.object);
|
||||||
mockServerInstance.setup(s => s.configure()).returns(() => Promise.resolve());
|
mockServerInstance.setup(s => s.configure()).returns(() => Promise.resolve());
|
||||||
mockServerInstance.setup(s => s.start()).returns(() => Promise.resolve());
|
mockServerInstance.setup(s => s.start()).returns(() => Promise.resolve());
|
||||||
mockServerInstance.setup(s => s.uri).returns(() => uri);
|
mockServerInstance.setup(s => s.uri).returns(() => uri);
|
||||||
return [mockInstall, mockServerInstance];
|
return mockServerInstance;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'vs/css!./code';
|
|||||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild } from '@angular/core';
|
import { OnInit, Component, Input, Inject, ElementRef, ViewChild } from '@angular/core';
|
||||||
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||||
import { nb } from 'azdata';
|
import { nb } from 'azdata';
|
||||||
|
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||||
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
|
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
|
||||||
import { MimeModel } from 'sql/parts/notebook/outputs/common/mimemodel';
|
import { MimeModel } from 'sql/parts/notebook/outputs/common/mimemodel';
|
||||||
import * as outputProcessor from 'sql/parts/notebook/outputs/common/outputProcessor';
|
import * as outputProcessor from 'sql/parts/notebook/outputs/common/outputProcessor';
|
||||||
@@ -22,6 +23,7 @@ export const OUTPUT_SELECTOR: string = 'output-component';
|
|||||||
export class OutputComponent extends AngularDisposable implements OnInit {
|
export class OutputComponent extends AngularDisposable implements OnInit {
|
||||||
@ViewChild('output', { read: ElementRef }) private outputElement: ElementRef;
|
@ViewChild('output', { read: ElementRef }) private outputElement: ElementRef;
|
||||||
@Input() cellOutput: nb.ICellOutput;
|
@Input() cellOutput: nb.ICellOutput;
|
||||||
|
@Input() cellModel: ICellModel;
|
||||||
private _trusted: boolean;
|
private _trusted: boolean;
|
||||||
private _initialized: boolean = false;
|
private _initialized: boolean = false;
|
||||||
private readonly _minimumHeight = 30;
|
private readonly _minimumHeight = 30;
|
||||||
@@ -38,6 +40,9 @@ export class OutputComponent extends AngularDisposable implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.renderOutput();
|
this.renderOutput();
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
this.cellModel.notebookModel.layoutChanged(() => {
|
||||||
|
this.renderOutput();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderOutput() {
|
private renderOutput() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
-->
|
-->
|
||||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||||
<div #outputarea class="notebook-output" 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 *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel">
|
||||||
</output-component>
|
</output-component>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,6 +129,10 @@ export class CellModel implements ICellModel {
|
|||||||
return this._cellUri;
|
return this._cellUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get notebookModel(): NotebookModel {
|
||||||
|
return <NotebookModel>this.options.notebook;
|
||||||
|
}
|
||||||
|
|
||||||
public set cellUri(value: URI) {
|
public set cellUri(value: URI) {
|
||||||
this._cellUri = value;
|
this._cellUri = value;
|
||||||
}
|
}
|
||||||
@@ -208,12 +212,12 @@ export class CellModel implements ICellModel {
|
|||||||
// TODO update source based on editor component contents
|
// TODO update source based on editor component contents
|
||||||
let content = this.source;
|
let content = this.source;
|
||||||
if (content) {
|
if (content) {
|
||||||
this.fireExecutionStateChanged();
|
|
||||||
let future = await kernel.requestExecute({
|
let future = await kernel.requestExecute({
|
||||||
code: content,
|
code: content,
|
||||||
stop_on_error: true
|
stop_on_error: true
|
||||||
}, false);
|
}, false);
|
||||||
this.setFuture(future as FutureInternal);
|
this.setFuture(future as FutureInternal);
|
||||||
|
this.fireExecutionStateChanged();
|
||||||
// For now, await future completion. Later we should just track and handle cancellation based on model notifications
|
// For now, await future completion. Later we should just track and handle cancellation based on model notifications
|
||||||
let result: nb.IExecuteReplyMsg = <nb.IExecuteReplyMsg><any>await future.done;
|
let result: nb.IExecuteReplyMsg = <nb.IExecuteReplyMsg><any>await future.done;
|
||||||
if (result && result.content) {
|
if (result && result.content) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
|
|||||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
|
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||||
|
|
||||||
export interface IClientSessionOptions {
|
export interface IClientSessionOptions {
|
||||||
notebookUri: URI;
|
notebookUri: URI;
|
||||||
@@ -449,8 +450,9 @@ export interface ICellModel {
|
|||||||
readonly outputs: ReadonlyArray<nb.ICellOutput>;
|
readonly outputs: ReadonlyArray<nb.ICellOutput>;
|
||||||
readonly onOutputsChanged: Event<ReadonlyArray<nb.ICellOutput>>;
|
readonly onOutputsChanged: Event<ReadonlyArray<nb.ICellOutput>>;
|
||||||
readonly onExecutionStateChange: Event<CellExecutionState>;
|
readonly onExecutionStateChange: Event<CellExecutionState>;
|
||||||
setFuture(future: FutureInternal): void;
|
|
||||||
readonly executionState: CellExecutionState;
|
readonly executionState: CellExecutionState;
|
||||||
|
readonly notebookModel: NotebookModel;
|
||||||
|
setFuture(future: FutureInternal): void;
|
||||||
runCell(notificationService?: INotificationService, connectionManagementService?: IConnectionManagementService): Promise<boolean>;
|
runCell(notificationService?: INotificationService, connectionManagementService?: IConnectionManagementService): Promise<boolean>;
|
||||||
setOverrideLanguage(language: string);
|
setOverrideLanguage(language: string);
|
||||||
equals(cellModel: ICellModel): boolean;
|
equals(cellModel: ICellModel): boolean;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/co
|
|||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||||
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
|
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
|
||||||
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
|
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange } from 'sql/parts/notebook/models/modelInterfaces';
|
||||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
@@ -38,8 +38,6 @@ import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction } from
|
|||||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
||||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
|
||||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
|
||||||
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
|
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
|
||||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||||
import { CellMagicMapper } from 'sql/parts/notebook/models/cellMagicMapper';
|
import { CellMagicMapper } from 'sql/parts/notebook/models/cellMagicMapper';
|
||||||
@@ -254,8 +252,9 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
this.detectChanges();
|
this.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setNotebookManager() {
|
private async setNotebookManager(): Promise<void> {
|
||||||
for (let providerId of this._notebookParams.providers) {
|
let providerInfo = await this._notebookParams.providerInfo;
|
||||||
|
for (let providerId of providerInfo.providers) {
|
||||||
let notebookManager = await this.notebookService.getOrCreateNotebookManager(providerId, this._notebookParams.notebookUri);
|
let notebookManager = await this.notebookService.getOrCreateNotebookManager(providerId, this._notebookParams.notebookUri);
|
||||||
this.notebookManagers.push(notebookManager);
|
this.notebookManagers.push(notebookManager);
|
||||||
}
|
}
|
||||||
@@ -265,12 +264,14 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
// Wait on registration for now. Long-term would be good to cache and refresh
|
// Wait on registration for now. Long-term would be good to cache and refresh
|
||||||
await this.notebookService.registrationComplete;
|
await this.notebookService.registrationComplete;
|
||||||
// Refresh the provider if we had been using default
|
// Refresh the provider if we had been using default
|
||||||
if (DEFAULT_NOTEBOOK_PROVIDER === this._notebookParams.providerId) {
|
let providerInfo = await this._notebookParams.providerInfo;
|
||||||
|
|
||||||
|
if (DEFAULT_NOTEBOOK_PROVIDER === providerInfo.providerId) {
|
||||||
let providers = notebookUtils.getProvidersForFileName(this._notebookParams.notebookUri.fsPath, this.notebookService);
|
let providers = notebookUtils.getProvidersForFileName(this._notebookParams.notebookUri.fsPath, this.notebookService);
|
||||||
let tsqlProvider = providers.find(provider => provider === SQL_NOTEBOOK_PROVIDER);
|
let tsqlProvider = providers.find(provider => provider === SQL_NOTEBOOK_PROVIDER);
|
||||||
this._notebookParams.providerId = tsqlProvider ? SQL_NOTEBOOK_PROVIDER : providers[0];
|
providerInfo.providerId = tsqlProvider ? SQL_NOTEBOOK_PROVIDER : providers[0];
|
||||||
}
|
}
|
||||||
if (DEFAULT_NOTEBOOK_PROVIDER === this._notebookParams.providerId) {
|
if (DEFAULT_NOTEBOOK_PROVIDER === providerInfo.providerId) {
|
||||||
// If it's still the default, warn them they should install an extension
|
// If it's still the default, warn them they should install an extension
|
||||||
this.notificationService.prompt(Severity.Warning,
|
this.notificationService.prompt(Severity.Warning,
|
||||||
localize('noKernelInstalled', 'Please install the SQL Server 2019 extension to run cells'),
|
localize('noKernelInstalled', 'Please install the SQL Server 2019 extension to run cells'),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
|||||||
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
|
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
|
||||||
import { NotebookModule } from 'sql/parts/notebook/notebook.module';
|
import { NotebookModule } from 'sql/parts/notebook/notebook.module';
|
||||||
import { NOTEBOOK_SELECTOR } from 'sql/parts/notebook/notebook.component';
|
import { NOTEBOOK_SELECTOR } from 'sql/parts/notebook/notebook.component';
|
||||||
import { INotebookParams, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
import { INotebookParams } from 'sql/workbench/services/notebook/common/notebookService';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
import { $ } from 'sql/base/browser/builder';
|
import { $ } from 'sql/base/browser/builder';
|
||||||
|
|
||||||
@@ -92,8 +92,7 @@ export class NotebookEditor extends BaseEditor {
|
|||||||
let params: INotebookParams = {
|
let params: INotebookParams = {
|
||||||
notebookUri: input.notebookUri,
|
notebookUri: input.notebookUri,
|
||||||
input: input,
|
input: input,
|
||||||
providerId: input.providerId ? input.providerId : DEFAULT_NOTEBOOK_PROVIDER,
|
providerInfo: input.getProviderInfo(),
|
||||||
providers: input.providers ? input.providers : [DEFAULT_NOTEBOOK_PROVIDER],
|
|
||||||
isTrusted: input.isTrusted,
|
isTrusted: input.isTrusted,
|
||||||
profile: input.connectionProfile
|
profile: input.connectionProfile
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import * as resources from 'vs/base/common/resources';
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
|
||||||
import { IStandardKernelWithProvider, getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
import { IStandardKernelWithProvider, getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
||||||
import { INotebookService, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
import { INotebookService, DEFAULT_NOTEBOOK_PROVIDER, IProviderInfo } from 'sql/workbench/services/notebook/common/notebookService';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||||
import { INotebookModel, IContentManager } from 'sql/parts/notebook/models/modelInterfaces';
|
import { INotebookModel, IContentManager } from 'sql/parts/notebook/models/modelInterfaces';
|
||||||
@@ -29,6 +29,7 @@ import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/c
|
|||||||
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
|
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
|
||||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||||
|
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
|
|
||||||
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
|
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
|
||||||
|
|
||||||
@@ -140,6 +141,7 @@ export class NotebookInput extends EditorInput {
|
|||||||
private _model: NotebookEditorModel;
|
private _model: NotebookEditorModel;
|
||||||
private _untitledEditorService: IUntitledEditorService;
|
private _untitledEditorService: IUntitledEditorService;
|
||||||
private _contentManager: IContentManager;
|
private _contentManager: IContentManager;
|
||||||
|
private _providersLoaded: Promise<void>;
|
||||||
|
|
||||||
constructor(private _title: string,
|
constructor(private _title: string,
|
||||||
private resource: URI,
|
private resource: URI,
|
||||||
@@ -147,13 +149,14 @@ export class NotebookInput extends EditorInput {
|
|||||||
@ITextModelService private textModelService: ITextModelService,
|
@ITextModelService private textModelService: ITextModelService,
|
||||||
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
|
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
|
||||||
@IInstantiationService private instantiationService: IInstantiationService,
|
@IInstantiationService private instantiationService: IInstantiationService,
|
||||||
@INotebookService private notebookService: INotebookService
|
@INotebookService private notebookService: INotebookService,
|
||||||
|
@IExtensionService private extensionService: IExtensionService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._untitledEditorService = untitledEditorService;
|
this._untitledEditorService = untitledEditorService;
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
this._standardKernels = [];
|
this._standardKernels = [];
|
||||||
this.assignProviders();
|
this._providersLoaded = this.assignProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get textInput(): UntitledEditorInput {
|
public get textInput(): UntitledEditorInput {
|
||||||
@@ -186,14 +189,13 @@ export class NotebookInput extends EditorInput {
|
|||||||
return this._title;
|
return this._title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get providerId(): string {
|
public async getProviderInfo(): Promise<IProviderInfo> {
|
||||||
return this._providerId;
|
await this._providersLoaded;
|
||||||
|
return {
|
||||||
|
providerId: this._providerId ? this._providerId : DEFAULT_NOTEBOOK_PROVIDER,
|
||||||
|
providers: this._providers ? this._providers : [DEFAULT_NOTEBOOK_PROVIDER]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public set providerId(value: string) {
|
|
||||||
this._providerId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isTrusted(): boolean {
|
public get isTrusted(): boolean {
|
||||||
return this._isTrusted;
|
return this._isTrusted;
|
||||||
}
|
}
|
||||||
@@ -214,14 +216,6 @@ export class NotebookInput extends EditorInput {
|
|||||||
return this._standardKernels;
|
return this._standardKernels;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get providers(): string[] {
|
|
||||||
return this._providers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set providers(value: string[]) {
|
|
||||||
this._providers = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public save(): TPromise<boolean> {
|
public save(): TPromise<boolean> {
|
||||||
let options: ISaveOptions = { force: false };
|
let options: ISaveOptions = { force: false };
|
||||||
return this._model.save(options);
|
return this._model.save(options);
|
||||||
@@ -280,7 +274,8 @@ export class NotebookInput extends EditorInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private assignProviders(): void {
|
private async assignProviders(): Promise<void> {
|
||||||
|
await this.extensionService.whenInstalledExtensionsRegistered();
|
||||||
let providerIds: string[] = getProvidersForFileName(this._title, this.notebookService);
|
let providerIds: string[] = getProvidersForFileName(this._title, this.notebookService);
|
||||||
if (providerIds && providerIds.length > 0) {
|
if (providerIds && providerIds.length > 0) {
|
||||||
this._providerId = providerIds.filter(provider => provider !== DEFAULT_NOTEBOOK_PROVIDER)[0];
|
this._providerId = providerIds.filter(provider => provider !== DEFAULT_NOTEBOOK_PROVIDER)[0];
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean): IDi
|
|||||||
box-shadow: 0;
|
box-shadow: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notebookEditor .notebook-cell.active:hover {
|
||||||
|
border-color: ${activeBorder};
|
||||||
|
}
|
||||||
|
|
||||||
.notebookEditor .notebook-cell:hover:not(.active) {
|
.notebookEditor .notebook-cell:hover:not(.active) {
|
||||||
box-shadow: ${lightBoxShadow};
|
box-shadow: ${lightBoxShadow};
|
||||||
}
|
}
|
||||||
@@ -98,13 +102,14 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean): IDi
|
|||||||
// Standard notebook cell behavior
|
// Standard notebook cell behavior
|
||||||
collector.addRule(`
|
collector.addRule(`
|
||||||
.notebookEditor .notebook-cell {
|
.notebookEditor .notebook-cell {
|
||||||
border-color: ${inactiveBorder};
|
border-color: transparent;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
.notebookEditor .notebook-cell.active {
|
.notebookEditor .notebook-cell.active {
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
.notebookEditor .notebook-cell:hover {
|
.notebookEditor .notebook-cell:hover {
|
||||||
|
border-color: ${inactiveBorder};
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { TreeNode, TreeItemCollapsibleState } from 'sql/parts/objectExplorer/com
|
|||||||
import { SERVER_GROUP_CONFIG, SERVER_GROUP_AUTOEXPAND_CONFIG } from 'sql/parts/objectExplorer/serverGroupDialog/serverGroup.contribution';
|
import { SERVER_GROUP_CONFIG, SERVER_GROUP_AUTOEXPAND_CONFIG } from 'sql/parts/objectExplorer/serverGroupDialog/serverGroup.contribution';
|
||||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||||
import { ServerTreeActionProvider } from 'sql/parts/objectExplorer/viewlet/serverTreeActionProvider';
|
import { ServerTreeActionProvider } from 'sql/parts/objectExplorer/viewlet/serverTreeActionProvider';
|
||||||
|
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||||
|
|
||||||
const $ = builder.$;
|
const $ = builder.$;
|
||||||
|
|
||||||
@@ -54,7 +55,8 @@ export class ServerTreeView {
|
|||||||
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
|
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
|
||||||
@IThemeService private _themeService: IThemeService,
|
@IThemeService private _themeService: IThemeService,
|
||||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||||
@IConfigurationService private _configurationService: IConfigurationService
|
@IConfigurationService private _configurationService: IConfigurationService,
|
||||||
|
@ICapabilitiesService capabilitiesService: ICapabilitiesService
|
||||||
) {
|
) {
|
||||||
this._activeConnectionsFilterAction = this._instantiationService.createInstance(
|
this._activeConnectionsFilterAction = this._instantiationService.createInstance(
|
||||||
ActiveConnectionsFilterAction,
|
ActiveConnectionsFilterAction,
|
||||||
@@ -64,6 +66,12 @@ export class ServerTreeView {
|
|||||||
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
|
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
|
||||||
this._onSelectionOrFocusChange = new Emitter();
|
this._onSelectionOrFocusChange = new Emitter();
|
||||||
this._actionProvider = this._instantiationService.createInstance(ServerTreeActionProvider);
|
this._actionProvider = this._instantiationService.createInstance(ServerTreeActionProvider);
|
||||||
|
capabilitiesService.onCapabilitiesRegistered(() => {
|
||||||
|
if (this._connectionManagementService.hasRegisteredServers()) {
|
||||||
|
this.refreshTree();
|
||||||
|
this._treeSelectionHandler.onTreeActionStateChange(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import {
|
|||||||
SqlMainContext, MainThreadNotebookDocumentsAndEditorsShape, SqlExtHostContext, ExtHostNotebookDocumentsAndEditorsShape,
|
SqlMainContext, MainThreadNotebookDocumentsAndEditorsShape, SqlExtHostContext, ExtHostNotebookDocumentsAndEditorsShape,
|
||||||
INotebookDocumentsAndEditorsDelta, INotebookEditorAddData, INotebookShowOptions, INotebookModelAddedData, INotebookModelChangedData
|
INotebookDocumentsAndEditorsDelta, INotebookEditorAddData, INotebookShowOptions, INotebookModelAddedData, INotebookModelChangedData
|
||||||
} from 'sql/workbench/api/node/sqlExtHost.protocol';
|
} from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
import { NotebookInput, NotebookEditorModel } from 'sql/parts/notebook/notebookInput';
|
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
|
||||||
import { INotebookService, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
import { INotebookService, INotebookEditor, IProviderInfo } from 'sql/workbench/services/notebook/common/notebookService';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { disposed } from 'vs/base/common/errors';
|
import { disposed } from 'vs/base/common/errors';
|
||||||
@@ -37,6 +37,7 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn
|
|||||||
class MainThreadNotebookEditor extends Disposable {
|
class MainThreadNotebookEditor extends Disposable {
|
||||||
private _contentChangedEmitter = new Emitter<NotebookContentChange>();
|
private _contentChangedEmitter = new Emitter<NotebookContentChange>();
|
||||||
public readonly contentChanged: Event<NotebookContentChange> = this._contentChangedEmitter.event;
|
public readonly contentChanged: Event<NotebookContentChange> = this._contentChangedEmitter.event;
|
||||||
|
private _providerInfo: IProviderInfo;
|
||||||
|
|
||||||
constructor(public readonly editor: INotebookEditor) {
|
constructor(public readonly editor: INotebookEditor) {
|
||||||
super();
|
super();
|
||||||
@@ -49,6 +50,9 @@ class MainThreadNotebookEditor extends Disposable {
|
|||||||
this._contentChangedEmitter.fire(changeEvent);
|
this._contentChangedEmitter.fire(changeEvent);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
editor.notebookParams.providerInfo.then(info => {
|
||||||
|
this._providerInfo = info;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get uri(): URI {
|
public get uri(): URI {
|
||||||
@@ -64,11 +68,11 @@ class MainThreadNotebookEditor extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get providerId(): string {
|
public get providerId(): string {
|
||||||
return this.editor.notebookParams.providerId;
|
return this._providerInfo ? this._providerInfo.providerId : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get providers(): string[] {
|
public get providers(): string[] {
|
||||||
return this.editor.notebookParams.providers;
|
return this._providerInfo ? this._providerInfo.providers : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get cells(): ICellModel[] {
|
public get cells(): ICellModel[] {
|
||||||
|
|||||||
@@ -85,11 +85,14 @@ export interface INotebookManager {
|
|||||||
readonly serverManager: azdata.nb.ServerManager;
|
readonly serverManager: azdata.nb.ServerManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IProviderInfo {
|
||||||
|
providerId: string;
|
||||||
|
providers: string[];
|
||||||
|
}
|
||||||
export interface INotebookParams extends IBootstrapParams {
|
export interface INotebookParams extends IBootstrapParams {
|
||||||
notebookUri: URI;
|
notebookUri: URI;
|
||||||
input: NotebookInput;
|
input: NotebookInput;
|
||||||
providerId: string;
|
providerInfo: Promise<IProviderInfo>;
|
||||||
providers: string[];
|
|
||||||
isTrusted: boolean;
|
isTrusted: boolean;
|
||||||
profile?: IConnectionProfile;
|
profile?: IConnectionProfile;
|
||||||
modelFactory?: ModelFactory;
|
modelFactory?: ModelFactory;
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
return new TPromise((resolve) => resolve({}));
|
return new TPromise((resolve) => resolve({}));
|
||||||
});
|
});
|
||||||
|
|
||||||
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined);
|
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService);
|
||||||
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
||||||
serverTreeView.setup(x => x.refreshTree());
|
serverTreeView.setup(x => x.refreshTree());
|
||||||
let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
||||||
@@ -222,7 +222,7 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
return new TPromise((resolve) => resolve({}));
|
return new TPromise((resolve) => resolve({}));
|
||||||
});
|
});
|
||||||
|
|
||||||
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined);
|
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService);
|
||||||
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
||||||
serverTreeView.setup(x => x.refreshTree());
|
serverTreeView.setup(x => x.refreshTree());
|
||||||
let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
||||||
@@ -240,7 +240,7 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
return new TPromise((resolve) => resolve({}));
|
return new TPromise((resolve) => resolve({}));
|
||||||
});
|
});
|
||||||
|
|
||||||
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined);
|
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService);
|
||||||
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
||||||
serverTreeView.setup(x => x.refreshTree());
|
serverTreeView.setup(x => x.refreshTree());
|
||||||
let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
||||||
@@ -257,7 +257,7 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
return new TPromise((resolve) => resolve({}));
|
return new TPromise((resolve) => resolve({}));
|
||||||
});
|
});
|
||||||
|
|
||||||
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined);
|
let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService);
|
||||||
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString()));
|
||||||
serverTreeView.setup(x => x.refreshTree());
|
serverTreeView.setup(x => x.refreshTree());
|
||||||
let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object);
|
||||||
|
|||||||
@@ -14,18 +14,21 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
|
|||||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||||
|
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
|
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
|
||||||
|
|
||||||
suite('ServerTreeView onAddConnectionProfile handler tests', () => {
|
suite('ServerTreeView onAddConnectionProfile handler tests', () => {
|
||||||
|
|
||||||
let serverTreeView: ServerTreeView;
|
let serverTreeView: ServerTreeView;
|
||||||
let mockTree: TypeMoq.Mock<Tree>;
|
let mockTree: TypeMoq.Mock<Tree>;
|
||||||
let mockRefreshTreeMethod: TypeMoq.Mock<Function>;
|
let mockRefreshTreeMethod: TypeMoq.Mock<Function>;
|
||||||
|
let capabilitiesService = new CapabilitiesTestService();
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
let instantiationService = new TestInstantiationService();
|
let instantiationService = new TestInstantiationService();
|
||||||
let mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}, new TestStorageService());
|
let mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}, new TestStorageService());
|
||||||
mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []);
|
mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []);
|
||||||
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, undefined, undefined, undefined);
|
mockConnectionManagementService.setup(x => x.hasRegisteredServers()).returns(() => true);
|
||||||
|
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, undefined, undefined, undefined, capabilitiesService);
|
||||||
let tree = <Tree>{
|
let tree = <Tree>{
|
||||||
clearSelection() { },
|
clearSelection() { },
|
||||||
getSelection() { },
|
getSelection() { },
|
||||||
@@ -91,4 +94,9 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
|
|||||||
mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never());
|
mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never());
|
||||||
mockTree.verify(x => x.select(TypeMoq.It.isAny()), TypeMoq.Times.never());
|
mockTree.verify(x => x.select(TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('The tree refreshes when new capabilities are registered', () => {
|
||||||
|
capabilitiesService.fireCapabilitiesRegistered(undefined);
|
||||||
|
mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -138,6 +138,10 @@ export class CapabilitiesTestService implements ICapabilitiesService {
|
|||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fireCapabilitiesRegistered(providerFeatures: ProviderFeatures): void {
|
||||||
|
this._onCapabilitiesRegistered.fire(providerFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
private _onCapabilitiesRegistered = new Emitter<ProviderFeatures>();
|
private _onCapabilitiesRegistered = new Emitter<ProviderFeatures>();
|
||||||
public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event;
|
public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,38 +203,40 @@ export class MenubarControl extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private detectAndRecommendCustomTitlebar(): void {
|
private detectAndRecommendCustomTitlebar(): void {
|
||||||
if (!isLinux) {
|
// {{SQL CARBON EDIT}} - Disable the custom titlebar recommendation
|
||||||
return;
|
// if (!isLinux) {
|
||||||
}
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
if (!this.storageService.getBoolean('menubar/electronFixRecommended', StorageScope.GLOBAL, false)) {
|
// if (!this.storageService.getBoolean('menubar/electronFixRecommended', StorageScope.GLOBAL, false)) {
|
||||||
if (this.currentMenubarVisibility === 'hidden' || this.currentTitlebarStyleSetting === 'custom') {
|
// if (this.currentMenubarVisibility === 'hidden' || this.currentTitlebarStyleSetting === 'custom') {
|
||||||
// Issue will not arise for user, abort notification
|
// // Issue will not arise for user, abort notification
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
const message = nls.localize('menubar.electronFixRecommendation', "If you experience hard to read text in the menu bar, we recommend trying out the custom title bar.");
|
// const message = nls.localize('menubar.electronFixRecommendation', "If you experience hard to read text in the menu bar, we recommend trying out the custom title bar.");
|
||||||
this.notificationService.prompt(Severity.Info, message, [
|
// this.notificationService.prompt(Severity.Info, message, [
|
||||||
{
|
// {
|
||||||
label: nls.localize('goToSetting', "Open Settings"),
|
// label: nls.localize('goToSetting', "Open Settings"),
|
||||||
run: () => {
|
// run: () => {
|
||||||
return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
|
// return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
label: nls.localize('moreInfo', "More Info"),
|
// label: nls.localize('moreInfo', "More Info"),
|
||||||
run: () => {
|
// run: () => {
|
||||||
window.open('https://go.microsoft.com/fwlink/?linkid=2038566');
|
// window.open('https://go.microsoft.com/fwlink/?linkid=2038566');
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
label: nls.localize('neverShowAgain', "Don't Show Again"),
|
// label: nls.localize('neverShowAgain', "Don't Show Again"),
|
||||||
run: () => {
|
// run: () => {
|
||||||
this.storageService.store('menubar/electronFixRecommended', true, StorageScope.GLOBAL);
|
// this.storageService.store('menubar/electronFixRecommended', true, StorageScope.GLOBAL);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
|
// {{SQL CARBON EDIT}} - End
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerListeners(): void {
|
private registerListeners(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user