Split up NotebookProvider into separate providers for handling file serialization and cell execution. (#17176)

This commit is contained in:
Cory Rivera
2021-09-29 16:15:28 -07:00
committed by GitHub
parent dfc2635aa7
commit 14904bb671
51 changed files with 1426 additions and 971 deletions

View File

@@ -8,81 +8,45 @@
import { nb } from 'azdata';
import * as json from 'vs/base/common/json';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { IFileService } from 'vs/platform/files/common/files';
import { JSONObject } from 'sql/workbench/services/notebook/common/jsonext';
import { OutputTypes } from 'sql/workbench/services/notebook/common/contracts';
import { nbversion } from 'sql/workbench/services/notebook/common/notebookConstants';
import { nbformat } from 'sql/workbench/services/notebook/common/nbformat';
import { VSBuffer } from 'vs/base/common/buffer';
type MimeBundle = { [key: string]: string | string[] | undefined };
export class LocalContentManager implements nb.ContentManager {
constructor(@IFileService private readonly fileService: IFileService) { }
constructor() { }
public async loadFromContentString(contentString: string): Promise<nb.INotebookContents> {
let contents: JSONObject;
if (contentString === '' || contentString === undefined) {
public async deserializeNotebook(contents: string): Promise<nb.INotebookContents> {
let jsonContents: JSONObject;
if (contents === '' || contents === undefined) {
return v4.createEmptyNotebook();
} else {
contents = this.parseFromJson(contentString);
jsonContents = this.parseFromJson(contents);
}
if (contents) {
if (contents.nbformat === 4) {
return v4.readNotebook(<any>contents);
} else if (contents.nbformat === 3) {
return v3.readNotebook(<any>contents);
if (jsonContents) {
if (jsonContents.nbformat === 4) {
return v4.readNotebook(<any>jsonContents);
} else if (jsonContents.nbformat === 3) {
return v3.readNotebook(<any>jsonContents);
}
if (contents.nbformat) {
throw new TypeError(localize('nbformatNotRecognized', "nbformat v{0}.{1} not recognized", contents.nbformat as any, contents.nbformat_minor as any));
if (jsonContents.nbformat) {
throw new TypeError(localize('nbformatNotRecognized', "nbformat v{0}.{1} not recognized", jsonContents.nbformat as any, jsonContents.nbformat_minor as any));
}
}
// else, fallthrough condition
throw new TypeError(localize('nbNotSupported', "This file does not have a valid notebook format"));
}
public async getNotebookContents(notebookUri: URI): Promise<nb.INotebookContents> {
if (!notebookUri) {
return undefined;
}
// Note: intentionally letting caller handle exceptions
let notebookFileBuffer = await this.fileService.readFile(notebookUri);
let stringContents = notebookFileBuffer.value.toString();
let contents: JSONObject;
if (stringContents === '' || stringContents === undefined) {
// Empty?
return v4.createEmptyNotebook();
} else {
contents = this.parseFromJson(stringContents);
}
if (contents) {
if (contents.nbformat === 4) {
return v4.readNotebook(<any>contents);
} else if (contents.nbformat === 3) {
return v3.readNotebook(<any>contents);
}
if (contents.nbformat) {
throw new TypeError(localize('nbformatNotRecognized', "nbformat v{0}.{1} not recognized", contents.nbformat as any, contents.nbformat_minor as any));
}
}
// else, fallthrough condition
throw new TypeError(localize('nbNotSupported', "This file does not have a valid notebook format"));
}
public async save(notebookUri: URI, notebook: nb.INotebookContents): Promise<nb.INotebookContents> {
public async serializeNotebook(notebook: nb.INotebookContents): Promise<string> {
// Convert to JSON with pretty-print functionality
let contents = JSON.stringify(notebook, undefined, ' ');
await this.fileService.writeFile(notebookUri, VSBuffer.fromString(contents));
return notebook;
return contents;
}
private parseFromJson(contentString: string): JSONObject {

View File

@@ -4,24 +4,26 @@
*--------------------------------------------------------------------------------------------*/
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
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';
export const NotebookProviderRegistryId = 'notebooks.providers.registry';
export const Extensions = {
NotebookProviderContribution: 'notebook.providers',
NotebookProviderDescriptionContribution: 'notebook.providers',
NotebookLanguageMagicContribution: 'notebook.languagemagics'
};
export interface NotebookProviderRegistration {
export interface ProviderDescriptionRegistration {
provider: string;
fileExtensions: string | string[];
standardKernels: azdata.nb.IStandardKernel | azdata.nb.IStandardKernel[];
}
let notebookProviderType: IJSONSchema = {
let providerDescriptionType: IJSONSchema = {
type: 'object',
default: { provider: '', fileExtensions: [], standardKernels: [] },
properties: {
@@ -86,13 +88,13 @@ let notebookProviderType: IJSONSchema = {
}
};
let notebookContrib: IJSONSchema = {
description: localize('vscode.extension.contributes.notebook.providers', "Contributes notebook providers."),
let providerDescriptionContrib: IJSONSchema = {
description: localize('vscode.extension.contributes.notebook.providersDescriptions', "Contributes notebook provider descriptions."),
oneOf: [
notebookProviderType,
providerDescriptionType,
{
type: 'array',
items: notebookProviderType
items: providerDescriptionType
}
]
};
@@ -146,81 +148,69 @@ export interface NotebookLanguageMagicRegistration {
}
export interface INotebookProviderRegistry {
readonly providers: NotebookProviderRegistration[];
readonly providerDescriptions: ProviderDescriptionRegistration[];
readonly languageMagics: NotebookLanguageMagicRegistration[];
readonly onNewRegistration: Event<{ id: string, registration: NotebookProviderRegistration }>;
registerNotebookProvider(provider: NotebookProviderRegistration): void;
readonly onNewDescriptionRegistration: Event<{ id: string, registration: ProviderDescriptionRegistration }>;
registerProviderDescription(provider: ProviderDescriptionRegistration): void;
registerNotebookLanguageMagic(magic: NotebookLanguageMagicRegistration): void;
}
class NotebookProviderRegistry implements INotebookProviderRegistry {
private providerIdToRegistration = new Map<string, NotebookProviderRegistration>();
private magicToRegistration = new Map<string, NotebookLanguageMagicRegistration>();
private _onNewRegistration = new Emitter<{ id: string, registration: NotebookProviderRegistration }>();
public readonly onNewRegistration: Event<{ id: string, registration: NotebookProviderRegistration }> = this._onNewRegistration.event;
private _providerDescriptionRegistration = new Map<string, ProviderDescriptionRegistration>();
private _magicToRegistration = new Map<string, NotebookLanguageMagicRegistration>();
registerNotebookProvider(registration: NotebookProviderRegistration): void {
// Note: this method intentionally overrides default provider for a file type.
// This means that any built-in provider will be overridden by registered extensions
this.providerIdToRegistration.set(registration.provider, registration);
this._onNewRegistration.fire({ id: registration.provider, registration: registration });
private _onNewDescriptionRegistration = new Emitter<{ id: string, registration: ProviderDescriptionRegistration }>();
public readonly onNewDescriptionRegistration: Event<{ id: string, registration: ProviderDescriptionRegistration }> = this._onNewDescriptionRegistration.event;
registerProviderDescription(registration: ProviderDescriptionRegistration): void {
this._providerDescriptionRegistration.set(registration.provider, registration);
this._onNewDescriptionRegistration.fire({ id: registration.provider, registration: registration });
}
public get providers(): NotebookProviderRegistration[] {
let registrationArray: NotebookProviderRegistration[] = [];
this.providerIdToRegistration.forEach(p => registrationArray.push(p));
public get providerDescriptions(): ProviderDescriptionRegistration[] {
let registrationArray: ProviderDescriptionRegistration[] = [];
this._providerDescriptionRegistration.forEach(p => registrationArray.push(p));
return registrationArray;
}
registerNotebookLanguageMagic(magicRegistration: NotebookLanguageMagicRegistration): void {
this.magicToRegistration.set(magicRegistration.magic, magicRegistration);
this._magicToRegistration.set(magicRegistration.magic, magicRegistration);
}
public get languageMagics(): NotebookLanguageMagicRegistration[] {
let registrationArray: NotebookLanguageMagicRegistration[] = [];
this.magicToRegistration.forEach(p => registrationArray.push(p));
this._magicToRegistration.forEach(p => registrationArray.push(p));
return registrationArray;
}
}
const notebookProviderRegistry = new NotebookProviderRegistry();
platform.Registry.add(Extensions.NotebookProviderContribution, notebookProviderRegistry);
ExtensionsRegistry.registerExtensionPoint<NotebookProviderRegistration | NotebookProviderRegistration[]>({ extensionPoint: Extensions.NotebookProviderContribution, jsonSchema: notebookContrib }).setHandler(extensions => {
function handleExtension(contrib: NotebookProviderRegistration, extension: IExtensionPointUser<any>) {
notebookProviderRegistry.registerNotebookProvider(contrib);
}
platform.Registry.add(NotebookProviderRegistryId, notebookProviderRegistry);
ExtensionsRegistry.registerExtensionPoint<ProviderDescriptionRegistration | ProviderDescriptionRegistration[]>({ extensionPoint: Extensions.NotebookProviderDescriptionContribution, jsonSchema: providerDescriptionContrib }).setHandler(extensions => {
for (let extension of extensions) {
const { value } = extension;
if (Array.isArray(value)) {
for (let command of value) {
handleExtension(command, extension);
notebookProviderRegistry.registerProviderDescription(command);
}
} else {
handleExtension(value, extension);
notebookProviderRegistry.registerProviderDescription(value);
}
}
});
ExtensionsRegistry.registerExtensionPoint<NotebookLanguageMagicRegistration | NotebookLanguageMagicRegistration[]>({ extensionPoint: Extensions.NotebookLanguageMagicContribution, jsonSchema: languageMagicContrib }).setHandler(extensions => {
function handleExtension(contrib: NotebookLanguageMagicRegistration, extension: IExtensionPointUser<any>) {
notebookProviderRegistry.registerNotebookLanguageMagic(contrib);
}
for (let extension of extensions) {
const { value } = extension;
if (Array.isArray(value)) {
for (let command of value) {
handleExtension(command, extension);
notebookProviderRegistry.registerNotebookLanguageMagic(command);
}
} else {
handleExtension(value, extension);
notebookProviderRegistry.registerNotebookLanguageMagic(value);
}
}
});