mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 01:25:36 -05:00
- Added `runCell` API. Updated runCell button to listen to events on the model so it'll reflect run cell when called from other sources
- Plumbed through kernelspec info to the extension side so when changed, it's updated
- Fixed bug in ConnectionProfile where it didn't copy from options but instead overrode with empty wrapper functions
Here's the rough test code (it's in the sql-vnext extension and will be out in a separate PR)
```ts
it('Should connect to local notebook server with result 2', async function() {
this.timeout(60000);
let pythonNotebook = Object.assign({}, expectedNotebookContent, { metadata: { kernelspec: { name: "python3", display_name: "Python 3" }}});
let uri = writeNotebookToFile(pythonNotebook);
await ensureJupyterInstalled();
let notebook = await sqlops.nb.showNotebookDocument(uri);
should(notebook.document.cells).have.length(1);
let ran = await notebook.runCell(notebook.document.cells[0]);
should(ran).be.true('Notebook runCell failed');
let cellOutputs = notebook.document.cells[0].contents.outputs;
should(cellOutputs).have.length(1);
let result = (<sqlops.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 connectionId = await connectToSparkIntegrationServer();
// When I open a Spark notebook and run the cell
let notebook = await sqlops.nb.showNotebookDocument(uri, {
connectionId: connectionId
});
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 = (<sqlops.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) {}
});
});
```
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
'use strict';
|
|
|
|
import * as sqlops from 'sqlops';
|
|
|
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
import URI from 'vs/base/common/uri';
|
|
import { ok } from 'vs/base/common/assert';
|
|
import { Schemas } from 'vs/base/common/network';
|
|
import { TPromise } from 'vs/base/common/winjs.base';
|
|
|
|
import { MainThreadNotebookDocumentsAndEditorsShape, INotebookModelChangedData } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
|
import { CellRange } from 'sql/workbench/api/common/sqlExtHostTypes';
|
|
|
|
|
|
export class ExtHostNotebookDocumentData implements IDisposable {
|
|
private _document: sqlops.nb.NotebookDocument;
|
|
private _isDisposed: boolean = false;
|
|
private _kernelSpec: sqlops.nb.IKernelSpec;
|
|
|
|
constructor(private readonly _proxy: MainThreadNotebookDocumentsAndEditorsShape,
|
|
private readonly _uri: URI,
|
|
private _providerId: string,
|
|
private _isDirty: boolean,
|
|
private _cells: sqlops.nb.NotebookCell[]
|
|
) {
|
|
}
|
|
|
|
dispose(): void {
|
|
// we don't really dispose documents but let
|
|
// extensions still read from them. some
|
|
// operations, live saving, will now error tho
|
|
ok(!this._isDisposed);
|
|
this._isDisposed = true;
|
|
this._isDirty = false;
|
|
}
|
|
|
|
|
|
get document(): sqlops.nb.NotebookDocument {
|
|
if (!this._document) {
|
|
const data = this;
|
|
this._document = {
|
|
get uri() { return data._uri; },
|
|
get fileName() { return data._uri.fsPath; },
|
|
get isUntitled() { return data._uri.scheme === Schemas.untitled; },
|
|
get providerId() { return data._providerId; },
|
|
get isClosed() { return data._isDisposed; },
|
|
get isDirty() { return data._isDirty; },
|
|
get cells() { return data._cells; },
|
|
get kernelSpec() { return data._kernelSpec; },
|
|
save() { return data._save(); },
|
|
validateCellRange(range) { return data._validateRange(range); },
|
|
};
|
|
}
|
|
return Object.freeze(this._document);
|
|
}
|
|
|
|
private _save(): Thenable<boolean> {
|
|
if (this._isDisposed) {
|
|
return TPromise.wrapError<boolean>(new Error('Document has been closed'));
|
|
}
|
|
return this._proxy.$trySaveDocument(this._uri);
|
|
|
|
}
|
|
|
|
public onModelChanged(data: INotebookModelChangedData) {
|
|
if (data) {
|
|
this._isDirty = data.isDirty;
|
|
this._cells = data.cells;
|
|
this._providerId = data.providerId;
|
|
this._kernelSpec = data.kernelSpec;
|
|
}
|
|
}
|
|
|
|
// ---- range math
|
|
|
|
private _validateRange(range: sqlops.nb.CellRange): sqlops.nb.CellRange {
|
|
if (!(range instanceof CellRange)) {
|
|
throw new Error('Invalid argument');
|
|
}
|
|
|
|
let start = this._validateIndex(range.start);
|
|
let end = this._validateIndex(range.end);
|
|
|
|
if (start === range.start && end === range.end) {
|
|
return range;
|
|
}
|
|
return new CellRange(start, end);
|
|
}
|
|
|
|
private _validateIndex(index: number): number {
|
|
if (typeof(index) !== 'number') {
|
|
throw new Error('Invalid argument');
|
|
}
|
|
|
|
if (index < 0) {
|
|
index = 0;
|
|
} else if (this._cells.length > 0 && index > this._cells.length) {
|
|
// We allow off by 1 as end needs to be outside current length in order to
|
|
// handle replace scenario. Long term should consider different start vs end validation instead
|
|
index = this._cells.length;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
}
|