/*--------------------------------------------------------------------------------------------- * 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'; import { URI } from 'vs/base/common/uri'; class VSCodeFuture implements azdata.nb.IFuture { private _inProgress = true; constructor(private readonly _executeCompletion: Promise) { } 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 { return this._executeCompletion.then(() => { return undefined; }).finally(() => { this._inProgress = false; }); } setReplyHandler(handler: azdata.nb.MessageHandler): void { // No-op } setStdInHandler(handler: azdata.nb.MessageHandler): void { // No-op } setIOPubHandler(handler: azdata.nb.MessageHandler): void { // No-op } registerMessageHook(hook: (msg: azdata.nb.IIOPubMessage) => boolean | Thenable): void { // No-op } removeMessageHook(hook: (msg: azdata.nb.IIOPubMessage) => boolean | Thenable): 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 { return Promise.resolve(); } public get info(): azdata.nb.IInfoReply | null { return this._info; } getSpec(): Thenable { return Promise.resolve(this._kernelSpec); } requestExecute(content: azdata.nb.IExecuteRequest, disposeOnDone?: boolean): azdata.nb.IFuture { let executePromise: Promise; if (this._controller.executeHandler) { let cell = convertToVSCodeNotebookCell(content.code, content.cellIndex, content.notebookUri, this._kernelSpec.language); 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 { let response: Partial = {}; return Promise.resolve(response as azdata.nb.ICompleteReplyMsg); } public async interrupt(): Promise { 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 { return Promise.resolve(this._kernel); } configureKernel(kernelInfo: azdata.nb.IKernelSpec): Thenable { return Promise.resolve(); } configureConnection(connection: azdata.IConnectionProfile): Thenable { 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 { 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(language => { return { name: language, language: language, display_name: language }; }) }; } public async startNew(options: azdata.nb.ISessionOptions): Promise { 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 { let index = this._sessions.findIndex(session => session.id === id); if (index > -1) { this._sessions.splice(index); } return Promise.resolve(); } public shutdownAll(): Thenable { 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 { return Promise.resolve(this._executeManager); } public handleNotebookClosed(notebookUri: vscode.Uri): void { // No-op } } export function convertToVSCodeNotebookCell(cellSource: string | string[], index: number, uri: URI, language: string): vscode.NotebookCell { return { index: index, document: { uri: uri, languageId: language, getText: () => Array.isArray(cellSource) ? cellSource.join('') : cellSource, }, notebook: { uri: uri } }; }