mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 09:35:37 -05:00
Remove API compatibility layer for VS Code notebook extensions (#21225)
This commit is contained in:
@@ -15,13 +15,9 @@ import { LocalContentManager } from 'sql/workbench/services/notebook/common/loca
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import type { FutureInternal } from 'sql/workbench/services/notebook/browser/interfaces';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { INotebookProviderRegistry, NotebookProviderRegistryId } from 'sql/workbench/services/notebook/common/notebookRegistry';
|
||||
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
import { SqlExtHostContext, SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
const notebookRegistry = Registry.as<INotebookProviderRegistry>(NotebookProviderRegistryId);
|
||||
|
||||
@extHostNamedCustomer(SqlMainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebook extends Disposable implements MainThreadNotebookShape {
|
||||
|
||||
@@ -101,14 +97,6 @@ export class MainThreadNotebook extends Disposable implements MainThreadNotebook
|
||||
future.onDone(done);
|
||||
}
|
||||
}
|
||||
|
||||
public $updateProviderKernels(providerId: string, languages: azdata.nb.IStandardKernel[]): void {
|
||||
notebookRegistry.updateProviderKernels(providerId, languages);
|
||||
}
|
||||
|
||||
public $updateKernelLanguages(providerId: string, kernelName: string, languages: string[]): void {
|
||||
notebookRegistry.updateKernelLanguages(providerId, kernelName, languages);
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -345,14 +345,6 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a new notebook with the specified provider and contents. Used for VS Code extension compatibility.
|
||||
*/
|
||||
async $tryCreateNotebookDocument(providerId: string, contents?: azdata.nb.INotebookContents): Promise<UriComponents> {
|
||||
let input = await this._notebookService.createNotebookInputFromContents(providerId, contents);
|
||||
return input.resource;
|
||||
}
|
||||
|
||||
$tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): Promise<string> {
|
||||
// Append a numbered suffix if an untitled notebook is already open with the same path
|
||||
if (resource.scheme === Schemas.untitled) {
|
||||
|
||||
@@ -13,11 +13,6 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
import { ExtHostNotebookShape, MainThreadNotebookShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
|
||||
import { IExecuteManagerDetails, INotebookSessionDetails, INotebookKernelDetails, INotebookFutureDetails, FutureMessageType, ISerializationManagerDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { VSCodeSerializationProvider } from 'sql/workbench/api/common/notebooks/vscodeSerializationProvider';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ADSNotebookController } from 'sql/workbench/api/common/notebooks/adsNotebookController';
|
||||
import { VSCodeExecuteProvider } from 'sql/workbench/api/common/notebooks/vscodeExecuteProvider';
|
||||
import { ExtHostNotebookDocumentsAndEditors } from 'sql/workbench/api/common/extHostNotebookDocumentsAndEditors';
|
||||
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
type Adapter = azdata.nb.NotebookSerializationProvider | azdata.nb.SerializationManager | azdata.nb.NotebookExecuteProvider | azdata.nb.ExecuteManager | azdata.nb.ISession | azdata.nb.IKernel | azdata.nb.IFuture;
|
||||
@@ -29,7 +24,7 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
|
||||
private _adapters = new Map<number, Adapter>();
|
||||
|
||||
// Notebook URI to manager lookup.
|
||||
constructor(_mainContext: IMainContext, private _extHostNotebookDocumentsAndEditors: ExtHostNotebookDocumentsAndEditors) {
|
||||
constructor(_mainContext: IMainContext) {
|
||||
this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadNotebook);
|
||||
}
|
||||
|
||||
@@ -174,10 +169,6 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
|
||||
}
|
||||
|
||||
$requestExecute(kernelId: number, content: azdata.nb.IExecuteRequest, disposeOnDone?: boolean): Thenable<INotebookFutureDetails> {
|
||||
// Revive request's URIs to restore functions
|
||||
content.notebookUri = URI.revive(content.notebookUri);
|
||||
content.cellUri = URI.revive(content.cellUri);
|
||||
|
||||
let kernel = this._getAdapter<azdata.nb.IKernel>(kernelId);
|
||||
let future = kernel.requestExecute(content, disposeOnDone);
|
||||
let futureId = this._addNewAdapter(future);
|
||||
@@ -258,35 +249,6 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
|
||||
this._proxy.$registerSerializationProvider(provider.providerId, handle);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
registerNotebookSerializer(notebookType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData): vscode.Disposable {
|
||||
let serializationProvider = new VSCodeSerializationProvider(notebookType, serializer);
|
||||
return this.registerSerializationProvider(serializationProvider);
|
||||
}
|
||||
|
||||
createNotebookController(
|
||||
extension: IExtensionDescription,
|
||||
id: string,
|
||||
viewType: string,
|
||||
label: string,
|
||||
getDocHandler: (notebookUri: URI) => azdata.nb.NotebookDocument,
|
||||
execHandler?: (cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController) => void | Thenable<void>,
|
||||
rendererScripts?: vscode.NotebookRendererScript[]
|
||||
): vscode.NotebookController {
|
||||
let languagesHandler = (languages: string[]) => this._proxy.$updateKernelLanguages(viewType, viewType, languages);
|
||||
let controller = new ADSNotebookController(extension, id, viewType, label, this._extHostNotebookDocumentsAndEditors, languagesHandler, getDocHandler, execHandler, extension.enabledApiProposals ? rendererScripts : undefined);
|
||||
let newKernel: azdata.nb.IStandardKernel = {
|
||||
name: viewType,
|
||||
displayName: controller.label,
|
||||
connectionProviderIds: [],
|
||||
supportedLanguages: [] // These will get set later from the controller
|
||||
};
|
||||
this._proxy.$updateProviderKernels(viewType, [newKernel]);
|
||||
|
||||
let executeProvider = new VSCodeExecuteProvider(controller);
|
||||
this.registerExecuteProvider(executeProvider);
|
||||
return controller;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region private methods
|
||||
|
||||
@@ -21,9 +21,6 @@ import {
|
||||
} from 'sql/workbench/api/common/sqlExtHost.protocol';
|
||||
import { ExtHostNotebookDocumentData } from 'sql/workbench/api/common/extHostNotebookDocumentData';
|
||||
import { ExtHostNotebookEditor } from 'sql/workbench/api/common/extHostNotebookEditor';
|
||||
import { VSCodeNotebookDocument } from 'sql/workbench/api/common/notebooks/vscodeNotebookDocument';
|
||||
import { VSCodeNotebookEditor } from 'sql/workbench/api/common/notebooks/vscodeNotebookEditor';
|
||||
import { docNotFoundForUriError } from 'sql/base/common/locConstants';
|
||||
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
type Adapter = azdata.nb.NavigationProvider;
|
||||
@@ -52,37 +49,12 @@ export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocume
|
||||
readonly onDidCloseNotebookDocument: Event<azdata.nb.NotebookDocument> = this._onDidCloseNotebook.event;
|
||||
readonly onDidChangeNotebookCell: Event<azdata.nb.NotebookCellChangeEvent> = this._onDidChangeNotebookCell.event;
|
||||
|
||||
private readonly _onDidChangeVisibleVSCodeEditors = new Emitter<vscode.NotebookEditor[]>();
|
||||
private readonly _onDidChangeActiveVSCodeEditor = new Emitter<vscode.NotebookEditor>();
|
||||
private readonly _onDidOpenVSCodeNotebook = new Emitter<vscode.NotebookDocument>();
|
||||
private readonly _onDidCloseVSCodeNotebook = new Emitter<vscode.NotebookDocument>();
|
||||
private readonly _onDidSaveVSCodeNotebook = new Emitter<vscode.NotebookDocument>();
|
||||
private readonly _onDidChangeVSCodeExecutionState = new Emitter<vscode.NotebookCellExecutionStateChangeEvent>();
|
||||
private readonly _onDidChangeVSCodeEditorSelection = new Emitter<vscode.NotebookEditorSelectionChangeEvent>();
|
||||
private readonly _onDidChangeVSCodeEditorRanges = new Emitter<vscode.NotebookEditorVisibleRangesChangeEvent>();
|
||||
private readonly _onDidChangeVSCodeNotebookDocument = new Emitter<vscode.NotebookDocumentChangeEvent>();
|
||||
|
||||
readonly onDidChangeVisibleVSCodeEditors: Event<vscode.NotebookEditor[]> = this._onDidChangeVisibleVSCodeEditors.event;
|
||||
readonly onDidChangeActiveVSCodeEditor: Event<vscode.NotebookEditor> = this._onDidChangeActiveVSCodeEditor.event;
|
||||
readonly onDidOpenVSCodeNotebookDocument: Event<vscode.NotebookDocument> = this._onDidOpenVSCodeNotebook.event;
|
||||
readonly onDidCloseVSCodeNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseVSCodeNotebook.event;
|
||||
readonly onDidSaveVSCodeNotebookDocument: Event<vscode.NotebookDocument> = this._onDidSaveVSCodeNotebook.event;
|
||||
readonly onDidChangeVSCodeExecutionState: Event<vscode.NotebookCellExecutionStateChangeEvent> = this._onDidChangeVSCodeExecutionState.event;
|
||||
readonly onDidChangeVSCodeEditorSelection: Event<vscode.NotebookEditorSelectionChangeEvent> = this._onDidChangeVSCodeEditorSelection.event;
|
||||
readonly onDidChangeVSCodeEditorRanges: Event<vscode.NotebookEditorVisibleRangesChangeEvent> = this._onDidChangeVSCodeEditorRanges.event;
|
||||
readonly onDidChangeVSCodeNotebookDocument: Event<vscode.NotebookDocumentChangeEvent> = this._onDidChangeVSCodeNotebookDocument.event;
|
||||
|
||||
constructor(
|
||||
private readonly _mainContext: IMainContext,
|
||||
) {
|
||||
if (this._mainContext) {
|
||||
this._proxy = this._mainContext.getProxy(SqlMainContext.MainThreadNotebookDocumentsAndEditors);
|
||||
}
|
||||
|
||||
this.onDidChangeVisibleNotebookEditors(editors => this._onDidChangeVisibleVSCodeEditors.fire(editors.map(editor => new VSCodeNotebookEditor(editor))));
|
||||
this.onDidChangeActiveNotebookEditor(editor => this._onDidChangeActiveVSCodeEditor.fire(new VSCodeNotebookEditor(editor)));
|
||||
this.onDidOpenNotebookDocument(notebook => this._onDidOpenVSCodeNotebook.fire(new VSCodeNotebookDocument(notebook)));
|
||||
this.onDidCloseNotebookDocument(notebook => this._onDidCloseVSCodeNotebook.fire(new VSCodeNotebookDocument(notebook)));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -218,43 +190,6 @@ export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocume
|
||||
//#endregion
|
||||
|
||||
//#region Extension accessible methods
|
||||
/**
|
||||
* Attempts to create a new notebook with the specified provider and contents. Used for VS Code extension compatibility.
|
||||
*/
|
||||
async createNotebookDocument(providerId: string, contents?: azdata.nb.INotebookContents): Promise<URI> {
|
||||
let uriComps = await this._proxy.$tryCreateNotebookDocument(providerId, contents);
|
||||
let uri = URI.revive(uriComps);
|
||||
let notebookCells = contents?.cells?.map<azdata.nb.NotebookCell>(cellContents => {
|
||||
return {
|
||||
contents: cellContents,
|
||||
uri: undefined
|
||||
};
|
||||
});
|
||||
|
||||
let documentData = new ExtHostNotebookDocumentData(
|
||||
this._proxy,
|
||||
uri,
|
||||
providerId,
|
||||
false,
|
||||
notebookCells ?? []
|
||||
);
|
||||
this._documents.set(uri.toString(), documentData);
|
||||
this._onDidOpenNotebook.fire(documentData.document);
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to open an existing notebook. Used for VS Code extension compatibility.
|
||||
*/
|
||||
async openNotebookDocument(uri: vscode.Uri): Promise<azdata.nb.NotebookDocument> {
|
||||
let docData = this._documents.get(uri.toString());
|
||||
if (!docData) {
|
||||
throw new Error(docNotFoundForUriError);
|
||||
}
|
||||
return docData.document;
|
||||
}
|
||||
|
||||
showNotebookDocument(uri: vscode.Uri, showOptions: azdata.nb.NotebookShowOptions): Thenable<azdata.nb.NotebookEditor> {
|
||||
return this.doShowNotebookDocument(uri, showOptions);
|
||||
}
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { INotebookKernelDto2 } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { ExtHostNotebookDocumentsAndEditors } from 'sql/workbench/api/common/extHostNotebookDocumentsAndEditors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { NotebookCellExecutionTaskState } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { asArray } from 'vs/base/common/arrays';
|
||||
import { convertToADSCellOutput } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
type SelectionChangedEvent = { selected: boolean, notebook: vscode.NotebookDocument; };
|
||||
type MessageReceivedEvent = { editor: vscode.NotebookEditor, message: any; };
|
||||
type ExecutionHandler = (cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController) => void | Thenable<void>;
|
||||
type LanguagesHandler = (languages: string[]) => void;
|
||||
type InterruptHandler = (notebook: vscode.NotebookDocument) => void | Promise<void>;
|
||||
type GetDocHandler = (notebookUri: URI) => azdata.nb.NotebookDocument;
|
||||
|
||||
/**
|
||||
* A VS Code Notebook Controller that is used as part of converting VS Code notebook extension APIs into ADS equivalents.
|
||||
*/
|
||||
export class ADSNotebookController implements vscode.NotebookController {
|
||||
private readonly _kernelData: INotebookKernelDto2;
|
||||
private _interruptHandler: (notebook: vscode.NotebookDocument) => void | Promise<void>;
|
||||
|
||||
private readonly _onDidChangeSelection = new Emitter<SelectionChangedEvent>();
|
||||
private readonly _onDidReceiveMessage = new Emitter<MessageReceivedEvent>();
|
||||
|
||||
private readonly _languagesAdded = new Deferred<void>();
|
||||
private readonly _executionHandlerAdded = new Deferred<void>();
|
||||
|
||||
private readonly _execMap: Map<string, ADSNotebookCellExecution> = new Map();
|
||||
|
||||
constructor(
|
||||
private _extension: IExtensionDescription,
|
||||
private _id: string,
|
||||
private _viewType: string,
|
||||
private _label: string,
|
||||
private _extHostNotebookDocumentsAndEditors: ExtHostNotebookDocumentsAndEditors,
|
||||
private _languagesHandler: LanguagesHandler,
|
||||
private _getDocHandler: GetDocHandler,
|
||||
private _execHandler?: ExecutionHandler,
|
||||
preloads?: vscode.NotebookRendererScript[]
|
||||
) {
|
||||
this._kernelData = {
|
||||
id: `${this._extension.identifier.value}/${this._id}`,
|
||||
notebookType: this._viewType,
|
||||
extensionId: this._extension.identifier,
|
||||
extensionLocation: this._extension.extensionLocation,
|
||||
label: this._label || this._extension.identifier.value,
|
||||
preloads: preloads ? preloads.map(extHostTypeConverters.NotebookRendererScript.from) : []
|
||||
};
|
||||
if (this._execHandler) {
|
||||
this._executionHandlerAdded.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
public get languagesAdded(): Promise<void> {
|
||||
return this._languagesAdded.promise;
|
||||
}
|
||||
|
||||
public get executionHandlerAdded(): Promise<void> {
|
||||
return this._executionHandlerAdded.promise;
|
||||
}
|
||||
|
||||
public get id(): string { return this._id; }
|
||||
|
||||
public get notebookType(): string { return this._viewType; }
|
||||
|
||||
public get onDidChangeSelectedNotebooks(): Event<SelectionChangedEvent> {
|
||||
return this._onDidChangeSelection.event;
|
||||
}
|
||||
|
||||
public get onDidReceiveMessage(): Event<MessageReceivedEvent> {
|
||||
return this._onDidReceiveMessage.event;
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
return this._kernelData.label;
|
||||
}
|
||||
|
||||
public set label(value: string) {
|
||||
this._kernelData.label = value ?? this._extension.displayName ?? this._extension.name;
|
||||
}
|
||||
|
||||
public get detail(): string {
|
||||
return this._kernelData.detail ?? '';
|
||||
}
|
||||
|
||||
public set detail(value: string) {
|
||||
this._kernelData.detail = value;
|
||||
}
|
||||
|
||||
public get description(): string {
|
||||
return this._kernelData.description ?? '';
|
||||
}
|
||||
|
||||
public set description(value: string) {
|
||||
this._kernelData.description = value;
|
||||
}
|
||||
|
||||
public get supportedLanguages(): string[] | undefined {
|
||||
return this._kernelData.supportedLanguages;
|
||||
}
|
||||
|
||||
public set supportedLanguages(value: string[]) {
|
||||
this._kernelData.supportedLanguages = value;
|
||||
this._languagesHandler(value);
|
||||
this._languagesAdded.resolve();
|
||||
}
|
||||
|
||||
public get supportsExecutionOrder(): boolean {
|
||||
return this._kernelData.supportsExecutionOrder ?? false;
|
||||
}
|
||||
|
||||
public set supportsExecutionOrder(value: boolean) {
|
||||
this._kernelData.supportsExecutionOrder = value;
|
||||
}
|
||||
|
||||
public get rendererScripts(): vscode.NotebookRendererScript[] {
|
||||
return this._kernelData.preloads ? this._kernelData.preloads.map(extHostTypeConverters.NotebookRendererScript.to) : [];
|
||||
}
|
||||
|
||||
public get executeHandler(): ExecutionHandler {
|
||||
return this._execHandler;
|
||||
}
|
||||
|
||||
public set executeHandler(value: ExecutionHandler) {
|
||||
this._execHandler = value;
|
||||
this._executionHandlerAdded.resolve();
|
||||
}
|
||||
|
||||
public get interruptHandler(): InterruptHandler {
|
||||
return this._interruptHandler;
|
||||
}
|
||||
|
||||
public set interruptHandler(value: InterruptHandler) {
|
||||
this._interruptHandler = value;
|
||||
this._kernelData.supportsInterrupt = Boolean(value);
|
||||
}
|
||||
|
||||
public getCellExecution(cellUri: URI): ADSNotebookCellExecution | undefined {
|
||||
return this._execMap.get(cellUri.toString());
|
||||
}
|
||||
|
||||
public removeCellExecution(cellUri: URI): void {
|
||||
this._execMap.delete(cellUri.toString());
|
||||
}
|
||||
|
||||
public getNotebookDocument(notebookUri: URI): azdata.nb.NotebookDocument {
|
||||
return this._getDocHandler(notebookUri);
|
||||
}
|
||||
|
||||
public createNotebookCellExecution(cell: vscode.NotebookCell): vscode.NotebookCellExecution {
|
||||
let exec = new ADSNotebookCellExecution(cell, this._extHostNotebookDocumentsAndEditors);
|
||||
this._execMap.set(cell.document.uri.toString(), exec);
|
||||
return exec;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public updateNotebookAffinity(notebook: vscode.NotebookDocument, affinity: vscode.NotebookControllerAffinity): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public postMessage(message: any, editor?: vscode.NotebookEditor): Thenable<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
public asWebviewUri(localResource: vscode.Uri): vscode.Uri {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class ADSNotebookCellExecution implements vscode.NotebookCellExecution {
|
||||
private _executionOrder: number;
|
||||
private _state = NotebookCellExecutionTaskState.Init;
|
||||
private _cancellationSource = new CancellationTokenSource();
|
||||
constructor(private readonly _cell: vscode.NotebookCell, private readonly _extHostNotebookDocumentsAndEditors: ExtHostNotebookDocumentsAndEditors) {
|
||||
this._executionOrder = this._cell.executionSummary?.executionOrder ?? -1;
|
||||
}
|
||||
|
||||
public get cell(): vscode.NotebookCell {
|
||||
return this._cell;
|
||||
}
|
||||
|
||||
public get tokenSource(): vscode.CancellationTokenSource {
|
||||
return this._cancellationSource;
|
||||
}
|
||||
|
||||
public get token(): vscode.CancellationToken {
|
||||
return this._cancellationSource.token;
|
||||
}
|
||||
|
||||
public get executionOrder(): number {
|
||||
return this._executionOrder;
|
||||
}
|
||||
|
||||
public set executionOrder(order: number) {
|
||||
this._executionOrder = order;
|
||||
}
|
||||
|
||||
public start(startTime?: number): void {
|
||||
this._state = NotebookCellExecutionTaskState.Started;
|
||||
}
|
||||
|
||||
public end(success: boolean, endTime?: number): void {
|
||||
this._state = NotebookCellExecutionTaskState.Resolved;
|
||||
}
|
||||
|
||||
public async clearOutput(cell?: vscode.NotebookCell): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
const targetCell = typeof cell === 'number' ? this._cell.notebook.cellAt(cell) : (cell ?? this._cell);
|
||||
const editor = this._extHostNotebookDocumentsAndEditors.getEditor(URI.from(targetCell.notebook.uri).toString());
|
||||
await editor.clearOutput(editor.document.cells[targetCell.index]);
|
||||
}
|
||||
|
||||
public async replaceOutput(out: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
return this.updateOutputs(out, cell, false);
|
||||
}
|
||||
|
||||
public async appendOutput(out: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
return this.updateOutputs(out, cell, true);
|
||||
}
|
||||
|
||||
public async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
return this.updateOutputItems(items, output, false);
|
||||
}
|
||||
|
||||
public async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
return this.updateOutputItems(items, output, true);
|
||||
}
|
||||
|
||||
private async updateOutputs(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell: vscode.NotebookCell | number | undefined, append: boolean): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
const targetCell = typeof cell === 'number' ? this._cell.notebook.cellAt(cell) : (cell ?? this._cell);
|
||||
const editor = this._extHostNotebookDocumentsAndEditors.getEditor(URI.from(targetCell.notebook.uri).toString());
|
||||
await editor.edit(builder => {
|
||||
const adsOutputs = convertToADSCellOutput(outputs);
|
||||
builder.updateCell(targetCell.index, { outputs: adsOutputs }, append);
|
||||
});
|
||||
}
|
||||
|
||||
private async updateOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput, append: boolean): Promise<void> {
|
||||
this.verifyStateForOutput();
|
||||
const editor = this._extHostNotebookDocumentsAndEditors.getEditor(URI.from(this._cell.notebook.uri).toString());
|
||||
await editor.edit(builder => {
|
||||
const adsOutput = convertToADSCellOutput({ id: output.id, items: asArray(items) }, undefined);
|
||||
builder.updateCellOutput(this._cell.index, { outputs: adsOutput }, append);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the execution is in a state where it's valid to modify the output (currently executing).
|
||||
*/
|
||||
private verifyStateForOutput() {
|
||||
if (this._state === NotebookCellExecutionTaskState.Init) {
|
||||
throw new Error('Must call start before modifying cell output');
|
||||
}
|
||||
|
||||
if (this._state === NotebookCellExecutionTaskState.Resolved) {
|
||||
throw new Error('Cannot modify cell output after calling end');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { asArray } from 'vs/base/common/arrays';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CellTypes, MimeTypes, OutputTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { NBFORMAT, NBFORMAT_MINOR } from 'sql/workbench/common/constants';
|
||||
import { NotebookCellKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
const DotnetInteractiveJupyterKernelPrefix = '.net-';
|
||||
export const DotnetInteractiveLanguagePrefix = 'dotnet-interactive.';
|
||||
export const DotnetInteractiveDisplayName = '.NET Interactive';
|
||||
|
||||
export function convertToVSCodeNotebookCell(cellKind: azdata.nb.CellType, cellIndex: number, cellUri: URI, docUri: URI, cellLanguage: string, cellSource?: string | string[]): vscode.NotebookCell {
|
||||
// We only use this notebook field for .NET Interactive's intellisense, which only uses the notebook's URI
|
||||
let notebook = <vscode.NotebookDocument>{
|
||||
uri: docUri
|
||||
};
|
||||
return <vscode.NotebookCell>{
|
||||
kind: cellKind === CellTypes.Code ? NotebookCellKind.Code : NotebookCellKind.Markup,
|
||||
index: cellIndex,
|
||||
document: <vscode.TextDocument>{
|
||||
uri: cellUri,
|
||||
languageId: cellLanguage,
|
||||
getText: () => Array.isArray(cellSource) ? cellSource.join('') : (cellSource ?? ''),
|
||||
notebook: notebook
|
||||
},
|
||||
notebook: notebook,
|
||||
outputs: [],
|
||||
metadata: {},
|
||||
mime: undefined,
|
||||
executionSummary: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function convertToADSCellOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], executionOrder?: number): azdata.nb.IDisplayResult[] {
|
||||
return asArray(outputs).map(output => {
|
||||
let outputData = {};
|
||||
for (let item of output.items) {
|
||||
outputData[item.mime] = VSBuffer.wrap(item.data).toString();
|
||||
}
|
||||
return {
|
||||
output_type: OutputTypes.ExecuteResult,
|
||||
data: outputData,
|
||||
execution_count: executionOrder,
|
||||
metadata: output.metadata,
|
||||
id: output.id
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function convertToVSCodeCellOutput(output: azdata.nb.ICellOutput): vscode.NotebookCellOutput {
|
||||
let convertedOutputItems: vscode.NotebookCellOutputItem[];
|
||||
switch (output.output_type) {
|
||||
case OutputTypes.ExecuteResult:
|
||||
case OutputTypes.DisplayData:
|
||||
case OutputTypes.UpdateDisplayData:
|
||||
let displayOutput = output as azdata.nb.IDisplayResult;
|
||||
convertedOutputItems = Object.keys(displayOutput.data).map<vscode.NotebookCellOutputItem>(key => {
|
||||
return {
|
||||
mime: key,
|
||||
data: VSBuffer.fromString(displayOutput.data[key]).buffer
|
||||
};
|
||||
});
|
||||
break;
|
||||
case OutputTypes.Stream:
|
||||
let streamOutput = output as azdata.nb.IStreamResult;
|
||||
convertedOutputItems = [{
|
||||
mime: MimeTypes.HTML,
|
||||
data: VSBuffer.fromString(Array.isArray(streamOutput.text) ? streamOutput.text.join('') : streamOutput.text).buffer
|
||||
}];
|
||||
break;
|
||||
case OutputTypes.Error:
|
||||
let errorOutput = output as azdata.nb.IErrorResult;
|
||||
let errorString = errorOutput.ename + ': ' + errorOutput.evalue + (errorOutput.traceback ? '\n' + errorOutput.traceback?.join('\n') : '');
|
||||
convertedOutputItems = [{
|
||||
mime: MimeTypes.HTML,
|
||||
data: VSBuffer.fromString(errorString).buffer
|
||||
}];
|
||||
break;
|
||||
}
|
||||
return {
|
||||
items: convertedOutputItems,
|
||||
metadata: output.metadata,
|
||||
id: output.id
|
||||
};
|
||||
}
|
||||
|
||||
export function convertToADSNotebookContents(notebookData: vscode.NotebookData | undefined): azdata.nb.INotebookContents {
|
||||
let result = {
|
||||
cells: notebookData?.cells?.map<azdata.nb.ICellContents>(cell => {
|
||||
let executionOrder = cell.executionSummary?.executionOrder;
|
||||
let convertedCell: azdata.nb.ICellContents = {
|
||||
cell_type: cell.kind === NotebookCellKind.Code ? CellTypes.Code : CellTypes.Markdown,
|
||||
source: cell.value,
|
||||
execution_count: executionOrder,
|
||||
outputs: cell.outputs ? convertToADSCellOutput(cell.outputs, executionOrder) : undefined
|
||||
};
|
||||
convertedCell.metadata = cell.metadata?.custom?.metadata ?? {};
|
||||
if (!convertedCell.metadata.language) {
|
||||
convertedCell.metadata.language = cell.languageId;
|
||||
}
|
||||
return convertedCell;
|
||||
}),
|
||||
metadata: notebookData?.metadata?.custom?.metadata ?? {},
|
||||
nbformat: notebookData?.metadata?.custom?.nbformat ?? NBFORMAT,
|
||||
nbformat_minor: notebookData?.metadata?.custom?.nbformat_minor ?? NBFORMAT_MINOR
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export function convertToVSCodeNotebookData(notebook: azdata.nb.INotebookContents): vscode.NotebookData {
|
||||
let result: vscode.NotebookData = {
|
||||
cells: notebook.cells?.map<vscode.NotebookCellData>(cell => {
|
||||
return {
|
||||
kind: cell.cell_type === CellTypes.Code ? NotebookCellKind.Code : NotebookCellKind.Markup,
|
||||
value: Array.isArray(cell.source) ? cell.source.join('') : cell.source,
|
||||
languageId: cell.metadata?.language ?? notebook.metadata.language_info?.name,
|
||||
outputs: cell.outputs?.map<vscode.NotebookCellOutput>(output => convertToVSCodeCellOutput(output)),
|
||||
executionSummary: {
|
||||
executionOrder: cell.execution_count
|
||||
},
|
||||
metadata: {
|
||||
custom: {
|
||||
metadata: cell.metadata
|
||||
}
|
||||
}
|
||||
};
|
||||
}),
|
||||
metadata: {
|
||||
custom: {
|
||||
metadata: notebook.metadata,
|
||||
nbformat: notebook.nbformat,
|
||||
nbformat_minor: notebook.nbformat_minor
|
||||
}
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// #region .NET Interactive Kernel Metadata Conversion
|
||||
|
||||
/*
|
||||
Since ADS relies on notebook kernelSpecs for provider metadata in a lot of places, we have to convert
|
||||
a .NET Interactive notebook's Jupyter kernelSpec to an internal representation so that it matches up with
|
||||
the contributed .NET Interactive notebook provider from the Jupyter extension. When saving a notebook, we
|
||||
then need to restore the original kernelSpec state so that it will work with other notebook apps like
|
||||
VS Code. VS Code does something similar by shifting a Jupyter notebook's original metadata over to a new
|
||||
"custom" field, which is then shifted back when saving the notebook.
|
||||
|
||||
This is an example of an internal kernel representation we use to get compatibility working (C#, in this case):
|
||||
kernelSpec: {
|
||||
name: 'jupyter-notebook', // Matches the name of the notebook provider from the Jupyter extension
|
||||
language: 'dotnet-interactive.csharp', // Matches the contributed languages from the .NET Interactive extension
|
||||
display_name: '.NET Interactive' // The kernel name we need to show in our dropdown to match VS Code's kernel dropdown
|
||||
}
|
||||
|
||||
This is how that C# kernel spec would need to be saved to work in VS Code:
|
||||
kernelSpec: {
|
||||
name: '.net-csharp',
|
||||
language: 'C#',
|
||||
display_name: '.NET (C#)'
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stores equivalent external kernel metadata in a newly created .NET Interactive notebook, which is used as the default metadata when saving the notebook. This is so that ADS notebooks are still usable in other apps.
|
||||
* @param kernelSpec The notebook kernel metadata to be modified.
|
||||
*/
|
||||
export function addExternalInteractiveKernelMetadata(kernelSpec: azdata.nb.IKernelSpec): void {
|
||||
if (kernelSpec.name === 'jupyter-notebook' && kernelSpec.display_name === DotnetInteractiveDisplayName && kernelSpec.language) {
|
||||
let language = kernelSpec.language.replace(DotnetInteractiveLanguagePrefix, '');
|
||||
let displayLanguage: string;
|
||||
switch (language) {
|
||||
case 'csharp':
|
||||
displayLanguage = 'C#';
|
||||
break;
|
||||
case 'fsharp':
|
||||
displayLanguage = 'F#';
|
||||
break;
|
||||
case 'pwsh':
|
||||
displayLanguage = 'PowerShell';
|
||||
break;
|
||||
default:
|
||||
displayLanguage = language;
|
||||
}
|
||||
if (!kernelSpec.oldName) {
|
||||
kernelSpec.oldName = `${DotnetInteractiveJupyterKernelPrefix}${language}`;
|
||||
}
|
||||
if (!kernelSpec.oldDisplayName) {
|
||||
kernelSpec.oldDisplayName = `.NET (${displayLanguage})`;
|
||||
}
|
||||
if (!kernelSpec.oldLanguage) {
|
||||
kernelSpec.oldLanguage = displayLanguage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a .NET Interactive notebook's metadata to an internal representation needed for VS Code notebook compatibility. This metadata is then restored when saving the notebook.
|
||||
* @param metadata The notebook metadata to be modified.
|
||||
*/
|
||||
export function convertToInternalInteractiveKernelMetadata(metadata: azdata.nb.INotebookMetadata | undefined): void {
|
||||
if (metadata?.kernelspec?.name?.startsWith(DotnetInteractiveJupyterKernelPrefix)) {
|
||||
metadata.kernelspec.oldDisplayName = metadata.kernelspec.display_name;
|
||||
metadata.kernelspec.display_name = DotnetInteractiveDisplayName;
|
||||
|
||||
let kernelName = metadata.kernelspec.name;
|
||||
let baseLanguageName = kernelName.replace(DotnetInteractiveJupyterKernelPrefix, '');
|
||||
if (baseLanguageName === 'powershell') {
|
||||
baseLanguageName = 'pwsh';
|
||||
}
|
||||
let languageName = `${DotnetInteractiveLanguagePrefix}${baseLanguageName}`;
|
||||
|
||||
metadata.kernelspec.oldLanguage = metadata.kernelspec.language;
|
||||
metadata.kernelspec.language = languageName;
|
||||
|
||||
metadata.language_info.oldName = metadata.language_info.name;
|
||||
metadata.language_info.name = languageName;
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
@@ -1,347 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import { ADSNotebookController } from 'sql/workbench/api/common/notebooks/adsNotebookController';
|
||||
import * as nls from 'vs/nls';
|
||||
import { addExternalInteractiveKernelMetadata, convertToVSCodeNotebookCell } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
import { CellTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { VSCodeNotebookDocument } from 'sql/workbench/api/common/notebooks/vscodeNotebookDocument';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { notebookMultipleRequestsError } from 'sql/workbench/common/constants';
|
||||
|
||||
class VSCodeFuture implements azdata.nb.IFuture {
|
||||
private _inProgress = true;
|
||||
|
||||
constructor(private readonly _executeCompletion: Promise<void>) {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public get inProgress(): boolean {
|
||||
return this._inProgress;
|
||||
}
|
||||
|
||||
public set inProgress(value: boolean) {
|
||||
this._inProgress = value;
|
||||
}
|
||||
|
||||
public get msg(): azdata.nb.IMessage | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get done(): Thenable<azdata.nb.IShellMessage | undefined> {
|
||||
return this._executeCompletion.then(() => {
|
||||
return undefined;
|
||||
}).finally(() => {
|
||||
this._inProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
setReplyHandler(handler: azdata.nb.MessageHandler<azdata.nb.IShellMessage>): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
setStdInHandler(handler: azdata.nb.MessageHandler<azdata.nb.IStdinMessage>): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
setIOPubHandler(handler: azdata.nb.MessageHandler<azdata.nb.IIOPubMessage>): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
registerMessageHook(hook: (msg: azdata.nb.IIOPubMessage) => boolean | Thenable<boolean>): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
removeMessageHook(hook: (msg: azdata.nb.IIOPubMessage) => boolean | Thenable<boolean>): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
sendInputReply(content: azdata.nb.IInputReply): void {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
class VSCodeKernel implements azdata.nb.IKernel {
|
||||
protected static kernelId = 0;
|
||||
private readonly _id: string;
|
||||
private readonly _name: string;
|
||||
private readonly _info: azdata.nb.IInfoReply;
|
||||
private readonly _kernelSpec: azdata.nb.IKernelSpec;
|
||||
private _activeRequest: azdata.nb.IExecuteRequest;
|
||||
|
||||
constructor(private readonly _controller: ADSNotebookController, private readonly _options: azdata.nb.ISessionOptions) {
|
||||
this._id = this._options.kernelId ?? (VSCodeKernel.kernelId++).toString();
|
||||
this._kernelSpec = this._options.kernelSpec ?? {
|
||||
name: this._controller.notebookType,
|
||||
display_name: this._controller.label,
|
||||
};
|
||||
if (!this._kernelSpec.language) {
|
||||
this._kernelSpec.language = this._controller.supportedLanguages[0];
|
||||
this._kernelSpec.supportedLanguages = this._controller.supportedLanguages;
|
||||
}
|
||||
|
||||
// Store external kernel names for .NET Interactive kernels for when notebook gets saved, so that notebook is usable outside of ADS
|
||||
addExternalInteractiveKernelMetadata(this._kernelSpec);
|
||||
|
||||
this._name = this._kernelSpec.name;
|
||||
this._info = {
|
||||
protocol_version: '',
|
||||
implementation: '',
|
||||
implementation_version: '',
|
||||
language_info: {
|
||||
name: this._kernelSpec.language,
|
||||
oldName: this._kernelSpec.oldLanguage
|
||||
},
|
||||
banner: '',
|
||||
help_links: [{
|
||||
text: '',
|
||||
url: ''
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get supportsIntellisense(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public get requiresConnection(): boolean | undefined {
|
||||
return false;
|
||||
}
|
||||
|
||||
public get isReady(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public get ready(): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public get info(): azdata.nb.IInfoReply | null {
|
||||
return this._info;
|
||||
}
|
||||
|
||||
public get spec(): azdata.nb.IKernelSpec {
|
||||
return this._kernelSpec;
|
||||
}
|
||||
|
||||
getSpec(): Thenable<azdata.nb.IKernelSpec> {
|
||||
return Promise.resolve(this.spec);
|
||||
}
|
||||
|
||||
private cleanUpActiveExecution(cellUri: URI) {
|
||||
this._activeRequest = undefined;
|
||||
this._controller.removeCellExecution(cellUri);
|
||||
}
|
||||
|
||||
requestExecute(content: azdata.nb.IExecuteRequest, disposeOnDone?: boolean): azdata.nb.IFuture {
|
||||
if (this._activeRequest) {
|
||||
throw new Error(notebookMultipleRequestsError);
|
||||
}
|
||||
let executePromise: Promise<void>;
|
||||
if (this._controller.executeHandler) {
|
||||
let cell = convertToVSCodeNotebookCell(CellTypes.Code, content.cellIndex, content.cellUri, content.notebookUri, content.language ?? this._kernelSpec.language, content.code);
|
||||
this._activeRequest = content;
|
||||
executePromise = Promise.resolve(this._controller.executeHandler([cell], cell.notebook, this._controller)).then(() => this.cleanUpActiveExecution(content.cellUri));
|
||||
} else {
|
||||
executePromise = Promise.resolve();
|
||||
}
|
||||
|
||||
return new VSCodeFuture(executePromise);
|
||||
}
|
||||
|
||||
requestComplete(content: azdata.nb.ICompleteRequest): Thenable<azdata.nb.ICompleteReplyMsg> {
|
||||
let response: Partial<azdata.nb.ICompleteReplyMsg> = {};
|
||||
return Promise.resolve(response as azdata.nb.ICompleteReplyMsg);
|
||||
}
|
||||
|
||||
public async interrupt(): Promise<void> {
|
||||
if (this._activeRequest) {
|
||||
if (this._controller.interruptHandler) {
|
||||
let doc = this._controller.getNotebookDocument(this._activeRequest.notebookUri);
|
||||
await this._controller.interruptHandler.call(this._controller, new VSCodeNotebookDocument(doc));
|
||||
} else {
|
||||
let exec = this._controller.getCellExecution(this._activeRequest.cellUri);
|
||||
exec?.tokenSource.cancel();
|
||||
}
|
||||
this.cleanUpActiveExecution(this._activeRequest.cellUri);
|
||||
}
|
||||
}
|
||||
|
||||
public async restart(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class VSCodeSession implements azdata.nb.ISession {
|
||||
private _kernel: VSCodeKernel;
|
||||
private _defaultKernelLoaded = false;
|
||||
constructor(controller: ADSNotebookController, private readonly _options: azdata.nb.ISessionOptions) {
|
||||
this._kernel = new VSCodeKernel(controller, this._options);
|
||||
}
|
||||
|
||||
public set defaultKernelLoaded(value) {
|
||||
this._defaultKernelLoaded = value;
|
||||
}
|
||||
|
||||
public get defaultKernelLoaded(): boolean {
|
||||
return this._defaultKernelLoaded;
|
||||
}
|
||||
|
||||
public get canChangeKernels(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this._options.kernelId || this._kernel ? this._kernel.id : '';
|
||||
}
|
||||
|
||||
public get path(): string {
|
||||
return this._options.path;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this._options.name || '';
|
||||
}
|
||||
|
||||
public get type(): string {
|
||||
return this._options.type || '';
|
||||
}
|
||||
|
||||
public get status(): azdata.nb.KernelStatus {
|
||||
return 'connected';
|
||||
}
|
||||
|
||||
public get vsKernel(): VSCodeKernel {
|
||||
return this._kernel;
|
||||
}
|
||||
|
||||
public get kernel(): azdata.nb.IKernel {
|
||||
return this.vsKernel;
|
||||
}
|
||||
|
||||
changeKernel(kernelInfo: azdata.nb.IKernelSpec): Thenable<azdata.nb.IKernel> {
|
||||
return Promise.resolve(this._kernel);
|
||||
}
|
||||
|
||||
configureKernel(kernelInfo: azdata.nb.IKernelSpec): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
configureConnection(connection: azdata.IConnectionProfile): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class VSCodeSessionManager implements azdata.nb.SessionManager {
|
||||
private _sessions: VSCodeSession[] = [];
|
||||
|
||||
constructor(private readonly _controller: ADSNotebookController) {
|
||||
}
|
||||
|
||||
public get isReady(): boolean {
|
||||
return this._controller.supportedLanguages?.length > 0 && this._controller.executeHandler !== undefined;
|
||||
}
|
||||
|
||||
public get ready(): Thenable<void> {
|
||||
return Promise.all([this._controller.languagesAdded, this._controller.executionHandlerAdded]).then();
|
||||
}
|
||||
|
||||
public get specs(): azdata.nb.IAllKernels {
|
||||
// Have to return the default kernel here, since the manager specs are accessed before kernels get added
|
||||
let defaultKernel: azdata.nb.IKernelSpec = {
|
||||
name: this._controller.notebookType,
|
||||
language: this._controller.supportedLanguages[0],
|
||||
display_name: this._controller.label,
|
||||
supportedLanguages: this._controller.supportedLanguages ?? []
|
||||
};
|
||||
return {
|
||||
defaultKernel: defaultKernel.name,
|
||||
kernels: [defaultKernel]
|
||||
};
|
||||
}
|
||||
|
||||
public async startNew(options: azdata.nb.ISessionOptions): Promise<azdata.nb.ISession> {
|
||||
if (!this.isReady) {
|
||||
return Promise.reject(new Error(nls.localize('errorStartBeforeReady', "Cannot start a session, the manager is not yet initialized")));
|
||||
}
|
||||
let session = new VSCodeSession(this._controller, options);
|
||||
let index = this._sessions.findIndex(session => session.path === options.path);
|
||||
if (index > -1) {
|
||||
this._sessions.splice(index);
|
||||
}
|
||||
this._sessions.push(session);
|
||||
return Promise.resolve(session);
|
||||
}
|
||||
|
||||
public shutdown(id: string): Thenable<void> {
|
||||
let index = this._sessions.findIndex(session => session.id === id);
|
||||
if (index > -1) {
|
||||
this._sessions.splice(index);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public shutdownAll(): Thenable<void> {
|
||||
return Promise.all(this._sessions.map(session => {
|
||||
return this.shutdown(session.id);
|
||||
})).then();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
class VSCodeExecuteManager implements azdata.nb.ExecuteManager {
|
||||
public readonly providerId: string;
|
||||
private readonly _sessionManager: azdata.nb.SessionManager;
|
||||
|
||||
constructor(controller: ADSNotebookController) {
|
||||
this.providerId = controller.notebookType;
|
||||
this._sessionManager = new VSCodeSessionManager(controller);
|
||||
}
|
||||
|
||||
public get sessionManager(): azdata.nb.SessionManager {
|
||||
return this._sessionManager;
|
||||
}
|
||||
|
||||
public get serverManager(): azdata.nb.ServerManager | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Notebook Execute Provider that is used to convert VS Code notebook extension APIs into ADS equivalents.
|
||||
*/
|
||||
export class VSCodeExecuteProvider implements azdata.nb.NotebookExecuteProvider {
|
||||
public readonly providerId: string;
|
||||
private readonly _executeManager: azdata.nb.ExecuteManager;
|
||||
|
||||
constructor(controller: ADSNotebookController) {
|
||||
this._executeManager = new VSCodeExecuteManager(controller);
|
||||
this.providerId = controller.notebookType;
|
||||
}
|
||||
|
||||
public getExecuteManager(notebookUri: vscode.Uri): Thenable<azdata.nb.ExecuteManager> {
|
||||
return Promise.resolve(this._executeManager);
|
||||
}
|
||||
|
||||
public handleNotebookClosed(notebookUri: vscode.Uri): void {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import { convertToVSCodeNotebookCell } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
|
||||
export class VSCodeNotebookDocument implements vscode.NotebookDocument {
|
||||
private readonly _convertedCells: vscode.NotebookCell[];
|
||||
|
||||
constructor(private readonly _notebookDoc: azdata.nb.NotebookDocument) {
|
||||
this._convertedCells = this._notebookDoc.cells?.map((cell, index) => convertToVSCodeNotebookCell(cell.contents.cell_type, index, cell.uri, this._notebookDoc.uri, cell.contents.metadata?.language, cell.contents.source));
|
||||
}
|
||||
|
||||
public get uri() { return this._notebookDoc.uri; }
|
||||
|
||||
public get version() { return undefined; }
|
||||
|
||||
public get notebookType() { return this._notebookDoc.providerId; }
|
||||
|
||||
public get isDirty() { return this._notebookDoc.isDirty; }
|
||||
|
||||
public get isUntitled() { return this._notebookDoc.isUntitled; }
|
||||
|
||||
public get isClosed() { return this._notebookDoc.isClosed; }
|
||||
|
||||
public get metadata() { return {}; }
|
||||
|
||||
public get cellCount() { return this._notebookDoc.cells?.length; }
|
||||
|
||||
cellAt(index: number): vscode.NotebookCell {
|
||||
if (this._notebookDoc.cells) {
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
} else if (index >= this._notebookDoc.cells.length) {
|
||||
index = this._convertedCells.length - 1;
|
||||
}
|
||||
return this._convertedCells[index];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCells(range?: vscode.NotebookRange): vscode.NotebookCell[] {
|
||||
let cells: vscode.NotebookCell[] = [];
|
||||
if (range) {
|
||||
cells = this._convertedCells?.slice(range.start, range.end);
|
||||
} else {
|
||||
cells = this._convertedCells;
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
save(): Thenable<boolean> {
|
||||
return this._notebookDoc.save();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import { VSCodeNotebookDocument } from 'sql/workbench/api/common/notebooks/vscodeNotebookDocument';
|
||||
import { functionalityNotSupportedError } from 'sql/base/common/locConstants';
|
||||
|
||||
export class VSCodeNotebookEditor implements vscode.NotebookEditor {
|
||||
private readonly _document: vscode.NotebookDocument;
|
||||
|
||||
constructor(editor: azdata.nb.NotebookEditor) {
|
||||
if (editor) {
|
||||
this._document = new VSCodeNotebookDocument(editor.document);
|
||||
}
|
||||
}
|
||||
|
||||
public get document(): vscode.NotebookDocument {
|
||||
return this._document;
|
||||
}
|
||||
|
||||
public get selections(): vscode.NotebookRange[] {
|
||||
throw new Error(functionalityNotSupportedError);
|
||||
}
|
||||
|
||||
public get visibleRanges(): vscode.NotebookRange[] {
|
||||
throw new Error(functionalityNotSupportedError);
|
||||
}
|
||||
|
||||
public get viewColumn(): vscode.ViewColumn | undefined {
|
||||
throw new Error(functionalityNotSupportedError);
|
||||
}
|
||||
|
||||
public revealRange(range: vscode.NotebookRange, revealType?: vscode.NotebookEditorRevealType): void {
|
||||
throw new Error(functionalityNotSupportedError);
|
||||
}
|
||||
|
||||
public edit(callback: (editBuilder: vscode.NotebookEditorEdit) => void): Promise<boolean> {
|
||||
return Promise.reject(functionalityNotSupportedError);
|
||||
}
|
||||
|
||||
public setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookRange): void {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { convertToADSNotebookContents, convertToVSCodeNotebookData } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
|
||||
export class VSCodeContentManager implements azdata.nb.ContentManager {
|
||||
constructor(private readonly _serializer: vscode.NotebookSerializer) {
|
||||
}
|
||||
|
||||
public async deserializeNotebook(contents: string): Promise<azdata.nb.INotebookContents> {
|
||||
let buffer = VSBuffer.fromString(contents);
|
||||
let notebookData = await this._serializer.deserializeNotebook(buffer.buffer, CancellationToken.None);
|
||||
return convertToADSNotebookContents(notebookData);
|
||||
}
|
||||
|
||||
public async serializeNotebook(notebook: azdata.nb.INotebookContents): Promise<string> {
|
||||
let notebookData = convertToVSCodeNotebookData(notebook);
|
||||
let bytes = await this._serializer.serializeNotebook(notebookData, CancellationToken.None);
|
||||
let buffer = VSBuffer.wrap(bytes);
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class VSCodeSerializationManager implements azdata.nb.SerializationManager {
|
||||
private _manager: VSCodeContentManager;
|
||||
|
||||
constructor(serializer: vscode.NotebookSerializer) {
|
||||
this._manager = new VSCodeContentManager(serializer);
|
||||
}
|
||||
|
||||
public get contentManager(): azdata.nb.ContentManager {
|
||||
return this._manager;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Notebook Serialization Provider that is used to convert VS Code notebook extension APIs into ADS equivalents.
|
||||
*/
|
||||
export class VSCodeSerializationProvider implements azdata.nb.NotebookSerializationProvider {
|
||||
private _manager: VSCodeSerializationManager;
|
||||
|
||||
constructor(public readonly providerId: string, serializer: vscode.NotebookSerializer) {
|
||||
this._manager = new VSCodeSerializationManager(serializer);
|
||||
}
|
||||
|
||||
public getSerializationManager(notebookUri: vscode.Uri): Thenable<azdata.nb.SerializationManager> {
|
||||
return Promise.resolve(this._manager);
|
||||
}
|
||||
}
|
||||
@@ -53,18 +53,16 @@ export interface IExtensionApiFactory {
|
||||
|
||||
export interface IAdsExtensionApiFactory {
|
||||
azdata: IAzdataExtensionApiFactory;
|
||||
extHostNotebook: ExtHostNotebook;
|
||||
extHostNotebookDocumentsAndEditors: ExtHostNotebookDocumentsAndEditors;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method instantiates and returns the extension API surface
|
||||
*/
|
||||
export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): IExtensionApiFactory {
|
||||
const { azdata, extHostNotebook, extHostNotebookDocumentsAndEditors } = createAdsApiFactory(accessor);
|
||||
const { azdata } = createAdsApiFactory(accessor);
|
||||
return {
|
||||
azdata,
|
||||
vscode: vsApiFactory(accessor, extHostNotebook, extHostNotebookDocumentsAndEditors)
|
||||
vscode: vsApiFactory(accessor)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -99,7 +97,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
const extHostModelViewDialog = rpcProtocol.set(SqlExtHostContext.ExtHostModelViewDialog, new ExtHostModelViewDialog(rpcProtocol, extHostModelView, extHostBackgroundTaskManagement));
|
||||
const extHostQueryEditor = rpcProtocol.set(SqlExtHostContext.ExtHostQueryEditor, new ExtHostQueryEditor(rpcProtocol));
|
||||
const extHostNotebookDocumentsAndEditors = rpcProtocol.set(SqlExtHostContext.ExtHostNotebookDocumentsAndEditors, new ExtHostNotebookDocumentsAndEditors(rpcProtocol));
|
||||
const extHostNotebook = rpcProtocol.set(SqlExtHostContext.ExtHostNotebook, new ExtHostNotebook(rpcProtocol, extHostNotebookDocumentsAndEditors));
|
||||
const extHostNotebook = rpcProtocol.set(SqlExtHostContext.ExtHostNotebook, new ExtHostNotebook(rpcProtocol));
|
||||
const extHostExtensionManagement = rpcProtocol.set(SqlExtHostContext.ExtHostExtensionManagement, new ExtHostExtensionManagement(rpcProtocol));
|
||||
const extHostWorkspace = rpcProtocol.set(SqlExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(rpcProtocol));
|
||||
return {
|
||||
@@ -670,8 +668,6 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
executionPlan: executionPlan,
|
||||
env
|
||||
};
|
||||
},
|
||||
extHostNotebook: extHostNotebook,
|
||||
extHostNotebookDocumentsAndEditors: extHostNotebookDocumentsAndEditors
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -935,8 +935,6 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
$unregisterExecuteProvider(handle: number): void;
|
||||
$onFutureMessage(futureId: number, type: FutureMessageType, payload: azdata.nb.IMessage): void;
|
||||
$onFutureDone(futureId: number, done: INotebookFutureDone): void;
|
||||
$updateProviderKernels(providerId: string, languages: azdata.nb.IStandardKernel[]): void;
|
||||
$updateKernelLanguages(providerId: string, kernelName: string, languages: string[]): void;
|
||||
}
|
||||
|
||||
export interface INotebookDocumentsAndEditorsDelta {
|
||||
@@ -991,7 +989,6 @@ export interface ExtHostNotebookDocumentsAndEditorsShape {
|
||||
export interface MainThreadNotebookDocumentsAndEditorsShape extends IDisposable {
|
||||
$trySetTrusted(_uri: UriComponents, isTrusted: boolean): Thenable<boolean>;
|
||||
$trySaveDocument(uri: UriComponents): Thenable<boolean>;
|
||||
$tryCreateNotebookDocument(providerId: string, contents?: azdata.nb.INotebookContents): Promise<UriComponents>;
|
||||
$tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): Promise<string>;
|
||||
$tryApplyEdits(id: string, modelVersionId: number, edits: INotebookEditOperation[], opts: IUndoStopOptions): Promise<boolean>;
|
||||
$runCell(id: string, cellUri: UriComponents): Promise<boolean>;
|
||||
|
||||
@@ -561,12 +561,6 @@ export interface ICellMetadata {
|
||||
tags?: string[] | undefined;
|
||||
azdata_cell_guid?: string | undefined;
|
||||
connection_name?: string;
|
||||
/**
|
||||
* .NET Interactive metadata. This is only required for compatibility with the .NET Interactive extension.
|
||||
*/
|
||||
dotnet_interactive?: {
|
||||
language: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISerializationManagerDetails {
|
||||
|
||||
@@ -41,7 +41,6 @@ export const RESOURCE_VIEWER_TYPEID = 'workbench.editorInput.resourceViewerInput
|
||||
export const JUPYTER_PROVIDER_ID = 'jupyter';
|
||||
export const VSCODE_JUPYTER_PROVIDER_ID = 'jupyter-notebook';
|
||||
export const IPYKERNEL_DISPLAY_NAME = 'Python 3 (ipykernel)';
|
||||
export const INTERACTIVE_PROVIDER_ID = 'dotnet-interactive';
|
||||
export const TSGOPS_WEB_QUALITY = 'tsgops-image';
|
||||
export const CELL_URI_PATH_PREFIX = 'notebook-editor-';
|
||||
|
||||
@@ -54,7 +53,6 @@ export const NBFORMAT_MINOR = 2;
|
||||
export const enum NotebookLanguage {
|
||||
Notebook = 'notebook',
|
||||
Ipynb = 'ipynb',
|
||||
Interactive = 'dib'
|
||||
}
|
||||
export interface INotebookSearchConfigurationProperties {
|
||||
exclude: glob.IExpression;
|
||||
|
||||
@@ -38,7 +38,6 @@ import { LocalContentManager } from 'sql/workbench/services/notebook/common/loca
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as LanguageAssociationExtensions, ILanguageAssociationRegistry } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
|
||||
import { NotebookLanguage } from 'sql/workbench/common/constants';
|
||||
import { convertToInternalInteractiveKernelMetadata } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
|
||||
import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
@@ -592,9 +591,6 @@ export class NotebookEditorContentLoader implements IContentLoader {
|
||||
notebookContents = await this.contentManager.deserializeNotebook(notebookEditorModel.contentString);
|
||||
}
|
||||
|
||||
// Special case .NET Interactive kernel spec to handle inconsistencies between notebook providers and jupyter kernel specs
|
||||
convertToInternalInteractiveKernelMetadata(notebookContents.metadata);
|
||||
|
||||
return notebookContents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,9 +438,7 @@ registerComponentType({
|
||||
mimeTypes: [
|
||||
'text/plain',
|
||||
'application/vnd.jupyter.stdout',
|
||||
'application/vnd.jupyter.stderr',
|
||||
'application/vnd.code.notebook.stdout',
|
||||
'application/vnd.code.notebook.stderr'
|
||||
'application/vnd.jupyter.stderr'
|
||||
],
|
||||
rank: 120,
|
||||
safe: true,
|
||||
@@ -448,19 +446,6 @@ registerComponentType({
|
||||
selector: MimeRendererComponent.SELECTOR
|
||||
});
|
||||
|
||||
/**
|
||||
* A mime renderer component for VS Code Notebook error data.
|
||||
*/
|
||||
registerComponentType({
|
||||
mimeTypes: [
|
||||
'application/vnd.code.notebook.error'
|
||||
],
|
||||
rank: 121,
|
||||
safe: true,
|
||||
ctor: MimeRendererComponent,
|
||||
selector: MimeRendererComponent.SELECTOR
|
||||
});
|
||||
|
||||
/**
|
||||
* A placeholder component for deprecated rendered JavaScript.
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,6 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te
|
||||
import { ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ControlType, IChartOption } from 'sql/workbench/contrib/charts/browser/chartOptions';
|
||||
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { ICellMetadata } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IModelContentChangedEvent } from 'vs/editor/common/textModelEvents';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
@@ -1533,46 +1532,4 @@ suite('Cell Model', function (): void {
|
||||
assert.strictEqual(cellModel.currentMode, CellEditModes.MARKDOWN, 'Should persist lastEditMode and be in markdown only');
|
||||
|
||||
});
|
||||
|
||||
test('should set .NET Interactive cell metadata when converting to JSON', async function () {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let contents: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: '',
|
||||
metadata: {
|
||||
language: 'dotnet-interactive.csharp'
|
||||
}
|
||||
};
|
||||
let model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
assert((model.metadata as ICellMetadata).dotnet_interactive === undefined, 'dotnet_interactive field should not be set in cell metadata before converting to JSON');
|
||||
let cellJson = model.toJSON();
|
||||
assert((cellJson.metadata as ICellMetadata).dotnet_interactive !== undefined, 'dotnet_interactive field should be set in JSON cell metadata');
|
||||
assert.strictEqual((cellJson.metadata as ICellMetadata).dotnet_interactive.language, 'csharp', 'Expected dotnet_interactive language field to be csharp');
|
||||
});
|
||||
|
||||
test('should overwrite pre-existing .NET Interactive cell metadata when converting to JSON', async function () {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let contents: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: '',
|
||||
metadata: {
|
||||
language: 'dotnet-interactive.csharp'
|
||||
}
|
||||
};
|
||||
(contents.metadata as ICellMetadata).dotnet_interactive = { language: 'fsharp' };
|
||||
|
||||
let model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
assert((model.metadata as ICellMetadata).dotnet_interactive !== undefined, 'dotnet_interactive field should exist in cell metadata');
|
||||
let cellJson = model.toJSON();
|
||||
assert((cellJson.metadata as ICellMetadata).dotnet_interactive !== undefined, 'dotnet_interactive field should be set in JSON cell metadata');
|
||||
assert.strictEqual((cellJson.metadata as ICellMetadata).dotnet_interactive.language, 'csharp', 'Expected dotnet_interactive language field to be csharp');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,6 @@ import { INotebookView, INotebookViewCard, INotebookViewMetadata, INotebookViews
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { INotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
|
||||
export class NotebookModelStub implements INotebookModel {
|
||||
constructor(private _languageInfo?: nb.ILanguageInfo, private _cells?: ICellModel[], private _testContents?: nb.INotebookContents) {
|
||||
@@ -236,15 +235,9 @@ export class NotebookServiceStub implements INotebookService {
|
||||
get onNotebookKernelsAdded(): vsEvent.Event<IStandardKernelWithProvider[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getNotebookURIForCell(cellUri: URI): URI {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getSupportedLanguagesForProvider(provider: string, kernelDisplayName?: string): Promise<string[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
createNotebookInputFromContents(providerId: string, contents?: nb.INotebookContents, resource?: UriComponents): Promise<EditorInput> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
_serviceBrand: undefined;
|
||||
get onNotebookEditorAdd(): vsEvent.Event<INotebookEditor> {
|
||||
throw new Error('Method not implemented.');
|
||||
|
||||
@@ -35,7 +35,6 @@ import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { ICellMetadata } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { CELL_URI_PATH_PREFIX } from 'sql/workbench/common/constants';
|
||||
import { DotnetInteractiveLanguagePrefix } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
import { IModelContentChangedEvent } from 'vs/editor/common/textModelEvents';
|
||||
|
||||
let modelId = 0;
|
||||
@@ -659,10 +658,7 @@ export class CellModel extends Disposable implements ICellModel {
|
||||
if (tryMatchCellMagic(this.source[0]) !== ads_execute_command || !this._isCommandExecutionSettingEnabled) {
|
||||
const future = kernel.requestExecute({
|
||||
code: content,
|
||||
cellIndex: this.notebookModel.findCellIndex(this),
|
||||
stop_on_error: true,
|
||||
notebookUri: this.notebookModel.notebookUri,
|
||||
cellUri: this.cellUri,
|
||||
language: this.language
|
||||
}, false);
|
||||
this.setFuture(future as FutureInternal);
|
||||
@@ -1020,10 +1016,6 @@ export class CellModel extends Disposable implements ICellModel {
|
||||
if (this._configurationService?.getValue('notebook.saveConnectionName')) {
|
||||
metadata.connection_name = this._savedConnectionName;
|
||||
}
|
||||
// Set .NET Interactive language field for vscode compatibility
|
||||
if (this._language?.startsWith(DotnetInteractiveLanguagePrefix)) {
|
||||
(cellJson.metadata as ICellMetadata).dotnet_interactive = { language: this._language.replace(DotnetInteractiveLanguagePrefix, '') };
|
||||
}
|
||||
} else if (this._cellType === CellTypes.Markdown && this._attachments) {
|
||||
cellJson.attachments = this._attachments;
|
||||
}
|
||||
@@ -1110,8 +1102,6 @@ export class CellModel extends Disposable implements ICellModel {
|
||||
this._language = 'markdown';
|
||||
} else if (metadata?.language) {
|
||||
this._language = metadata.language;
|
||||
} else if (metadata?.dotnet_interactive?.language) {
|
||||
this._language = `dotnet-interactive.${metadata.dotnet_interactive.language}`;
|
||||
} else {
|
||||
this._language = this._options?.notebook?.language;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { AddCellEdit, CellOutputEdit, ConvertCellTypeEdit, DeleteCellEdit, MoveCellEdit, CellOutputDataEdit, SplitCellEdit } from 'sql/workbench/services/notebook/browser/models/cellEdit';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { DotnetInteractiveDisplayName } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
import { IPYKERNEL_DISPLAY_NAME } from 'sql/workbench/common/constants';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
@@ -1404,11 +1403,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
let standardKernel = this._standardKernels.find(kernel => kernel.displayName === displayName || displayName.startsWith(kernel.displayName));
|
||||
if (standardKernel) {
|
||||
if (this._savedKernelInfo.name && this._savedKernelInfo.name !== standardKernel.name) {
|
||||
// Special case .NET Interactive kernel name to handle inconsistencies between notebook providers and jupyter kernel specs
|
||||
if (this._savedKernelInfo.display_name === DotnetInteractiveDisplayName) {
|
||||
this._savedKernelInfo.oldName = this._savedKernelInfo.name;
|
||||
}
|
||||
|
||||
this._savedKernelInfo.name = standardKernel.name;
|
||||
this._savedKernelInfo.display_name = standardKernel.displayName;
|
||||
} else if (displayName === IPYKERNEL_DISPLAY_NAME && this._savedKernelInfo.name === standardKernel.name) {
|
||||
@@ -1500,9 +1494,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
display_name: spec.display_name,
|
||||
language: spec.language,
|
||||
supportedLanguages: spec.supportedLanguages,
|
||||
oldName: spec.oldName,
|
||||
oldDisplayName: spec.oldDisplayName,
|
||||
oldLanguage: spec.oldLanguage
|
||||
};
|
||||
this.clientSession?.configureKernel(this._savedKernelInfo);
|
||||
} catch (err) {
|
||||
@@ -1631,26 +1622,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
delete metadata.kernelspec?.supportedLanguages;
|
||||
|
||||
metadata.language_info = this.languageInfo;
|
||||
|
||||
// Undo special casing for .NET Interactive
|
||||
if (metadata.kernelspec?.oldName) {
|
||||
metadata.kernelspec.name = metadata.kernelspec.oldName;
|
||||
delete metadata.kernelspec.oldName;
|
||||
}
|
||||
if (metadata.kernelspec?.oldDisplayName) {
|
||||
metadata.kernelspec.display_name = metadata.kernelspec.oldDisplayName;
|
||||
delete metadata.kernelspec.oldDisplayName;
|
||||
}
|
||||
if (metadata.kernelspec?.oldLanguage) {
|
||||
metadata.kernelspec.language = metadata.kernelspec.oldLanguage;
|
||||
delete metadata.kernelspec.oldLanguage;
|
||||
}
|
||||
if (metadata.language_info?.oldName) {
|
||||
metadata.language_info.name = metadata.language_info?.oldName;
|
||||
delete metadata.language_info?.oldName;
|
||||
}
|
||||
|
||||
|
||||
metadata.tags = this._tags;
|
||||
metadata.multi_connection_mode = this._multiConnectionMode ? this._multiConnectionMode : undefined;
|
||||
if (this.configurationService.getValue(saveConnectionNameConfigName)) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import { IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { INotebookInput } from 'sql/workbench/services/notebook/browser/interface';
|
||||
import { INotebookShowOptions } from 'sql/workbench/api/common/sqlExtHost.protocol';
|
||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { ICodeEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
import { JUPYTER_PROVIDER_ID } from 'sql/workbench/common/constants';
|
||||
import { IStandardKernelWithProvider } from 'sql/workbench/services/notebook/browser/models/notebookUtils';
|
||||
@@ -145,13 +144,9 @@ export interface INotebookService {
|
||||
*/
|
||||
notifyCellExecutionStarted(): void;
|
||||
|
||||
createNotebookInputFromContents(providerId: string, contents?: azdata.nb.INotebookContents, resource?: UriComponents): Promise<EditorInput | undefined>;
|
||||
|
||||
openNotebook(resource: UriComponents, options: INotebookShowOptions): Promise<IEditorPane | undefined>;
|
||||
|
||||
getUntitledUriPath(originalTitle: string): string;
|
||||
|
||||
getNotebookURIForCell(cellUri: URI): URI | undefined;
|
||||
}
|
||||
|
||||
export interface IExecuteProvider {
|
||||
|
||||
@@ -50,7 +50,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
|
||||
import { IEditorPane, IUntypedFileEditorInput } from 'vs/workbench/common/editor';
|
||||
import { isINotebookInput } from 'sql/workbench/services/notebook/browser/interface';
|
||||
import { INotebookShowOptions } from 'sql/workbench/api/common/sqlExtHost.protocol';
|
||||
import { DEFAULT_NOTEBOOK_FILETYPE, INTERACTIVE_PROVIDER_ID, NotebookLanguage } from 'sql/workbench/common/constants';
|
||||
import { DEFAULT_NOTEBOOK_FILETYPE, NotebookLanguage } from 'sql/workbench/common/constants';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { SqlSerializationProvider } from 'sql/workbench/services/notebook/browser/sql/sqlSerializationProvider';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
@@ -263,22 +263,6 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public async createNotebookInputFromContents(providerId: string, contents?: nb.INotebookContents, resource?: UriComponents): Promise<EditorInput> {
|
||||
let uri: URI;
|
||||
if (resource) {
|
||||
uri = URI.revive(resource);
|
||||
} else {
|
||||
uri = this.getUntitledFileUri();
|
||||
resource = uri;
|
||||
}
|
||||
|
||||
let options: INotebookShowOptions = {
|
||||
providerId: providerId,
|
||||
initialContent: contents
|
||||
};
|
||||
return this.createNotebookInput(options, resource);
|
||||
}
|
||||
|
||||
private async createNotebookInput(options: INotebookShowOptions, resource?: UriComponents): Promise<EditorInput | undefined> {
|
||||
let uri: URI;
|
||||
if (resource) {
|
||||
@@ -292,7 +276,7 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
let isUntitled: boolean = uri.scheme === Schemas.untitled;
|
||||
|
||||
let fileInput: EditorInput;
|
||||
let languageId = options.providerId === INTERACTIVE_PROVIDER_ID ? NotebookLanguage.Interactive : NotebookLanguage.Notebook;
|
||||
let languageId = NotebookLanguage.Notebook;
|
||||
let initialStringContents: string;
|
||||
if (options.initialContent) {
|
||||
if (typeof options.initialContent === 'string') {
|
||||
@@ -385,19 +369,6 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
return title;
|
||||
}
|
||||
|
||||
public getNotebookURIForCell(cellUri: URI): URI | undefined {
|
||||
for (let editor of this.listNotebookEditors()) {
|
||||
if (editor.cells) {
|
||||
for (let cell of editor.cells) {
|
||||
if (cell.cellUri === cellUri) {
|
||||
return editor.notebookParams.notebookUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private updateSQLRegistrationWithConnectionProviders() {
|
||||
// Update the SQL extension
|
||||
let sqlNotebookKernels = this._providerToStandardKernels.get(notebookConstants.SQL);
|
||||
@@ -439,12 +410,6 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
this._executeProviders.set(p.id, new ExecuteProviderDescriptor(p.id));
|
||||
}
|
||||
this.addStandardKernels(registration, registration.fileExtensions);
|
||||
} else {
|
||||
// Standard kernels might get registered later for VSCode notebooks, so add a descriptor to wait on
|
||||
if (!this._providerToStandardKernels.has(p.id)) {
|
||||
let descriptor = new StandardKernelsDescriptor(p.id);
|
||||
this._providerToStandardKernels.set(p.id.toUpperCase(), descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit activation event if the provider is not one of the default options
|
||||
|
||||
@@ -55,26 +55,12 @@ export const textRendererFactory: IRenderMime.IRendererFactory = {
|
||||
mimeTypes: [
|
||||
'text/plain',
|
||||
'application/vnd.jupyter.stdout',
|
||||
'application/vnd.jupyter.stderr',
|
||||
'application/vnd.code.notebook.stdout',
|
||||
'application/vnd.code.notebook.stderr'
|
||||
'application/vnd.jupyter.stderr'
|
||||
],
|
||||
defaultRank: 120,
|
||||
createRenderer: options => new widgets.RenderedText(options)
|
||||
};
|
||||
|
||||
/**
|
||||
* A mime renderer factory for VS Code Notebook error data.
|
||||
*/
|
||||
export const errorRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: true,
|
||||
mimeTypes: [
|
||||
'application/vnd.code.notebook.error'
|
||||
],
|
||||
defaultRank: 121,
|
||||
createRenderer: options => new widgets.ErrorText(options)
|
||||
};
|
||||
|
||||
/**
|
||||
* A placeholder factory for deprecated rendered JavaScript.
|
||||
*/
|
||||
@@ -115,7 +101,6 @@ export const standardRendererFactories: ReadonlyArray<IRenderMime.IRendererFacto
|
||||
imageRendererFactory,
|
||||
javaScriptRendererFactory,
|
||||
textRendererFactory,
|
||||
errorRendererFactory,
|
||||
dataResourceRendererFactory,
|
||||
ipywidgetFactory
|
||||
];
|
||||
|
||||
@@ -319,34 +319,6 @@ export class RenderedText extends RenderedCommon {
|
||||
}
|
||||
}
|
||||
|
||||
export class ErrorText extends RenderedCommon {
|
||||
/**
|
||||
* Construct a new error text widget.
|
||||
*
|
||||
* @param options - The options for initializing the widget.
|
||||
*/
|
||||
constructor(options: IRenderMime.IRendererOptions) {
|
||||
super(options);
|
||||
this.addClass('jp-ErrorText');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a mime model.
|
||||
*
|
||||
* @param model - The mime model to render.
|
||||
*
|
||||
* @returns A promise which resolves when rendering is complete.
|
||||
*/
|
||||
render(model: IRenderMime.IMimeModel): Promise<void> {
|
||||
let err = JSON.parse(String(model.data[this.mimeType]));
|
||||
let text = err.name && err.message ? `${err.name}: ${err.message}` : err.name || err.message;
|
||||
return renderers.renderText({
|
||||
host: this.node,
|
||||
source: text
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A widget for displaying deprecated JavaScript output.
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,6 @@ import { localize } from 'vs/nls';
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import * as azdata from 'azdata';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { DEFAULT_NOTEBOOK_FILETYPE, VSCODE_JUPYTER_PROVIDER_ID } from 'sql/workbench/common/constants';
|
||||
|
||||
export const NotebookProviderRegistryId = 'notebooks.providers.registry';
|
||||
|
||||
@@ -119,8 +118,6 @@ export interface INotebookProviderRegistry {
|
||||
|
||||
readonly onNewDescriptionRegistration: Event<{ id: string, registration: ProviderDescriptionRegistration }>;
|
||||
|
||||
updateProviderKernels(providerId: string, kernels: azdata.nb.IStandardKernel[]): void;
|
||||
updateKernelLanguages(providerId: string, kernelName: string, languages: string[]): void;
|
||||
registerProviderDescription(provider: ProviderDescriptionRegistration): void;
|
||||
registerNotebookLanguageMagic(magic: NotebookLanguageMagicRegistration): void;
|
||||
}
|
||||
@@ -132,43 +129,6 @@ class NotebookProviderRegistry implements INotebookProviderRegistry {
|
||||
private _onNewDescriptionRegistration = new Emitter<{ id: string, registration: ProviderDescriptionRegistration }>();
|
||||
public readonly onNewDescriptionRegistration: Event<{ id: string, registration: ProviderDescriptionRegistration }> = this._onNewDescriptionRegistration.event;
|
||||
|
||||
private readonly providerNotInRegistryError = (providerId: string): string => localize('providerNotInRegistryError', "The specified provider '{0}' is not present in the notebook registry.", providerId);
|
||||
|
||||
updateProviderKernels(providerId: string, kernels: azdata.nb.IStandardKernel[]): void {
|
||||
let registration = this._providerDescriptionRegistration.get(providerId);
|
||||
if (!registration) {
|
||||
// Newer versions of the Jupyter extension don't contribute a provider for the default file type, so
|
||||
// register the original provider details here to preserve backwards compatibility for .NET Interactive
|
||||
if (providerId === VSCODE_JUPYTER_PROVIDER_ID) {
|
||||
registration = {
|
||||
provider: VSCODE_JUPYTER_PROVIDER_ID,
|
||||
fileExtensions: [DEFAULT_NOTEBOOK_FILETYPE],
|
||||
standardKernels: undefined
|
||||
};
|
||||
} else {
|
||||
throw new Error(this.providerNotInRegistryError(providerId));
|
||||
}
|
||||
}
|
||||
registration.standardKernels = kernels;
|
||||
|
||||
// Update provider description with new info
|
||||
this.registerProviderDescription(registration);
|
||||
}
|
||||
|
||||
updateKernelLanguages(providerId: string, kernelName: string, languages: string[]): void {
|
||||
let registration = this._providerDescriptionRegistration.get(providerId);
|
||||
if (!registration) {
|
||||
throw new Error(this.providerNotInRegistryError(providerId));
|
||||
}
|
||||
let kernel = registration.standardKernels?.find(kernel => kernel.name === kernelName);
|
||||
if (kernel) {
|
||||
kernel.supportedLanguages = languages;
|
||||
}
|
||||
|
||||
// Update provider description with new info
|
||||
this.registerProviderDescription(registration);
|
||||
}
|
||||
|
||||
registerProviderDescription(registration: ProviderDescriptionRegistration): void {
|
||||
this._providerDescriptionRegistration.set(registration.provider, registration);
|
||||
this._onNewDescriptionRegistration.fire({ id: registration.provider, registration: registration });
|
||||
|
||||
@@ -37,7 +37,7 @@ suite('ExtHostNotebook Tests', () => {
|
||||
drain: () => { return undefined; },
|
||||
dispose: () => { return; },
|
||||
};
|
||||
extHostNotebook = new ExtHostNotebook(mainContext, undefined);
|
||||
extHostNotebook = new ExtHostNotebook(mainContext);
|
||||
notebookUri = URI.parse('file:/user/default/my.ipynb');
|
||||
serializationProviderMock = TypeMoq.Mock.ofType(SerializationProviderStub, TypeMoq.MockBehavior.Loose);
|
||||
serializationProviderMock.callBase = true;
|
||||
|
||||
@@ -1,605 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSCodeContentManager } from 'sql/workbench/api/common/notebooks/vscodeSerializationProvider';
|
||||
import type * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import * as sinon from 'sinon';
|
||||
import { NotebookCellKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import * as assert from 'assert';
|
||||
import { OutputTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { NBFORMAT, NBFORMAT_MINOR } from 'sql/workbench/common/constants';
|
||||
import { convertToVSCodeNotebookCell, convertToVSCodeCellOutput, convertToADSCellOutput, convertToInternalInteractiveKernelMetadata, addExternalInteractiveKernelMetadata } from 'sql/workbench/api/common/notebooks/notebookUtils';
|
||||
import { VSCodeNotebookDocument } from 'sql/workbench/api/common/notebooks/vscodeNotebookDocument';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { VSCodeNotebookEditor } from 'sql/workbench/api/common/notebooks/vscodeNotebookEditor';
|
||||
|
||||
class MockNotebookSerializer implements vscode.NotebookSerializer {
|
||||
deserializeNotebook(content: Uint8Array, token: vscode.CancellationToken): vscode.NotebookData | Thenable<vscode.NotebookData> {
|
||||
return undefined;
|
||||
}
|
||||
serializeNotebook(data: vscode.NotebookData, token: vscode.CancellationToken): Uint8Array | Thenable<Uint8Array> {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
}
|
||||
|
||||
suite('Notebook Serializer', () => {
|
||||
let contentManager: VSCodeContentManager;
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let serializeSpy: sinon.SinonSpy;
|
||||
|
||||
const deserializeResult: vscode.NotebookData = {
|
||||
cells: [{
|
||||
kind: NotebookCellKind.Code,
|
||||
value: '1+1',
|
||||
languageId: 'python',
|
||||
outputs: [{
|
||||
id: '1',
|
||||
items: [{
|
||||
mime: 'text/plain',
|
||||
data: VSBuffer.fromString('2').buffer
|
||||
}],
|
||||
metadata: {}
|
||||
}],
|
||||
executionSummary: {
|
||||
executionOrder: 1
|
||||
}
|
||||
}, {
|
||||
kind: NotebookCellKind.Code,
|
||||
value: 'print(1)',
|
||||
languageId: 'python',
|
||||
outputs: [{
|
||||
id: '2',
|
||||
items: [{
|
||||
mime: 'text/plain',
|
||||
data: VSBuffer.fromString('1').buffer
|
||||
}],
|
||||
metadata: {}
|
||||
}],
|
||||
executionSummary: {
|
||||
executionOrder: 2
|
||||
}
|
||||
}],
|
||||
metadata: {
|
||||
custom: {
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
name: 'python3',
|
||||
display_name: 'Python 3',
|
||||
language: 'python'
|
||||
},
|
||||
language_info: {
|
||||
name: 'python',
|
||||
version: '3.8.10',
|
||||
mimetype: 'text/x-python',
|
||||
codemirror_mode: {
|
||||
name: 'ipython',
|
||||
version: '3'
|
||||
}
|
||||
}
|
||||
},
|
||||
nbformat: NBFORMAT,
|
||||
nbformat_minor: NBFORMAT_MINOR
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const expectedDeserializedNotebook: azdata.nb.INotebookContents = {
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
name: 'python3',
|
||||
display_name: 'Python 3',
|
||||
language: 'python'
|
||||
},
|
||||
language_info: {
|
||||
name: 'python',
|
||||
version: '3.8.10',
|
||||
mimetype: 'text/x-python',
|
||||
codemirror_mode: {
|
||||
name: 'ipython',
|
||||
version: '3'
|
||||
}
|
||||
}
|
||||
},
|
||||
nbformat: NBFORMAT,
|
||||
nbformat_minor: NBFORMAT_MINOR,
|
||||
cells: [
|
||||
{
|
||||
cell_type: 'code',
|
||||
source: '1+1',
|
||||
outputs: [
|
||||
{
|
||||
id: '1',
|
||||
output_type: 'execute_result',
|
||||
data: {
|
||||
'text/plain': '2'
|
||||
},
|
||||
metadata: {},
|
||||
execution_count: 1
|
||||
} as azdata.nb.IExecuteResult
|
||||
],
|
||||
execution_count: 1,
|
||||
metadata: {
|
||||
language: 'python'
|
||||
}
|
||||
},
|
||||
{
|
||||
cell_type: 'code',
|
||||
source: 'print(1)',
|
||||
outputs: [
|
||||
{
|
||||
id: '2',
|
||||
output_type: 'execute_result',
|
||||
data: {
|
||||
'text/plain': '1'
|
||||
},
|
||||
metadata: {},
|
||||
execution_count: 2
|
||||
} as azdata.nb.IExecuteResult
|
||||
],
|
||||
execution_count: 2,
|
||||
metadata: {
|
||||
language: 'python'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const expectedSerializeArg: vscode.NotebookData = {
|
||||
cells: [{
|
||||
kind: NotebookCellKind.Code,
|
||||
value: '1+1',
|
||||
languageId: 'python',
|
||||
outputs: [{
|
||||
items: [{
|
||||
mime: 'text/plain',
|
||||
data: VSBuffer.fromString('2').buffer
|
||||
}],
|
||||
metadata: {},
|
||||
id: '1'
|
||||
}],
|
||||
executionSummary: {
|
||||
executionOrder: 1
|
||||
},
|
||||
metadata: {
|
||||
custom: {
|
||||
metadata: {
|
||||
language: 'python'
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
kind: NotebookCellKind.Code,
|
||||
value: 'print(1)',
|
||||
languageId: 'python',
|
||||
outputs: [{
|
||||
items: [{
|
||||
mime: 'text/plain',
|
||||
data: VSBuffer.fromString('1').buffer
|
||||
}],
|
||||
metadata: {},
|
||||
id: '2'
|
||||
}],
|
||||
executionSummary: {
|
||||
executionOrder: 2
|
||||
},
|
||||
metadata: {
|
||||
custom: {
|
||||
metadata: {
|
||||
language: 'python'
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
metadata: {
|
||||
custom: {
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
name: 'python3',
|
||||
display_name: 'Python 3',
|
||||
language: 'python'
|
||||
},
|
||||
language_info: {
|
||||
name: 'python',
|
||||
version: '3.8.10',
|
||||
mimetype: 'text/x-python',
|
||||
codemirror_mode: {
|
||||
name: 'ipython',
|
||||
version: '3'
|
||||
}
|
||||
}
|
||||
},
|
||||
nbformat: NBFORMAT,
|
||||
nbformat_minor: NBFORMAT_MINOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
let serializer = new MockNotebookSerializer();
|
||||
sandbox.stub(serializer, 'deserializeNotebook').returns(deserializeResult);
|
||||
serializeSpy = sandbox.spy(serializer, 'serializeNotebook');
|
||||
|
||||
contentManager = new VSCodeContentManager(serializer);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
||||
test('Convert VSCode notebook output to ADS notebook output', async () => {
|
||||
let cellOutput: vscode.NotebookCellOutput = {
|
||||
items: [{
|
||||
mime: 'text/plain',
|
||||
data: VSBuffer.fromString('2').buffer
|
||||
}, {
|
||||
mime: 'text/html',
|
||||
data: VSBuffer.fromString('<i>2</i>').buffer
|
||||
}],
|
||||
metadata: {},
|
||||
id: '1'
|
||||
};
|
||||
let expectedADSOutput: azdata.nb.IExecuteResult[] = [
|
||||
{
|
||||
id: '1',
|
||||
output_type: 'execute_result',
|
||||
data: {
|
||||
'text/plain': '2',
|
||||
'text/html': '<i>2</i>'
|
||||
},
|
||||
metadata: {},
|
||||
execution_count: 1
|
||||
}
|
||||
];
|
||||
|
||||
let actualOutput = convertToADSCellOutput(cellOutput, 1);
|
||||
assert.deepStrictEqual(actualOutput, expectedADSOutput);
|
||||
});
|
||||
|
||||
test('Convert ADS notebook execute result to VSCode notebook output', async () => {
|
||||
let cellOutput: azdata.nb.IExecuteResult = {
|
||||
id: 'testId',
|
||||
output_type: OutputTypes.ExecuteResult,
|
||||
data: {
|
||||
'text/plain': 'abc',
|
||||
'text/html': '<i>abc</i>'
|
||||
},
|
||||
execution_count: 1
|
||||
};
|
||||
let expectedVSCodeOutput: vscode.NotebookCellOutput = {
|
||||
items: [{
|
||||
mime: 'text/plain',
|
||||
data: VSBuffer.fromString('abc').buffer
|
||||
}, {
|
||||
mime: 'text/html',
|
||||
data: VSBuffer.fromString('<i>abc</i>').buffer
|
||||
}],
|
||||
id: 'testId',
|
||||
metadata: undefined
|
||||
};
|
||||
let actualOutput = convertToVSCodeCellOutput(cellOutput);
|
||||
assert.deepStrictEqual(actualOutput, expectedVSCodeOutput);
|
||||
});
|
||||
|
||||
test('Convert ADS notebook stream result to VSCode notebook output', async () => {
|
||||
let cellOutput: azdata.nb.IStreamResult = {
|
||||
id: 'testId',
|
||||
output_type: 'stream',
|
||||
name: 'stdout',
|
||||
text: [
|
||||
'abc'
|
||||
]
|
||||
};
|
||||
let expectedVSCodeOutput: vscode.NotebookCellOutput = {
|
||||
items: [{
|
||||
mime: 'text/html',
|
||||
data: VSBuffer.fromString('abc').buffer
|
||||
}],
|
||||
id: 'testId',
|
||||
metadata: undefined
|
||||
};
|
||||
let actualOutput = convertToVSCodeCellOutput(cellOutput);
|
||||
assert.deepStrictEqual(actualOutput, expectedVSCodeOutput);
|
||||
});
|
||||
|
||||
test('Convert ADS notebook error with trace to VSCode notebook output', async () => {
|
||||
let cellOutput: azdata.nb.IErrorResult = {
|
||||
id: 'testId',
|
||||
output_type: 'error',
|
||||
ename: 'TestException',
|
||||
evalue: 'Expected test error',
|
||||
traceback: ['Trace line 1', 'Trace line 2']
|
||||
};
|
||||
let expectedVSCodeOutput: vscode.NotebookCellOutput = {
|
||||
items: [{
|
||||
mime: 'text/html',
|
||||
data: VSBuffer.fromString('TestException: Expected test error\nTrace line 1\nTrace line 2').buffer
|
||||
}],
|
||||
id: 'testId',
|
||||
metadata: undefined
|
||||
};
|
||||
let actualOutput = convertToVSCodeCellOutput(cellOutput);
|
||||
assert.deepStrictEqual(actualOutput, expectedVSCodeOutput);
|
||||
});
|
||||
|
||||
test('Convert ADS notebook error without trace to VSCode notebook output', async () => {
|
||||
let cellOutput: azdata.nb.IErrorResult = {
|
||||
id: 'testId',
|
||||
output_type: 'error',
|
||||
ename: 'TestException',
|
||||
evalue: 'Expected test error'
|
||||
};
|
||||
let expectedVSCodeOutput: vscode.NotebookCellOutput = {
|
||||
items: [{
|
||||
mime: 'text/html',
|
||||
data: VSBuffer.fromString('TestException: Expected test error').buffer
|
||||
}],
|
||||
id: 'testId',
|
||||
metadata: undefined
|
||||
};
|
||||
let actualOutput = convertToVSCodeCellOutput(cellOutput);
|
||||
assert.deepStrictEqual(actualOutput, expectedVSCodeOutput);
|
||||
});
|
||||
|
||||
test('Deserialize VSCode notebook into ADS notebook data', async () => {
|
||||
let output = await contentManager.deserializeNotebook(''); // Argument is ignored since we're returning a mocked result
|
||||
assert.deepStrictEqual(output, expectedDeserializedNotebook);
|
||||
});
|
||||
|
||||
test('Serialize ADS notebook data into VSCode notebook strings', async () => {
|
||||
await contentManager.serializeNotebook(expectedDeserializedNotebook); // Argument is ignored since we're returning a mocked result
|
||||
assert(serializeSpy.calledOnce);
|
||||
assert.deepStrictEqual(serializeSpy.firstCall.args[0], expectedSerializeArg);
|
||||
});
|
||||
|
||||
const testDoc: azdata.nb.NotebookDocument = {
|
||||
uri: URI.parse('untitled:a/b/c/testNotebook.ipynb'),
|
||||
fileName: 'testFile',
|
||||
providerId: 'testProvider',
|
||||
isUntitled: true,
|
||||
isDirty: true,
|
||||
isClosed: false,
|
||||
cells: [{
|
||||
contents: {
|
||||
cell_type: 'code',
|
||||
source: '1+1',
|
||||
metadata: {
|
||||
language: 'python'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
contents: {
|
||||
cell_type: 'markdown',
|
||||
source: 'abc'
|
||||
}
|
||||
}],
|
||||
kernelSpec: {
|
||||
name: 'testKernel',
|
||||
language: 'testLanguage',
|
||||
display_name: 'testKernelName'
|
||||
},
|
||||
save: () => undefined,
|
||||
setTrusted: () => undefined,
|
||||
validateCellRange: () => undefined
|
||||
};
|
||||
|
||||
function validateDocsMatch(actualDoc: vscode.NotebookDocument, expectedDoc: vscode.NotebookDocument): void {
|
||||
assert.deepStrictEqual(actualDoc.uri, expectedDoc.uri);
|
||||
assert.strictEqual(actualDoc.notebookType, expectedDoc.notebookType);
|
||||
assert.strictEqual(actualDoc.version, expectedDoc.version);
|
||||
assert.strictEqual(actualDoc.isDirty, expectedDoc.isDirty);
|
||||
assert.strictEqual(actualDoc.isUntitled, expectedDoc.isUntitled);
|
||||
assert.strictEqual(actualDoc.isClosed, expectedDoc.isClosed);
|
||||
assert.deepStrictEqual(actualDoc.metadata, expectedDoc.metadata);
|
||||
assert.strictEqual(actualDoc.cellCount, expectedDoc.cellCount);
|
||||
}
|
||||
|
||||
test('Convert ADS NotebookDocument into VS Code NotebookDocument', async () => {
|
||||
let expectedDoc: vscode.NotebookDocument = {
|
||||
get uri() { return testDoc.uri; },
|
||||
get notebookType() { return testDoc.providerId; },
|
||||
get version() { return undefined; },
|
||||
get isDirty() { return true; },
|
||||
get isUntitled() { return true; },
|
||||
get isClosed() { return false; },
|
||||
get metadata() { return {}; },
|
||||
get cellCount() { return 2; },
|
||||
cellAt: () => undefined,
|
||||
getCells: () => undefined,
|
||||
save: () => undefined
|
||||
};
|
||||
|
||||
let actualDoc = new VSCodeNotebookDocument(testDoc);
|
||||
validateDocsMatch(actualDoc, expectedDoc);
|
||||
});
|
||||
|
||||
// Have to validate cell fields manually since one of the NotebookCell fields is a function pointer,
|
||||
// which throws off the deepEqual assertions.
|
||||
function validateCellMatches(actual: vscode.NotebookCell, expected: vscode.NotebookCell): void {
|
||||
assert.strictEqual(actual.index, expected.index);
|
||||
assert.deepStrictEqual(actual.document.uri, expected.document.uri);
|
||||
assert.strictEqual(actual.document.languageId, expected.document.languageId);
|
||||
assert.deepStrictEqual(actual.notebook.uri, expected.notebook.uri);
|
||||
assert.deepStrictEqual(actual.document.notebook.uri, expected.document.notebook.uri);
|
||||
assert.deepStrictEqual(actual.document.notebook.uri, expected.notebook.uri);
|
||||
assert.deepStrictEqual(actual.notebook.uri, expected.document.notebook.uri);
|
||||
}
|
||||
function validateCellsMatch(actual: vscode.NotebookCell[], expected: vscode.NotebookCell[]): void {
|
||||
assert.strictEqual(actual.length, expected.length, 'Cell arrays did not have equal lengths.');
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
validateCellMatches(actual[i], expected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test('Retrieve range of cells from VS Code NotebookDocument', async () => {
|
||||
let expectedCells: vscode.NotebookCell[] = testDoc.cells.map((cell, index) => convertToVSCodeNotebookCell(cell.contents.cell_type, index, cell.uri, testDoc.uri, cell.contents.metadata?.language, cell.contents.source));
|
||||
let vsDoc = new VSCodeNotebookDocument(testDoc);
|
||||
|
||||
let actualCells = vsDoc.getCells();
|
||||
validateCellsMatch(actualCells, expectedCells);
|
||||
|
||||
actualCells = vsDoc.getCells({ start: 0, end: 2, isEmpty: false, with: () => undefined });
|
||||
validateCellsMatch(actualCells, expectedCells);
|
||||
|
||||
actualCells = vsDoc.getCells({ start: 0, end: 1, isEmpty: false, with: () => undefined });
|
||||
validateCellsMatch(actualCells, [expectedCells[0]]);
|
||||
|
||||
actualCells = vsDoc.getCells({ start: 1, end: 2, isEmpty: false, with: () => undefined });
|
||||
validateCellsMatch(actualCells, [expectedCells[1]]);
|
||||
});
|
||||
|
||||
test('Retrieve specific cell from VS Code NotebookDocument', async () => {
|
||||
let expectedCells: vscode.NotebookCell[] = testDoc.cells.map((cell, index) => convertToVSCodeNotebookCell(cell.contents.cell_type, index, cell.uri, testDoc.uri, cell.contents.metadata?.language, cell.contents.source));
|
||||
let vsDoc = new VSCodeNotebookDocument(testDoc);
|
||||
|
||||
let firstCell = vsDoc.cellAt(0);
|
||||
validateCellMatches(firstCell, expectedCells[0]);
|
||||
|
||||
firstCell = vsDoc.cellAt(-5);
|
||||
validateCellMatches(firstCell, expectedCells[0]);
|
||||
|
||||
let secondCell = vsDoc.cellAt(1);
|
||||
validateCellMatches(secondCell, expectedCells[1]);
|
||||
|
||||
secondCell = vsDoc.cellAt(10);
|
||||
validateCellMatches(secondCell, expectedCells[1]);
|
||||
});
|
||||
|
||||
test('VS Code NotebookEditor functionality', async () => {
|
||||
let editor = <azdata.nb.NotebookEditor>{ document: testDoc };
|
||||
let vscodeEditor = new VSCodeNotebookEditor(editor);
|
||||
let expectedDoc = new VSCodeNotebookDocument(testDoc);
|
||||
|
||||
validateDocsMatch(vscodeEditor.document, expectedDoc);
|
||||
|
||||
// We only need the document field for VSCodeNotebookEditor, so the other
|
||||
// fields should be non-functional
|
||||
assert.throws(() => vscodeEditor.selections);
|
||||
assert.throws(() => vscodeEditor.visibleRanges);
|
||||
assert.throws(() => vscodeEditor.viewColumn);
|
||||
assert.throws(() => vscodeEditor.revealRange(undefined));
|
||||
await assert.rejects(() => vscodeEditor.edit(() => undefined));
|
||||
});
|
||||
});
|
||||
|
||||
suite('.NET Interactive Kernel Metadata Conversion', async () => {
|
||||
test('Convert to internal kernel metadata', async () => {
|
||||
let originalMetadata: azdata.nb.INotebookMetadata = {
|
||||
kernelspec: {
|
||||
name: '.net-csharp',
|
||||
display_name: '.NET (C#)',
|
||||
language: 'C#'
|
||||
},
|
||||
language_info: {
|
||||
name: 'C#'
|
||||
}
|
||||
};
|
||||
let expectedCovertedMetadata: azdata.nb.INotebookMetadata = {
|
||||
kernelspec: {
|
||||
name: '.net-csharp',
|
||||
display_name: '.NET Interactive',
|
||||
language: 'dotnet-interactive.csharp',
|
||||
oldDisplayName: '.NET (C#)',
|
||||
oldLanguage: 'C#'
|
||||
},
|
||||
language_info: {
|
||||
name: 'dotnet-interactive.csharp',
|
||||
oldName: 'C#'
|
||||
}
|
||||
};
|
||||
|
||||
convertToInternalInteractiveKernelMetadata(originalMetadata);
|
||||
assert.deepStrictEqual(originalMetadata, expectedCovertedMetadata);
|
||||
});
|
||||
|
||||
test('Do not convert to internal metadata for non-Interactive kernels', async () => {
|
||||
let originalMetadata: azdata.nb.INotebookMetadata = {
|
||||
kernelspec: {
|
||||
name: 'not-interactive',
|
||||
display_name: '.NET (C#)',
|
||||
language: 'C#'
|
||||
},
|
||||
language_info: {
|
||||
name: 'C#'
|
||||
}
|
||||
};
|
||||
let expectedCovertedMetadata: azdata.nb.INotebookMetadata = {
|
||||
kernelspec: {
|
||||
name: 'not-interactive',
|
||||
display_name: '.NET (C#)',
|
||||
language: 'C#'
|
||||
},
|
||||
language_info: {
|
||||
name: 'C#'
|
||||
}
|
||||
};
|
||||
|
||||
convertToInternalInteractiveKernelMetadata(originalMetadata);
|
||||
assert.deepStrictEqual(originalMetadata, expectedCovertedMetadata);
|
||||
});
|
||||
|
||||
test('Add external kernel metadata', async () => {
|
||||
let originalKernelSpec: azdata.nb.IKernelSpec = {
|
||||
name: 'jupyter-notebook',
|
||||
display_name: '.NET Interactive',
|
||||
language: 'dotnet-interactive.csharp'
|
||||
};
|
||||
let expectedCovertedKernel: azdata.nb.IKernelSpec = {
|
||||
name: 'jupyter-notebook',
|
||||
display_name: '.NET Interactive',
|
||||
language: 'dotnet-interactive.csharp',
|
||||
oldName: '.net-csharp',
|
||||
oldDisplayName: '.NET (C#)',
|
||||
oldLanguage: 'C#'
|
||||
};
|
||||
addExternalInteractiveKernelMetadata(originalKernelSpec);
|
||||
assert.deepStrictEqual(originalKernelSpec, expectedCovertedKernel);
|
||||
});
|
||||
|
||||
test('Do not add external metadata to non-Interactive kernels', async () => {
|
||||
// Different kernel name
|
||||
let originalKernelSpec: azdata.nb.IKernelSpec = {
|
||||
name: 'not-interactive',
|
||||
display_name: '.NET Interactive',
|
||||
language: 'dotnet-interactive.csharp'
|
||||
};
|
||||
let expectedCovertedKernel: azdata.nb.IKernelSpec = {
|
||||
name: 'not-interactive',
|
||||
display_name: '.NET Interactive',
|
||||
language: 'dotnet-interactive.csharp'
|
||||
};
|
||||
addExternalInteractiveKernelMetadata(originalKernelSpec);
|
||||
assert.deepStrictEqual(originalKernelSpec, expectedCovertedKernel);
|
||||
|
||||
// Different display name
|
||||
originalKernelSpec = {
|
||||
name: 'jupyter-notebook',
|
||||
display_name: 'Not An Interactive Kernel',
|
||||
language: 'dotnet-interactive.csharp'
|
||||
};
|
||||
expectedCovertedKernel = {
|
||||
name: 'jupyter-notebook',
|
||||
display_name: 'Not An Interactive Kernel',
|
||||
language: 'dotnet-interactive.csharp'
|
||||
};
|
||||
addExternalInteractiveKernelMetadata(originalKernelSpec);
|
||||
assert.deepStrictEqual(originalKernelSpec, expectedCovertedKernel);
|
||||
|
||||
// No language provided
|
||||
originalKernelSpec = {
|
||||
name: 'jupyter-notebook',
|
||||
display_name: '.NET Interactive'
|
||||
};
|
||||
expectedCovertedKernel = {
|
||||
name: 'jupyter-notebook',
|
||||
display_name: '.NET Interactive'
|
||||
};
|
||||
addExternalInteractiveKernelMetadata(originalKernelSpec);
|
||||
assert.deepStrictEqual(originalKernelSpec, expectedCovertedKernel);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user