Compare commits

...

39 Commits

Author SHA1 Message Date
Karl Burtram
eb35dae1d1 Turn off failing Insights tests 2019-04-16 22:59:03 -07:00
Karl Burtram
cc0a144169 Remove gap from bottom of Server view (#5072) 2019-04-16 19:54:30 -07:00
Anthony Dresser
b973f9e0ec changes strings for data explorer (#4946) 2019-04-16 18:49:59 -07:00
Alan Ren
d62068025c Merge fix the selectbox issue for chart 2019-04-16 13:40:24 -07:00
Aditya Bist
b2952d2ddf fix job action context (#5053) 2019-04-16 13:36:30 -07:00
Gene Lee
a21244816d Add support for new endpoint key string 'gateway' (#4954) 2019-04-16 10:45:00 -07:00
Chris LaFreniere
a9aeb57dc4 Fix to ensure that we rewrite spark ui links correctly (#4962) 2019-04-16 10:34:01 -07:00
Gene Lee
00537ed199 Fixed bug: CheckboxTreeNode label overflows, and node icon disappears (#5022) 2019-04-16 10:31:40 -07:00
Alex Ross
c2df3e0e0a Merge 51b0b28134d51361cf996d2f0a1c698247aeabd8 2019-04-15 10:15:55 -07:00
Charles Gagnon
e3afb1cffc Fix wrong release notes being loaded (#5008) 2019-04-11 20:56:19 -07:00
Kevin Cunnane
b475311f85 Fix #4500 Untitled notebook reopen doesn't show dirty (#5005)
* Fix 2 notebook issues
- Do not create notebook model twice on start
- Do not cause disposed warnings due to markdown cell deserialization

* Fix notebook dirty on open issue
Before model is resolved we weren't getting dirty events.
Solution is to use backing text model until it's ready.
Must hook to the dirty event & notify to get the dot to appear

# Conflicts:
#	src/sql/workbench/parts/notebook/cellViews/code.component.ts
2019-04-11 20:56:03 -07:00
Maddy
45005d61e0 add wrap to the <pre> tag (#5002)
* add wrap to the <pre> tag

* removed styles for browser support
2019-04-11 20:46:41 -07:00
Anthony Dresser
adfddeae27 restore line height to account list renderer (#4999) 2019-04-11 20:46:27 -07:00
Anthony Dresser
a7429267bb revert data explorer id to connections (#5003) 2019-04-11 20:46:08 -07:00
kisantia
ff415d6a03 bump SQL Tools to 1.5.0-alpha.85 to get invalid dacpac version fix (#5001) 2019-04-11 15:25:55 -07:00
udeeshagautam
b4dc35a4de Fix for 4104 : Multiple consecutive spaces in query results cells are condensed into one (#4983)
* preserving spaces in query results - all beginning, trailing and middle spaces will be shown as is

* removing the change through formatting and replacing with css change formatting was leaving special char while removing nbsp
2019-04-11 15:25:41 -07:00
Aditya Bist
29c7ccad39 Added some usage details (#4711)
* added some details

* remove unused import

* added new metrics, removed churn

* merged master and code review comments

* code review comments

* normalized days to calendar days/weeks/months

* cleaned up code

* changed comment to start required check for PR

* fix failing test

* fix test

* removed null assignment

* fix null test script
2019-04-11 15:25:24 -07:00
Yurong He
1da3635d03 Fix ##3479 ctrl+a select active cell output or preview markdown (#4981)
* Enable ctrl+a to select the output or markdown content when the cell is active

* Moved toggleUserSelect into ngOnChanges

* Resolve PR comments
2019-04-11 15:25:02 -07:00
Chris LaFreniere
1f50015ed2 Add New Notebook from Server Dashboard (#4971) 2019-04-11 15:24:47 -07:00
Aditya Bist
01892422cb Azure extension changes (#4987)
* removed search box

* removed commented code

# Conflicts:
#	src/sql/parts/objectExplorer/viewlet/serverTreeView.ts
2019-04-11 15:24:31 -07:00
Cory Rivera
afce60b06f Add additional error handling to Python installation for Notebooks (#4891)
* Also enabled integration tests for python installation.
2019-04-11 15:01:02 -07:00
Chris LaFreniere
a2b87f6158 Fix for relative markdown image paths (#4889)
* Fix for relative markdown image paths

* PR comments
2019-04-11 15:00:45 -07:00
Chris LaFreniere
76a2f92daf Notebooks: Potential Fix for "Notebook Provider does not Exist" Error (#4848)
* Fallback to SQL

* Fix providers not found issue

* await whenInstalledExtensionsRegistered

* PR comments
2019-04-11 15:00:21 -07:00
Gene Lee
5b09d57196 Fixed Broken Notebook Preview (#4895) 2019-04-11 14:59:09 -07:00
Karl Burtram
95bf18f859 Fix merge build break 2019-04-10 16:16:43 -07:00
Kevin Cunnane
1fc648ff37 Mitigate (but not fully fix) Run Cell from disconnected notebook (#4960)
This is a partial fix that lays groundwork for full "Prompt to connect" if a kernel needs a connection.
I am waiting on Yurong's refactoring of connection handling before doing any of the prompt work.

- Adds kernel metadata about whether a connection is required.
- For Jupyter, only Spark kernels are listed as requiring a connection
- If this is true and there's no active connection, will show notification and not call execute

In the future, this path will still be used if user is prompted to connect and cancels out.
The future change will be to inject a "connect" handler from notebook.component to the cell callback and use to set connection context
2019-04-10 13:50:23 -07:00
Kevin Cunnane
37ba956bad Fix #4893 New Notebook Can Open Existing Noteboon (#4959)
Add back check for textDocuments with same name, should've been there anyhow

On rehydration files show as text docs before clicking as only get
changed by customInputConverter code path.
We should look at this long term - ideally we'd update notebookDocuments
with correct values on initial start. #4958 opened to track this.
2019-04-10 13:47:49 -07:00
Kevin Cunnane
697f887539 Fix #4930 Text cells are referred to as text and markdown in commands (#4956) 2019-04-10 13:44:20 -07:00
Anthony Dresser
7b42141958 Merge 2de47c2a50 2019-04-10 13:44:11 -07:00
Chris LaFreniere
158b00f9b4 always serialize execution count (#4864) 2019-04-10 13:39:54 -07:00
Matt Bierner
43ac8dfd20 Adopt TS 3.4.3
Fixes #72005
2019-04-10 13:30:11 -07:00
Sandeep Somavarapu
34d8d52e7a Fix #71947 2019-04-09 13:09:10 -07:00
Sandeep Somavarapu
c738b26c04 Fix #71585 2019-04-09 13:09:03 -07:00
Johannes Rieken
6090e7173f properly check picked formatter, #71988 2019-04-09 13:06:45 -07:00
Johannes Rieken
7ebf746584 use editor for format on save and honor silent mode 2019-04-09 13:06:36 -07:00
Matt Bierner
e9d04d75ac Fixes #71688 2019-04-09 13:06:20 -07:00
Joao Moreno
605160a1ba Cherry pick 3773487012c98ef7974a0182771ea19178f2a525 2019-04-09 13:04:37 -07:00
Johannes Rieken
82b8750b63 show notification when formatter is disabled/uninstalled, message otherwise 2019-04-09 12:59:00 -07:00
Rob Lourens
61f7f19d12 Fix #71465 - "find in files shortcut broken" 2019-04-09 12:58:48 -07:00
145 changed files with 949 additions and 515 deletions

View File

@@ -114,7 +114,7 @@ const vscodeResources = [
'out-build/sql/parts/jobManagement/common/media/*.svg',
'out-build/sql/media/objectTypes/*.svg',
'out-build/sql/media/icons/*.svg',
'out-build/sql/parts/notebook/media/**/*.svg',
'out-build/sql/workbench/parts/notebook/media/**/*.svg',
'!**/test/**'
];

View File

@@ -134,8 +134,10 @@ export class MarkdownEngine {
// {{SQL CARBON EDIT}} - Add renderText method
public async renderText(document: vscode.Uri, text: string): Promise<string> {
const engine = await this.getEngine(this.getConfig(document));
return engine.render(text);
const config = this.getConfig(document);
const engine = await this.getEngine(config);
this.currentDocument = document;
return engine.render(text, config);
}
// {{SQL CARBON EDIT}} - End

View File

@@ -109,6 +109,14 @@
{
"command": "mssqlCluster.livy.cmd.submitFileToSparkJob",
"title": "%title.submitSparkJob%"
},
{
"command": "mssql.searchServers",
"title": "%title.searchServers%"
},
{
"command": "mssql.clearSearchServerResult",
"title": "%title.clearSearchServerResult%"
}
],
"outputChannels": [

View File

@@ -24,5 +24,8 @@
"title.openYarnHistory": "View Yarn History",
"title.tasks": "Tasks",
"title.installPackages": "Install Packages",
"title.configurePython": "Configure Python for Notebooks"
"title.configurePython": "Configure Python for Notebooks",
"title.searchServers": "Search: Servers",
"title.clearSearchServerResult": "Search: Clear Search Server Results"
}

View File

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

View File

@@ -13,7 +13,8 @@ export const extensionConfigSectionName = 'mssql';
// DATA PROTOCOL VALUES ///////////////////////////////////////////////////////////
export const mssqlClusterProviderName = 'mssqlCluster';
export const hadoopKnoxEndpointName = 'Knox';
export const hadoopEndpointNameKnox = 'Knox';
export const hadoopEndpointNameGateway = 'gateway';
export const protocolVersion = '1.0';
export const hostPropName = 'host';
export const userPropName = 'user';

View File

@@ -32,6 +32,7 @@ import { OpenSparkJobSubmissionDialogCommand, OpenSparkJobSubmissionDialogFromFi
import { OpenSparkYarnHistoryTask } from './sparkFeature/historyTask';
import { MssqlObjectExplorerNodeProvider, mssqlOutputChannel } from './objectExplorerNodeProvider/objectExplorerNodeProvider';
import { CmsService } from './cms/cmsService';
import { registerSearchServerCommand } from './objectExplorerNodeProvider/command';
const baseConfig = require('./config.json');
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
@@ -120,6 +121,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<MssqlE
vscode.window.showErrorMessage('Failed to start Sql tools service');
});
registerSearchServerCommand(appContext);
let contextProvider = new ContextProvider();
context.subscriptions.push(contextProvider);
context.subscriptions.push(credentialsStore);

View File

@@ -174,3 +174,18 @@ export abstract class ProgressCommand extends Command {
});
}
}
export function registerSearchServerCommand(appContext: AppContext): void {
appContext.apiWrapper.registerCommand('mssql.searchServers', () => {
vscode.window.showInputBox({
placeHolder: localize('mssql.searchServers', 'Search Server Names')
}).then((stringSearch) => {
if (stringSearch) {
vscode.commands.executeCommand('registeredServers.searchServer', (stringSearch));
}
});
});
appContext.apiWrapper.registerCommand('mssql.clearSearchServerResult', () => {
vscode.commands.executeCommand('registeredServers.clearSearchServerResult');
});
}

View File

@@ -305,11 +305,20 @@ export class PreviewFileCommand extends ProgressCommand {
await this.executeWithProgress(
async (cancelToken: vscode.CancellationTokenSource) => {
let contents = await fileNode.getFileContentsAsString(PreviewFileCommand.DefaultMaxSize);
let doc = await this.openTextDocument(fspath.basename(fileNode.hdfsPath));
let editor = await this.apiWrapper.showTextDocument(doc, vscode.ViewColumn.Active, false);
await editor.edit(edit => {
edit.insert(new vscode.Position(0, 0), contents);
});
let fileName: string = fspath.basename(fileNode.hdfsPath);
if (fspath.extname(fileName) !== '.ipynb') {
let doc = await this.openTextDocument(fileName);
let editor = await this.apiWrapper.showTextDocument(doc, vscode.ViewColumn.Active, false);
await editor.edit(edit => {
edit.insert(new vscode.Position(0, 0), contents);
});
} else {
let connectionProfile: azdata.IConnectionProfile = undefined;
if (context.type === constants.ObjectExplorerService) {
connectionProfile = context.explorerContext.connectionProfile;
}
await this.showNotebookDocument(fileName, connectionProfile, contents);
}
},
localize('previewing', 'Generating preview'),
false);
@@ -322,6 +331,18 @@ export class PreviewFileCommand extends ProgressCommand {
}
}
private async showNotebookDocument(fileName: string, connectionProfile?: azdata.IConnectionProfile,
initialContent?: string
): Promise<azdata.nb.NotebookEditor> {
let docUri: vscode.Uri = getSaveableUri(this.apiWrapper, fileName, true)
.with({ scheme: constants.UNTITLED_SCHEMA });
return await azdata.nb.showNotebookDocument(docUri, {
connectionProfile: connectionProfile,
preview: false,
initialContent: initialContent
});
}
private async openTextDocument(fileName: string): Promise<vscode.TextDocument> {
let docUri: vscode.Uri = getSaveableUri(this.apiWrapper, fileName, true);
if (docUri) {

View File

@@ -79,7 +79,11 @@ async function createSqlClusterConnInfo(sqlConnInfo: azdata.IConnectionProfile |
let endpoints: IEndpoint[] = serverInfo.options[constants.clusterEndpointsProperty];
if (!endpoints || endpoints.length === 0) { return undefined; }
let index = endpoints.findIndex(ep => ep.serviceName === constants.hadoopKnoxEndpointName);
let index = endpoints.findIndex(ep => {
let serviceName: string = ep.serviceName.toLowerCase();
return serviceName === constants.hadoopEndpointNameKnox.toLowerCase() ||
serviceName === constants.hadoopEndpointNameGateway.toLowerCase();
});
if (index < 0) { return undefined; }
let credentials = await azdata.connection.getCredentials(connectionId);

View File

@@ -129,7 +129,7 @@ export class ConfigurePythonDialog {
}
// Don't wait on installation, since there's currently no Cancel functionality
this.jupyterInstallation.startInstallProcess(pythonLocation)
this.jupyterInstallation.startInstallProcess(false, pythonLocation)
.then(() => {
this._setupComplete.resolve();
})

View File

@@ -13,6 +13,7 @@ import * as nls from 'vscode-nls';
import { JupyterController } from './jupyter/jupyterController';
import { AppContext } from './common/appContext';
import { ApiWrapper } from './common/apiWrapper';
import { IExtensionApi } from './types';
const localize = nls.loadMessageBundle();
@@ -20,10 +21,9 @@ const JUPYTER_NOTEBOOK_PROVIDER = 'jupyter';
const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', 'This sample code loads the file into a data frame and shows the first 10 results.');
const noNotebookVisible = localize('noNotebookVisible', 'No notebook editor is active');
let controller: JupyterController;
export let controller: JupyterController;
export function activate(extensionContext: vscode.ExtensionContext) {
export async function activate(extensionContext: vscode.ExtensionContext): Promise<IExtensionApi> {
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.new', (context?: azdata.ConnectedContext) => {
let connectionProfile: azdata.IConnectionProfile = undefined;
if (context && context.connectionProfile) {
@@ -49,7 +49,16 @@ export function activate(extensionContext: vscode.ExtensionContext) {
let appContext = new AppContext(extensionContext, new ApiWrapper());
controller = new JupyterController(appContext);
controller.activate();
let result = await controller.activate();
if (!result) {
return undefined;
}
return {
getJupyterController() {
return controller;
}
};
}
function newNotebook(connectionProfile: azdata.IConnectionProfile) {
@@ -75,8 +84,9 @@ function findNextUntitledEditorName(): string {
// Note: this will go forever if it's coded wrong, or you have infinite Untitled notebooks!
while (true) {
let title = `Notebook-${nextVal}`;
let hasTextDoc = vscode.workspace.textDocuments.findIndex(doc => doc.isUntitled && doc.fileName === title) > -1;
let hasNotebookDoc = azdata.nb.notebookDocuments.findIndex(doc => doc.isUntitled && doc.fileName === title) > -1;
if (!hasNotebookDoc) {
if (!hasTextDoc && !hasNotebookDoc) {
return title;
}
nextVal++;

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const testRunner = require('vscode/lib/testrunner');
const suite = 'Notebook Extension Integration Tests';
const options: any = {
ui: 'bdd',
useColors: true,
timeout: 600000
};
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
testsuitesTitle: `${suite} ${process.platform}`,
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
}
};
}
testRunner.configure(options);
export = testRunner;

View File

@@ -6,16 +6,17 @@
'use strict';
import * as should from 'should';
import * as assert from 'assert';
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as tempWrite from 'temp-write';
import * as assert from 'assert';
import 'mocha';
import { JupyterController } from '../jupyter/jupyterController';
import { INotebook, CellTypes } from '../contracts/content';
import JupyterServerInstallation from '../jupyter/jupyterServerInstallation';
describe('Notebook Integration Test', function (): void {
describe('Notebook Extension Integration Tests', function () {
this.timeout(600000);
let expectedNotebookContent: INotebook = {
@@ -35,12 +36,34 @@ describe('Notebook Integration Test', function (): void {
nbformat_minor: 2
};
let installComplete = false;
let pythonInstallDir = process.env.PYTHON_TEST_PATH;
let jupyterController: JupyterController;
before(async function () {
assert.ok(pythonInstallDir, 'Python install directory was not defined.');
let notebookExtension: vscode.Extension<any>;
while (true) {
notebookExtension = vscode.extensions.getExtension('Microsoft.notebook');
if (notebookExtension && notebookExtension.isActive) {
break;
} else {
await new Promise(resolve => { setTimeout(resolve, 1000); });
}
}
jupyterController = notebookExtension.exports.getJupyterController() as JupyterController;
await jupyterController.jupyterInstallation.startInstallProcess(false, pythonInstallDir);
installComplete = true;
});
it('Should connect to local notebook server with result 2', async function () {
this.timeout(60000);
should(installComplete).be.true('Python setup did not complete.');
should(JupyterServerInstallation.getPythonInstallPath(jupyterController.jupyterInstallation.apiWrapper)).be.equal(pythonInstallDir);
let pythonNotebook = Object.assign({}, expectedNotebookContent, { metadata: { kernelspec: { name: 'python3', display_name: 'Python 3' } } });
let uri = writeNotebookToFile(pythonNotebook);
await ensureJupyterInstalled();
let notebook = await azdata.nb.showNotebookDocument(uri);
should(notebook.document.cells).have.length(1);
@@ -51,82 +74,13 @@ describe('Notebook Integration Test', function (): void {
let result = (<azdata.nb.IExecuteResult>cellOutputs[0]).data['text/plain'];
should(result).equal('2');
try {
// TODO support closing the editor. Right now this prompts and there's no override for this. Need to fix in core
// Close the editor using the recommended vscode API
//await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
}
catch (e) { }
});
it('Should connect to remote spark server with result 2', async function () {
this.timeout(240000);
let uri = writeNotebookToFile(expectedNotebookContent);
await ensureJupyterInstalled();
// Given a connection to a server exists
let connectionProfile = await connectToSparkIntegrationServer();
// When I open a Spark notebook and run the cell
let notebook = await azdata.nb.showNotebookDocument(uri, {
connectionProfile: connectionProfile
});
should(notebook.document.cells).have.length(1);
let ran = await notebook.runCell(notebook.document.cells[0]);
should(ran).be.true('Notebook runCell failed');
// Then I expect to get the output result of 1+1, executed remotely against the Spark endpoint
let cellOutputs = notebook.document.cells[0].contents.outputs;
should(cellOutputs).have.length(4);
let sparkResult = (<azdata.nb.IStreamResult>cellOutputs[3]).text;
should(sparkResult).equal('2');
try {
// TODO support closing the editor. Right now this prompts and there's no override for this. Need to fix in core
// Close the editor using the recommended vscode API
//await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
}
catch (e) { }
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
});
async function connectToSparkIntegrationServer(): Promise<azdata.IConnectionProfile> {
assert.ok(process.env.BACKEND_HOSTNAME, 'BACKEND_HOSTNAME, BACKEND_USERNAME, BACKEND_PWD must be set using ./tasks/setbackenvariables.sh or .\\tasks\\setbackendvaraibles.bat');
let connInfo: azdata.connection.Connection = {
options: {
'host': process.env.BACKEND_HOSTNAME,
'groupId': 'C777F06B-202E-4480-B475-FA416154D458',
'knoxport': '',
'user': process.env.BACKEND_USERNAME,
'password': process.env.BACKEND_PWD
},
providerName: 'HADOOP_KNOX',
connectionId: 'abcd1234',
};
connInfo['savePassword'] = true;
let result = await azdata.connection.connect(<any>connInfo as azdata.IConnectionProfile);
should(result.connected).be.true();
should(result.connectionId).not.be.undefined();
should(result.connectionId).not.be.empty();
should(result.errorMessage).be.undefined();
let activeConnections = await azdata.connection.getActiveConnections();
should(activeConnections).have.length(1);
return <azdata.IConnectionProfile><any>connInfo;
}
function writeNotebookToFile(pythonNotebook: INotebook): vscode.Uri {
let notebookContentString = JSON.stringify(pythonNotebook);
let localFile = tempWrite.sync(notebookContentString, 'notebook.ipynb');
let uri = vscode.Uri.file(localFile);
return uri;
}
async function ensureJupyterInstalled(): Promise<void> {
let jupterControllerExports = vscode.extensions.getExtension('Microsoft.sql-vnext').exports;
let jupyterController = jupterControllerExports.getJupterController() as JupyterController;
await jupyterController.jupyterInstallation.installReady;
}
}

View File

@@ -175,13 +175,13 @@ export class JupyterController implements vscode.Disposable {
}
private async handleDependenciesReinstallation(): Promise<void> {
if (await this.confirmReinstall()) {
this._jupyterInstallation = await JupyterServerInstallation.getInstallation(
this.extensionContext.extensionPath,
this.outputChannel,
this.apiWrapper,
undefined,
true);
try {
let doReinstall = await this.confirmReinstall();
if (doReinstall) {
await this._jupyterInstallation.startInstallProcess(true);
}
} catch (err) {
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
}
}

View File

@@ -63,6 +63,12 @@ export class JupyterKernel implements nb.IKernel {
return true;
}
public get requiresConnection(): boolean {
// TODO would be good to have a smarter way to do this.
// for now only Spark kernels need a connection
return !!(this.kernelImpl.name && this.kernelImpl.name.toLowerCase().indexOf('spark') > -1);
}
public get isReady(): boolean {
return this.kernelImpl.isReady;
}

View File

@@ -31,6 +31,8 @@ const msgPythonUnpackError = localize('msgPythonUnpackError', 'Error while unpac
const msgTaskName = localize('msgTaskName', 'Installing Notebook dependencies');
const msgInstallPkgStart = localize('msgInstallPkgStart', 'Installing Notebook dependencies, see Tasks view for more information');
const msgInstallPkgFinish = localize('msgInstallPkgFinish', 'Notebook dependencies installation is complete');
const msgPythonRunningError = localize('msgPythonRunningError', 'Cannot overwrite existing Python installation while python is running.');
const msgPendingInstallError = localize('msgPendingInstallError', 'Another Python installation is currently in progress.');
function msgDependenciesInstallationFailed(errorMessage: string): string { return localize('msgDependenciesInstallationFailed', 'Installing Notebook dependencies failed with error: {0}', errorMessage); }
function msgDownloadPython(platform: string, pythonDownloadUrl: string): string { return localize('msgDownloadPython', 'Downloading local python for platform: {0} to {1}', platform, pythonDownloadUrl); }
@@ -52,41 +54,19 @@ export default class JupyterServerInstallation {
// Allows dependencies to be installed even if an existing installation is already present
private _forceInstall: boolean;
private _installInProgress: boolean;
private static readonly DefaultPythonLocation = path.join(utils.getUserHome(), 'azuredatastudio-python');
private _installReady: Deferred<void>;
constructor(extensionPath: string, outputChannel: OutputChannel, apiWrapper: ApiWrapper, pythonInstallationPath?: string, forceInstall?: boolean) {
constructor(extensionPath: string, outputChannel: OutputChannel, apiWrapper: ApiWrapper, pythonInstallationPath?: string) {
this.extensionPath = extensionPath;
this.outputChannel = outputChannel;
this.apiWrapper = apiWrapper;
this._pythonInstallationPath = pythonInstallationPath || JupyterServerInstallation.getPythonInstallPath(this.apiWrapper);
this._forceInstall = !!forceInstall;
this._forceInstall = false;
this._installInProgress = false;
this.configurePackagePaths();
this._installReady = new Deferred<void>();
if (JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
this._installReady.resolve();
}
}
public get installReady(): Promise<void> {
return this._installReady.promise;
}
public static async getInstallation(
extensionPath: string,
outputChannel: OutputChannel,
apiWrapper: ApiWrapper,
pythonInstallationPath?: string,
forceInstall?: boolean): Promise<JupyterServerInstallation> {
let installation = new JupyterServerInstallation(extensionPath, outputChannel, apiWrapper, pythonInstallationPath, forceInstall);
await installation.startInstallProcess();
return installation;
}
private async installDependencies(backgroundOperation: azdata.BackgroundOperation): Promise<void> {
@@ -217,7 +197,7 @@ export default class JupyterServerInstallation {
// Update python paths and properties to reference user's local python.
let pythonBinPathSuffix = process.platform === constants.winPlatform ? '' : 'bin';
this._pythonExecutable = path.join(pythonSourcePath, process.platform === constants.winPlatform ? 'python.exe' : 'bin/python3');
this._pythonExecutable = JupyterServerInstallation.getPythonExePath(this._pythonInstallationPath);
this.pythonBinPath = path.join(pythonSourcePath, pythonBinPathSuffix);
// Store paths to python libraries required to run jupyter.
@@ -230,6 +210,11 @@ export default class JupyterServerInstallation {
}
this.pythonEnvVarPath = this.pythonBinPath + delimiter + this.pythonEnvVarPath;
// Delete existing Python variables in ADS to prevent conflict with other installs
delete process.env['PYTHONPATH'];
delete process.env['PYTHONSTARTUP'];
delete process.env['PYTHONHOME'];
// Store the executable options to run child processes with env var without interfering parent env var.
let env = Object.assign({}, process.env);
delete env['Path']; // Delete extra 'Path' variable for Windows, just in case.
@@ -239,15 +224,51 @@ export default class JupyterServerInstallation {
};
}
public startInstallProcess(pythonInstallationPath?: string): Promise<void> {
if (pythonInstallationPath) {
this._pythonInstallationPath = pythonInstallationPath;
this.configurePackagePaths();
private isPythonRunning(pythonInstallPath: string): Promise<boolean> {
let pythonExePath = JupyterServerInstallation.getPythonExePath(pythonInstallPath);
return new Promise<boolean>(resolve => {
fs.open(pythonExePath, 'r+', (err, fd) => {
if (!err) {
fs.close(fd, err => {
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
});
resolve(false);
} else {
resolve(err.code === 'EBUSY' || err.code === 'EPERM');
}
});
});
}
/**
* Installs Python and associated dependencies to the specified directory.
* @param forceInstall Indicates whether an existing installation should be overwritten, if it exists.
* @param installationPath Optional parameter that specifies where to install python.
* The previous path (or the default) is used if a new path is not specified.
*/
public async startInstallProcess(forceInstall: boolean, installationPath?: string): Promise<void> {
let isPythonRunning = await this.isPythonRunning(installationPath ? installationPath : this._pythonInstallationPath);
if (isPythonRunning) {
return Promise.reject(msgPythonRunningError);
}
let updateConfig = () => {
if (this._installInProgress) {
return Promise.reject(msgPendingInstallError);
}
this._installInProgress = true;
this._forceInstall = forceInstall;
if (installationPath) {
this._pythonInstallationPath = installationPath;
}
this.configurePackagePaths();
let updateConfig = async () => {
let notebookConfig = this.apiWrapper.getConfiguration(constants.notebookConfigKey);
notebookConfig.update(constants.pythonPathConfigKey, this._pythonInstallationPath, ConfigurationTarget.Global);
await notebookConfig.update(constants.pythonPathConfigKey, this._pythonInstallationPath, ConfigurationTarget.Global);
};
let installReady = new Deferred<void>();
if (!fs.existsSync(this._pythonExecutable) || this._forceInstall) {
this.apiWrapper.startBackgroundOperation({
displayName: msgTaskName,
@@ -255,27 +276,32 @@ export default class JupyterServerInstallation {
isCancelable: false,
operation: op => {
this.installDependencies(op)
.then(() => {
this._installReady.resolve();
updateConfig();
.then(async () => {
await updateConfig();
installReady.resolve();
this._installInProgress = false;
})
.catch(err => {
let errorMsg = msgDependenciesInstallationFailed(utils.getErrorMessage(err));
op.updateStatus(azdata.TaskStatus.Failed, errorMsg);
this.apiWrapper.showErrorMessage(errorMsg);
this._installReady.reject(errorMsg);
installReady.reject(errorMsg);
this._installInProgress = false;
});
}
});
} else {
// Python executable already exists, but the path setting wasn't defined,
// so update it here
this._installReady.resolve();
updateConfig();
await updateConfig();
installReady.resolve();
}
return this._installReady.promise;
return installReady.promise;
}
/**
* Opens a dialog for configuring the installation path for the Notebook Python dependencies.
*/
public async promptForPythonInstall(): Promise<void> {
if (!JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, this.outputChannel, this);
@@ -312,6 +338,10 @@ export default class JupyterServerInstallation {
return this._pythonExecutable;
}
/**
* Checks if a python executable exists at the "notebook.pythonPath" defined in the user's settings.
* @param apiWrapper An ApiWrapper to use when retrieving user settings info.
*/
public static isPythonInstalled(apiWrapper: ApiWrapper): boolean {
// Don't use _pythonExecutable here, since it could be populated with a default value
let pathSetting = JupyterServerInstallation.getPythonPathSetting(apiWrapper);
@@ -319,13 +349,15 @@ export default class JupyterServerInstallation {
return false;
}
let pythonExe = path.join(
pathSetting,
constants.pythonBundleVersion,
process.platform === constants.winPlatform ? 'python.exe' : 'bin/python3');
let pythonExe = JupyterServerInstallation.getPythonExePath(pathSetting);
return fs.existsSync(pythonExe);
}
/**
* Returns the Python installation path defined in "notebook.pythonPath" in the user's settings.
* Returns a default path if the setting is not defined.
* @param apiWrapper An ApiWrapper to use when retrieving user settings info.
*/
public static getPythonInstallPath(apiWrapper: ApiWrapper): string {
let userPath = JupyterServerInstallation.getPythonPathSetting(apiWrapper);
return userPath ? userPath : JupyterServerInstallation.DefaultPythonLocation;
@@ -345,6 +377,11 @@ export default class JupyterServerInstallation {
return path;
}
/**
* Returns the folder containing the python executable under the path defined in
* "notebook.pythonPath" in the user's settings.
* @param apiWrapper An ApiWrapper to use when retrieving user settings info.
*/
public static getPythonBinPath(apiWrapper: ApiWrapper): string {
let pythonBinPathSuffix = process.platform === constants.winPlatform ? '' : 'bin';
@@ -353,4 +390,11 @@ export default class JupyterServerInstallation {
constants.pythonBundleVersion,
pythonBinPathSuffix);
}
private static getPythonExePath(pythonInstallPath: string): string {
return path.join(
pythonInstallPath,
constants.pythonBundleVersion,
process.platform === constants.winPlatform ? 'python.exe' : 'bin/python3');
}
}

View File

@@ -57,7 +57,8 @@ const configBase = {
const KNOX_ENDPOINT_SERVER = 'host';
const KNOX_ENDPOINT_PORT = 'knoxport';
const KNOX_ENDPOINT = 'knox';
const KNOX_ENDPOINT_KNOX = 'knox';
const KNOX_ENDPOINT_GATEWAY = 'gateway';
const SQL_PROVIDER = 'MSSQL';
const USER = 'user';
const DEFAULT_CLUSTER_USER_NAME = 'root';
@@ -242,7 +243,9 @@ export class JupyterSession implements nb.ISession {
//Update server info with bigdata endpoint - Unified Connection
if (connection.providerName === SQL_PROVIDER) {
let clusterEndpoint: utils.IEndpoint = await this.getClusterEndpoint(connection.id, KNOX_ENDPOINT);
let clusterEndpoint: utils.IEndpoint =
await this.getClusterEndpoint(connection.id, KNOX_ENDPOINT_KNOX) ||
await this.getClusterEndpoint(connection.id, KNOX_ENDPOINT_GATEWAY);
if (!clusterEndpoint) {
let kernelDisplayName: string = await this.getKernelDisplayName();
return Promise.reject(new Error(localize('connectionNotValid', 'Spark kernels require a connection to a SQL Server big data cluster master instance.')));

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// import * as vscode from 'vscode';
// import { context } from './testContext';
const path = require('path');
const testRunner = require('vscode/lib/testrunner');

16
extensions/notebook/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { JupyterController } from './jupyter/jupyterController';
/**
* The API provided by this extension.
*
* @export
* @interface IExtensionApi
*/
export interface IExtensionApi {
getJupyterController(): JupyterController;
}

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "3.4.1"
"typescript": "3.4.3"
},
"scripts": {
"postinstall": "node ./postinstall"

View File

@@ -2,7 +2,7 @@
# yarn lockfile v1
typescript@3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6"
integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==
typescript@3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.3.tgz#0eb320e4ace9b10eadf5bc6103286b0f8b7c224f"
integrity sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ==

View File

@@ -4,10 +4,14 @@ pushd %~dp0\..
set VSCODEUSERDATADIR=%TMP%\adsuser-%RANDOM%-%TIME:~6,5%
set VSCODEEXTENSIONSDIR=%TMP%\adsext-%RANDOM%-%TIME:~6,5%
set PYTHON_TEST_PATH=%VSCODEUSERDATADIR%\TestPythonInstallation
echo %VSCODEUSERDATADIR%
echo %VSCODEEXTENSIONSDIR%
echo %PYTHON_TEST_PATH%
@echo OFF
:: This first notebook test will do the python install that the later tests depend on
call .\scripts\code.bat --extensionDevelopmentPath=%~dp0\..\extensions\notebook --extensionTestsPath=%~dp0\..\extensions\notebook\out\integrationTest --user-data-dir=%VSCODEUSERDATADIR% --extensions-dir=%VSCODEEXTENSIONSDIR% --remote-debugging-port=9222
call .\scripts\code.bat --extensionDevelopmentPath=%~dp0\..\extensions\integration-tests --extensionTestsPath=%~dp0\..\extensions\integration-tests\out --user-data-dir=%VSCODEUSERDATADIR% --extensions-dir=%VSCODEEXTENSIONSDIR% --remote-debugging-port=9222
if %errorlevel% neq 0 exit /b %errorlevel%

View File

@@ -12,12 +12,15 @@ else
VSCODEEXTDIR=`mktemp -d 2>/dev/null`
fi
export PYTHON_TEST_PATH=$VSCODEUSERDATADIR/TestPythonInstallation
cd $ROOT
echo $VSCODEUSERDATADIR
echo $VSCODEEXTDIR
echo $PYTHON_TEST_PATH
./scripts/code.sh --extensionDevelopmentPath=$ROOT/extensions/notebook --extensionTestsPath=$ROOT/extensions/notebook/out/integrationTest --user-data-dir=$VSCODEUSERDATADIR --extensions-dir=$VSCODEEXTDIR --remote-debugging-port=9222
./scripts/code.sh --extensionDevelopmentPath=$ROOT/extensions/integration-tests --extensionTestsPath=$ROOT/extensions/integration-tests/out --user-data-dir=$VSCODEUSERDATADIR --extensions-dir=$VSCODEEXTDIR --remote-debugging-port=9222
rm -r $VSCODEUSERDATADIR
rm -r -f $VSCODEUSERDATADIR
rm -r $VSCODEEXTDIR

View File

@@ -4085,6 +4085,11 @@ declare module 'azdata' {
* Default kernel for notebook
*/
defaultKernel?: nb.IKernelSpec;
/**
* Optional content used to give an initial notebook state
*/
initialContent?: nb.INotebookContents | string;
}
/**
@@ -4475,6 +4480,7 @@ declare module 'azdata' {
readonly id: string;
readonly name: string;
readonly supportsIntellisense: boolean;
readonly requiresConnection?: boolean;
/**
* Test whether the kernel is ready.
*/

View File

@@ -5,6 +5,7 @@
.monaco-table * {
box-sizing: border-box;
white-space: pre;
}
.monaco-table {

View File

@@ -15,6 +15,12 @@
.list-row.account-picker-list .label .contextual-display-name {
font-size: 15px;
line-height: 15px;
}
.list-row.account-picker-list .label .display-name {
font-size: 13px;
line-height: 13px;
}
.list-row.account-picker-list .label .content {

View File

@@ -12,7 +12,7 @@ import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { QueryInput } from 'sql/parts/query/common/queryInput';
import { IQueryEditorOptions } from 'sql/workbench/services/queryEditor/common/queryEditorService';
import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput';
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
import { NotebookInput } from 'sql/workbench/parts/notebook/notebookInput';
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { notebookModeId } from 'sql/common/constants';

View File

@@ -77,7 +77,7 @@ let defaultVal = [
{
name: 'Tasks',
widget: {
'tasks-widget': [{ name: 'restore', when: '!mssql:iscloud' }, 'configureDashboard', 'newQuery']
'tasks-widget': [{ name: 'restore', when: '!mssql:iscloud' }, 'configureDashboard', 'newQuery', 'mssqlCluster.task.newNotebook']
},
gridItemConfig: {
sizex: 1,

View File

@@ -82,6 +82,7 @@ export abstract class JobManagementView extends TabChild implements AfterContent
let actionContext = {
ownerUri: ownerUri,
targetObject: targetObject
};
let anchor = { x: event.pageX + 1, y: event.pageY };
@@ -116,7 +117,8 @@ export abstract class JobManagementView extends TabChild implements AfterContent
{ action: refreshAction },
{ action: newAction }
]);
this._actionBar.context = { component: this };
let context: IJobActionInfo = { component: this };
this._actionBar.context = context;
}
public refreshJobs() {

View File

@@ -2,7 +2,13 @@
display: flex;
}
.tree-component-node-tile .model-view-tree-node-item-icon{
width: 15px;
height: 15px;
.tree-component-node-tile .model-view-tree-node-item-icon {
width: 17px;
height: 17px;
flex-shrink: 0;
}
.tree-component-node-tile .model-view-tree-node-item-label {
overflow: hidden;
text-overflow: ellipsis;
}

View File

@@ -147,6 +147,8 @@ export class TreeComponentRenderer extends Disposable implements IRenderer {
const icon = this.themeService.getTheme().type === LIGHT ? element.icon : element.iconDark;
const iconUri = icon ? URI.revive(icon) : null;
templateData.icon.style.backgroundImage = iconUri ? `url('${iconUri.toString(true)}')` : '';
templateData.icon.style.backgroundRepeat = 'no-repeat';
templateData.icon.style.backgroundPosition = 'center';
dom.toggleClass(templateData.icon, 'model-view-tree-node-item-icon', !!icon);
if (element) {
element.onCheckedChanged = (checked: boolean) => {

View File

@@ -5,7 +5,7 @@
import 'vs/css!./media/serverTreeActions';
import * as errors from 'vs/base/common/errors';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import * as builder from 'sql/base/browser/builder';
import Severity from 'vs/base/common/severity';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -33,6 +33,7 @@ import { SERVER_GROUP_CONFIG, SERVER_GROUP_AUTOEXPAND_CONFIG } from 'sql/parts/o
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { ServerTreeActionProvider } from 'sql/parts/objectExplorer/viewlet/serverTreeActionProvider';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
const $ = builder.$;
@@ -72,6 +73,7 @@ export class ServerTreeView {
this._treeSelectionHandler.onTreeActionStateChange(false);
}
});
this.registerCommands();
}
/**
@@ -96,6 +98,25 @@ export class ServerTreeView {
return this._tree;
}
/**
*
* Register search related commands
*/
public registerCommands(): void {
CommandsRegistry.registerCommand({
id: 'registeredServers.searchServer',
handler: (accessor: ServicesAccessor, ...args: any[]) => {
this.searchTree(args[0]);
}
});
CommandsRegistry.registerCommand({
id: 'registeredServers.clearSearchServerResult',
handler: (accessor: ServicesAccessor, ...args: any[]) => {
this.refreshTree();
}
});
}
/**
* Render the view body
*/

View File

@@ -20,7 +20,7 @@ import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { Registry } from 'vs/platform/registry/common/platform';
import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { Builder } from 'sql/base/browser/builder';
@@ -211,8 +211,13 @@ export class ChartView extends Disposable implements IPanelView {
}
private buildOptions() {
dispose(this.optionDisposables);
this.optionDisposables = [];
// The first element in the disposables list is for the chart type: the master dropdown that controls other option controls.
// whiling rebuilding the options we should not dispose it, otherwise it would react to the theme change event
if (this.optionDisposables.length > 1) {
dispose(this.optionDisposables.slice(1));
this.optionDisposables.splice(1);
}
this.optionMap = {
'type': this.optionMap['type']
};
@@ -284,7 +289,7 @@ export class ChartView extends Disposable implements IPanelView {
};
break;
case ControlType.combo:
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
let dropdown = new SelectBox(option.displayableOptions || option.options, undefined, this._contextViewService);
dropdown.select(option.options.indexOf(value));
dropdown.render(optionContainer);
dropdown.onDidSelect(e => {

View File

@@ -30,8 +30,8 @@ export enum JobActions {
}
export class IJobActionInfo {
ownerUri: string;
targetObject: any;
ownerUri?: string;
targetObject?: any;
component: JobManagementView;
}
@@ -69,10 +69,11 @@ export class NewJobAction extends Action {
super(NewJobAction.ID, NewJobAction.LABEL, 'newStepIcon');
}
public run(context: JobsViewComponent): Promise<boolean> {
public run(context: IJobActionInfo): Promise<boolean> {
let component = context.component as JobsViewComponent;
return new Promise<boolean>(async (resolve, reject) => {
try {
await context.openCreateJobDialog();
await component.openCreateJobDialog();
resolve(true);
} catch (e) {
reject(e);
@@ -293,10 +294,11 @@ export class NewAlertAction extends Action {
super(NewAlertAction.ID, NewAlertAction.LABEL, 'newStepIcon');
}
public run(context: AlertsViewComponent): Promise<boolean> {
public run(context: IJobActionInfo): Promise<boolean> {
let component = context.component as AlertsViewComponent;
return new Promise<boolean>((resolve, reject) => {
try {
context.openCreateAlertDialog();
component.openCreateAlertDialog();
resolve(true);
} catch (e) {
reject(e);
@@ -380,10 +382,11 @@ export class NewOperatorAction extends Action {
super(NewOperatorAction.ID, NewOperatorAction.LABEL, 'newStepIcon');
}
public run(context: OperatorsViewComponent): Promise<boolean> {
public run(context: IJobActionInfo): Promise<boolean> {
let component = context.component as OperatorsViewComponent;
return new Promise<boolean>((resolve, reject) => {
try {
context.openCreateOperatorDialog();
component.openCreateOperatorDialog();
resolve(true);
} catch (e) {
reject(e);
@@ -466,10 +469,11 @@ export class NewProxyAction extends Action {
super(NewProxyAction.ID, NewProxyAction.LABEL, 'newStepIcon');
}
public run(context: ProxiesViewComponent): Promise<boolean> {
public run(context: IJobActionInfo): Promise<boolean> {
let component = context.component as ProxiesViewComponent;
return new Promise<boolean>((resolve, reject) => {
try {
context.openCreateProxyDialog();
component.openCreateProxyDialog();
resolve(true);
} catch (e) {
reject(e);

View File

@@ -13,42 +13,156 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
export class SqlTelemetryContribution extends Disposable implements IWorkbenchContribution {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@ITelemetryService private telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService
) {
super();
const dailyLastUseDate = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
const weeklyLastUseDate = Date.parse(storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0'));
const monthlyLastUseDate = Date.parse(storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0'));
const dailyLastUseDate: number = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
const weeklyLastUseDate: number = Date.parse(storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0'));
const monthlyLastUseDate: number = Date.parse(storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0'));
const firstTimeUser: boolean = dailyLastUseDate === Date.parse('0');
let today = new Date().toUTCString();
let todayString: string = new Date().toUTCString();
// daily user event
if (this.diffInDays(Date.parse(today), dailyLastUseDate) >= 1) {
if (this.didDayChange(dailyLastUseDate)) {
// daily first use
telemetryService.publicLog('telemetry.dailyFirstUse', { dailyFirstUse: true });
storageService.store('telemetry.dailyLastUseDate', today, StorageScope.GLOBAL);
storageService.store('telemetry.dailyLastUseDate', todayString, StorageScope.GLOBAL);
}
// weekly user event
if (this.diffInDays(Date.parse(today), weeklyLastUseDate) >= 7) {
if (this.didWeekChange(weeklyLastUseDate)) {
// weekly first use
telemetryService.publicLog('telemetry.weeklyFirstUse', { weeklyFirstUse: true });
storageService.store('telemetry.weeklyLastUseDate', today, StorageScope.GLOBAL);
storageService.store('telemetry.weeklyLastUseDate', todayString, StorageScope.GLOBAL);
}
// monthly user events
if (this.diffInDays(Date.parse(today), monthlyLastUseDate) >= 30) {
/* send monthly uses once the user launches on a day that's in a month
after the last time we sent a monthly usage count */
const monthlyUseCount: number = storageService.getNumber('telemetry.monthlyUseCount', StorageScope.GLOBAL, 0);
if (this.didMonthChange(monthlyLastUseDate)) {
telemetryService.publicLog('telemetry.monthlyUse', { monthlyFirstUse: true });
storageService.store('telemetry.monthlyLastUseDate', today, StorageScope.GLOBAL);
}
// the month changed, so send the user usage type event based on monthly count for last month
// and reset the count for this month
let lastMonthDate = new Date(monthlyLastUseDate);
this.sendUsageEvent(monthlyUseCount, lastMonthDate);
const wasActiveLastMonth: boolean = storageService.getBoolean('telemetry.wasActiveLastMonth', StorageScope.GLOBAL, false);
if (firstTimeUser) {
// new user
this.sendGrowthTypeEvent(UserGrowthType.NewUser, lastMonthDate);
}
// continuing or returning user
this.sendGrowthTypeEvent(wasActiveLastMonth ? UserGrowthType.ContinuingUser : UserGrowthType.ReturningUser, lastMonthDate);
// set wasActiveUserLastMonth
storageService.store('telemetry.wasActiveLastMonth', true, StorageScope.GLOBAL);
// reset the monthly count for the new month
storageService.store('telemetry.monthlyUseCount', 1, StorageScope.GLOBAL);
storageService.store('telemetry.monthlyLastUseDate', todayString, StorageScope.GLOBAL);
} else {
// if it's the same month, increment the monthly use count
storageService.store('telemetry.monthlyUseCount', monthlyUseCount + 1, StorageScope.GLOBAL);
}
}
private didDayChange(lastUseDateNumber: number): boolean {
let nowDateNumber: number = Date.parse(new Date().toUTCString());
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 1) {
return true;
} else {
let nowDate = new Date(nowDateNumber);
let lastUseDate = new Date(lastUseDateNumber);
return nowDate.getUTCDay() !== lastUseDate.getUTCDay();
}
}
private didWeekChange(lastUseDateNumber: number): boolean {
let nowDateNumber: number = Date.parse(new Date().toUTCString());
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 7) {
return true;
} else {
let nowDate = new Date(nowDateNumber);
let lastUseDate = new Date(lastUseDateNumber);
return nowDate.getUTCDay() < lastUseDate.getUTCDay();
}
}
private didMonthChange(lastUseDateNumber: number): boolean {
let nowDateNumber: number = Date.parse(new Date().toUTCString());
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 30) {
return true;
} else {
let nowDate = new Date(nowDateNumber);
let lastUseDate = new Date(lastUseDateNumber);
return nowDate.getUTCMonth() !== lastUseDate.getUTCMonth();
}
}
private diffInDays(nowDate: number, lastUseDate: number): number {
return (nowDate - lastUseDate) / (24 * 3600 * 1000);
return (nowDate - lastUseDate) / (3600 * 1000 * 24);
}
// Usage Metrics
private sendUsageEvent(monthlyUseCount: number, lastMonthDate: Date): void {
let userUsageType: UserUsageType | undefined;
if (monthlyUseCount === 1) {
userUsageType = UserUsageType.TireKicker;
} else if (monthlyUseCount >= 2 && monthlyUseCount <= 11) {
userUsageType = UserUsageType.Occasional;
} else if (monthlyUseCount >= 12 && monthlyUseCount <= 20) {
userUsageType = UserUsageType.Engaged;
} else if (monthlyUseCount > 20) {
userUsageType = UserUsageType.Dedicated;
}
if (userUsageType) {
this.telemetryService.publicLog('telemetry.userUsage',
{ userType: userUsageType, monthlyUseCount: monthlyUseCount, month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() });
}
}
// Growth Metrics
private sendGrowthTypeEvent(growthType: UserGrowthType, lastMonthDate: Date): void {
this.telemetryService.publicLog('telemetry.userGrowthType', {
userGrowthType: growthType, month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString()
});
}
}
/**
Growth Metrics
Active here means opened app atleast 1 time in a month
*/
export enum UserGrowthType {
// first time opening app
NewUser = 1,
// was active before, wasn't active last month, but is active this month
ReturningUser = 2,
// was active last month and this month
ContinuingUser = 3
}
/**
* Usage Metrics
* TireKicker = 1 day/month
* Occasional = 2-11 days/month
* Engaged = 12-20 days/month
* Dedicated = 20+ days/month
*/
export enum UserUsageType {
/* 1 day per month */
TireKicker = 1,
/* 2-11 days per month */
Occasional = 2,
/* 12-20 days per month */
Engaged = 3,
/* 20+ days per month */
Dedicated = 4
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SqlTelemetryContribution, LifecyclePhase.Starting);

View File

@@ -463,6 +463,7 @@ export interface INotebookKernelDetails {
readonly id: string;
readonly name: string;
readonly supportsIntellisense: boolean;
readonly requiresConnection: boolean;
readonly info?: any;
}

View File

@@ -111,7 +111,8 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
id: kernel.id,
info: kernel.info,
name: kernel.name,
supportsIntellisense: kernel.supportsIntellisense
supportsIntellisense: kernel.supportsIntellisense,
requiresConnection: kernel.requiresConnection
};
return kernelDetails;
}

View File

@@ -169,6 +169,13 @@ export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocume
options.providerId = showOptions.providerId;
options.connectionProfile = showOptions.connectionProfile;
options.defaultKernel = showOptions.defaultKernel;
if (showOptions.initialContent) {
if (typeof (showOptions.initialContent) !== 'string') {
options.initialContent = JSON.stringify(showOptions.initialContent);
} else {
options.initialContent = showOptions.initialContent;
}
}
}
let id = await this._proxy.$tryShowNotebookDocument(uri, options);
let editor = this.getEditor(id);

View File

@@ -16,7 +16,7 @@ import { INotebookService, INotebookProvider, INotebookManager } from 'sql/workb
import { INotebookManagerDetails, INotebookSessionDetails, INotebookKernelDetails, FutureMessageType, INotebookFutureDetails, INotebookFutureDone } from 'sql/workbench/api/common/sqlExtHostTypes';
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
import { Deferred } from 'sql/base/common/promise';
import { FutureInternal } from 'sql/parts/notebook/models/modelInterfaces';
import { FutureInternal } from 'sql/workbench/parts/notebook/models/modelInterfaces';
@extHostNamedCustomer(SqlMainContext.MainThreadNotebook)
export class MainThreadNotebook extends Disposable implements MainThreadNotebookShape {
@@ -352,6 +352,10 @@ class KernelWrapper implements azdata.nb.IKernel {
return this.kernelDetails.supportsIntellisense;
}
get requiresConnection(): boolean {
return this.kernelDetails.requiresConnection;
}
get info(): azdata.nb.IInfoReply {
return this._info;
}

View File

@@ -21,12 +21,12 @@ import {
SqlMainContext, MainThreadNotebookDocumentsAndEditorsShape, SqlExtHostContext, ExtHostNotebookDocumentsAndEditorsShape,
INotebookDocumentsAndEditorsDelta, INotebookEditorAddData, INotebookShowOptions, INotebookModelAddedData, INotebookModelChangedData
} from 'sql/workbench/api/node/sqlExtHost.protocol';
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
import { NotebookInput } from 'sql/workbench/parts/notebook/notebookInput';
import { INotebookService, INotebookEditor, IProviderInfo } from 'sql/workbench/services/notebook/common/notebookService';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { disposed } from 'vs/base/common/errors';
import { ICellModel, NotebookContentChange, INotebookModel } from 'sql/parts/notebook/models/modelInterfaces';
import { NotebookChangeType, CellTypes } from 'sql/parts/notebook/models/contracts';
import { ICellModel, NotebookContentChange, INotebookModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { NotebookChangeType, CellTypes } from 'sql/workbench/parts/notebook/models/contracts';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { notebookModeId } from 'sql/common/constants';
@@ -399,8 +399,8 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
};
let isUntitled: boolean = uri.scheme === Schemas.untitled;
const fileInput = isUntitled ? this._untitledEditorService.createOrGet(uri, notebookModeId) :
this._editorService.createInput({ resource: uri, language: notebookModeId });
const fileInput = isUntitled ? this._untitledEditorService.createOrGet(uri, notebookModeId, options.initialContent) :
this._editorService.createInput({ resource: uri, language: notebookModeId });
let input = this._instantiationService.createInstance(NotebookInput, path.basename(uri.fsPath), uri, fileInput);
input.isTrusted = isUntitled;
input.defaultKernel = options.defaultKernel;

View File

@@ -854,6 +854,7 @@ export interface INotebookShowOptions {
providerId?: string;
connectionProfile?: azdata.IConnectionProfile;
defaultKernel?: azdata.nb.IKernelSpec;
initialContent?: string;
}
export interface ExtHostNotebookDocumentsAndEditorsShape {

View File

@@ -32,10 +32,8 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet {
private _root: HTMLElement;
private _searchBox: InputBox;
private _toDisposeViewlet: IDisposable[] = [];
private _serverTreeView: ServerTreeView;
private _clearSearchAction: ClearSearchAction;
private _addServerAction: IAction;
private _addServerGroupAction: IAction;
private _activeConnectionsFilterAction: ActiveConnectionsFilterAction;
@@ -53,7 +51,6 @@ export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet {
super(VIEWLET_ID, configurationService, layoutService, telemetryService, _themeService, storageService);
this._clearSearchAction = this._instantiationService.createInstance(ClearSearchAction, ClearSearchAction.ID, ClearSearchAction.LABEL, this);
this._addServerAction = this._instantiationService.createInstance(AddServerAction,
AddServerAction.ID,
AddServerAction.LABEL);
@@ -79,24 +76,6 @@ export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet {
super.create(parent);
this._root = parent;
const viewletContainer = DOM.append(parent, DOM.$('div.server-explorer-viewlet'));
const searchBoxContainer = DOM.append(viewletContainer, DOM.$('div.search-box'));
this._searchBox = new InputBox(
searchBoxContainer,
null,
{
placeholder: localize('Search server names', 'Search server names'),
actions: [this._clearSearchAction],
ariaLabel: localize('Search server names', 'Search server names')
}
);
this._searchBox.onDidChange(() => {
this.search(this._searchBox.value);
});
// Theme styler
this._toDisposeViewlet.push(attachInputBoxStyler(this._searchBox, this._themeService));
const viewContainer = DOM.append(viewletContainer, DOM.$('div.object-explorer-view'));
this._serverTreeView.renderBody(viewContainer).then(undefined, error => {
warn('render registered servers: ' + error);
@@ -105,7 +84,6 @@ export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet {
public search(value: string): void {
if (value) {
this._clearSearchAction.enabled = true;
this._serverTreeView.searchTree(value);
} else {
this.clearSearch();
@@ -129,8 +107,7 @@ export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet {
}
public layout({ height, width }: DOM.Dimension): void {
this._searchBox.layout();
this._serverTreeView.layout(height - 36); // account for search box
this._serverTreeView.layout(height);
DOM.toggleClass(this._root, 'narrow', width <= 350);
}
@@ -140,9 +117,6 @@ export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet {
public clearSearch() {
this._serverTreeView.refreshTree();
this._searchBox.value = '';
this._clearSearchAction.enabled = false;
this._searchBox.focus();
}
public dispose(): void {

View File

@@ -29,7 +29,7 @@
}
.server-explorer-viewlet .object-explorer-view {
height: calc(100% - 36px);
height: 100%;
}
.server-explorer-viewlet .server-group {

View File

@@ -31,10 +31,8 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
export class ConnectionViewletPanel extends ViewletPanel {
private _root: HTMLElement;
private _searchBox: InputBox;
private _toDisposeViewlet: IDisposable[] = [];
private _serverTreeView: ServerTreeView;
private _clearSearchAction: ClearSearchAction;
private _addServerAction: IAction;
private _addServerGroupAction: IAction;
private _activeConnectionsFilterAction: ActiveConnectionsFilterAction;
@@ -54,7 +52,7 @@ export class ConnectionViewletPanel extends ViewletPanel {
@IObjectExplorerService private objectExplorerService: IObjectExplorerService
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
this._clearSearchAction = this.instantiationService.createInstance(ClearSearchAction, ClearSearchAction.ID, ClearSearchAction.LABEL, this);
//this._clearSearchAction = this.instantiationService.createInstance(ClearSearchAction, ClearSearchAction.ID, ClearSearchAction.LABEL, this);
this._addServerAction = this.instantiationService.createInstance(AddServerAction,
AddServerAction.ID,
AddServerAction.LABEL);
@@ -77,24 +75,6 @@ export class ConnectionViewletPanel extends ViewletPanel {
renderBody(container: HTMLElement): void {
const viewletContainer = DOM.append(container, DOM.$('div.server-explorer-viewlet'));
const searchBoxContainer = DOM.append(viewletContainer, DOM.$('div.search-box'));
this._searchBox = new InputBox(
searchBoxContainer,
null,
{
placeholder: localize('Search server names', 'Search server names'),
actions: [this._clearSearchAction],
ariaLabel: localize('Search server names', 'Search server names')
}
);
this._searchBox.onDidChange(() => {
this.search(this._searchBox.value);
});
// Theme styler
this._toDisposeViewlet.push(attachInputBoxStyler(this._searchBox, this.themeService));
const viewContainer = DOM.append(viewletContainer, DOM.$('div.object-explorer-view'));
this._serverTreeView.renderBody(viewContainer).then(undefined, error => {
console.warn('render registered servers: ' + error);
@@ -103,8 +83,7 @@ export class ConnectionViewletPanel extends ViewletPanel {
}
layoutBody(size: number): void {
this._searchBox.layout();
this._serverTreeView.layout(size - 46); // account for search box and horizontal scroll bar
this._serverTreeView.layout(size);
DOM.toggleClass(this._root, 'narrow', this._root.clientWidth < 300);
}
@@ -140,14 +119,10 @@ export class ConnectionViewletPanel extends ViewletPanel {
public clearSearch() {
this._serverTreeView.refreshTree();
this._searchBox.value = '';
this._clearSearchAction.enabled = false;
this._searchBox.focus();
}
public search(value: string): void {
if (value) {
this._clearSearchAction.enabled = true;
this._serverTreeView.searchTree(value);
} else {
this.clearSearch();

View File

@@ -22,7 +22,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
// Viewlet Action
export class OpenDataExplorerViewletAction extends ShowViewletAction {
public static ID = VIEWLET_ID;
public static LABEL = localize('showDataExplorer', "Show Data Explorer");
public static LABEL = localize('showDataExplorer', "Show Connections");
constructor(
id: string,
@@ -39,7 +39,7 @@ export class OpenDataExplorerViewletAction extends ShowViewletAction {
const viewletDescriptor = new ViewletDescriptor(
DataExplorerViewlet,
VIEWLET_ID,
localize('workbench.dataExplorer', "Data Explorer"),
localize('workbench.dataExplorer', "Connections"),
'dataExplorer',
0
);

View File

@@ -19,14 +19,9 @@ import { viewsContainersExtensionPoint } from 'vs/workbench/api/browser/viewsExt
import { CustomTreeView } from 'sql/workbench/browser/parts/views/customView';
export const DataExplorerViewlet = {
DataExplorer: 'dataExplorer'
};
export const VIEWLET_ID = 'workbench.view.dataExplorer';
export const VIEWLET_ID = 'workbench.view.connections';
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID);
interface IUserFriendlyViewDescriptor {
id: string;
name: string;
@@ -156,7 +151,7 @@ class DataExplorerContainerExtensionHandler implements IWorkbenchContribution {
}
private showCollapsed(container: ViewContainer): boolean {
return false;
return true;
}
}

View File

@@ -10,7 +10,7 @@ import { DISCONNECT_COMMAND_ID, MANAGE_COMMAND_ID, NEW_QUERY_COMMAND_ID, REFRESH
MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
group: 'connection',
order: 4,
order: 3,
command: {
id: DISCONNECT_COMMAND_ID,
title: localize('disconnect', 'Disconnect')
@@ -20,6 +20,7 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
group: 'connection',
order: 2,
command: {
id: NEW_QUERY_COMMAND_ID,
title: localize('newQuery', 'New Query')
@@ -29,7 +30,7 @@ MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
MenuRegistry.appendMenuItem(MenuId.DataExplorerContext, {
group: 'connection',
order: 4,
order: 1,
command: {
id: MANAGE_COMMAND_ID,
title: localize('manage', 'Manage')

View File

@@ -12,12 +12,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import * as DOM from 'vs/base/browser/dom';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { CellContext, CellActionBase } from 'sql/parts/notebook/cellViews/codeActions';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { CellContext, CellActionBase } from 'sql/workbench/parts/notebook/cellViews/codeActions';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { ToggleMoreWidgetAction } from 'sql/parts/dashboard/common/actions';
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
import { CellModel } from 'sql/parts/notebook/models/cell';
import { CellTypes, CellType } from 'sql/workbench/parts/notebook/models/contracts';
import { CellModel } from 'sql/workbench/parts/notebook/models/cell';
export const HIDDEN_CLASS ='actionhidden';
@@ -31,8 +31,8 @@ export class CellToggleMoreActions {
instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', 'Delete')),
instantiationService.createInstance(AddCellFromContextAction,'codeBefore', localize('codeBefore', 'Insert Code before'), CellTypes.Code, false),
instantiationService.createInstance(AddCellFromContextAction, 'codeAfter', localize('codeAfter', 'Insert Code after'), CellTypes.Code, true),
instantiationService.createInstance(AddCellFromContextAction, 'markdownBefore', localize('markdownBefore', 'Insert Markdown before'), CellTypes.Markdown, false),
instantiationService.createInstance(AddCellFromContextAction, 'markdownAfter', localize('markdownAfter', 'Insert Markdown after'), CellTypes.Markdown, true),
instantiationService.createInstance(AddCellFromContextAction, 'markdownBefore', localize('markdownBefore', 'Insert Text before'), CellTypes.Markdown, false),
instantiationService.createInstance(AddCellFromContextAction, 'markdownAfter', localize('markdownAfter', 'Insert Text after'), CellTypes.Markdown, true),
instantiationService.createInstance(ClearCellOutputAction, 'clear', localize('clear', 'Clear output'))
);
}

View File

@@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./code';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, Output, EventEmitter, OnChanges, SimpleChange } from '@angular/core';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, Output, EventEmitter, OnChanges, SimpleChange, forwardRef, ChangeDetectorRef } from '@angular/core';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor';
import { CellToggleMoreActions } from 'sql/parts/notebook/cellToggleMoreActions';
import { ICellModel, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
import { CellToggleMoreActions } from 'sql/workbench/parts/notebook/cellToggleMoreActions';
import { ICellModel, notebookConstants } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { RunCellAction, CellContext } from 'sql/parts/notebook/cellViews/codeActions';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { RunCellAction, CellContext } from 'sql/workbench/parts/notebook/cellViews/codeActions';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as themeColors from 'vs/workbench/common/theme';
@@ -28,9 +28,9 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Event, Emitter } from 'vs/base/common/event';
import { CellTypes } from 'sql/parts/notebook/models/contracts';
import { CellTypes } from 'sql/workbench/parts/notebook/models/contracts';
import { OVERRIDE_EDITOR_THEMING_SETTING } from 'sql/workbench/services/notebook/common/notebookService';
import * as notebookUtils from 'sql/parts/notebook/notebookUtils';
import * as notebookUtils from 'sql/workbench/parts/notebook/notebookUtils';
import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
@@ -103,7 +103,8 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IConfigurationService) private _configurationService: IConfigurationService
@Inject(IConfigurationService) private _configurationService: IConfigurationService,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef
) {
super();
this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions);
@@ -161,7 +162,14 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
&& this.cellModel.cellUri;
}
private get destroyed(): boolean{
return !!(this._changeRef['destroyed']);
}
ngAfterContentInit(): void {
if (this.destroyed) {
return;
}
this.createEditor();
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
this._layoutEmitter.fire();
@@ -199,6 +207,14 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
let overrideEditorSetting = this._configurationService.getValue<boolean>(OVERRIDE_EDITOR_THEMING_SETTING);
this._editor.hideLineNumbers = (overrideEditorSetting && this.cellModel.cellType === CellTypes.Markdown);
if (this.destroyed) {
// At this point, we may have been disposed (scenario: restoring markdown cell in preview mode).
// Exiting early to avoid warnings on registering already disposed items, which causes some churning
// due to re-disposing things.
// There's no negative impact as at this point the component isn't visible (it was removed from the DOM)
return;
}
this._register(this._editor);
this._register(this._editorInput);
this._register(this._editorModel.onDidChangeContent(e => {

View File

@@ -11,11 +11,11 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import * as types from 'vs/base/common/types';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { getErrorMessage } from 'sql/parts/notebook/notebookUtils';
import { ICellModel, CellExecutionState } from 'sql/parts/notebook/models/modelInterfaces';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { getErrorMessage } from 'sql/workbench/parts/notebook/notebookUtils';
import { ICellModel, CellExecutionState } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { MultiStateAction, IMultiStateData, IActionStateData } from 'sql/parts/notebook/notebookActions';
import { MultiStateAction, IMultiStateData, IActionStateData } from 'sql/workbench/parts/notebook/notebookActions';
let notebookMoreActionMsg = localize('notebook.failed', "Please select active cell and try again");
const emptyExecutionCountLabel = '[ ]';

View File

@@ -9,7 +9,7 @@
<code-component [cellModel]="cellModel" [model]="model" [activeCellId]="activeCellId"></code-component>
</div>
<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 *ngIf="cellModel.outputs && cellModel.outputs.length > 0" [cellModel]="cellModel" [activeCellId]="activeCellId">
</output-area-component>
</div>
</div>

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, SimpleChange, OnChanges, AfterViewInit } from '@angular/core';
import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { CellView } from 'sql/workbench/parts/notebook/cellViews/interfaces';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
export const CODE_SELECTOR: string = 'code-cell-component';

View File

@@ -5,7 +5,7 @@
*--------------------------------------------------------------------------------------------*/
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
<div style="flex: 0 0 auto; user-select: initial;">
<div #output ></div>
<div style="flex: 0 0 auto; user-select: none;">
<div #output class="output-userselect" ></div>
</div>
</div>

View File

@@ -3,19 +3,21 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./code';
import 'vs/css!./media/output';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild } from '@angular/core';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, SimpleChange } from '@angular/core';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { nb } from 'azdata';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
import { MimeModel } from 'sql/parts/notebook/outputs/common/mimemodel';
import * as outputProcessor from 'sql/parts/notebook/outputs/common/outputProcessor';
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
import 'vs/css!sql/parts/notebook/outputs/style/index';
import { MimeModel } from 'sql/workbench/parts/notebook/outputs/common/mimemodel';
import * as outputProcessor from 'sql/workbench/parts/notebook/outputs/common/outputProcessor';
import { RenderMimeRegistry } from 'sql/workbench/parts/notebook/outputs/registry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import * as DOM from 'vs/base/browser/dom';
export const OUTPUT_SELECTOR: string = 'output-component';
const USER_SELECT_CLASS ='actionselect';
@Component({
selector: OUTPUT_SELECTOR,
@@ -28,6 +30,7 @@ export class OutputComponent extends AngularDisposable implements OnInit {
private _trusted: boolean;
private _initialized: boolean = false;
private readonly _minimumHeight = 30;
private _activeCellId: string;
registry: RenderMimeRegistry;
@@ -47,6 +50,27 @@ export class OutputComponent extends AngularDisposable implements OnInit {
});
}
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
for (let propName in changes) {
if (propName === 'activeCellId') {
this.toggleUserSelect(this.isActive());
break;
}
}
}
private toggleUserSelect(userSelect: boolean): void {
if (!this.outputElement) {
return;
}
if (userSelect) {
DOM.addClass(this.outputElement.nativeElement, USER_SELECT_CLASS);
} else {
DOM.removeClass(this.outputElement.nativeElement, USER_SELECT_CLASS);
}
}
private renderOutput() {
let node = this.outputElement.nativeElement;
let output = this.cellOutput;
@@ -63,14 +87,21 @@ export class OutputComponent extends AngularDisposable implements OnInit {
return this._trusted;
}
@Input()
set trustedMode(value: boolean) {
@Input() set trustedMode(value: boolean) {
this._trusted = value;
if (this._initialized) {
this.renderOutput();
}
}
@Input() set activeCellId(value: string) {
this._activeCellId = value;
}
get activeCellId(): string {
return this._activeCellId;
}
protected createRenderedMimetype(options: MimeModel.IOptions, node: HTMLElement): void {
let mimeType = this.registry.preferredMimeType(
options.data,
@@ -100,4 +131,7 @@ export class OutputComponent extends AngularDisposable implements OnInit {
//this.setState({ node: node });
}
}
protected isActive() {
return this.cellModel && this.cellModel.id === this.activeCellId;
}
}

View File

@@ -6,7 +6,7 @@
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
<div #outputarea class="notebook-output" style="flex: 0 0 auto;">
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel">
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel" [activeCellId]="activeCellId">
</output-component>
</div>
</div>

View File

@@ -6,7 +6,7 @@ import 'vs/css!./code';
import 'vs/css!./outputArea';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, forwardRef, ChangeDetectorRef } from '@angular/core';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import * as themeColors from 'vs/workbench/common/theme';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
@@ -20,6 +20,8 @@ export class OutputAreaComponent extends AngularDisposable implements OnInit {
@ViewChild('outputarea', { read: ElementRef }) private outputArea: ElementRef;
@Input() cellModel: ICellModel;
private _activeCellId: string;
private readonly _minimumHeight = 30;
constructor(
@@ -41,6 +43,14 @@ export class OutputAreaComponent extends AngularDisposable implements OnInit {
}
}
@Input() set activeCellId(value: string) {
this._activeCellId = value;
}
get activeCellId(): string {
return this._activeCellId;
}
private updateTheme(theme: IColorTheme): void {
let outputElement = <HTMLElement>this.outputArea.nativeElement;
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();

View File

@@ -10,4 +10,13 @@ output-area-component .notebook-output {
border-top-width: 0px;
user-select: text;
padding: 5px 20px 0px;
}
.output-userselect.actionselect {
user-select: text;
}
.output-userselect pre{
white-space: pre-wrap;
word-wrap: break-word;
}

View File

@@ -5,11 +5,11 @@
import 'vs/css!./placeholder';
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, SimpleChange, OnChanges } from '@angular/core';
import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { CellView } from 'sql/workbench/parts/notebook/cellViews/interfaces';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { localize } from 'vs/nls';
import { CellType } from 'sql/parts/notebook/models/contracts';
import { CellType } from 'sql/workbench/parts/notebook/models/contracts';
export const PLACEHOLDER_SELECTOR: string = 'placeholder-cell-component';

View File

@@ -11,7 +11,7 @@
</code-component>
</div>
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
<div #preview class ="notebook-preview" style="flex: 1 1 auto; user-select: initial;" (dblclick)="toggleEditMode()">
<div #preview class ="notebook-preview" style="flex: 1 1 auto" (dblclick)="toggleEditMode()">
</div>
<div #moreactions class="moreActions" style="flex: 0 0 auto; display: flex; flex-flow:column;width: 20px; min-height: 20px; max-height: 20px; padding-top: 0px; orientation: portrait">
</div>

View File

@@ -5,6 +5,7 @@
import 'vs/css!./textCell';
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, OnChanges, SimpleChange } from '@angular/core';
import * as path from 'path';
import { localize } from 'vs/nls';
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
@@ -14,15 +15,17 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import * as DOM from 'vs/base/browser/dom';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { ISanitizer, defaultSanitizer } from 'sql/parts/notebook/outputs/sanitizer';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { CellToggleMoreActions } from 'sql/parts/notebook/cellToggleMoreActions';
import { CellView } from 'sql/workbench/parts/notebook/cellViews/interfaces';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { ISanitizer, defaultSanitizer } from 'sql/workbench/parts/notebook/outputs/sanitizer';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { CellToggleMoreActions } from 'sql/workbench/parts/notebook/cellToggleMoreActions';
export const TEXT_SELECTOR: string = 'text-cell-component';
const USER_SELECT_CLASS ='actionselect';
@Component({
selector: TEXT_SELECTOR,
@@ -111,6 +114,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
if (propName === 'activeCellId') {
let changedProp = changes[propName];
this._activeCellId = changedProp.currentValue;
this.toggleUserSelect(this.isActive());
// If the activeCellId is undefined (i.e. in an active cell update), don't unnecessarily set editMode to false;
// it will be set to true in a subsequent call to toggleEditMode()
if (changedProp.previousValue !== undefined) {
@@ -133,8 +137,9 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
} else {
this._content = this.sanitizeContent(this.cellModel.source);
}
// todo: pass in the notebook filename instead of undefined value
this._commandService.executeCommand<string>('notebook.showPreview', undefined, this._content).then((htmlcontent) => {
this._commandService.executeCommand<string>('notebook.showPreview', this.cellModel.notebookModel.notebookUri, this._content).then((htmlcontent) => {
htmlcontent = this.convertVscodeResourceToFileInSubDirectories(htmlcontent);
let outputElement = <HTMLElement>this.output.nativeElement;
outputElement.innerHTML = htmlcontent;
});
@@ -149,6 +154,24 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
return content;
}
// Only replace vscode-resource with file when in the same (or a sub) directory
// This matches Jupyter Notebook viewer behavior
private convertVscodeResourceToFileInSubDirectories(htmlContent: string): string {
let htmlContentCopy = htmlContent;
while (htmlContentCopy.search('(?<=img src=\"vscode-resource:)') > 0) {
let pathStartIndex = htmlContentCopy.search('(?<=img src=\"vscode-resource:)');
let pathEndIndex = htmlContentCopy.indexOf('\" ', pathStartIndex);
let filePath = htmlContentCopy.substring(pathStartIndex, pathEndIndex);
// If the asset is in the same folder or a subfolder, replace 'vscode-resource:' with 'file:', so the image is visible
if (!path.relative(path.dirname(this.cellModel.notebookModel.notebookUri.fsPath), filePath).includes('..')) {
// ok to change from vscode-resource: to file:
htmlContent = htmlContent.replace('vscode-resource:'+ filePath, 'file:' + filePath);
}
htmlContentCopy = htmlContentCopy.slice(pathEndIndex);
}
return htmlContent;
}
// Todo: implement layout
public layout() {
@@ -182,6 +205,17 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
}
}
private toggleUserSelect(userSelect: boolean): void {
if (!this.output) {
return;
}
if (userSelect) {
DOM.addClass(this.output.nativeElement, USER_SELECT_CLASS);
} else {
DOM.removeClass(this.output.nativeElement, USER_SELECT_CLASS);
}
}
private setFocusAndScroll(): void {
this.toggleEditMode(this.isActive());

View File

@@ -8,7 +8,11 @@ text-cell-component {
}
text-cell-component .notebook-preview {
user-select: initial;
user-select: none;
padding-left: 8px;
padding-right: 8px;
}
.notebook-preview.actionselect {
user-select: text;
}

View File

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 285 B

After

Width:  |  Height:  |  Size: 285 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 549 B

After

Width:  |  Height:  |  Size: 549 B

View File

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 818 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 276 B

View File

Before

Width:  |  Height:  |  Size: 317 B

After

Width:  |  Height:  |  Size: 317 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

View File

Before

Width:  |  Height:  |  Size: 261 B

After

Width:  |  Height:  |  Size: 261 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -13,9 +13,9 @@ import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import * as notebookUtils from '../notebookUtils';
import { CellTypes, CellType, NotebookChangeType } from 'sql/parts/notebook/models/contracts';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { CellTypes, CellType, NotebookChangeType } from 'sql/workbench/parts/notebook/models/contracts';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { ICellModelOptions, IModelFactory, FutureInternal, CellExecutionState } from './modelInterfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
@@ -210,6 +210,10 @@ export class CellModel implements ICellModel {
await kernel.interrupt();
} else {
// TODO update source based on editor component contents
if (kernel.requiresConnection && !this.notebookModel.activeConnection) {
this.sendNotification(notificationService, Severity.Error, localize('kernelRequiresConnection', "Please select a connection to run cells for this kernel"));
return false;
}
let content = this.source;
if (content) {
let future = await kernel.requestExecute({
@@ -386,16 +390,8 @@ export class CellModel implements ICellModel {
let endpoint = this.getKnoxEndpoint(model.activeConnection);
let host = endpoint && endpoint.ipAddress ? endpoint.ipAddress : model.activeConnection.serverName;
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;
});
html =this.rewriteUrlUsingRegex(/(https?:\/\/mssql-master.*\/proxy)(.*)/g, html, host);
html =this.rewriteUrlUsingRegex(/(https?:\/\/master.*master-svc.*\/proxy)(.*)/g, html, host);
(<nb.IDisplayResult>output).data['text/html'] = html;
}
}
@@ -405,6 +401,19 @@ export class CellModel implements ICellModel {
return output;
}
private rewriteUrlUsingRegex(regex: RegExp, html: string, host: string): string {
return html.replace(regex, function (a, b, c) {
let ret = '';
if (b !== '') {
ret = 'https://' + host + ':30443/gateway/default/yarn/proxy';
}
if (c !== '') {
ret = ret + c;
}
return ret;
});
}
private getDisplayId(msg: nb.IIOPubMessage): string | undefined {
let transient = (msg.content.transient || {});
return transient['display_id'] as string;
@@ -420,7 +429,7 @@ export class CellModel implements ICellModel {
if (this._cellType === CellTypes.Code) {
cellJson.metadata.language = this._language,
cellJson.outputs = this._outputs;
cellJson.execution_count = this.executionCount;
cellJson.execution_count = this.executionCount ? this.executionCount : 0;
}
return cellJson as nb.ICellContents;
}
@@ -481,7 +490,10 @@ export class CellModel implements ICellModel {
if (serverInfo && serverInfo.options && serverInfo.options['clusterEndpoints']) {
let endpoints: notebookUtils.IEndpoint[] = serverInfo.options['clusterEndpoints'];
if (endpoints && endpoints.length > 0) {
endpoint = endpoints.find(ep => ep.serviceName.toLowerCase() === 'knox');
endpoint = endpoints.find(ep => {
let serviceName: string = ep.serviceName.toLowerCase();
return serviceName === 'knox' || serviceName === 'gateway';
});
}
}
}

View File

@@ -6,7 +6,7 @@
'use strict';
import { ICellMagicMapper, ILanguageMagic } from 'sql/parts/notebook/models/modelInterfaces';
import { ICellMagicMapper, ILanguageMagic } from 'sql/workbench/parts/notebook/models/modelInterfaces';
const defaultKernel = '*';
export class CellMagicMapper implements ICellMagicMapper {

View File

@@ -14,16 +14,16 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { CellType, NotebookChangeType } from 'sql/parts/notebook/models/contracts';
import { CellType, NotebookChangeType } from 'sql/workbench/parts/notebook/models/contracts';
import { INotebookManager } from 'sql/workbench/services/notebook/common/notebookService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
import { IStandardKernelWithProvider } from 'sql/workbench/parts/notebook/notebookUtils';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { localize } from 'vs/nls';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
export interface IClientSessionOptions {
notebookUri: URI;

View File

@@ -8,7 +8,7 @@
import { nb } from 'azdata';
import { localize } from 'vs/nls';
import { IDefaultConnection, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
import { IDefaultConnection, notebookConstants } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';

View File

@@ -12,11 +12,11 @@ import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IClientSession, INotebookModel, IDefaultConnection, INotebookModelOptions, ICellModel, NotebookContentChange, notebookConstants } from './modelInterfaces';
import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contracts';
import { NotebookChangeType, CellType } from 'sql/workbench/parts/notebook/models/contracts';
import { nbversion } from '../notebookConstants';
import * as notebookUtils from '../notebookUtils';
import { INotebookManager, SQL_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
import { NotebookContexts } from 'sql/parts/notebook/models/notebookContexts';
import { NotebookContexts } from 'sql/workbench/parts/notebook/models/notebookContexts';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { INotification, Severity } from 'vs/platform/notification/common/notification';
import { URI } from 'vs/base/common/uri';

View File

@@ -22,26 +22,26 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange } from 'sql/parts/notebook/models/modelInterfaces';
import { CellTypes, CellType } from 'sql/workbench/parts/notebook/models/contracts';
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange } from 'sql/workbench/parts/notebook/models/modelInterfaces';
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 { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
import * as notebookUtils from 'sql/parts/notebook/notebookUtils';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { ModelFactory } from 'sql/workbench/parts/notebook/models/modelFactory';
import * as notebookUtils from 'sql/workbench/parts/notebook/notebookUtils';
import { Deferred } from 'sql/base/common/promise';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction, RunAllCellsAction, ClearAllOutputsAction } from 'sql/parts/notebook/notebookActions';
import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction, RunAllCellsAction, ClearAllOutputsAction } from 'sql/workbench/parts/notebook/notebookActions';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { CellMagicMapper } from 'sql/parts/notebook/models/cellMagicMapper';
import { CellMagicMapper } from 'sql/workbench/parts/notebook/models/cellMagicMapper';
import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { CellModel } from 'sql/parts/notebook/models/cell';
import { CellModel } from 'sql/workbench/parts/notebook/models/cell';
export const NOTEBOOK_SELECTOR: string = 'notebook-component';

View File

@@ -6,8 +6,8 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
import { NotebookEditor } from 'sql/parts/notebook/notebookEditor';
import { NotebookInput } from 'sql/workbench/parts/notebook/notebookInput';
import { NotebookEditor } from 'sql/workbench/parts/notebook/notebookEditor';
// Model View editor registration
const viewModelEditorDescriptor = new EditorDescriptor(

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