Support notebook file types contribution (#3196)

* Support notebook file types contribution
- Extensions can define a provider and what file types it should be used for
- Verified that this works for Jupyter Content & Server Managers.
- Starts Jupyter server as expected

Not in this PR:
- Support for session manager end to end
- Tests
This commit is contained in:
Kevin Cunnane
2018-11-12 17:32:53 -08:00
committed by GitHub
parent 0a486a280d
commit 0b571737b7
6 changed files with 198 additions and 19 deletions

View File

@@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { localize } from 'vs/nls';
import * as platform from 'vs/platform/registry/common/platform';
export const Extensions = {
NotebookProviderContribution: 'notebook.providers'
};
export interface NotebookProviderDescription {
provider: string;
fileExtensions: string | string[];
}
let notebookProviderType: IJSONSchema = {
type: 'object',
default: { provider: '', fileExtensions: [] },
properties: {
provider: {
description: localize('carbon.extension.contributes.notebook.provider', 'Identifier of the notebook provider.'),
type: 'string'
},
fileExtensions: {
description: localize('carbon.extension.contributes.notebook.fileExtensions', 'What file extensions should be registered to this notebook provider'),
oneOf: [
{ type: 'string' },
{
type: 'array',
items: {
type: 'string'
}
}
]
}
}
};
let notebookContrib: IJSONSchema = {
description: localize('vscode.extension.contributes.notebook.providers', "Contributes notebook providers."),
oneOf: [
notebookProviderType,
{
type: 'array',
items: notebookProviderType
}
]
};
export interface INotebookProviderRegistry {
registerNotebookProvider(provider: NotebookProviderDescription): void;
getSupportedFileExtensions(): string[];
getProviderForFileType(fileType: string): string;
}
class NotebookProviderRegistry implements INotebookProviderRegistry {
private providerIdToProviders = new Map<string, NotebookProviderDescription>();
private fileToProviders = new Map<string, NotebookProviderDescription>();
registerNotebookProvider(provider: NotebookProviderDescription): 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.providerIdToProviders.set(provider.provider, provider);
if (provider.fileExtensions) {
if (Array.isArray<string>(provider.fileExtensions)) {
for (let fileType of provider.fileExtensions) {
this.addFileProvider(fileType, provider);
}
} else {
this.addFileProvider(provider.fileExtensions, provider);
}
}
}
private addFileProvider(fileType: string, provider: NotebookProviderDescription) {
this.fileToProviders.set(fileType.toUpperCase(), provider);
}
getSupportedFileExtensions(): string[] {
return Array.from(this.fileToProviders.keys());
}
getProviderForFileType(fileType: string): string {
fileType = fileType.toUpperCase();
let provider = this.fileToProviders.get(fileType);
return provider ? provider.provider : undefined;
}
}
const notebookProviderRegistry = new NotebookProviderRegistry();
platform.Registry.add(Extensions.NotebookProviderContribution, notebookProviderRegistry);
ExtensionsRegistry.registerExtensionPoint<NotebookProviderDescription | NotebookProviderDescription[]>(Extensions.NotebookProviderContribution, [], notebookContrib).setHandler(extensions => {
function handleExtension(contrib: NotebookProviderDescription, extension: IExtensionPointUser<any>) {
notebookProviderRegistry.registerNotebookProvider(contrib);
}
for (let extension of extensions) {
const { value } = extension;
if (Array.isArray<NotebookProviderDescription>(value)) {
for (let command of value) {
handleExtension(command, extension);
}
} else {
handleExtension(value, extension);
}
}
});

View File

@@ -40,6 +40,8 @@ export interface INotebookService {
*/
getOrCreateNotebookManager(providerId: string, uri: URI): Thenable<INotebookManager>;
handleNotebookClosed(uri: URI): void;
shutdown(): void;
getMimeRegistry(): RenderMimeRegistry;

View File

@@ -6,26 +6,37 @@
'use strict';
import { nb } from 'sqlops';
import * as nls from 'vs/nls';
import { INotebookService, INotebookManager, INotebookProvider, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/services/notebook/notebookService';
import { localize } from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Registry } from 'vs/platform/registry/common/platform';
import { INotebookService, INotebookManager, INotebookProvider, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/services/notebook/notebookService';
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
import { standardRendererFactories } from 'sql/parts/notebook/outputs/factories';
import { LocalContentManager } from 'sql/services/notebook/localContentManager';
import { session } from 'electron';
import { SessionManager } from 'sql/services/notebook/sessionManager';
import { Extensions, INotebookProviderRegistry } from 'sql/services/notebook/notebookRegistry';
const DEFAULT_NOTEBOOK_FILETYPE = 'IPYNB';
export class NotebookService implements INotebookService {
_serviceBrand: any;
private _mimeRegistry: RenderMimeRegistry;
private _providers: Map<string, INotebookProvider> = new Map();
private _managers: Map<URI, INotebookManager> = new Map();
private _managers: Map<string, INotebookManager> = new Map();
constructor() {
mimeRegistry: RenderMimeRegistry;
this.registerDefaultProvider();
}
private registerDefaultProvider() {
let defaultProvider = new BuiltinProvider();
this.registerProvider(defaultProvider.providerId, defaultProvider);
let registry = Registry.as<INotebookProviderRegistry>(Extensions.NotebookProviderContribution);
registry.registerNotebookProvider({
provider: defaultProvider.providerId,
fileExtensions: DEFAULT_NOTEBOOK_FILETYPE
});
}
registerProvider(providerId: string, provider: INotebookProvider): void {
@@ -47,24 +58,37 @@ export class NotebookService implements INotebookService {
async getOrCreateNotebookManager(providerId: string, uri: URI): Promise<INotebookManager> {
if (!uri) {
throw new Error(nls.localize('notebookUriNotDefined', 'No URI was passed when creating a notebook manager'));
throw new Error(localize('notebookUriNotDefined', 'No URI was passed when creating a notebook manager'));
}
let manager = this._managers.get(uri);
let uriString = uri.toString();
let manager = this._managers.get(uriString);
if (!manager) {
manager = await this.doWithProvider(providerId, (provider) => provider.getNotebookManager(uri));
if (manager) {
this._managers.set(uri, manager);
this._managers.set(uriString, manager);
}
}
return manager;
}
handleNotebookClosed(notebookUri: URI): void {
// Remove the manager from the tracked list, and let the notebook provider know that it should update its mappings
let uriString = notebookUri.toString();
let manager = this._managers.get(uriString);
if (manager) {
this._managers.delete(uriString);
let provider = this._providers.get(manager.providerId);
provider.handleNotebookClosed(notebookUri);
}
}
// PRIVATE HELPERS /////////////////////////////////////////////////////
private doWithProvider<T>(providerId: string, op: (provider: INotebookProvider) => Thenable<T>): Thenable<T> {
// Make sure the provider exists before attempting to retrieve accounts
let provider = this._providers.get(providerId);
if (!provider) {
return Promise.reject(new Error(nls.localize('notebookServiceNoProvider', 'Notebook provider does not exist'))).then();
return Promise.reject(new Error(localize('notebookServiceNoProvider', 'Notebook provider does not exist'))).then();
}
return op(provider);