mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
Initial implementation for VSCode Notebook support (#17885)
This commit is contained in:
207
src/sql/workbench/api/common/adsNotebookController.ts
Normal file
207
src/sql/workbench/api/common/adsNotebookController.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { 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';
|
||||
|
||||
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 InterruptHandler = (notebook: vscode.NotebookDocument) => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* 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>();
|
||||
|
||||
constructor(
|
||||
private _extension: IExtensionDescription,
|
||||
private _id: string,
|
||||
private _viewType: string,
|
||||
private _label: string,
|
||||
private _addLanguagesHandler: (providerId, languages) => void,
|
||||
private _handler?: 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._handler) {
|
||||
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._addLanguagesHandler(this._viewType, 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._handler;
|
||||
}
|
||||
|
||||
public set executeHandler(value: ExecutionHandler) {
|
||||
this._handler = 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 createNotebookCellExecution(cell: vscode.NotebookCell): vscode.NotebookCellExecution {
|
||||
return new ADSNotebookCellExecution(cell);
|
||||
}
|
||||
|
||||
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;
|
||||
constructor(private readonly _cell: vscode.NotebookCell) {
|
||||
this._executionOrder = this._cell.executionSummary?.executionOrder ?? -1;
|
||||
}
|
||||
|
||||
public get cell(): vscode.NotebookCell {
|
||||
return this._cell;
|
||||
}
|
||||
|
||||
public get token(): vscode.CancellationToken {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get executionOrder(): number {
|
||||
return this._executionOrder;
|
||||
}
|
||||
|
||||
public set executionOrder(order: number) {
|
||||
this._executionOrder = order;
|
||||
}
|
||||
|
||||
public start(startTime?: number): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public end(success: boolean, endTime?: number): void {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public async clearOutput(cell?: vscode.NotebookCell): Promise<void> {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public async replaceOutput(out: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell): Promise<void> {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public async appendOutput(out: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell): Promise<void> {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput): Promise<void> {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput): Promise<void> {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
|
||||
@@ -13,6 +13,10 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
import { ExtHostNotebookShape, MainThreadNotebookShape, SqlMainContext } 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/vscodeSerializationProvider';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ADSNotebookController } from 'sql/workbench/api/common/adsNotebookController';
|
||||
import { VSCodeExecuteProvider } from 'sql/workbench/api/common/vscodeExecuteProvider';
|
||||
|
||||
type Adapter = azdata.nb.NotebookSerializationProvider | azdata.nb.SerializationManager | azdata.nb.NotebookExecuteProvider | azdata.nb.ExecuteManager | azdata.nb.ISession | azdata.nb.IKernel | azdata.nb.IFuture;
|
||||
|
||||
@@ -232,7 +236,6 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
|
||||
return sessionManager.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region APIs called by extensions
|
||||
@@ -253,11 +256,22 @@ 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, handler?: (cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController) => void | Thenable<void>, rendererScripts?: vscode.NotebookRendererScript[]): vscode.NotebookController {
|
||||
let addLanguagesHandler = (id, languages) => this._proxy.$updateProviderDescriptionLanguages(id, languages);
|
||||
let controller = new ADSNotebookController(extension, id, viewType, label, addLanguagesHandler, handler, extension.enableProposedApi ? rendererScripts : undefined);
|
||||
let executeProvider = new VSCodeExecuteProvider(controller);
|
||||
this.registerExecuteProvider(executeProvider);
|
||||
return controller;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region private methods
|
||||
|
||||
private getAdapters<A>(ctor: { new(...args: any[]): A }): A[] {
|
||||
let matchingAdapters = [];
|
||||
this._adapters.forEach(a => {
|
||||
|
||||
@@ -48,24 +48,20 @@ export interface IExtensionApiFactory {
|
||||
|
||||
export interface IAdsExtensionApiFactory {
|
||||
azdata: IAzdataExtensionApiFactory;
|
||||
extHostNotebook: ExtHostNotebook;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method instantiates and returns the extension API surface
|
||||
*/
|
||||
export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): IExtensionApiFactory {
|
||||
const { azdata } = createAdsApiFactory(accessor);
|
||||
const { azdata, extHostNotebook } = createAdsApiFactory(accessor);
|
||||
return {
|
||||
azdata,
|
||||
vscode: vsApiFactory(accessor)
|
||||
vscode: vsApiFactory(accessor, extHostNotebook)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface IAdsExtensionApiFactory {
|
||||
azdata: IAzdataExtensionApiFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method instantiates and returns the extension API surface
|
||||
*/
|
||||
@@ -634,6 +630,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
TextType: sqlExtHostTypes.TextType,
|
||||
designers: designers
|
||||
};
|
||||
}
|
||||
},
|
||||
extHostNotebook: extHostNotebook
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import type * as azdata from 'azdata';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { ITaskHandlerDescription } from 'sql/workbench/services/tasks/common/tasks';
|
||||
@@ -887,7 +887,6 @@ export interface MainThreadQueryEditorShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
|
||||
/**
|
||||
* Looks up a notebook manager for a given notebook URI
|
||||
* @returns handle of the manager to be used when sending
|
||||
@@ -935,6 +934,7 @@ 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;
|
||||
$updateProviderDescriptionLanguages(providerId: string, languages: string[]): void;
|
||||
}
|
||||
|
||||
export interface INotebookDocumentsAndEditorsDelta {
|
||||
|
||||
316
src/sql/workbench/api/common/vscodeExecuteProvider.ts
Normal file
316
src/sql/workbench/api/common/vscodeExecuteProvider.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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/adsNotebookController';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
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;
|
||||
|
||||
constructor(private readonly _controller: ADSNotebookController, private readonly _options: azdata.nb.ISessionOptions, language: string) {
|
||||
this._id = this._options.kernelId ?? (VSCodeKernel.kernelId++).toString();
|
||||
this._name = this._options.kernelName ?? this._controller.notebookType;
|
||||
this._info = {
|
||||
protocol_version: '',
|
||||
implementation: '',
|
||||
implementation_version: '',
|
||||
language_info: {
|
||||
name: language,
|
||||
version: '',
|
||||
},
|
||||
banner: '',
|
||||
help_links: [{
|
||||
text: '',
|
||||
url: ''
|
||||
}]
|
||||
};
|
||||
this._kernelSpec = {
|
||||
name: this._name,
|
||||
language: language,
|
||||
display_name: this._name
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
getSpec(): Thenable<azdata.nb.IKernelSpec> {
|
||||
return Promise.resolve(this._kernelSpec);
|
||||
}
|
||||
|
||||
requestExecute(content: azdata.nb.IExecuteRequest, disposeOnDone?: boolean): azdata.nb.IFuture {
|
||||
let executePromise: Promise<void>;
|
||||
if (this._controller.executeHandler) {
|
||||
let cell = <vscode.NotebookCell>{
|
||||
document: <vscode.TextDocument>{
|
||||
uri: content.notebookUri,
|
||||
languageId: this._kernelSpec.language,
|
||||
getText: () => Array.isArray(content.code) ? content.code.join('') : content.code,
|
||||
},
|
||||
notebook: <vscode.NotebookDocument>{
|
||||
uri: content.notebookUri
|
||||
}
|
||||
};
|
||||
|
||||
executePromise = Promise.resolve(this._controller.executeHandler([cell], cell.notebook, this._controller));
|
||||
}
|
||||
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> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class VSCodeSession implements azdata.nb.ISession {
|
||||
private _kernel: VSCodeKernel;
|
||||
private _defaultKernelLoaded = false;
|
||||
constructor(controller: ADSNotebookController, private readonly _options: azdata.nb.ISessionOptions, language: string) {
|
||||
this._kernel = new VSCodeKernel(controller, this._options, language);
|
||||
}
|
||||
|
||||
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 kernel(): azdata.nb.IKernel {
|
||||
return this._kernel;
|
||||
}
|
||||
|
||||
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: azdata.nb.ISession[] = [];
|
||||
|
||||
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 {
|
||||
let languages = this._controller.supportedLanguages?.length > 0 ? this._controller.supportedLanguages : [this._controller.label];
|
||||
return {
|
||||
defaultKernel: languages[0],
|
||||
kernels: languages.map<azdata.nb.IKernelSpec>(language => {
|
||||
return {
|
||||
name: language,
|
||||
language: language,
|
||||
display_name: language
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
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: azdata.nb.ISession = new VSCodeSession(this._controller, options, this.specs.defaultKernel);
|
||||
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
|
||||
}
|
||||
}
|
||||
150
src/sql/workbench/api/common/vscodeSerializationProvider.ts
Normal file
150
src/sql/workbench/api/common/vscodeSerializationProvider.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { NotebookCellKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { OutputTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { NBFORMAT, NBFORMAT_MINOR } from 'sql/workbench/common/constants';
|
||||
|
||||
/**
|
||||
* A Notebook Content Manager that is used as part of converting VS Code notebook extension APIs into ADS equivalents.
|
||||
*/
|
||||
export class VSCodeContentManager implements azdata.nb.ContentManager {
|
||||
constructor(private readonly _serializer: vscode.NotebookSerializer) {
|
||||
}
|
||||
|
||||
public static convertToADSCellOutput(output: vscode.NotebookCellOutput, executionOrder?: number): azdata.nb.IExecuteResult {
|
||||
let outputData = {};
|
||||
for (let item of output.items) {
|
||||
outputData[item.mime] = VSBuffer.wrap(item.data).toString();
|
||||
}
|
||||
return {
|
||||
output_type: 'execute_result',
|
||||
data: outputData,
|
||||
execution_count: executionOrder,
|
||||
metadata: output.metadata,
|
||||
id: output.id
|
||||
};
|
||||
}
|
||||
|
||||
public async deserializeNotebook(contents: string): Promise<azdata.nb.INotebookContents> {
|
||||
let buffer = VSBuffer.fromString(contents);
|
||||
let notebookData = await this._serializer.deserializeNotebook(buffer.buffer, new CancellationTokenSource().token);
|
||||
let result = {
|
||||
cells: notebookData.cells?.map<azdata.nb.ICellContents>(cell => {
|
||||
let executionOrder = cell.executionSummary?.executionOrder;
|
||||
return {
|
||||
cell_type: cell.kind === NotebookCellKind.Code ? 'code' : 'markdown',
|
||||
source: cell.value,
|
||||
metadata: {
|
||||
language: cell.languageId
|
||||
},
|
||||
execution_count: executionOrder,
|
||||
outputs: cell.outputs?.map<azdata.nb.IExecuteResult>(output => VSCodeContentManager.convertToADSCellOutput(output, executionOrder))
|
||||
};
|
||||
}),
|
||||
metadata: notebookData.metadata ?? {},
|
||||
nbformat: notebookData.metadata?.custom?.nbformat ?? NBFORMAT,
|
||||
nbformat_minor: notebookData.metadata?.custom?.nbformat_minor ?? NBFORMAT_MINOR
|
||||
};
|
||||
|
||||
// Clear out extra lingering vscode custom metadata
|
||||
delete result.metadata.custom;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static 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: 'text/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: 'text/html',
|
||||
data: VSBuffer.fromString(errorString).buffer
|
||||
}];
|
||||
break;
|
||||
}
|
||||
return {
|
||||
items: convertedOutputItems,
|
||||
metadata: output.metadata,
|
||||
id: output.id
|
||||
};
|
||||
}
|
||||
|
||||
public async serializeNotebook(notebook: azdata.nb.INotebookContents): Promise<string> {
|
||||
let notebookData: vscode.NotebookData = {
|
||||
cells: notebook.cells?.map<vscode.NotebookCellData>(cell => {
|
||||
return {
|
||||
kind: cell.cell_type === 'code' ? NotebookCellKind.Code : NotebookCellKind.Markup,
|
||||
value: Array.isArray(cell.source) ? cell.source.join('\n') : cell.source,
|
||||
languageId: cell.metadata?.language,
|
||||
outputs: cell.outputs?.map<vscode.NotebookCellOutput>(output => VSCodeContentManager.convertToVSCodeCellOutput(output)),
|
||||
executionSummary: {
|
||||
executionOrder: cell.execution_count
|
||||
}
|
||||
};
|
||||
}),
|
||||
metadata: notebook.metadata
|
||||
};
|
||||
notebookData.metadata.custom = {
|
||||
nbformat: notebook.nbformat,
|
||||
nbformat_minor: notebook.nbformat_minor
|
||||
};
|
||||
|
||||
let bytes = await this._serializer.serializeNotebook(notebookData, new CancellationTokenSource().token);
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user