mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 01:32:34 -05:00
Split up NotebookProvider into separate providers for handling file serialization and cell execution. (#17176)
This commit is contained in:
@@ -13,11 +13,10 @@ import { IStandardKernelWithProvider, getProvidersForFileName, getStandardKernel
|
||||
import { INotebookService, DEFAULT_NOTEBOOK_PROVIDER, IProviderInfo } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { INotebookModel, IContentManager, NotebookContentChange } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookModel, IContentLoader, NotebookContentChange } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ITextFileSaveOptions, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -37,6 +36,7 @@ import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/no
|
||||
import { INotebookInput } from 'sql/workbench/services/notebook/browser/interface';
|
||||
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
|
||||
|
||||
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
|
||||
|
||||
@@ -61,8 +61,8 @@ export class NotebookEditorModel extends EditorModel {
|
||||
notebook.modelReady.then((model) => {
|
||||
if (!this._changeEventsHookedUp) {
|
||||
this._changeEventsHookedUp = true;
|
||||
this._register(model.kernelChanged(e => this.updateModel(undefined, NotebookChangeType.KernelChanged)));
|
||||
this._register(model.contentChanged(e => this.updateModel(e, e.changeType)));
|
||||
this._register(model.kernelChanged(e => this.updateModel(undefined, NotebookChangeType.KernelChanged).catch(e => onUnexpectedError(e))));
|
||||
this._register(model.contentChanged(e => this.updateModel(e, e.changeType).catch(e => onUnexpectedError(e))));
|
||||
this._register(notebook.model.onActiveCellChanged((cell) => {
|
||||
if (cell) {
|
||||
this._notebookTextFileModel.activeCellGuid = cell.cellGuid;
|
||||
@@ -120,7 +120,7 @@ export class NotebookEditorModel extends EditorModel {
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
public updateModel(contentChange?: NotebookContentChange, type?: NotebookChangeType): void {
|
||||
public async updateModel(contentChange?: NotebookContentChange, type?: NotebookChangeType): Promise<void> {
|
||||
// If text editor model is readonly, exit early as no changes need to occur on the model
|
||||
// Note: this follows what happens in fileCommands where update/save logic is skipped for readonly text editor models
|
||||
if (this.textEditorModel?.isReadonly()) {
|
||||
@@ -171,14 +171,14 @@ export class NotebookEditorModel extends EditorModel {
|
||||
if (editAppliedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
this.replaceEntireTextEditorModel(notebookModel, type);
|
||||
await this.replaceEntireTextEditorModel(notebookModel, type);
|
||||
this._lastEditFullReplacement = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public replaceEntireTextEditorModel(notebookModel: INotebookModel, type: NotebookChangeType) {
|
||||
this._notebookTextFileModel.replaceEntireTextEditorModel(notebookModel, type, this.textEditorModel);
|
||||
public replaceEntireTextEditorModel(notebookModel: INotebookModel, type: NotebookChangeType): Promise<void> {
|
||||
return this._notebookTextFileModel.replaceEntireTextEditorModel(notebookModel, type, this.textEditorModel);
|
||||
}
|
||||
|
||||
private sendNotebookSerializationStateChange(): void {
|
||||
@@ -224,7 +224,7 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
private readonly _layoutChanged: Emitter<void> = this._register(new Emitter<void>());
|
||||
private _model: NotebookEditorModel;
|
||||
private _untitledEditorModel: IUntitledTextEditorModel;
|
||||
private _contentManager: IContentManager;
|
||||
private _contentLoader: IContentLoader;
|
||||
private _providersLoaded: Promise<void>;
|
||||
private _dirtyListener: IDisposable;
|
||||
private _notebookEditorOpenedTimestamp: number;
|
||||
@@ -274,11 +274,12 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
return this._notebookFindModel;
|
||||
}
|
||||
|
||||
public get contentManager(): IContentManager {
|
||||
if (!this._contentManager) {
|
||||
this._contentManager = this.instantiationService.createInstance(NotebookEditorContentManager, this);
|
||||
public get contentLoader(): IContentLoader {
|
||||
if (!this._contentLoader) {
|
||||
let contentManager = this.instantiationService.createInstance(LocalContentManager);
|
||||
this._contentLoader = this.instantiationService.createInstance(NotebookEditorContentLoader, this, contentManager);
|
||||
}
|
||||
return this._contentManager;
|
||||
return this._contentLoader;
|
||||
}
|
||||
|
||||
public override getName(): string {
|
||||
@@ -292,6 +293,10 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
return EditorInputCapabilities.None;
|
||||
}
|
||||
|
||||
public get providersLoaded(): Promise<void> {
|
||||
return this._providersLoaded;
|
||||
}
|
||||
|
||||
public async getProviderInfo(): Promise<IProviderInfo> {
|
||||
await this._providersLoaded;
|
||||
return {
|
||||
@@ -313,14 +318,14 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
}
|
||||
|
||||
override async save(groupId: number, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
|
||||
this.updateModel();
|
||||
await this.updateModel();
|
||||
let input = await this.textInput.save(groupId, options);
|
||||
await this.setTrustForNewEditor(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
override async saveAs(group: number, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
|
||||
this.updateModel();
|
||||
await this.updateModel();
|
||||
let input = await this.textInput.saveAs(group, options);
|
||||
await this.setTrustForNewEditor(input);
|
||||
return input;
|
||||
@@ -438,6 +443,8 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
let standardKernels = getStandardKernelsForProvider(provider, this.notebookService);
|
||||
this._standardKernels.push(...standardKernels);
|
||||
});
|
||||
let serializationProvider = await this.notebookService.getOrCreateSerializationManager(this._providerId, this._resource);
|
||||
this._contentLoader = this.instantiationService.createInstance(NotebookEditorContentLoader, this, serializationProvider.contentManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,8 +504,8 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
}
|
||||
}
|
||||
|
||||
updateModel(): void {
|
||||
this._model.updateModel();
|
||||
public updateModel(): Promise<void> {
|
||||
return this._model.updateModel();
|
||||
}
|
||||
|
||||
public override matches(otherInput: any): boolean {
|
||||
@@ -510,17 +517,14 @@ export abstract class NotebookInput extends EditorInput implements INotebookInpu
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookEditorContentManager implements IContentManager {
|
||||
export class NotebookEditorContentLoader implements IContentLoader {
|
||||
constructor(
|
||||
private notebookInput: NotebookInput,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService) {
|
||||
private contentManager: azdata.nb.ContentManager) {
|
||||
}
|
||||
|
||||
async loadContent(): Promise<azdata.nb.INotebookContents> {
|
||||
let notebookEditorModel = await this.notebookInput.resolve();
|
||||
let contentManager = this.instantiationService.createInstance(LocalContentManager);
|
||||
let contents = await contentManager.loadFromContentString(notebookEditorModel.contentString);
|
||||
return contents;
|
||||
return this.contentManager.deserializeNotebook(notebookEditorModel.contentString);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -200,8 +200,15 @@ export class NotebookTextFileModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
public replaceEntireTextEditorModel(notebookModel: INotebookModel, type: NotebookChangeType, textEditorModel: ITextEditorModel) {
|
||||
let content = JSON.stringify(notebookModel.toJSON(type), undefined, ' ');
|
||||
public async replaceEntireTextEditorModel(notebookModel: INotebookModel, type: NotebookChangeType, textEditorModel: ITextEditorModel): Promise<void> {
|
||||
let content: string;
|
||||
let notebookContents = notebookModel.toJSON(type);
|
||||
let serializer = notebookModel.serializationManager;
|
||||
if (serializer) {
|
||||
content = await serializer.contentManager.serializeNotebook(notebookContents);
|
||||
} else {
|
||||
content = JSON.stringify(notebookContents, undefined, ' ');
|
||||
}
|
||||
let model = textEditorModel.textEditorModel;
|
||||
let endLine = model.getLineCount();
|
||||
let endCol = model.getLineMaxColumn(endLine);
|
||||
|
||||
@@ -53,7 +53,7 @@ import { ImageMimeTypes, TextCellEditModes } from 'sql/workbench/services/notebo
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookManager } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IExecuteManager } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { NotebookExplorerViewletViewsContribution } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ContributedEditorPriority, IEditorOverrideService } from 'vs/workbench/services/editor/common/editorOverrideService';
|
||||
@@ -62,6 +62,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { useNewMarkdownRendererKey } from 'sql/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { JUPYTER_PROVIDER_ID } from 'sql/workbench/common/constants';
|
||||
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories)
|
||||
.registerEditorInputSerializer(FileNotebookInput.ID, FileNoteBookEditorInputSerializer);
|
||||
@@ -189,10 +190,10 @@ CommandsRegistry.registerCommand({
|
||||
for (let editor of editors) {
|
||||
if (editor instanceof NotebookInput) {
|
||||
let model: INotebookModel = editor.notebookModel;
|
||||
if (model.providerId === 'jupyter' && model.clientSession.isReady) {
|
||||
if (model.providerId === JUPYTER_PROVIDER_ID && model.clientSession.isReady) {
|
||||
// Jupyter server needs to be restarted so that the correct Python installation is used
|
||||
if (!jupyterServerRestarted && restartJupyterServer) {
|
||||
let jupyterNotebookManager: INotebookManager = model.notebookManagers.find(x => x.providerId === 'jupyter');
|
||||
let jupyterNotebookManager: IExecuteManager = model.executeManagers.find(x => x.providerId === JUPYTER_PROVIDER_ID);
|
||||
// Shutdown all current Jupyter sessions before stopping the server
|
||||
await jupyterNotebookManager.sessionManager.shutdownAll();
|
||||
// Jupyter session manager needs to be disposed so that a new one is created with the new server info
|
||||
@@ -222,8 +223,8 @@ CommandsRegistry.registerCommand({
|
||||
for (let editor of editors) {
|
||||
if (editor instanceof NotebookInput) {
|
||||
let model: INotebookModel = editor.notebookModel;
|
||||
if (model?.providerId === 'jupyter') {
|
||||
let jupyterNotebookManager: INotebookManager = model.notebookManagers.find(x => x.providerId === 'jupyter');
|
||||
if (model?.providerId === JUPYTER_PROVIDER_ID) {
|
||||
let jupyterNotebookManager: IExecuteManager = model.executeManagers.find(x => x.providerId === JUPYTER_PROVIDER_ID);
|
||||
await jupyterNotebookManager.sessionManager.shutdownAll();
|
||||
jupyterNotebookManager.sessionManager.dispose();
|
||||
await jupyterNotebookManager.serverManager.stopServer();
|
||||
|
||||
@@ -7,7 +7,7 @@ import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/no
|
||||
import * as notebookUtils from 'sql/workbench/services/notebook/browser/models/notebookUtils';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
||||
import { INotebookParams, INotebookService, INotebookManager, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { INotebookParams, INotebookService, IExecuteManager, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER, ISerializationManager } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { CellMagicMapper } from 'sql/workbench/contrib/notebook/browser/models/cellMagicMapper';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -38,7 +38,8 @@ export const NOTEBOOKEDITOR_SELECTOR: string = 'notebookeditor-component';
|
||||
export class NotebookEditorComponent extends AngularDisposable {
|
||||
private readonly defaultViewMode = ViewMode.Notebook;
|
||||
private profile: IConnectionProfile;
|
||||
private notebookManagers: INotebookManager[] = [];
|
||||
private serializationManagers: ISerializationManager[] = [];
|
||||
private executeManagers: IExecuteManager[] = [];
|
||||
private _modelReadyDeferred = new Deferred<NotebookModel>();
|
||||
|
||||
public model: NotebookModel;
|
||||
@@ -80,7 +81,8 @@ export class NotebookEditorComponent extends AngularDisposable {
|
||||
|
||||
private async doLoad(): Promise<void> {
|
||||
await this.createModelAndLoadContents();
|
||||
await this.setNotebookManager();
|
||||
await this.setSerializationManager();
|
||||
await this.setExecuteManager();
|
||||
await this.loadModel();
|
||||
|
||||
this.setActiveView();
|
||||
@@ -93,21 +95,23 @@ export class NotebookEditorComponent extends AngularDisposable {
|
||||
await this.model.requestModelLoad();
|
||||
this.detectChanges();
|
||||
this.setContextKeyServiceWithProviderId(this.model.providerId);
|
||||
await this.model.startSession(this.model.notebookManager, undefined, true);
|
||||
await this.model.startSession(this.model.executeManager, undefined, true);
|
||||
this.fillInActionsForCurrentContext();
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
private async createModelAndLoadContents(): Promise<void> {
|
||||
let providerInfo = await this._notebookParams.providerInfo;
|
||||
let model = new NotebookModel({
|
||||
factory: this.modelFactory,
|
||||
notebookUri: this._notebookParams.notebookUri,
|
||||
connectionService: this.connectionManagementService,
|
||||
notificationService: this.notificationService,
|
||||
notebookManagers: this.notebookManagers,
|
||||
contentManager: this._notebookParams.input.contentManager,
|
||||
serializationManagers: this.serializationManagers,
|
||||
executeManagers: this.executeManagers,
|
||||
contentLoader: this._notebookParams.input.contentLoader,
|
||||
cellMagicMapper: new CellMagicMapper(this.notebookService.languageMagics),
|
||||
providerId: 'sql',
|
||||
providerId: providerInfo.providerId,
|
||||
defaultKernel: this._notebookParams.input.defaultKernel,
|
||||
layoutChanged: this._notebookParams.input.layoutChanged,
|
||||
capabilitiesService: this.capabilitiesService,
|
||||
@@ -132,11 +136,19 @@ export class NotebookEditorComponent extends AngularDisposable {
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
private async setNotebookManager(): Promise<void> {
|
||||
private async setSerializationManager(): Promise<void> {
|
||||
let providerInfo = await this._notebookParams.providerInfo;
|
||||
for (let providerId of providerInfo.providers) {
|
||||
let notebookManager = await this.notebookService.getOrCreateNotebookManager(providerId, this._notebookParams.notebookUri);
|
||||
this.notebookManagers.push(notebookManager);
|
||||
let manager = await this.notebookService.getOrCreateSerializationManager(providerId, this._notebookParams.notebookUri);
|
||||
this.serializationManagers.push(manager);
|
||||
}
|
||||
}
|
||||
|
||||
private async setExecuteManager(): Promise<void> {
|
||||
let providerInfo = await this._notebookParams.providerInfo;
|
||||
for (let providerId of providerInfo.providers) {
|
||||
let manager = await this.notebookService.getOrCreateExecuteManager(providerId, this._notebookParams.notebookUri);
|
||||
this.executeManagers.push(manager);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user